You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
5.6 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package dummy
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "strconv"
  6. "strings"
  7. "github.com/tendermint/abci/types"
  8. crypto "github.com/tendermint/go-crypto"
  9. "github.com/tendermint/iavl"
  10. cmn "github.com/tendermint/tmlibs/common"
  11. dbm "github.com/tendermint/tmlibs/db"
  12. "github.com/tendermint/tmlibs/log"
  13. )
  14. const (
  15. ValidatorSetChangePrefix string = "val:"
  16. )
  17. //-----------------------------------------
  18. type PersistentDummyApplication struct {
  19. app *DummyApplication
  20. // validator set
  21. changes []*types.Validator
  22. logger log.Logger
  23. }
  24. func NewPersistentDummyApplication(dbDir string) *PersistentDummyApplication {
  25. name := "dummy"
  26. db, err := dbm.NewGoLevelDB(name, dbDir)
  27. if err != nil {
  28. panic(err)
  29. }
  30. stateTree := iavl.NewVersionedTree(500, db)
  31. stateTree.Load()
  32. return &PersistentDummyApplication{
  33. app: &DummyApplication{state: stateTree},
  34. logger: log.NewNopLogger(),
  35. }
  36. }
  37. func (app *PersistentDummyApplication) SetLogger(l log.Logger) {
  38. app.logger = l
  39. }
  40. func (app *PersistentDummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
  41. resInfo = app.app.Info(req)
  42. resInfo.LastBlockHeight = app.app.state.LatestVersion()
  43. resInfo.LastBlockAppHash = app.app.state.Hash()
  44. return resInfo
  45. }
  46. func (app *PersistentDummyApplication) SetOption(key string, value string) (log string) {
  47. return app.app.SetOption(key, value)
  48. }
  49. // tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes
  50. func (app *PersistentDummyApplication) DeliverTx(tx []byte) types.Result {
  51. // if it starts with "val:", update the validator set
  52. // format is "val:pubkey/power"
  53. if isValidatorTx(tx) {
  54. // update validators in the merkle tree
  55. // and in app.changes
  56. return app.execValidatorTx(tx)
  57. }
  58. // otherwise, update the key-value store
  59. return app.app.DeliverTx(tx)
  60. }
  61. func (app *PersistentDummyApplication) CheckTx(tx []byte) types.Result {
  62. return app.app.CheckTx(tx)
  63. }
  64. // Commit will panic if InitChain was not called
  65. func (app *PersistentDummyApplication) Commit() types.Result {
  66. // Save a new version for next height
  67. height := app.app.state.LatestVersion() + 1
  68. var appHash []byte
  69. var err error
  70. appHash, err = app.app.state.SaveVersion(height)
  71. if err != nil {
  72. // if this wasn't a dummy app, we'd do something smarter
  73. panic(err)
  74. }
  75. app.logger.Info("Commit block", "height", height, "root", appHash)
  76. return types.NewResultOK(appHash, "")
  77. }
  78. func (app *PersistentDummyApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
  79. return app.app.Query(reqQuery)
  80. }
  81. // Save the validators in the merkle tree
  82. func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) {
  83. for _, v := range params.Validators {
  84. r := app.updateValidator(v)
  85. if r.IsErr() {
  86. app.logger.Error("Error updating validators", "r", r)
  87. }
  88. }
  89. }
  90. // Track the block hash and header information
  91. func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) {
  92. // reset valset changes
  93. app.changes = make([]*types.Validator, 0)
  94. }
  95. // Update the validator set
  96. func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
  97. return types.ResponseEndBlock{Diffs: app.changes}
  98. }
  99. //---------------------------------------------
  100. // update validators
  101. func (app *PersistentDummyApplication) Validators() (validators []*types.Validator) {
  102. app.app.state.Iterate(func(key, value []byte) bool {
  103. if isValidatorTx(key) {
  104. validator := new(types.Validator)
  105. err := types.ReadMessage(bytes.NewBuffer(value), validator)
  106. if err != nil {
  107. panic(err)
  108. }
  109. validators = append(validators, validator)
  110. }
  111. return false
  112. })
  113. return
  114. }
  115. func MakeValSetChangeTx(pubkey []byte, power uint64) []byte {
  116. return []byte(cmn.Fmt("val:%X/%d", pubkey, power))
  117. }
  118. func isValidatorTx(tx []byte) bool {
  119. return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
  120. }
  121. // format is "val:pubkey1/power1,addr2/power2,addr3/power3"tx
  122. func (app *PersistentDummyApplication) execValidatorTx(tx []byte) types.Result {
  123. tx = tx[len(ValidatorSetChangePrefix):]
  124. //get the pubkey and power
  125. pubKeyAndPower := strings.Split(string(tx), "/")
  126. if len(pubKeyAndPower) != 2 {
  127. return types.ErrEncodingError.SetLog(cmn.Fmt("Expected 'pubkey/power'. Got %v", pubKeyAndPower))
  128. }
  129. pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
  130. // decode the pubkey, ensuring its go-crypto encoded
  131. pubkey, err := hex.DecodeString(pubkeyS)
  132. if err != nil {
  133. return types.ErrEncodingError.SetLog(cmn.Fmt("Pubkey (%s) is invalid hex", pubkeyS))
  134. }
  135. _, err = crypto.PubKeyFromBytes(pubkey)
  136. if err != nil {
  137. return types.ErrEncodingError.SetLog(cmn.Fmt("Pubkey (%X) is invalid go-crypto encoded", pubkey))
  138. }
  139. // decode the power
  140. power, err := strconv.Atoi(powerS)
  141. if err != nil {
  142. return types.ErrEncodingError.SetLog(cmn.Fmt("Power (%s) is not an int", powerS))
  143. }
  144. // update
  145. return app.updateValidator(&types.Validator{pubkey, uint64(power)})
  146. }
  147. // add, update, or remove a validator
  148. func (app *PersistentDummyApplication) updateValidator(v *types.Validator) types.Result {
  149. key := []byte("val:" + string(v.PubKey))
  150. if v.Power == 0 {
  151. // remove validator
  152. if !app.app.state.Has(key) {
  153. return types.ErrUnauthorized.SetLog(cmn.Fmt("Cannot remove non-existent validator %X", key))
  154. }
  155. app.app.state.Remove(key)
  156. } else {
  157. // add or update validator
  158. value := bytes.NewBuffer(make([]byte, 0))
  159. if err := types.WriteMessage(v, value); err != nil {
  160. return types.ErrInternalError.SetLog(cmn.Fmt("Error encoding validator: %v", err))
  161. }
  162. app.app.state.Set(key, value.Bytes())
  163. }
  164. // we only update the changes array if we successfully updated the tree
  165. app.changes = append(app.changes, v)
  166. return types.OK
  167. }