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.

181 lines
4.9 KiB

  1. package state
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. . "github.com/tendermint/go-common"
  7. "github.com/tendermint/tendermint/proxy"
  8. "github.com/tendermint/tendermint/types"
  9. tmsp "github.com/tendermint/tmsp/types"
  10. )
  11. // Execute the block to mutate State.
  12. // Also, execute txs on the proxyAppCtx and validate apphash
  13. // Rolls back before executing transactions.
  14. // Rolls back if invalid, but never commits.
  15. func (s *State) ExecBlock(proxyAppCtx proxy.AppContext, block *types.Block, blockPartsHeader types.PartSetHeader) error {
  16. // Validate the block.
  17. err := s.validateBlock(block)
  18. if err != nil {
  19. return err
  20. }
  21. // Update the validator set
  22. valSet := s.Validators.Copy()
  23. // Update valSet with signatures from block.
  24. updateValidatorsWithBlock(s.LastValidators, valSet, block)
  25. // TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?)
  26. nextValSet := valSet.Copy()
  27. // First, rollback.
  28. if err != nil {
  29. proxyAppCtx.RollbackSync()
  30. return err
  31. }
  32. // Execute, or rollback. (Does not commit)
  33. err = s.execBlockOnProxyApp(proxyAppCtx, block)
  34. if err != nil {
  35. proxyAppCtx.RollbackSync()
  36. return err
  37. }
  38. // All good!
  39. nextValSet.IncrementAccum(1)
  40. s.Validators = nextValSet
  41. s.LastValidators = valSet
  42. s.LastAppHash = block.AppHash
  43. s.LastBlockHeight = block.Height
  44. s.LastBlockHash = block.Hash()
  45. s.LastBlockParts = blockPartsHeader
  46. s.LastBlockTime = block.Time
  47. return nil
  48. }
  49. // Commits block on proxyAppCtx.
  50. func (s *State) Commit(proxyAppCtx proxy.AppContext) error {
  51. err := proxyAppCtx.CommitSync()
  52. return err
  53. }
  54. // Executes transactions on proxyAppCtx.
  55. func (s *State) execBlockOnProxyApp(proxyAppCtx proxy.AppContext, block *types.Block) error {
  56. // Execute transactions and get hash
  57. var invalidTxErr error
  58. proxyCb := func(req tmsp.Request, res tmsp.Response) {
  59. switch res := res.(type) {
  60. case tmsp.ResponseAppendTx:
  61. reqAppendTx := req.(tmsp.RequestAppendTx)
  62. if res.RetCode != tmsp.RetCodeOK {
  63. if invalidTxErr == nil {
  64. invalidTxErr = InvalidTxError{reqAppendTx.TxBytes, res.RetCode}
  65. }
  66. }
  67. case tmsp.ResponseEvent:
  68. s.evc.FireEvent(types.EventStringApp(), types.EventDataApp{res.Key, res.Data})
  69. }
  70. }
  71. proxyAppCtx.SetResponseCallback(proxyCb)
  72. for _, tx := range block.Data.Txs {
  73. proxyAppCtx.AppendTxAsync(tx)
  74. if err := proxyAppCtx.Error(); err != nil {
  75. return err
  76. }
  77. }
  78. hash, err := proxyAppCtx.GetHashSync()
  79. if err != nil {
  80. log.Warn("Error computing proxyAppCtx hash", "error", err)
  81. return err
  82. }
  83. if invalidTxErr != nil {
  84. log.Warn("Invalid transaction in block")
  85. return invalidTxErr
  86. }
  87. // Check that appHash matches
  88. if !bytes.Equal(block.AppHash, hash) {
  89. log.Warn(Fmt("App hash in proposal was %X, computed %X instead", block.AppHash, hash))
  90. return InvalidAppHashError{block.AppHash, hash}
  91. }
  92. return nil
  93. }
  94. func (s *State) validateBlock(block *types.Block) error {
  95. // Basic block validation.
  96. err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime)
  97. if err != nil {
  98. return err
  99. }
  100. // Validate block LastValidation.
  101. if block.Height == 1 {
  102. if len(block.LastValidation.Precommits) != 0 {
  103. return errors.New("Block at height 1 (first block) should have no LastValidation precommits")
  104. }
  105. } else {
  106. if len(block.LastValidation.Precommits) != s.LastValidators.Size() {
  107. return fmt.Errorf("Invalid block validation size. Expected %v, got %v",
  108. s.LastValidators.Size(), len(block.LastValidation.Precommits))
  109. }
  110. err := s.LastValidators.VerifyValidation(
  111. s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation)
  112. if err != nil {
  113. return err
  114. }
  115. }
  116. return nil
  117. }
  118. // Updates the LastCommitHeight of the validators in valSet, in place.
  119. // Assumes that lastValSet matches the valset of block.LastValidators
  120. // CONTRACT: lastValSet is not mutated.
  121. func updateValidatorsWithBlock(lastValSet *types.ValidatorSet, valSet *types.ValidatorSet, block *types.Block) {
  122. for i, precommit := range block.LastValidation.Precommits {
  123. if precommit == nil {
  124. continue
  125. }
  126. _, val := lastValSet.GetByIndex(i)
  127. if val == nil {
  128. PanicCrisis(Fmt("Failed to fetch validator at index %v", i))
  129. }
  130. if _, val_ := valSet.GetByAddress(val.Address); val_ != nil {
  131. val_.LastCommitHeight = block.Height - 1
  132. updated := valSet.Update(val_)
  133. if !updated {
  134. PanicCrisis("Failed to update validator LastCommitHeight")
  135. }
  136. } else {
  137. // XXX This is not an error if validator was removed.
  138. // But, we don't mutate validators yet so go ahead and panic.
  139. PanicCrisis("Could not find validator")
  140. }
  141. }
  142. }
  143. //-----------------------------------------------------------------------------
  144. type InvalidTxError struct {
  145. Tx types.Tx
  146. tmsp.RetCode
  147. }
  148. func (txErr InvalidTxError) Error() string {
  149. return Fmt("Invalid tx: [%v] code: [%v]", txErr.Tx, txErr.RetCode)
  150. }
  151. type InvalidAppHashError struct {
  152. Expected []byte
  153. Got []byte
  154. }
  155. func (hashErr InvalidAppHashError) Error() string {
  156. return Fmt("Invalid hash: [%X] got: [%X]", hashErr.Expected, hashErr.Got)
  157. }