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.

359 lines
11 KiB

  1. package types
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/tendermint/tendermint/crypto/batch"
  6. "github.com/tendermint/tendermint/crypto/tmhash"
  7. tmmath "github.com/tendermint/tendermint/libs/math"
  8. )
  9. // VerifyCommit verifies +2/3 of the set had signed the given commit.
  10. //
  11. // It checks all the signatures! While it's safe to exit as soon as we have
  12. // 2/3+ signatures, doing so would impact incentivization logic in the ABCI
  13. // application that depends on the LastCommitInfo sent in BeginBlock, which
  14. // includes which validators signed. For instance, Gaia incentivizes proposers
  15. // with a bonus for including more than +2/3 of the signatures.
  16. func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
  17. height int64, commit *Commit) error {
  18. // run a basic validation of the arguments
  19. if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
  20. return err
  21. }
  22. // calculate voting power needed. Note that total voting power is capped to
  23. // 1/8th of max int64 so this operation should never overflow
  24. votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
  25. // ignore all absent signatures
  26. ignore := func(c CommitSig) bool { return c.Absent() }
  27. // only count the signatures that are for the block
  28. count := func(c CommitSig) bool { return c.ForBlock() }
  29. // attempt to batch verify
  30. cacheSignBytes, success, err := tryVerifyCommitBatch(
  31. chainID, vals, commit, votingPowerNeeded, ignore, count, true, true)
  32. if err != nil {
  33. return err
  34. }
  35. if success {
  36. return nil
  37. }
  38. // if verification failed or is not supported then fallback to single verification
  39. return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
  40. cacheSignBytes, ignore, count, true, true)
  41. }
  42. // LIGHT CLIENT VERIFICATION METHODS
  43. // VerifyCommitLight verifies +2/3 of the set had signed the given commit.
  44. //
  45. // This method is primarily used by the light client and does not check all the
  46. // signatures.
  47. func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
  48. height int64, commit *Commit) error {
  49. // run a basic validation of the arguments
  50. if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
  51. return err
  52. }
  53. // calculate voting power needed
  54. votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
  55. // ignore all commit signatures that are not for the block
  56. ignore := func(c CommitSig) bool { return !c.ForBlock() }
  57. // count all the remaining signatures
  58. count := func(c CommitSig) bool { return true }
  59. // attempt to batch verify
  60. cacheSignBytes, success, err := tryVerifyCommitBatch(
  61. chainID, vals, commit, votingPowerNeeded, ignore, count, false, true)
  62. if err != nil {
  63. return err
  64. }
  65. if success {
  66. return nil
  67. }
  68. // if verification failed or is not supported then fallback to single verification
  69. return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
  70. cacheSignBytes, ignore, count, false, true)
  71. }
  72. // VerifyCommitLightTrusting verifies that trustLevel of the validator set signed
  73. // this commit.
  74. //
  75. // NOTE the given validators do not necessarily correspond to the validator set
  76. // for this commit, but there may be some intersection.
  77. //
  78. // This method is primarily used by the light client and does not check all the
  79. // signatures.
  80. func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commit, trustLevel tmmath.Fraction) error {
  81. // sanity checks
  82. if vals == nil {
  83. return errors.New("nil validator set")
  84. }
  85. if trustLevel.Denominator == 0 {
  86. return errors.New("trustLevel has zero Denominator")
  87. }
  88. if commit == nil {
  89. return errors.New("nil commit")
  90. }
  91. // safely calculate voting power needed.
  92. totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator))
  93. if overflow {
  94. return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator")
  95. }
  96. votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
  97. // ignore all commit signatures that are not for the block
  98. ignore := func(c CommitSig) bool { return !c.ForBlock() }
  99. // count all the remaining signatures
  100. count := func(c CommitSig) bool { return true }
  101. // attempt to batch verify commit. As the validator set doesn't necessarily
  102. // correspond with the validator set that signed the block we need to look
  103. // up by address rather than index.
  104. cacheSignBytes, success, err := tryVerifyCommitBatch(
  105. chainID, vals, commit, votingPowerNeeded, ignore, count, false, false)
  106. if err != nil {
  107. return err
  108. }
  109. if success {
  110. return nil
  111. }
  112. // attempt with single verification
  113. return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
  114. cacheSignBytes, ignore, count, false, false)
  115. }
  116. // ValidateHash returns an error if the hash is not empty, but its
  117. // size != tmhash.Size.
  118. func ValidateHash(h []byte) error {
  119. if len(h) > 0 && len(h) != tmhash.Size {
  120. return fmt.Errorf("expected size to be %d bytes, got %d bytes",
  121. tmhash.Size,
  122. len(h),
  123. )
  124. }
  125. return nil
  126. }
  127. // Batch verification
  128. // tryVerifyCommitBatch attempts to batch verify. If it is not supported or
  129. // verification fails it returns false. If there is an error in the signatures
  130. // or the way that they are counted an error is returned. A cache of all the
  131. // commits in byte form is returned in case it needs to be used again for single
  132. // verification
  133. func tryVerifyCommitBatch(
  134. chainID string,
  135. vals *ValidatorSet,
  136. commit *Commit,
  137. votingPowerNeeded int64,
  138. ignoreSig func(CommitSig) bool,
  139. countSig func(CommitSig) bool,
  140. countAllSignatures bool,
  141. lookUpByIndex bool,
  142. ) (map[string][]byte, bool, error) {
  143. var (
  144. val *Validator
  145. valIdx int32
  146. seenVals = make(map[int32]int, len(commit.Signatures))
  147. talliedVotingPower int64 = 0
  148. // we keep a cache of the signed bytes to make it quicker to verify
  149. // individually if we need to
  150. cacheSignBytes = make(map[string][]byte, len(commit.Signatures))
  151. )
  152. // attempt to create a batch verifier
  153. bv, ok := batch.CreateBatchVerifier(vals.GetProposer().PubKey)
  154. // check if batch verification is supported
  155. if !ok || len(commit.Signatures) < 2 {
  156. return cacheSignBytes, false, nil
  157. }
  158. for idx, commitSig := range commit.Signatures {
  159. // skip over signatures that should be ignored
  160. if ignoreSig(commitSig) {
  161. continue
  162. }
  163. // If the vals and commit have a 1-to-1 correspondance we can retrieve
  164. // them by index else we need to retrieve them by address
  165. if lookUpByIndex {
  166. val = vals.Validators[idx]
  167. } else {
  168. valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress)
  169. // if the signature doesn't belong to anyone in the validator set
  170. // then we just skip over it
  171. if val == nil {
  172. continue
  173. }
  174. // because we are getting validators by address we need to make sure
  175. // that the same validator doesn't commit twice
  176. if firstIndex, ok := seenVals[valIdx]; ok {
  177. secondIndex := idx
  178. return cacheSignBytes, false, fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
  179. }
  180. seenVals[valIdx] = idx
  181. }
  182. // Validate signature.
  183. voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
  184. // cache the signBytes in case batch verification fails
  185. cacheSignBytes[string(val.PubKey.Bytes())] = voteSignBytes
  186. // add the key, sig and message to the verifier
  187. if err := bv.Add(val.PubKey, voteSignBytes, commitSig.Signature); err != nil {
  188. return cacheSignBytes, false, err
  189. }
  190. // If this signature counts then add the voting power of the validator
  191. // to the tally
  192. if countSig(commitSig) {
  193. talliedVotingPower += val.VotingPower
  194. }
  195. // if we don't need to verify all signatures and already have sufficient
  196. // voting power we can break from batching and verify all the signatures
  197. if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
  198. break
  199. }
  200. }
  201. // ensure that we have batched together enough signatures to exceed the
  202. // voting power needed else there is no need to even verify
  203. if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed {
  204. return cacheSignBytes, false, ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
  205. }
  206. // attempt to verify the batch. If this fails, fall back to single
  207. // verification
  208. if bv.Verify() {
  209. // success
  210. return cacheSignBytes, true, nil
  211. }
  212. // verification failed
  213. return cacheSignBytes, false, nil
  214. }
  215. // Single Verification
  216. // verifyCommitSingle single verifies commits.
  217. // If a key does not support batch verification, or batch verification fails this will be used
  218. // This method is used to check all the signatures included in a commit.
  219. // It is used in consensus for validating a block LastCommit.
  220. // CONTRACT: both commit and validator set should have passed validate basic
  221. func verifyCommitSingle(
  222. chainID string,
  223. vals *ValidatorSet,
  224. commit *Commit,
  225. votingPowerNeeded int64,
  226. cachedVals map[string][]byte,
  227. ignoreSig func(CommitSig) bool,
  228. countSig func(CommitSig) bool,
  229. countAllSignatures bool,
  230. lookUpByIndex bool,
  231. ) error {
  232. var (
  233. val *Validator
  234. valIdx int32
  235. seenVals = make(map[int32]int, len(commit.Signatures))
  236. talliedVotingPower int64 = 0
  237. voteSignBytes []byte
  238. )
  239. for idx, commitSig := range commit.Signatures {
  240. if ignoreSig(commitSig) {
  241. continue
  242. }
  243. // If the vals and commit have a 1-to-1 correspondance we can retrieve
  244. // them by index else we need to retrieve them by address
  245. if lookUpByIndex {
  246. val = vals.Validators[idx]
  247. } else {
  248. valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress)
  249. // if the signature doesn't belong to anyone in the validator set
  250. // then we just skip over it
  251. if val == nil {
  252. continue
  253. }
  254. // because we are getting validators by address we need to make sure
  255. // that the same validator doesn't commit twice
  256. if firstIndex, ok := seenVals[valIdx]; ok {
  257. secondIndex := idx
  258. return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
  259. }
  260. seenVals[valIdx] = idx
  261. }
  262. // Check if we have the validator in the cache
  263. if cachedVote, ok := cachedVals[string(val.PubKey.Bytes())]; !ok {
  264. voteSignBytes = commit.VoteSignBytes(chainID, int32(idx))
  265. } else {
  266. voteSignBytes = cachedVote
  267. }
  268. if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
  269. return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
  270. }
  271. // If this signature counts then add the voting power of the validator
  272. // to the tally
  273. if countSig(commitSig) {
  274. talliedVotingPower += val.VotingPower
  275. }
  276. // check if we have enough signatures and can thus exit early
  277. if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
  278. return nil
  279. }
  280. }
  281. if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed {
  282. return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
  283. }
  284. return nil
  285. }
  286. func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, blockID BlockID) error {
  287. if vals == nil {
  288. return errors.New("nil validator set")
  289. }
  290. if commit == nil {
  291. return errors.New("nil commit")
  292. }
  293. if vals.Size() != len(commit.Signatures) {
  294. return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
  295. }
  296. // Validate Height and BlockID.
  297. if height != commit.Height {
  298. return NewErrInvalidCommitHeight(height, commit.Height)
  299. }
  300. if !blockID.Equals(commit.BlockID) {
  301. return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v",
  302. blockID, commit.BlockID)
  303. }
  304. return nil
  305. }