github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/follower_loop.go (about) 1 package hotstuff 2 3 import ( 4 "time" 5 6 "github.com/rs/zerolog" 7 8 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/runner" 10 "github.com/koko1123/flow-go-1/model/flow" 11 "github.com/koko1123/flow-go-1/utils/logging" 12 ) 13 14 // proposalTask struct used to send a proposal and done channel in one message 15 type proposalTask struct { 16 *model.Proposal 17 done chan struct{} // closed when the proposal has finished being processed 18 } 19 20 // FollowerLoop implements interface FollowerLoop 21 type FollowerLoop struct { 22 log zerolog.Logger 23 followerLogic FollowerLogic 24 proposals chan *proposalTask 25 26 runner runner.SingleRunner // lock for preventing concurrent state transitions 27 } 28 29 // NewFollowerLoop creates an instance of EventLoop 30 func NewFollowerLoop(log zerolog.Logger, followerLogic FollowerLogic) (*FollowerLoop, error) { 31 return &FollowerLoop{ 32 log: log, 33 followerLogic: followerLogic, 34 proposals: make(chan *proposalTask), 35 runner: runner.NewSingleRunner(), 36 }, nil 37 } 38 39 // SubmitProposal feeds a new block proposal (header) into the FollowerLoop. 40 // This method blocks until the proposal is accepted to the event queue. 41 // 42 // Block proposals must be submitted in order, i.e. a proposal's parent must 43 // have been previously processed by the FollowerLoop. 44 func (fl *FollowerLoop) SubmitProposal(proposalHeader *flow.Header, parentView uint64) <-chan struct{} { 45 received := time.Now() 46 proposal := &proposalTask{ 47 Proposal: model.ProposalFromFlow(proposalHeader, parentView), 48 done: make(chan struct{}), 49 } 50 51 fl.proposals <- proposal 52 53 // the busy duration is measured as how long it takes from a block being 54 // received to a block being handled by the event handler. 55 busyDuration := time.Since(received) 56 fl.log.Debug().Hex("block_id", logging.ID(proposal.Block.BlockID)). 57 Uint64("view", proposal.Block.View). 58 Dur("busy_duration", busyDuration). 59 Msg("busy duration to handle a proposal") 60 61 return proposal.done 62 } 63 64 // loop will synchronously processes all events. 65 // All errors from FollowerLogic are fatal: 66 // - known critical error: some prerequisites of the HotStuff follower have been broken 67 // - unknown critical error: bug-related 68 func (fl *FollowerLoop) loop() { 69 shutdownSignal := fl.runner.ShutdownSignal() 70 for { 71 select { // to ensure we are not skipping over a termination signal 72 case <-shutdownSignal: 73 return 74 default: 75 } 76 77 select { 78 case p := <-fl.proposals: 79 err := fl.followerLogic.AddBlock(p.Proposal) 80 close(p.done) 81 82 if err != nil { // all errors are fatal 83 fl.log.Error(). 84 Hex("block_id", logging.ID(p.Block.BlockID)). 85 Uint64("view", p.Block.View). 86 Err(err). 87 Msg("terminating FollowerLoop") 88 return 89 } 90 case <-shutdownSignal: 91 return 92 } 93 } 94 } 95 96 // Ready implements interface module.ReadyDoneAware 97 // Method call will starts the FollowerLoop's internal processing loop. 98 // Multiple calls are handled gracefully and the follower will only start once. 99 func (fl *FollowerLoop) Ready() <-chan struct{} { 100 return fl.runner.Start(fl.loop) 101 } 102 103 // Done implements interface module.ReadyDoneAware 104 func (fl *FollowerLoop) Done() <-chan struct{} { 105 return fl.runner.Abort() 106 }