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