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

     1  package eventhandler
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  	"github.com/koko1123/flow-go-1/module/mempool"
    14  )
    15  
    16  // EventHandler is the main handler for individual events that trigger state transition.
    17  // It exposes API to handle one event at a time synchronously. The caller is
    18  // responsible for running the event loop to ensure that.
    19  type EventHandler struct {
    20  	log            zerolog.Logger
    21  	paceMaker      hotstuff.PaceMaker
    22  	blockProducer  hotstuff.BlockProducer
    23  	forks          hotstuff.Forks
    24  	persist        hotstuff.Persister
    25  	communicator   hotstuff.Communicator
    26  	committee      hotstuff.Committee
    27  	voteAggregator hotstuff.VoteAggregator
    28  	voter          hotstuff.Voter
    29  	validator      hotstuff.Validator
    30  	notifier       hotstuff.Consumer
    31  	ownProposal    flow.Identifier
    32  }
    33  
    34  var _ hotstuff.EventHandler = (*EventHandler)(nil)
    35  
    36  // NewEventHandler creates an EventHandler instance with initial components.
    37  func NewEventHandler(
    38  	log zerolog.Logger,
    39  	paceMaker hotstuff.PaceMaker,
    40  	blockProducer hotstuff.BlockProducer,
    41  	forks hotstuff.Forks,
    42  	persist hotstuff.Persister,
    43  	communicator hotstuff.Communicator,
    44  	committee hotstuff.Committee,
    45  	voteAggregator hotstuff.VoteAggregator,
    46  	voter hotstuff.Voter,
    47  	validator hotstuff.Validator,
    48  	notifier hotstuff.Consumer,
    49  ) (*EventHandler, error) {
    50  	e := &EventHandler{
    51  		log:            log.With().Str("hotstuff", "participant").Logger(),
    52  		paceMaker:      paceMaker,
    53  		blockProducer:  blockProducer,
    54  		forks:          forks,
    55  		persist:        persist,
    56  		communicator:   communicator,
    57  		voter:          voter,
    58  		validator:      validator,
    59  		committee:      committee,
    60  		voteAggregator: voteAggregator,
    61  		notifier:       notifier,
    62  		ownProposal:    flow.ZeroID,
    63  	}
    64  	return e, nil
    65  }
    66  
    67  // OnQCConstructed processes constructed QC by our vote aggregator
    68  func (e *EventHandler) OnQCConstructed(qc *flow.QuorumCertificate) error {
    69  	curView := e.paceMaker.CurView()
    70  
    71  	log := e.log.With().
    72  		Uint64("cur_view", curView).
    73  		Uint64("qc_view", qc.View).
    74  		Hex("qc_block_id", qc.BlockID[:]).
    75  		Logger()
    76  
    77  	e.notifier.OnQcConstructedFromVotes(curView, qc)
    78  	defer e.notifier.OnEventProcessed()
    79  
    80  	log.Debug().Msg("received constructed QC")
    81  
    82  	// ignore stale qc
    83  	if qc.View < e.forks.FinalizedView() {
    84  		log.Debug().Msg("stale qc")
    85  		return nil
    86  	}
    87  
    88  	return e.processQC(qc)
    89  }
    90  
    91  // OnReceiveProposal processes the block when a block proposal is received.
    92  // It is assumed that the block proposal is incorporated. (its parent can be found
    93  // in the forks)
    94  func (e *EventHandler) OnReceiveProposal(proposal *model.Proposal) error {
    95  
    96  	block := proposal.Block
    97  	curView := e.paceMaker.CurView()
    98  
    99  	log := e.log.With().
   100  		Uint64("cur_view", curView).
   101  		Uint64("block_view", block.View).
   102  		Hex("block_id", block.BlockID[:]).
   103  		Uint64("qc_view", block.QC.View).
   104  		Hex("proposer_id", block.ProposerID[:]).
   105  		Logger()
   106  
   107  	e.notifier.OnReceiveProposal(curView, proposal)
   108  	defer e.notifier.OnEventProcessed()
   109  	log.Debug().Msg("proposal forwarded from compliance engine")
   110  
   111  	// ignore stale proposals
   112  	if block.View < e.forks.FinalizedView() {
   113  		log.Debug().Msg("stale proposal")
   114  		return nil
   115  	}
   116  
   117  	// we skip validation for our last own proposal
   118  	if proposal.Block.BlockID != e.ownProposal {
   119  
   120  		// validate the block. exit if the proposal is invalid
   121  		err := e.validator.ValidateProposal(proposal)
   122  		if model.IsInvalidBlockError(err) {
   123  			perr := e.voteAggregator.InvalidBlock(proposal)
   124  			if mempool.IsDecreasingPruningHeightError(perr) {
   125  				log.Warn().Err(err).Msgf("invalid block proposal, but vote aggregator has pruned this height: %v", perr)
   126  				return nil
   127  			}
   128  
   129  			if perr != nil {
   130  				return fmt.Errorf("vote aggregator could not process invalid block proposal %v, err %v: %w",
   131  					block.BlockID, err, perr)
   132  			}
   133  
   134  			log.Warn().Err(err).Msg("invalid block proposal")
   135  			return nil
   136  		}
   137  
   138  		if errors.Is(err, model.ErrUnverifiableBlock) {
   139  			log.Warn().Err(err).Msg("unverifiable block proposal")
   140  
   141  			// even if the block is unverifiable because the QC has been
   142  			// pruned, it still needs to be added to the forks, otherwise,
   143  			// a new block with a QC to this block will fail to be added
   144  			// to forks and crash the event loop.
   145  		} else if err != nil {
   146  			return fmt.Errorf("cannot validate proposal (%x): %w", block.BlockID, err)
   147  		}
   148  	}
   149  
   150  	// notify vote aggregator about a new block, so that it can start verifying
   151  	// votes for it.
   152  	err := e.voteAggregator.AddBlock(proposal)
   153  	if err != nil {
   154  		if !mempool.IsDecreasingPruningHeightError(err) {
   155  			return fmt.Errorf("could not add block (%v) to vote aggregator: %w", block.BlockID, err)
   156  		}
   157  	}
   158  
   159  	// store the block.
   160  	err = e.forks.AddBlock(block)
   161  	if err != nil {
   162  		return fmt.Errorf("cannot add block to fork (%x): %w", block.BlockID, err)
   163  	}
   164  
   165  	// if the block is not for the current view, then process the QC
   166  	if block.View != curView {
   167  		return e.processQC(block.QC)
   168  	}
   169  
   170  	// if the block is for the current view, then check whether to vote for this block
   171  	err = e.processBlockForCurrentView(block)
   172  	if err != nil {
   173  		return fmt.Errorf("failed processing current block: %w", err)
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  // TimeoutChannel returns the channel for subscribing the waiting timeout on receiving
   180  // block or votes for the current view.
   181  func (e *EventHandler) TimeoutChannel() <-chan time.Time {
   182  	return e.paceMaker.TimeoutChannel()
   183  }
   184  
   185  // OnLocalTimeout is called when the timeout event created by pacemaker looped through the
   186  // event loop.
   187  func (e *EventHandler) OnLocalTimeout() error {
   188  
   189  	curView := e.paceMaker.CurView()
   190  	newView := e.paceMaker.OnTimeout()
   191  	defer e.notifier.OnEventProcessed()
   192  
   193  	log := e.log.With().
   194  		Uint64("cur_view", curView).
   195  		Uint64("new_view", newView.View).
   196  		Logger()
   197  	// 	notifications about time-outs and view-changes are generated by PaceMaker; no need to send a notification here
   198  	log.Debug().Msg("timeout received from event loop")
   199  
   200  	if curView == newView.View {
   201  		return fmt.Errorf("OnLocalTimeout should guarantee that the pacemaker should go to next view, but didn't: (curView: %v, newView: %v)", curView, newView.View)
   202  	}
   203  
   204  	// current view has changed, go to new view
   205  	err := e.startNewView()
   206  	if err != nil {
   207  		return fmt.Errorf("could not start new view: %w", err)
   208  	}
   209  
   210  	log.Debug().Msg("local timeout processed")
   211  
   212  	return nil
   213  }
   214  
   215  // Start will start the pacemaker's timer and start the new view
   216  func (e *EventHandler) Start() error {
   217  	e.paceMaker.Start()
   218  	return e.startNewView()
   219  }
   220  
   221  // startNewView will only be called when there is a view change from pacemaker.
   222  // It reads the current view, and check if it needs to propose or vote in this view.
   223  func (e *EventHandler) startNewView() error {
   224  
   225  	// track the start time
   226  	start := time.Now()
   227  
   228  	curView := e.paceMaker.CurView()
   229  
   230  	err := e.persist.PutStarted(curView)
   231  	if err != nil {
   232  		return fmt.Errorf("could not persist current view: %w", err)
   233  	}
   234  
   235  	currentLeader, err := e.committee.LeaderForView(curView)
   236  	if err != nil {
   237  		return fmt.Errorf("failed to determine primary for new view %d: %w", curView, err)
   238  	}
   239  
   240  	log := e.log.With().
   241  		Uint64("cur_view", curView).
   242  		Hex("leader_id", currentLeader[:]).Logger()
   243  	log.Debug().
   244  		Uint64("finalized_view", e.forks.FinalizedView()).
   245  		Msg("entering new view")
   246  	e.notifier.OnEnteringView(curView, currentLeader)
   247  
   248  	if e.committee.Self() == currentLeader {
   249  		log.Debug().Msg("generating block proposal as leader")
   250  
   251  		// as the leader of the current view,
   252  		// build the block proposal for the current view
   253  		qc, _, err := e.forks.MakeForkChoice(curView)
   254  		if err != nil {
   255  			return fmt.Errorf("can not make fork choice for view %v: %w", curView, err)
   256  		}
   257  
   258  		proposal, err := e.blockProducer.MakeBlockProposal(qc, curView)
   259  		if err != nil {
   260  			return fmt.Errorf("can not make block proposal for curView %v: %w", curView, err)
   261  		}
   262  		e.notifier.OnProposingBlock(proposal)
   263  
   264  		block := proposal.Block
   265  		log.Debug().
   266  			Uint64("block_view", block.View).
   267  			Hex("block_id", block.BlockID[:]).
   268  			Uint64("parent_view", qc.View).
   269  			Hex("parent_id", qc.BlockID[:]).
   270  			Hex("signer", block.ProposerID[:]).
   271  			Msg("forwarding proposal to communicator for broadcasting")
   272  
   273  		// broadcast the proposal
   274  		header := model.ProposalToFlow(proposal)
   275  		delay := e.paceMaker.BlockRateDelay()
   276  		elapsed := time.Since(start)
   277  		if elapsed > delay {
   278  			delay = 0
   279  		} else {
   280  			delay = delay - elapsed
   281  		}
   282  		err = e.communicator.BroadcastProposalWithDelay(header, delay)
   283  		if err != nil {
   284  			log.Warn().Err(err).Msg("could not forward proposal")
   285  		}
   286  		// mark our own proposals to avoid double validation
   287  		e.ownProposal = proposal.Block.BlockID
   288  
   289  		// We return here to correspond to the HotStuff state machine.
   290  		return nil
   291  		// Algorithmically, this return statement is optional:
   292  		//  * If this replica is the leader for the current view, there can be no valid proposal from any
   293  		//    other node. This replica's proposal is the only valid proposal.
   294  		//  * This replica's proposal got just sent out above. It will enter the HotStuff logic from the
   295  		//    EventLoop. In other words, the own proposal is not yet stored in Forks.
   296  		//    Hence, Forks cannot contain _any_ valid proposal for the current view.
   297  		//  Therefore, if this replica is the leader, the following code is a no-op.
   298  	}
   299  
   300  	// as a replica of the current view, find and process the block for the current view
   301  	blocks := e.forks.GetBlocksForView(curView)
   302  	if len(blocks) == 0 {
   303  		// if there is no block stored before for the current view, then exit and keep waiting
   304  		log.Debug().Msg("waiting for proposal from leader")
   305  		return nil
   306  	}
   307  
   308  	// when there are multiple block proposals, we will just pick the first one.
   309  	// forks is responsible for slashing double proposal behavior, and
   310  	// event handler is aware of double proposals, but picking any should work and
   311  	// won't hurt safety
   312  	block := blocks[0]
   313  
   314  	log.Debug().
   315  		Uint64("block_view", block.View).
   316  		Hex("block_id", block.BlockID[:]).
   317  		Uint64("parent_view", block.QC.View).
   318  		Hex("parent_id", block.QC.BlockID[:]).
   319  		Hex("signer", block.ProposerID[:]).
   320  		Msg("processing cached proposal from leader")
   321  
   322  	return e.processBlockForCurrentView(block)
   323  }
   324  
   325  // processBlockForCurrentView processes the block for the current view.
   326  // It is called AFTER the block has been stored or found in Forks
   327  // It checks whether to vote for this block.
   328  // It might trigger a view change to go to a different view, which might re-enter this function.
   329  func (e *EventHandler) processBlockForCurrentView(block *model.Block) error {
   330  	// sanity check that block is really for the current view:
   331  	curView := e.paceMaker.CurView()
   332  	if block.View != curView {
   333  		return fmt.Errorf("sanity check fails: block proposal's view does not match with curView, (blockView: %v, curView: %v)",
   334  			block.View, curView)
   335  	}
   336  	// leader (node ID) for next view
   337  	nextLeader, err := e.committee.LeaderForView(curView + 1)
   338  	if err != nil {
   339  		return fmt.Errorf("failed to determine primary for next view %d: %w", curView+1, err)
   340  	}
   341  
   342  	// voter performs all the checks to decide whether to vote for this block or not.
   343  	err = e.ownVote(block, curView, nextLeader)
   344  	if err != nil {
   345  		return fmt.Errorf("unexpected error in voting logic: %w", err)
   346  	}
   347  
   348  	// Inform PaceMaker that we've processed a block for the current view. We expect
   349  	// a view change if an only if we are _not_ the next leader. We perform a sanity
   350  	// check here, because a wrong view change can have disastrous consequences.
   351  	isSelfNextLeader := e.committee.Self() == nextLeader
   352  	_, viewChanged := e.paceMaker.UpdateCurViewWithBlock(block, isSelfNextLeader)
   353  	if viewChanged == isSelfNextLeader {
   354  		if isSelfNextLeader {
   355  			return fmt.Errorf("I am primary for next view (%v) and should be collecting votes, but pacemaker triggered already view change", curView+1)
   356  		}
   357  		return fmt.Errorf("pacemaker should trigger a view change to net view (%v), but didn't", curView+1)
   358  	}
   359  
   360  	if viewChanged {
   361  		return e.startNewView()
   362  	}
   363  	return nil
   364  }
   365  
   366  // ownVote generates and forwards the own vote, if we decide to vote.
   367  // Any errors are potential symptoms of uncovered edge cases or corrupted internal state (fatal).
   368  func (e *EventHandler) ownVote(block *model.Block, curView uint64, nextLeader flow.Identifier) error {
   369  	log := e.log.With().
   370  		Uint64("block_view", block.View).
   371  		Hex("block_id", block.BlockID[:]).
   372  		Uint64("parent_view", block.QC.View).
   373  		Hex("parent_id", block.QC.BlockID[:]).
   374  		Hex("signer", block.ProposerID[:]).
   375  		Logger()
   376  
   377  	// voter performs all the checks to decide whether to vote for this block or not.
   378  	ownVote, err := e.voter.ProduceVoteIfVotable(block, curView)
   379  	if err != nil {
   380  		if !model.IsNoVoteError(err) {
   381  			// unknown error, exit the event loop
   382  			return fmt.Errorf("could not produce vote: %w", err)
   383  		}
   384  		log.Debug().Err(err).Msg("should not vote for this block")
   385  		return nil
   386  	}
   387  
   388  	// The following code is only reached, if this replica has produced a vote.
   389  	// Send the vote to the next leader (or directly process it, if I am the next leader).
   390  	e.notifier.OnVoting(ownVote)
   391  	log.Debug().Msg("forwarding vote to compliance engine")
   392  	if e.committee.Self() == nextLeader { // I am the next leader
   393  		e.voteAggregator.AddVote(ownVote)
   394  	} else {
   395  		err = e.communicator.SendVote(ownVote.BlockID, ownVote.View, ownVote.SigData, nextLeader)
   396  		if err != nil {
   397  			log.Warn().Err(err).Msg("could not forward vote")
   398  		}
   399  	}
   400  	return nil
   401  }
   402  
   403  // processQC stores the QC and check whether the QC will trigger view change.
   404  // If triggered, then go to the new view.
   405  func (e *EventHandler) processQC(qc *flow.QuorumCertificate) error {
   406  
   407  	log := e.log.With().
   408  		Uint64("block_view", qc.View).
   409  		Hex("block_id", qc.BlockID[:]).
   410  		Logger()
   411  
   412  	err := e.forks.AddQC(qc)
   413  	if err != nil {
   414  		return fmt.Errorf("cannot add QC to forks: %w", err)
   415  	}
   416  
   417  	_, viewChanged := e.paceMaker.UpdateCurViewWithQC(qc)
   418  	if !viewChanged {
   419  		log.Debug().Msg("QC didn't trigger view change, nothing to do")
   420  		return nil
   421  	}
   422  	log.Debug().Msg("QC triggered view change, starting new view now")
   423  
   424  	// current view has changed, go to new view
   425  	return e.startNewView()
   426  }