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