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