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 }