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

9 years ago
  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. . "github.com/tendermint/go-common"
  6. "github.com/tendermint/go-events"
  7. "github.com/tendermint/tendermint/proxy"
  8. "github.com/tendermint/tendermint/types"
  9. tmsp "github.com/tendermint/tmsp/types"
  10. )
  11. // Validate block
  12. func (s *State) ValidateBlock(block *types.Block) error {
  13. return s.validateBlock(block)
  14. }
  15. // Execute the block to mutate State.
  16. // Validates block and then executes Data.Txs in the block.
  17. func (s *State) ExecBlock(evsw *events.EventSwitch, proxyAppConn proxy.AppConn, block *types.Block, blockPartsHeader types.PartSetHeader) error {
  18. // Validate the block.
  19. err := s.validateBlock(block)
  20. if err != nil {
  21. return err
  22. }
  23. // Update the validator set
  24. valSet := s.Validators.Copy()
  25. // Update valSet with signatures from block.
  26. updateValidatorsWithBlock(s.LastValidators, valSet, block)
  27. // TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?)
  28. nextValSet := valSet.Copy()
  29. // Execute the block txs
  30. err = s.execBlockOnProxyApp(evsw, proxyAppConn, block)
  31. if err != nil {
  32. // There was some error in proxyApp
  33. // TODO Report error and wait for proxyApp to be available.
  34. return err
  35. }
  36. // All good!
  37. nextValSet.IncrementAccum(1)
  38. s.LastBlockHeight = block.Height
  39. s.LastBlockHash = block.Hash()
  40. s.LastBlockParts = blockPartsHeader
  41. s.LastBlockTime = block.Time
  42. s.Validators = nextValSet
  43. s.LastValidators = valSet
  44. return nil
  45. }
  46. // Executes block's transactions on proxyAppConn.
  47. // TODO: Generate a bitmap or otherwise store tx validity in state.
  48. func (s *State) execBlockOnProxyApp(evsw *events.EventSwitch, proxyAppConn proxy.AppConn, block *types.Block) error {
  49. var validTxs, invalidTxs = 0, 0
  50. // Execute transactions and get hash
  51. proxyCb := func(req *tmsp.Request, res *tmsp.Response) {
  52. switch res.Type {
  53. case tmsp.MessageType_AppendTx:
  54. // TODO: make use of res.Log
  55. // TODO: make use of this info
  56. // Blocks may include invalid txs.
  57. // reqAppendTx := req.(tmsp.RequestAppendTx)
  58. if res.Code == tmsp.CodeType_OK {
  59. validTxs += 1
  60. } else {
  61. log.Debug("Invalid tx", "code", res.Code, "log", res.Log)
  62. invalidTxs += 1
  63. }
  64. }
  65. }
  66. proxyAppConn.SetResponseCallback(proxyCb)
  67. // Run txs of block
  68. for _, tx := range block.Txs {
  69. proxyAppConn.AppendTxAsync(tx)
  70. if err := proxyAppConn.Error(); err != nil {
  71. return err
  72. }
  73. }
  74. // End block
  75. changedValidators, err := proxyAppConn.EndBlockSync(uint64(block.Height))
  76. if err != nil {
  77. log.Warn("Error in proxyAppConn.EndBlock", "error", err)
  78. return err
  79. }
  80. // TODO: Do something with changedValidators
  81. log.Info("TODO: Do something with changedValidators", changedValidators)
  82. // Commit block, get hash back
  83. res := proxyAppConn.CommitSync()
  84. if res.IsErr() {
  85. log.Warn("Error in proxyAppConn.CommitSync", "error", res)
  86. return res
  87. }
  88. if res.Log != "" {
  89. log.Debug("Commit.Log: " + res.Log)
  90. }
  91. log.Info(Fmt("ExecBlock got %v valid txs and %v invalid txs", validTxs, invalidTxs))
  92. // Set the state's new AppHash
  93. s.AppHash = res.Data
  94. return nil
  95. }
  96. func (s *State) validateBlock(block *types.Block) error {
  97. // Basic block validation.
  98. err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime, s.AppHash)
  99. if err != nil {
  100. return err
  101. }
  102. // Validate block LastCommit.
  103. if block.Height == 1 {
  104. if len(block.LastCommit.Precommits) != 0 {
  105. return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
  106. }
  107. } else {
  108. if len(block.LastCommit.Precommits) != s.LastValidators.Size() {
  109. return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
  110. s.LastValidators.Size(), len(block.LastCommit.Precommits))
  111. }
  112. err := s.LastValidators.VerifyCommit(
  113. s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastCommit)
  114. if err != nil {
  115. return err
  116. }
  117. }
  118. return nil
  119. }
  120. // Updates the LastCommitHeight of the validators in valSet, in place.
  121. // Assumes that lastValSet matches the valset of block.LastCommit
  122. // CONTRACT: lastValSet is not mutated.
  123. func updateValidatorsWithBlock(lastValSet *types.ValidatorSet, valSet *types.ValidatorSet, block *types.Block) {
  124. for i, precommit := range block.LastCommit.Precommits {
  125. if precommit == nil {
  126. continue
  127. }
  128. _, val := lastValSet.GetByIndex(i)
  129. if val == nil {
  130. PanicCrisis(Fmt("Failed to fetch validator at index %v", i))
  131. }
  132. if _, val_ := valSet.GetByAddress(val.Address); val_ != nil {
  133. val_.LastCommitHeight = block.Height - 1
  134. updated := valSet.Update(val_)
  135. if !updated {
  136. PanicCrisis("Failed to update validator LastCommitHeight")
  137. }
  138. } else {
  139. // XXX This is not an error if validator was removed.
  140. // But, we don't mutate validators yet so go ahead and panic.
  141. PanicCrisis("Could not find validator")
  142. }
  143. }
  144. }
  145. //-----------------------------------------------------------------------------
  146. type InvalidTxError struct {
  147. Tx types.Tx
  148. Code tmsp.CodeType
  149. }
  150. func (txErr InvalidTxError) Error() string {
  151. return Fmt("Invalid tx: [%v] code: [%v]", txErr.Tx, txErr.Code)
  152. }