github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/voter/voter.go (about)

     1  package voter
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
     7  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
     8  )
     9  
    10  // Voter produces votes for the given block
    11  type Voter struct {
    12  	signer        hotstuff.Signer
    13  	forks         hotstuff.ForksReader
    14  	persist       hotstuff.Persister
    15  	committee     hotstuff.Committee // only produce votes when we are valid committee members
    16  	lastVotedView uint64             // need to keep track of the last view we voted for so we don't double vote accidentally
    17  }
    18  
    19  // New creates a new Voter instance
    20  func New(
    21  	signer hotstuff.Signer,
    22  	forks hotstuff.ForksReader,
    23  	persist hotstuff.Persister,
    24  	committee hotstuff.Committee,
    25  	lastVotedView uint64,
    26  ) *Voter {
    27  
    28  	return &Voter{
    29  		signer:        signer,
    30  		forks:         forks,
    31  		persist:       persist,
    32  		committee:     committee,
    33  		lastVotedView: lastVotedView,
    34  	}
    35  }
    36  
    37  // ProduceVoteIfVotable will make a decision on whether it will vote for the given proposal, the returned
    38  // error indicates whether to vote or not.
    39  // In order to ensure that only a safe node will be voted, Voter will ask Forks whether a vote is a safe node or not.
    40  // The curView is taken as input to ensure Voter will only vote for proposals at current view and prevent double voting.
    41  // Returns:
    42  //   - (vote, nil): On the _first_ block for the current view that is safe to vote for.
    43  //     Subsequently, voter does _not_ vote for any other block with the same (or lower) view.
    44  //   - (nil, model.NoVoteError): If the voter decides that it does not want to vote for the given block.
    45  //     This is a sentinel error and _expected_ during normal operation.
    46  //
    47  // All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal).
    48  func (v *Voter) ProduceVoteIfVotable(block *model.Block, curView uint64) (*model.Vote, error) {
    49  	// sanity checks:
    50  	if curView != block.View {
    51  		return nil, fmt.Errorf("expecting block for current view %d, but block's view is %d", curView, block.View)
    52  	}
    53  	if curView <= v.lastVotedView {
    54  		return nil, fmt.Errorf("current view (%d) must be larger than the last voted view (%d)", curView, v.lastVotedView)
    55  	}
    56  
    57  	// Do not produce a vote for blocks where we are not a valid committee member.
    58  	// HotStuff will ask for a vote for the first block of the next epoch, even if we
    59  	// have zero weight in the next epoch. Such vote can't be used to produce valid QCs.
    60  	_, err := v.committee.Identity(block.BlockID, v.committee.Self())
    61  	if model.IsInvalidSignerError(err) {
    62  		return nil, model.NoVoteError{Msg: "not voting committee member for block"}
    63  	}
    64  	if err != nil {
    65  		return nil, fmt.Errorf("could not get self identity: %w", err)
    66  	}
    67  
    68  	// generate vote if block is safe to vote for
    69  	if !v.forks.IsSafeBlock(block) {
    70  		return nil, model.NoVoteError{Msg: "not safe block"}
    71  	}
    72  	vote, err := v.signer.CreateVote(block)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("could not vote for block: %w", err)
    75  	}
    76  
    77  	// vote for the current view has been produced, update lastVotedView
    78  	// to prevent from voting for the same view again
    79  	v.lastVotedView = curView
    80  	err = v.persist.PutVoted(curView)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("could not persist last voted: %w", err)
    83  	}
    84  
    85  	return vote, nil
    86  }