github.com/fiagdao/tendermint@v0.32.11-0.20220824195748-2087fcc480c1/consensus/types/height_vote_set.go (about)

     1  package types
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/tendermint/tendermint/p2p"
    10  	"github.com/tendermint/tendermint/types"
    11  )
    12  
    13  type RoundVoteSet struct {
    14  	Prevotes   *types.VoteSet
    15  	Precommits *types.VoteSet
    16  }
    17  
    18  var (
    19  	ErrGotVoteFromUnwantedRound = errors.New(
    20  		"peer has sent a vote that does not match our round for more than one round",
    21  	)
    22  )
    23  
    24  /*
    25  Keeps track of all VoteSets from round 0 to round 'round'.
    26  
    27  Also keeps track of up to one RoundVoteSet greater than
    28  'round' from each peer, to facilitate catchup syncing of commits.
    29  
    30  A commit is +2/3 precommits for a block at a round,
    31  but which round is not known in advance, so when a peer
    32  provides a precommit for a round greater than mtx.round,
    33  we create a new entry in roundVoteSets but also remember the
    34  peer to prevent abuse.
    35  We let each peer provide us with up to 2 unexpected "catchup" rounds.
    36  One for their LastCommit round, and another for the official commit round.
    37  */
    38  type HeightVoteSet struct {
    39  	chainID string
    40  	height  int64
    41  	valSet  *types.ValidatorSet
    42  
    43  	mtx               sync.Mutex
    44  	round             int                  // max tracked round
    45  	roundVoteSets     map[int]RoundVoteSet // keys: [0...round]
    46  	peerCatchupRounds map[p2p.ID][]int     // keys: peer.ID; values: at most 2 rounds
    47  }
    48  
    49  func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
    50  	hvs := &HeightVoteSet{
    51  		chainID: chainID,
    52  	}
    53  	hvs.Reset(height, valSet)
    54  	return hvs
    55  }
    56  
    57  func (hvs *HeightVoteSet) Reset(height int64, valSet *types.ValidatorSet) {
    58  	hvs.mtx.Lock()
    59  	defer hvs.mtx.Unlock()
    60  
    61  	hvs.height = height
    62  	hvs.valSet = valSet
    63  	hvs.roundVoteSets = make(map[int]RoundVoteSet)
    64  	hvs.peerCatchupRounds = make(map[p2p.ID][]int)
    65  
    66  	hvs.addRound(0)
    67  	hvs.round = 0
    68  }
    69  
    70  func (hvs *HeightVoteSet) Height() int64 {
    71  	hvs.mtx.Lock()
    72  	defer hvs.mtx.Unlock()
    73  	return hvs.height
    74  }
    75  
    76  func (hvs *HeightVoteSet) Round() int {
    77  	hvs.mtx.Lock()
    78  	defer hvs.mtx.Unlock()
    79  	return hvs.round
    80  }
    81  
    82  // Create more RoundVoteSets up to round.
    83  func (hvs *HeightVoteSet) SetRound(round int) {
    84  	hvs.mtx.Lock()
    85  	defer hvs.mtx.Unlock()
    86  	if hvs.round != 0 && (round < hvs.round+1) {
    87  		panic("SetRound() must increment hvs.round")
    88  	}
    89  	for r := hvs.round + 1; r <= round; r++ {
    90  		if _, ok := hvs.roundVoteSets[r]; ok {
    91  			continue // Already exists because peerCatchupRounds.
    92  		}
    93  		hvs.addRound(r)
    94  	}
    95  	hvs.round = round
    96  }
    97  
    98  func (hvs *HeightVoteSet) addRound(round int) {
    99  	if _, ok := hvs.roundVoteSets[round]; ok {
   100  		panic("addRound() for an existing round")
   101  	}
   102  	// log.Debug("addRound(round)", "round", round)
   103  	prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrevoteType, hvs.valSet)
   104  	precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrecommitType, hvs.valSet)
   105  	hvs.roundVoteSets[round] = RoundVoteSet{
   106  		Prevotes:   prevotes,
   107  		Precommits: precommits,
   108  	}
   109  }
   110  
   111  // Duplicate votes return added=false, err=nil.
   112  // By convention, peerID is "" if origin is self.
   113  func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) {
   114  	hvs.mtx.Lock()
   115  	defer hvs.mtx.Unlock()
   116  	if !types.IsVoteTypeValid(vote.Type) {
   117  		return
   118  	}
   119  	voteSet := hvs.getVoteSet(vote.Round, vote.Type)
   120  	if voteSet == nil {
   121  		if rndz := hvs.peerCatchupRounds[peerID]; len(rndz) < 2 {
   122  			hvs.addRound(vote.Round)
   123  			voteSet = hvs.getVoteSet(vote.Round, vote.Type)
   124  			hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round)
   125  		} else {
   126  			// punish peer
   127  			err = ErrGotVoteFromUnwantedRound
   128  			return
   129  		}
   130  	}
   131  	added, err = voteSet.AddVote(vote)
   132  	return
   133  }
   134  
   135  func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
   136  	hvs.mtx.Lock()
   137  	defer hvs.mtx.Unlock()
   138  	return hvs.getVoteSet(round, types.PrevoteType)
   139  }
   140  
   141  func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
   142  	hvs.mtx.Lock()
   143  	defer hvs.mtx.Unlock()
   144  	return hvs.getVoteSet(round, types.PrecommitType)
   145  }
   146  
   147  // Last round and blockID that has +2/3 prevotes for a particular block or nil.
   148  // Returns -1 if no such round exists.
   149  func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
   150  	hvs.mtx.Lock()
   151  	defer hvs.mtx.Unlock()
   152  	for r := hvs.round; r >= 0; r-- {
   153  		rvs := hvs.getVoteSet(r, types.PrevoteType)
   154  		polBlockID, ok := rvs.TwoThirdsMajority()
   155  		if ok {
   156  			return r, polBlockID
   157  		}
   158  	}
   159  	return -1, types.BlockID{}
   160  }
   161  
   162  func (hvs *HeightVoteSet) getVoteSet(round int, voteType types.SignedMsgType) *types.VoteSet {
   163  	rvs, ok := hvs.roundVoteSets[round]
   164  	if !ok {
   165  		return nil
   166  	}
   167  	switch voteType {
   168  	case types.PrevoteType:
   169  		return rvs.Prevotes
   170  	case types.PrecommitType:
   171  		return rvs.Precommits
   172  	default:
   173  		panic(fmt.Sprintf("Unexpected vote type %X", voteType))
   174  	}
   175  }
   176  
   177  // If a peer claims that it has 2/3 majority for given blockKey, call this.
   178  // NOTE: if there are too many peers, or too much peer churn,
   179  // this can cause memory issues.
   180  // TODO: implement ability to remove peers too
   181  func (hvs *HeightVoteSet) SetPeerMaj23(
   182  	round int,
   183  	voteType types.SignedMsgType,
   184  	peerID p2p.ID,
   185  	blockID types.BlockID) error {
   186  	hvs.mtx.Lock()
   187  	defer hvs.mtx.Unlock()
   188  	if !types.IsVoteTypeValid(voteType) {
   189  		return fmt.Errorf("setPeerMaj23: Invalid vote type %X", voteType)
   190  	}
   191  	voteSet := hvs.getVoteSet(round, voteType)
   192  	if voteSet == nil {
   193  		return nil // something we don't know about yet
   194  	}
   195  	return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
   196  }
   197  
   198  //---------------------------------------------------------
   199  // string and json
   200  
   201  func (hvs *HeightVoteSet) String() string {
   202  	return hvs.StringIndented("")
   203  }
   204  
   205  func (hvs *HeightVoteSet) StringIndented(indent string) string {
   206  	hvs.mtx.Lock()
   207  	defer hvs.mtx.Unlock()
   208  	vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
   209  	// rounds 0 ~ hvs.round inclusive
   210  	for round := 0; round <= hvs.round; round++ {
   211  		voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
   212  		vsStrings = append(vsStrings, voteSetString)
   213  		voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
   214  		vsStrings = append(vsStrings, voteSetString)
   215  	}
   216  	// all other peer catchup rounds
   217  	for round, roundVoteSet := range hvs.roundVoteSets {
   218  		if round <= hvs.round {
   219  			continue
   220  		}
   221  		voteSetString := roundVoteSet.Prevotes.StringShort()
   222  		vsStrings = append(vsStrings, voteSetString)
   223  		voteSetString = roundVoteSet.Precommits.StringShort()
   224  		vsStrings = append(vsStrings, voteSetString)
   225  	}
   226  	return fmt.Sprintf(`HeightVoteSet{H:%v R:0~%v
   227  %s  %v
   228  %s}`,
   229  		hvs.height, hvs.round,
   230  		indent, strings.Join(vsStrings, "\n"+indent+"  "),
   231  		indent)
   232  }
   233  
   234  func (hvs *HeightVoteSet) MarshalJSON() ([]byte, error) {
   235  	hvs.mtx.Lock()
   236  	defer hvs.mtx.Unlock()
   237  
   238  	allVotes := hvs.toAllRoundVotes()
   239  	return cdc.MarshalJSON(allVotes)
   240  }
   241  
   242  func (hvs *HeightVoteSet) toAllRoundVotes() []roundVotes {
   243  	totalRounds := hvs.round + 1
   244  	allVotes := make([]roundVotes, totalRounds)
   245  	// rounds 0 ~ hvs.round inclusive
   246  	for round := 0; round < totalRounds; round++ {
   247  		allVotes[round] = roundVotes{
   248  			Round:              round,
   249  			Prevotes:           hvs.roundVoteSets[round].Prevotes.VoteStrings(),
   250  			PrevotesBitArray:   hvs.roundVoteSets[round].Prevotes.BitArrayString(),
   251  			Precommits:         hvs.roundVoteSets[round].Precommits.VoteStrings(),
   252  			PrecommitsBitArray: hvs.roundVoteSets[round].Precommits.BitArrayString(),
   253  		}
   254  	}
   255  	// TODO: all other peer catchup rounds
   256  	return allVotes
   257  }
   258  
   259  type roundVotes struct {
   260  	Round              int      `json:"round"`
   261  	Prevotes           []string `json:"prevotes"`
   262  	PrevotesBitArray   string   `json:"prevotes_bit_array"`
   263  	Precommits         []string `json:"precommits"`
   264  	PrecommitsBitArray string   `json:"precommits_bit_array"`
   265  }