github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/types/vote_set.go (about)

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