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  }