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  }