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.

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