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.

117 lines
3.3 KiB

10 years ago
  1. package state
  2. import (
  3. "bytes"
  4. "errors"
  5. . "github.com/tendermint/go-common"
  6. "github.com/tendermint/tendermint/events"
  7. "github.com/tendermint/tendermint/types"
  8. )
  9. // NOTE: If an error occurs during block execution, state will be left
  10. // at an invalid state. Copy the state before calling ExecBlock!
  11. func ExecBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error {
  12. err := execBlock(s, block, blockPartsHeader)
  13. if err != nil {
  14. return err
  15. }
  16. // State.Hash should match block.StateHash
  17. stateHash := s.Hash()
  18. if !bytes.Equal(stateHash, block.StateHash) {
  19. return errors.New(Fmt("Invalid state hash. Expected %X, got %X",
  20. stateHash, block.StateHash))
  21. }
  22. return nil
  23. }
  24. // executes transactions of a block, does not check block.StateHash
  25. // NOTE: If an error occurs during block execution, state will be left
  26. // at an invalid state. Copy the state before calling execBlock!
  27. func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error {
  28. // Basic block validation.
  29. err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime)
  30. if err != nil {
  31. return err
  32. }
  33. // Validate block LastValidation.
  34. if block.Height == 1 {
  35. if len(block.LastValidation.Precommits) != 0 {
  36. return errors.New("Block at height 1 (first block) should have no LastValidation precommits")
  37. }
  38. } else {
  39. if len(block.LastValidation.Precommits) != s.LastValidators.Size() {
  40. return errors.New(Fmt("Invalid block validation size. Expected %v, got %v",
  41. s.LastValidators.Size(), len(block.LastValidation.Precommits)))
  42. }
  43. err := s.LastValidators.VerifyValidation(
  44. s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation)
  45. if err != nil {
  46. return err
  47. }
  48. }
  49. // Update Validator.LastCommitHeight as necessary.
  50. for i, precommit := range block.LastValidation.Precommits {
  51. if precommit == nil {
  52. continue
  53. }
  54. _, val := s.LastValidators.GetByIndex(i)
  55. if val == nil {
  56. PanicCrisis(Fmt("Failed to fetch validator at index %v", i))
  57. }
  58. if _, val_ := s.Validators.GetByAddress(val.Address); val_ != nil {
  59. val_.LastCommitHeight = block.Height - 1
  60. updated := s.Validators.Update(val_)
  61. if !updated {
  62. PanicCrisis("Failed to update validator LastCommitHeight")
  63. }
  64. } else {
  65. PanicCrisis("Could not find validator")
  66. }
  67. }
  68. // Remember LastValidators
  69. s.LastValidators = s.Validators.Copy()
  70. // Execute each tx
  71. for _, tx := range block.Data.Txs {
  72. err := ExecTx(s, tx, s.evc)
  73. if err != nil {
  74. return InvalidTxError{tx, err}
  75. }
  76. }
  77. // Increment validator AccumPowers
  78. s.Validators.IncrementAccum(1)
  79. s.LastBlockHeight = block.Height
  80. s.LastBlockHash = block.Hash()
  81. s.LastBlockParts = blockPartsHeader
  82. s.LastBlockTime = block.Time
  83. return nil
  84. }
  85. // If the tx is invalid, an error will be returned.
  86. // Unlike ExecBlock(), state will not be altered.
  87. func ExecTx(s *State, tx types.Tx, evc events.Fireable) (err error) {
  88. // TODO: do something with fees
  89. //fees := int64(0)
  90. //_s := blockCache.State() // hack to access validators and block height
  91. // XXX Query ledger application
  92. return nil
  93. }
  94. //-----------------------------------------------------------------------------
  95. type InvalidTxError struct {
  96. Tx types.Tx
  97. Reason error
  98. }
  99. func (txErr InvalidTxError) Error() string {
  100. return Fmt("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason)
  101. }