github.com/okex/exchain@v1.8.0/libs/tendermint/types/vote_set.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/okex/exchain/libs/tendermint/libs/automation" 7 "strings" 8 "sync" 9 10 "github.com/pkg/errors" 11 12 "github.com/okex/exchain/libs/tendermint/libs/bits" 13 ) 14 15 const ( 16 // MaxVotesCount is the maximum number of votes in a set. Used in ValidateBasic funcs for 17 // protection against DOS attacks. Note this implies a corresponding equal limit to 18 // the number of validators. 19 MaxVotesCount = 10000 20 ) 21 22 // UNSTABLE 23 // XXX: duplicate of p2p.ID to avoid dependence between packages. 24 // Perhaps we can have a minimal types package containing this (and other things?) 25 // that both `types` and `p2p` import ? 26 type P2PID string 27 28 /* 29 VoteSet helps collect signatures from validators at each height+round for a 30 predefined vote type. 31 32 We need VoteSet to be able to keep track of conflicting votes when validators 33 double-sign. Yet, we can't keep track of *all* the votes seen, as that could 34 be a DoS attack vector. 35 36 There are two storage areas for votes. 37 1. voteSet.votes 38 2. voteSet.votesByBlock 39 40 `.votes` is the "canonical" list of votes. It always has at least one vote, 41 if a vote from a validator had been seen at all. Usually it keeps track of 42 the first vote seen, but when a 2/3 majority is found, votes for that get 43 priority and are copied over from `.votesByBlock`. 44 45 `.votesByBlock` keeps track of a list of votes for a particular block. There 46 are two ways a &blockVotes{} gets created in `.votesByBlock`. 47 1. the first vote seen by a validator was for the particular block. 48 2. a peer claims to have seen 2/3 majority for the particular block. 49 50 Since the first vote from a validator will always get added in `.votesByBlock` 51 , all votes in `.votes` will have a corresponding entry in `.votesByBlock`. 52 53 When a &blockVotes{} in `.votesByBlock` reaches a 2/3 majority quorum, its 54 votes are copied into `.votes`. 55 56 All this is memory bounded because conflicting votes only get added if a peer 57 told us to track that block, each peer only gets to tell us 1 such block, and, 58 there's only a limited number of peers. 59 60 NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64. 61 */ 62 type VoteSet struct { 63 chainID string 64 height int64 65 round int 66 signedMsgType SignedMsgType 67 valSet *ValidatorSet 68 69 mtx sync.Mutex 70 votesBitArray *bits.BitArray 71 votes []*Vote // Primary votes to share 72 sum int64 // Sum of voting power for seen votes, discounting conflicts 73 maj23 *BlockID // First 2/3 majority seen 74 votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes 75 peerMaj23s map[P2PID]BlockID // Maj23 for each peer 76 } 77 78 // Constructs a new VoteSet struct used to accumulate votes for given height/round. 79 func NewVoteSet(chainID string, height int64, round int, signedMsgType SignedMsgType, valSet *ValidatorSet) *VoteSet { 80 if height == 0 { 81 panic("Cannot make VoteSet for height == 0, doesn't make sense.") 82 } 83 return &VoteSet{ 84 chainID: chainID, 85 height: height, 86 round: round, 87 signedMsgType: signedMsgType, 88 valSet: valSet, 89 votesBitArray: bits.NewBitArray(valSet.Size()), 90 votes: make([]*Vote, valSet.Size()), 91 sum: 0, 92 maj23: nil, 93 votesByBlock: make(map[string]*blockVotes, valSet.Size()), 94 peerMaj23s: make(map[P2PID]BlockID), 95 } 96 } 97 98 func (voteSet *VoteSet) ChainID() string { 99 return voteSet.chainID 100 } 101 102 // Implements VoteSetReader. 103 func (voteSet *VoteSet) GetHeight() int64 { 104 if voteSet == nil { 105 return 0 106 } 107 return voteSet.height 108 } 109 110 // Implements VoteSetReader. 111 func (voteSet *VoteSet) GetRound() int { 112 if voteSet == nil { 113 return -1 114 } 115 return voteSet.round 116 } 117 118 // Implements VoteSetReader. 119 func (voteSet *VoteSet) Type() byte { 120 if voteSet == nil { 121 return 0x00 122 } 123 return byte(voteSet.signedMsgType) 124 } 125 126 // Implements VoteSetReader. 127 func (voteSet *VoteSet) Size() int { 128 if voteSet == nil { 129 return 0 130 } 131 return voteSet.valSet.Size() 132 } 133 134 // Returns added=true if vote is valid and new. 135 // Otherwise returns err=ErrVote[ 136 // UnexpectedStep | InvalidIndex | InvalidAddress | 137 // InvalidSignature | InvalidBlockHash | ConflictingVotes ] 138 // Duplicate votes return added=false, err=nil. 139 // Conflicting votes return added=*, err=ErrVoteConflictingVotes. 140 // NOTE: vote should not be mutated after adding. 141 // NOTE: VoteSet must not be nil 142 // NOTE: Vote must not be nil 143 func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) { 144 if voteSet == nil { 145 panic("AddVote() on nil VoteSet") 146 } 147 voteSet.mtx.Lock() 148 defer voteSet.mtx.Unlock() 149 150 return voteSet.addVote(vote) 151 } 152 153 // NOTE: Validates as much as possible before attempting to verify the signature. 154 func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { 155 if vote == nil { 156 return false, ErrVoteNil 157 } 158 valIndex := vote.ValidatorIndex 159 valAddr := vote.ValidatorAddress 160 blockKey := vote.BlockID.Key() 161 162 // Ensure that validator index was set 163 if valIndex < 0 { 164 return false, errors.Wrap(ErrVoteInvalidValidatorIndex, "Index < 0") 165 } else if len(valAddr) == 0 { 166 return false, errors.Wrap(ErrVoteInvalidValidatorAddress, "Empty address") 167 } 168 169 // Make sure the step matches. 170 if (vote.Height != voteSet.height) || 171 (vote.Round != voteSet.round) || 172 (vote.Type != voteSet.signedMsgType) { 173 return false, errors.Wrapf(ErrVoteUnexpectedStep, "Expected %d/%d/%d, but got %d/%d/%d", 174 voteSet.height, voteSet.round, voteSet.signedMsgType, 175 vote.Height, vote.Round, vote.Type) 176 } 177 178 // Ensure that signer is a validator. 179 lookupAddr, val := voteSet.valSet.GetByIndex(valIndex) 180 if val == nil { 181 return false, errors.Wrapf(ErrVoteInvalidValidatorIndex, 182 "Cannot find validator %d in valSet of size %d", valIndex, voteSet.valSet.Size()) 183 } 184 185 // Ensure that the signer has the right address. 186 if !bytes.Equal(valAddr, lookupAddr) { 187 return false, errors.Wrapf(ErrVoteInvalidValidatorAddress, 188 "vote.ValidatorAddress (%X) does not match address (%X) for vote.ValidatorIndex (%d)\n"+ 189 "Ensure the genesis file is correct across all validators.", 190 valAddr, lookupAddr, valIndex) 191 } 192 193 // If we already know of this vote, return false. 194 if existing, ok := voteSet.getVote(valIndex, blockKey); ok { 195 if bytes.Equal(existing.Signature, vote.Signature) { 196 return false, nil // duplicate 197 } 198 return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", existing, vote) 199 } 200 201 // Check signature. 202 if err := vote.Verify(voteSet.chainID, val.PubKey); err != nil { 203 return false, errors.Wrapf(err, "Failed to verify vote with ChainID %s and PubKey %s", voteSet.chainID, val.PubKey) 204 } 205 206 // Add vote and get conflicting vote if any. 207 added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower) 208 if conflicting != nil { 209 return added, NewConflictingVoteError(val, conflicting, vote) 210 } 211 if !added { 212 panic("Expected to add non-conflicting vote") 213 } 214 return added, nil 215 } 216 217 // Returns (vote, true) if vote exists for valIndex and blockKey. 218 func (voteSet *VoteSet) getVote(valIndex int, blockKey string) (vote *Vote, ok bool) { 219 if existing := voteSet.votes[valIndex]; existing != nil && existing.BlockID.Key() == blockKey { 220 return existing, true 221 } 222 if existing := voteSet.votesByBlock[blockKey].getByIndex(valIndex); existing != nil { 223 return existing, true 224 } 225 return nil, false 226 } 227 228 // Assumes signature is valid. 229 // If conflicting vote exists, returns it. 230 func (voteSet *VoteSet) addVerifiedVote( 231 vote *Vote, 232 blockKey string, 233 votingPower int64, 234 ) (added bool, conflicting *Vote) { 235 valIndex := vote.ValidatorIndex 236 237 // Already exists in voteSet.votes? 238 if existing := voteSet.votes[valIndex]; existing != nil { 239 if existing.BlockID.Equals(vote.BlockID) { 240 panic("addVerifiedVote does not expect duplicate votes") 241 } else { 242 conflicting = existing 243 } 244 // Replace vote if blockKey matches voteSet.maj23. 245 if voteSet.maj23 != nil && voteSet.maj23.Key() == blockKey { 246 voteSet.votes[valIndex] = vote 247 voteSet.votesBitArray.SetIndex(valIndex, true) 248 } 249 // Otherwise don't add it to voteSet.votes 250 } else { 251 // Add to voteSet.votes and incr .sum 252 voteSet.votes[valIndex] = vote 253 voteSet.votesBitArray.SetIndex(valIndex, true) 254 voteSet.sum += votingPower 255 } 256 257 votesByBlock, ok := voteSet.votesByBlock[blockKey] 258 if ok { 259 if conflicting != nil && !votesByBlock.peerMaj23 { 260 // There's a conflict and no peer claims that this block is special. 261 return false, conflicting 262 } 263 // We'll add the vote in a bit. 264 } else { 265 // .votesByBlock doesn't exist... 266 if conflicting != nil { 267 // ... and there's a conflicting vote. 268 // We're not even tracking this blockKey, so just forget it. 269 return false, conflicting 270 } 271 // ... and there's no conflicting vote. 272 // Start tracking this blockKey 273 votesByBlock = newBlockVotes(false, voteSet.valSet.Size()) 274 voteSet.votesByBlock[blockKey] = votesByBlock 275 // We'll add the vote in a bit. 276 } 277 278 // Before adding to votesByBlock, see if we'll exceed quorum 279 origSum := votesByBlock.sum 280 quorum := voteSet.valSet.TotalVotingPower()*2/3 + 1 281 282 // Add vote to votesByBlock 283 votesByBlock.addVerifiedVote(vote, votingPower) 284 285 // If we just crossed the quorum threshold and have 2/3 majority... 286 if origSum < quorum && quorum <= votesByBlock.sum { 287 // Only consider the first quorum reached 288 if voteSet.maj23 == nil { 289 maj23BlockID := vote.BlockID 290 voteSet.maj23 = &maj23BlockID 291 // And also copy votes over to voteSet.votes 292 for i, vote := range votesByBlock.votes { 293 if vote != nil { 294 voteSet.votes[i] = vote 295 } 296 } 297 } 298 } 299 300 return true, conflicting 301 } 302 303 // If a peer claims that it has 2/3 majority for given blockKey, call this. 304 // NOTE: if there are too many peers, or too much peer churn, 305 // this can cause memory issues. 306 // TODO: implement ability to remove peers too 307 // NOTE: VoteSet must not be nil 308 func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error { 309 if voteSet == nil { 310 panic("SetPeerMaj23() on nil VoteSet") 311 } 312 voteSet.mtx.Lock() 313 defer voteSet.mtx.Unlock() 314 315 blockKey := blockID.Key() 316 317 // Make sure peer hasn't already told us something. 318 if existing, ok := voteSet.peerMaj23s[peerID]; ok { 319 if existing.Equals(blockID) { 320 return nil // Nothing to do 321 } 322 return fmt.Errorf("setPeerMaj23: Received conflicting blockID from peer %v. Got %v, expected %v", 323 peerID, blockID, existing) 324 } 325 voteSet.peerMaj23s[peerID] = blockID 326 327 // Create .votesByBlock entry if needed. 328 votesByBlock, ok := voteSet.votesByBlock[blockKey] 329 if ok { 330 if votesByBlock.peerMaj23 { 331 return nil // Nothing to do 332 } 333 votesByBlock.peerMaj23 = true 334 // No need to copy votes, already there. 335 } else { 336 votesByBlock = newBlockVotes(true, voteSet.valSet.Size()) 337 voteSet.votesByBlock[blockKey] = votesByBlock 338 // No need to copy votes, no votes to copy over. 339 } 340 return nil 341 } 342 343 // Implements VoteSetReader. 344 func (voteSet *VoteSet) BitArray() *bits.BitArray { 345 if voteSet == nil { 346 return nil 347 } 348 voteSet.mtx.Lock() 349 defer voteSet.mtx.Unlock() 350 return voteSet.votesBitArray.Copy() 351 } 352 353 func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *bits.BitArray { 354 if voteSet == nil { 355 return nil 356 } 357 voteSet.mtx.Lock() 358 defer voteSet.mtx.Unlock() 359 votesByBlock, ok := voteSet.votesByBlock[blockID.Key()] 360 if ok { 361 return votesByBlock.bitArray.Copy() 362 } 363 return nil 364 } 365 366 // NOTE: if validator has conflicting votes, returns "canonical" vote 367 // Implements VoteSetReader. 368 func (voteSet *VoteSet) GetByIndex(valIndex int) *Vote { 369 if voteSet == nil { 370 return nil 371 } 372 voteSet.mtx.Lock() 373 defer voteSet.mtx.Unlock() 374 return voteSet.votes[valIndex] 375 } 376 377 func (voteSet *VoteSet) GetByAddress(address []byte) *Vote { 378 if voteSet == nil { 379 return nil 380 } 381 voteSet.mtx.Lock() 382 defer voteSet.mtx.Unlock() 383 valIndex, val := voteSet.valSet.GetByAddress(address) 384 if val == nil { 385 panic("GetByAddress(address) returned nil") 386 } 387 return voteSet.votes[valIndex] 388 } 389 390 func (voteSet *VoteSet) HasTwoThirdsMajority() bool { 391 if voteSet == nil { 392 return false 393 } 394 voteSet.mtx.Lock() 395 defer voteSet.mtx.Unlock() 396 return voteSet.maj23 != nil 397 } 398 399 // Implements VoteSetReader. 400 func (voteSet *VoteSet) IsCommit() bool { 401 if voteSet == nil { 402 return false 403 } 404 if voteSet.signedMsgType != PrecommitType { 405 return false 406 } 407 voteSet.mtx.Lock() 408 defer voteSet.mtx.Unlock() 409 return voteSet.maj23 != nil 410 } 411 412 func (voteSet *VoteSet) HasTwoThirdsAny() bool { 413 if voteSet == nil { 414 return false 415 } 416 voteSet.mtx.Lock() 417 defer voteSet.mtx.Unlock() 418 return voteSet.sum > voteSet.valSet.TotalVotingPower()*2/3 419 } 420 421 func (voteSet *VoteSet) HasAll() bool { 422 voteSet.mtx.Lock() 423 defer voteSet.mtx.Unlock() 424 return voteSet.sum == voteSet.valSet.TotalVotingPower() 425 } 426 427 // If there was a +2/3 majority for blockID, return blockID and true. 428 // Else, return the empty BlockID{} and false. 429 func (voteSet *VoteSet) TwoThirdsMajority() (blockID BlockID, ok bool) { 430 if voteSet == nil { 431 return BlockID{}, false 432 } 433 434 switch voteSet.signedMsgType { 435 case PrevoteType: 436 if automation.PrevotesNotMaj23(voteSet.height, voteSet.round) { 437 return BlockID{}, false 438 } 439 case PrecommitType: 440 if automation.PrecommitsNotMaj23(voteSet.height, voteSet.round) { 441 return BlockID{}, false 442 } 443 } 444 445 voteSet.mtx.Lock() 446 defer voteSet.mtx.Unlock() 447 if voteSet.maj23 != nil { 448 return *voteSet.maj23, true 449 } 450 return BlockID{}, false 451 } 452 453 //-------------------------------------------------------------------------------- 454 // Strings and JSON 455 456 func (voteSet *VoteSet) String() string { 457 if voteSet == nil { 458 return "nil-VoteSet" 459 } 460 return voteSet.StringIndented("") 461 } 462 463 func (voteSet *VoteSet) StringIndented(indent string) string { 464 voteSet.mtx.Lock() 465 defer voteSet.mtx.Unlock() 466 voteStrings := make([]string, len(voteSet.votes)) 467 for i, vote := range voteSet.votes { 468 if vote == nil { 469 voteStrings[i] = nilVoteStr 470 } else { 471 voteStrings[i] = vote.String() 472 } 473 } 474 return fmt.Sprintf(`VoteSet{ 475 %s H:%v R:%v T:%v 476 %s %v 477 %s %v 478 %s %v 479 %s}`, 480 indent, voteSet.height, voteSet.round, voteSet.signedMsgType, 481 indent, strings.Join(voteStrings, "\n"+indent+" "), 482 indent, voteSet.votesBitArray, 483 indent, voteSet.peerMaj23s, 484 indent) 485 } 486 487 // Marshal the VoteSet to JSON. Same as String(), just in JSON, 488 // and without the height/round/signedMsgType (since its already included in the votes). 489 func (voteSet *VoteSet) MarshalJSON() ([]byte, error) { 490 voteSet.mtx.Lock() 491 defer voteSet.mtx.Unlock() 492 return cdc.MarshalJSON(VoteSetJSON{ 493 voteSet.voteStrings(), 494 voteSet.bitArrayString(), 495 voteSet.peerMaj23s, 496 }) 497 } 498 499 // More human readable JSON of the vote set 500 // NOTE: insufficient for unmarshalling from (compressed votes) 501 // TODO: make the peerMaj23s nicer to read (eg just the block hash) 502 type VoteSetJSON struct { 503 Votes []string `json:"votes"` 504 VotesBitArray string `json:"votes_bit_array"` 505 PeerMaj23s map[P2PID]BlockID `json:"peer_maj_23s"` 506 } 507 508 // Return the bit-array of votes including 509 // the fraction of power that has voted like: 510 // "BA{29:xx__x__x_x___x__x_______xxx__} 856/1304 = 0.66" 511 func (voteSet *VoteSet) BitArrayString() string { 512 voteSet.mtx.Lock() 513 defer voteSet.mtx.Unlock() 514 return voteSet.bitArrayString() 515 } 516 517 func (voteSet *VoteSet) bitArrayString() string { 518 bAString := voteSet.votesBitArray.String() 519 voted, total, fracVoted := voteSet.sumTotalFrac() 520 return fmt.Sprintf("%s %d/%d = %.2f", bAString, voted, total, fracVoted) 521 } 522 523 // Returns a list of votes compressed to more readable strings. 524 func (voteSet *VoteSet) VoteStrings() []string { 525 voteSet.mtx.Lock() 526 defer voteSet.mtx.Unlock() 527 return voteSet.voteStrings() 528 } 529 530 func (voteSet *VoteSet) voteStrings() []string { 531 voteStrings := make([]string, len(voteSet.votes)) 532 for i, vote := range voteSet.votes { 533 if vote == nil { 534 voteStrings[i] = nilVoteStr 535 } else { 536 voteStrings[i] = vote.String() 537 } 538 } 539 return voteStrings 540 } 541 542 func (voteSet *VoteSet) StringShort() string { 543 if voteSet == nil { 544 return "nil-VoteSet" 545 } 546 voteSet.mtx.Lock() 547 defer voteSet.mtx.Unlock() 548 _, _, frac := voteSet.sumTotalFrac() 549 return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v +2/3:%v(%v) %v %v}`, 550 voteSet.height, voteSet.round, voteSet.signedMsgType, voteSet.maj23, frac, voteSet.votesBitArray, voteSet.peerMaj23s) 551 } 552 553 // return the power voted, the total, and the fraction 554 func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { 555 voted, total := voteSet.sum, voteSet.valSet.TotalVotingPower() 556 fracVoted := float64(voted) / float64(total) 557 return voted, total, fracVoted 558 } 559 560 //-------------------------------------------------------------------------------- 561 // Commit 562 563 // MakeCommit constructs a Commit from the VoteSet. It only includes precommits 564 // for the block, which has 2/3+ majority, and nil. 565 // 566 // Panics if the vote type is not PrecommitType or if there's no +2/3 votes for 567 // a single block. 568 func (voteSet *VoteSet) MakeCommit() *Commit { 569 if voteSet.signedMsgType != PrecommitType { 570 panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType") 571 } 572 voteSet.mtx.Lock() 573 defer voteSet.mtx.Unlock() 574 575 // Make sure we have a 2/3 majority 576 if voteSet.maj23 == nil { 577 panic("Cannot MakeCommit() unless a blockhash has +2/3") 578 } 579 580 // For every validator, get the precommit 581 commitSigs := make([]CommitSig, len(voteSet.votes)) 582 for i, v := range voteSet.votes { 583 commitSig := v.CommitSig() 584 // if block ID exists but doesn't match, exclude sig 585 if commitSig.ForBlock() && !v.BlockID.Equals(*voteSet.maj23) { 586 commitSig = NewCommitSigAbsent() 587 } 588 commitSigs[i] = commitSig 589 } 590 591 return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) 592 } 593 594 //-------------------------------------------------------------------------------- 595 596 /* 597 Votes for a particular block 598 There are two ways a *blockVotes gets created for a blockKey. 599 1. first (non-conflicting) vote of a validator w/ blockKey (peerMaj23=false) 600 2. A peer claims to have a 2/3 majority w/ blockKey (peerMaj23=true) 601 */ 602 type blockVotes struct { 603 peerMaj23 bool // peer claims to have maj23 604 bitArray *bits.BitArray // valIndex -> hasVote? 605 votes []*Vote // valIndex -> *Vote 606 sum int64 // vote sum 607 } 608 609 func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes { 610 return &blockVotes{ 611 peerMaj23: peerMaj23, 612 bitArray: bits.NewBitArray(numValidators), 613 votes: make([]*Vote, numValidators), 614 sum: 0, 615 } 616 } 617 618 func (vs *blockVotes) addVerifiedVote(vote *Vote, votingPower int64) { 619 valIndex := vote.ValidatorIndex 620 if existing := vs.votes[valIndex]; existing == nil { 621 vs.bitArray.SetIndex(valIndex, true) 622 vs.votes[valIndex] = vote 623 vs.sum += votingPower 624 } 625 } 626 627 func (vs *blockVotes) getByIndex(index int) *Vote { 628 if vs == nil { 629 return nil 630 } 631 return vs.votes[index] 632 } 633 634 //-------------------------------------------------------------------------------- 635 636 // Common interface between *consensus.VoteSet and types.Commit 637 type VoteSetReader interface { 638 GetHeight() int64 639 GetRound() int 640 Type() byte 641 Size() int 642 BitArray() *bits.BitArray 643 GetByIndex(int) *Vote 644 IsCommit() bool 645 }