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