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.

268 lines
10 KiB

  1. package evidence
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "time"
  8. "github.com/tendermint/tendermint/light"
  9. "github.com/tendermint/tendermint/types"
  10. )
  11. // verify verifies the evidence fully by checking:
  12. // - It has not already been committed
  13. // - it is sufficiently recent (MaxAge)
  14. // - it is from a key who was a validator at the given height
  15. // - it is internally consistent with state
  16. // - it was properly signed by the alleged equivocator and meets the individual evidence verification requirements
  17. //
  18. // NOTE: Evidence may be provided that we do not have the block or validator
  19. // set for. In these cases, we do not return a ErrInvalidEvidence as not to have
  20. // the sending peer disconnect. All other errors are treated as invalid evidence
  21. // (i.e. ErrInvalidEvidence).
  22. func (evpool *Pool) verify(ctx context.Context, evidence types.Evidence) error {
  23. var (
  24. state = evpool.State()
  25. height = state.LastBlockHeight
  26. evidenceParams = state.ConsensusParams.Evidence
  27. ageNumBlocks = height - evidence.Height()
  28. )
  29. // ensure we have the block for the evidence height
  30. //
  31. // NOTE: It is currently possible for a peer to send us evidence we're not
  32. // able to process because we're too far behind (e.g. syncing), so we DO NOT
  33. // return an invalid evidence error because we do not want the peer to
  34. // disconnect or signal an error in this particular case.
  35. blockMeta := evpool.blockStore.LoadBlockMeta(evidence.Height())
  36. if blockMeta == nil {
  37. return fmt.Errorf("failed to verify evidence; missing block for height %d", evidence.Height())
  38. }
  39. // verify the time of the evidence
  40. evTime := blockMeta.Header.Time
  41. ageDuration := state.LastBlockTime.Sub(evTime)
  42. // check that the evidence hasn't expired
  43. if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks {
  44. return types.NewErrInvalidEvidence(
  45. evidence,
  46. fmt.Errorf(
  47. "evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v",
  48. evidence.Height(),
  49. evTime,
  50. height-evidenceParams.MaxAgeNumBlocks,
  51. state.LastBlockTime.Add(evidenceParams.MaxAgeDuration),
  52. ),
  53. )
  54. }
  55. // apply the evidence-specific verification logic
  56. switch ev := evidence.(type) {
  57. case *types.DuplicateVoteEvidence:
  58. valSet, err := evpool.stateDB.LoadValidators(evidence.Height())
  59. if err != nil {
  60. return err
  61. }
  62. if err := VerifyDuplicateVote(ev, state.ChainID, valSet); err != nil {
  63. return types.NewErrInvalidEvidence(evidence, err)
  64. }
  65. _, val := valSet.GetByAddress(ev.VoteA.ValidatorAddress)
  66. if err := ev.ValidateABCI(val, valSet, evTime); err != nil {
  67. ev.GenerateABCI(val, valSet, evTime)
  68. if addErr := evpool.addPendingEvidence(ctx, ev); addErr != nil {
  69. evpool.logger.Error("adding pending duplicate vote evidence failed", "err", addErr)
  70. }
  71. return err
  72. }
  73. return nil
  74. case *types.LightClientAttackEvidence:
  75. commonHeader, err := getSignedHeader(evpool.blockStore, evidence.Height())
  76. if err != nil {
  77. return err
  78. }
  79. commonVals, err := evpool.stateDB.LoadValidators(evidence.Height())
  80. if err != nil {
  81. return err
  82. }
  83. trustedHeader := commonHeader
  84. // in the case of lunatic the trusted header is different to the common header
  85. if evidence.Height() != ev.ConflictingBlock.Height {
  86. trustedHeader, err = getSignedHeader(evpool.blockStore, ev.ConflictingBlock.Height)
  87. if err != nil {
  88. // FIXME: This multi step process is a bit unergonomic. We may want to consider a more efficient process
  89. // that doesn't require as much io and is atomic.
  90. // If the node doesn't have a block at the height of the conflicting block, then this could be
  91. // a forward lunatic attack. Thus the node must get the latest height it has
  92. latestHeight := evpool.blockStore.Height()
  93. trustedHeader, err = getSignedHeader(evpool.blockStore, latestHeight)
  94. if err != nil {
  95. return err
  96. }
  97. if trustedHeader.Time.Before(ev.ConflictingBlock.Time) {
  98. return fmt.Errorf("latest block time (%v) is before conflicting block time (%v)",
  99. trustedHeader.Time, ev.ConflictingBlock.Time,
  100. )
  101. }
  102. }
  103. }
  104. err = VerifyLightClientAttack(
  105. ev,
  106. commonHeader,
  107. trustedHeader,
  108. commonVals,
  109. state.LastBlockTime,
  110. state.ConsensusParams.Evidence.MaxAgeDuration,
  111. )
  112. if err != nil {
  113. return types.NewErrInvalidEvidence(evidence, err)
  114. }
  115. // validate the ABCI component of evidence. If this fails but the rest
  116. // is valid then we regenerate the ABCI component, save the rectified
  117. // evidence and return an error
  118. if err := ev.ValidateABCI(commonVals, trustedHeader, evTime); err != nil {
  119. ev.GenerateABCI(commonVals, trustedHeader, evTime)
  120. if addErr := evpool.addPendingEvidence(ctx, ev); addErr != nil {
  121. evpool.logger.Error("adding pending light client attack evidence failed", "err", addErr)
  122. }
  123. return err
  124. }
  125. return nil
  126. default:
  127. return types.NewErrInvalidEvidence(evidence, fmt.Errorf("unrecognized evidence type: %T", evidence))
  128. }
  129. }
  130. // VerifyLightClientAttack verifies LightClientAttackEvidence against the state of the full node. This involves
  131. // the following checks:
  132. // - the common header from the full node has at least 1/3 voting power which is also present in
  133. // the conflicting header's commit
  134. // - 2/3+ of the conflicting validator set correctly signed the conflicting block
  135. // - the nodes trusted header at the same height as the conflicting header has a different hash
  136. //
  137. // CONTRACT: must run ValidateBasic() on the evidence before verifying
  138. // must check that the evidence has not expired (i.e. is outside the maximum age threshold)
  139. func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, trustedHeader *types.SignedHeader,
  140. commonVals *types.ValidatorSet, now time.Time, trustPeriod time.Duration) error {
  141. // In the case of lunatic attack there will be a different commonHeader height. Therefore the node perform a single
  142. // verification jump between the common header and the conflicting one
  143. if commonHeader.Height != e.ConflictingBlock.Height {
  144. err := commonVals.VerifyCommitLightTrusting(trustedHeader.ChainID, e.ConflictingBlock.Commit, light.DefaultTrustLevel)
  145. if err != nil {
  146. return fmt.Errorf("skipping verification of conflicting block failed: %w", err)
  147. }
  148. // In the case of equivocation and amnesia we expect all header hashes to be correctly derived
  149. } else if e.ConflictingHeaderIsInvalid(trustedHeader.Header) {
  150. return errors.New("common height is the same as conflicting block height so expected the conflicting" +
  151. " block to be correctly derived yet it wasn't")
  152. }
  153. // Verify that the 2/3+ commits from the conflicting validator set were for the conflicting header
  154. if err := e.ConflictingBlock.ValidatorSet.VerifyCommitLight(trustedHeader.ChainID, e.ConflictingBlock.Commit.BlockID,
  155. e.ConflictingBlock.Height, e.ConflictingBlock.Commit); err != nil {
  156. return fmt.Errorf("invalid commit from conflicting block: %w", err)
  157. }
  158. // check in the case of a forward lunatic attack that monotonically increasing time has been violated
  159. if e.ConflictingBlock.Height > trustedHeader.Height && e.ConflictingBlock.Time.After(trustedHeader.Time) {
  160. return fmt.Errorf("conflicting block doesn't violate monotonically increasing time (%v is after %v)",
  161. e.ConflictingBlock.Time, trustedHeader.Time,
  162. )
  163. // In all other cases check that the hashes of the conflicting header and the trusted header are different
  164. } else if bytes.Equal(trustedHeader.Hash(), e.ConflictingBlock.Hash()) {
  165. return fmt.Errorf("trusted header hash matches the evidence's conflicting header hash: %X",
  166. trustedHeader.Hash())
  167. }
  168. return nil
  169. }
  170. // VerifyDuplicateVote verifies DuplicateVoteEvidence against the state of full node. This involves the
  171. // following checks:
  172. // - the validator is in the validator set at the height of the evidence
  173. // - the height, round, type and validator address of the votes must be the same
  174. // - the block ID's must be different
  175. // - The signatures must both be valid
  176. func VerifyDuplicateVote(e *types.DuplicateVoteEvidence, chainID string, valSet *types.ValidatorSet) error {
  177. _, val := valSet.GetByAddress(e.VoteA.ValidatorAddress)
  178. if val == nil {
  179. return fmt.Errorf("address %X was not a validator at height %d", e.VoteA.ValidatorAddress, e.Height())
  180. }
  181. pubKey := val.PubKey
  182. // H/R/S must be the same
  183. if e.VoteA.Height != e.VoteB.Height ||
  184. e.VoteA.Round != e.VoteB.Round ||
  185. e.VoteA.Type != e.VoteB.Type {
  186. return fmt.Errorf("h/r/s does not match: %d/%d/%v vs %d/%d/%v",
  187. e.VoteA.Height, e.VoteA.Round, e.VoteA.Type,
  188. e.VoteB.Height, e.VoteB.Round, e.VoteB.Type)
  189. }
  190. // Address must be the same
  191. if !bytes.Equal(e.VoteA.ValidatorAddress, e.VoteB.ValidatorAddress) {
  192. return fmt.Errorf("validator addresses do not match: %X vs %X",
  193. e.VoteA.ValidatorAddress,
  194. e.VoteB.ValidatorAddress,
  195. )
  196. }
  197. // BlockIDs must be different
  198. if e.VoteA.BlockID.Equals(e.VoteB.BlockID) {
  199. return fmt.Errorf(
  200. "block IDs are the same (%v) - not a real duplicate vote",
  201. e.VoteA.BlockID,
  202. )
  203. }
  204. // pubkey must match address (this should already be true, sanity check)
  205. addr := e.VoteA.ValidatorAddress
  206. if !bytes.Equal(pubKey.Address(), addr) {
  207. return fmt.Errorf("address (%X) doesn't match pubkey (%v - %X)",
  208. addr, pubKey, pubKey.Address())
  209. }
  210. va := e.VoteA.ToProto()
  211. vb := e.VoteB.ToProto()
  212. // Signatures must be valid
  213. if !pubKey.VerifySignature(types.VoteSignBytes(chainID, va), e.VoteA.Signature) {
  214. return fmt.Errorf("verifying VoteA: %w", types.ErrVoteInvalidSignature)
  215. }
  216. if !pubKey.VerifySignature(types.VoteSignBytes(chainID, vb), e.VoteB.Signature) {
  217. return fmt.Errorf("verifying VoteB: %w", types.ErrVoteInvalidSignature)
  218. }
  219. return nil
  220. }
  221. func getSignedHeader(blockStore BlockStore, height int64) (*types.SignedHeader, error) {
  222. blockMeta := blockStore.LoadBlockMeta(height)
  223. if blockMeta == nil {
  224. return nil, fmt.Errorf("don't have header at height #%d", height)
  225. }
  226. commit := blockStore.LoadBlockCommit(height)
  227. if commit == nil {
  228. return nil, fmt.Errorf("don't have commit at height #%d", height)
  229. }
  230. return &types.SignedHeader{
  231. Header: &blockMeta.Header,
  232. Commit: commit,
  233. }, nil
  234. }