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.

184 lines
5.2 KiB

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