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.

176 lines
5.0 KiB

  1. package state
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "github.com/tendermint/go-crypto/tmhash"
  7. dbm "github.com/tendermint/tendermint/libs/db"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. //-----------------------------------------------------
  11. // Validate block
  12. func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
  13. // Validate internal consistency.
  14. if err := block.ValidateBasic(); err != nil {
  15. return err
  16. }
  17. // Validate basic info.
  18. if block.ChainID != state.ChainID {
  19. return fmt.Errorf(
  20. "Wrong Block.Header.ChainID. Expected %v, got %v",
  21. state.ChainID,
  22. block.ChainID,
  23. )
  24. }
  25. if block.Height != state.LastBlockHeight+1 {
  26. return fmt.Errorf(
  27. "Wrong Block.Header.Height. Expected %v, got %v",
  28. state.LastBlockHeight+1,
  29. block.Height,
  30. )
  31. }
  32. /* TODO: Determine bounds for Time
  33. See blockchain/reactor "stopSyncingDurationMinutes"
  34. if !block.Time.After(lastBlockTime) {
  35. return errors.New("Invalid Block.Header.Time")
  36. }
  37. */
  38. // Validate prev block info.
  39. if !block.LastBlockID.Equals(state.LastBlockID) {
  40. return fmt.Errorf(
  41. "Wrong Block.Header.LastBlockID. Expected %v, got %v",
  42. state.LastBlockID,
  43. block.LastBlockID,
  44. )
  45. }
  46. newTxs := int64(len(block.Data.Txs))
  47. if block.TotalTxs != state.LastBlockTotalTx+newTxs {
  48. return fmt.Errorf(
  49. "Wrong Block.Header.TotalTxs. Expected %v, got %v",
  50. state.LastBlockTotalTx+newTxs,
  51. block.TotalTxs,
  52. )
  53. }
  54. // Validate app info
  55. if !bytes.Equal(block.AppHash, state.AppHash) {
  56. return fmt.Errorf(
  57. "Wrong Block.Header.AppHash. Expected %X, got %v",
  58. state.AppHash,
  59. block.AppHash,
  60. )
  61. }
  62. if !bytes.Equal(block.ConsensusHash, state.ConsensusParams.Hash()) {
  63. return fmt.Errorf(
  64. "Wrong Block.Header.ConsensusHash. Expected %X, got %v",
  65. state.ConsensusParams.Hash(),
  66. block.ConsensusHash,
  67. )
  68. }
  69. if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) {
  70. return fmt.Errorf(
  71. "Wrong Block.Header.LastResultsHash. Expected %X, got %v",
  72. state.LastResultsHash,
  73. block.LastResultsHash,
  74. )
  75. }
  76. if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
  77. return fmt.Errorf(
  78. "Wrong Block.Header.ValidatorsHash. Expected %X, got %v",
  79. state.Validators.Hash(),
  80. block.ValidatorsHash,
  81. )
  82. }
  83. if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) {
  84. return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v", state.NextValidators.Hash(), block.NextValidatorsHash)
  85. }
  86. // Validate block LastCommit.
  87. if block.Height == 1 {
  88. if len(block.LastCommit.Precommits) != 0 {
  89. return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
  90. }
  91. } else {
  92. if len(block.LastCommit.Precommits) != state.LastValidators.Size() {
  93. return fmt.Errorf(
  94. "Invalid block commit size. Expected %v, got %v",
  95. state.LastValidators.Size(),
  96. len(block.LastCommit.Precommits),
  97. )
  98. }
  99. err := state.LastValidators.VerifyCommit(
  100. state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit)
  101. if err != nil {
  102. return err
  103. }
  104. }
  105. // Validate all evidence.
  106. // TODO: Each check requires loading an old validator set.
  107. // We should cap the amount of evidence per block
  108. // to prevent potential proposer DoS.
  109. for _, ev := range block.Evidence.Evidence {
  110. if err := VerifyEvidence(stateDB, state, ev); err != nil {
  111. return types.NewEvidenceInvalidErr(ev, err)
  112. }
  113. }
  114. // NOTE: We can't actually verify it's the right proposer because we dont
  115. // know what round the block was first proposed. So just check that it's
  116. // a legit address and a known validator.
  117. if len(block.ProposerAddress) != tmhash.Size ||
  118. !state.Validators.HasAddress(block.ProposerAddress) {
  119. return fmt.Errorf(
  120. "Block.Header.ProposerAddress, %X, is not a validator",
  121. block.ProposerAddress,
  122. )
  123. }
  124. return nil
  125. }
  126. // VerifyEvidence verifies the evidence fully by checking:
  127. // - it is sufficiently recent (MaxAge)
  128. // - it is from a key who was a validator at the given height
  129. // - it is internally consistent
  130. // - it was properly signed by the alleged equivocator
  131. func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence) error {
  132. height := state.LastBlockHeight
  133. evidenceAge := height - evidence.Height()
  134. maxAge := state.ConsensusParams.EvidenceParams.MaxAge
  135. if evidenceAge > maxAge {
  136. return fmt.Errorf("Evidence from height %d is too old. Min height is %d",
  137. evidence.Height(), height-maxAge)
  138. }
  139. valset, err := LoadValidators(stateDB, evidence.Height())
  140. if err != nil {
  141. // TODO: if err is just that we cant find it cuz we pruned, ignore.
  142. // TODO: if its actually bad evidence, punish peer
  143. return err
  144. }
  145. // The address must have been an active validator at the height.
  146. // NOTE: we will ignore evidence from H if the key was not a validator
  147. // at H, even if it is a validator at some nearby H'
  148. ev := evidence
  149. height, addr := ev.Height(), ev.Address()
  150. _, val := valset.GetByAddress(addr)
  151. if val == nil {
  152. return fmt.Errorf("Address %X was not a validator at height %d", addr, height)
  153. }
  154. if err := evidence.Verify(state.ChainID, val.PubKey); err != nil {
  155. return err
  156. }
  157. return nil
  158. }