github.com/Finschia/ostracon@v1.1.5/types/vote_set.go (about)

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