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  }