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

     1  package follower
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/rs/zerolog"
     8  
     9  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff/forks"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    12  	"github.com/koko1123/flow-go-1/utils/logging"
    13  )
    14  
    15  // FollowerLogic runs in non-consensus nodes. It informs other components within the node
    16  // about finalization of blocks. The consensus Follower consumes all block proposals
    17  // broadcasts by the consensus node, verifies the block header and locally evaluates
    18  // the finalization rules.
    19  //
    20  // CAUTION: Follower is NOT CONCURRENCY safe
    21  type FollowerLogic struct {
    22  	log               zerolog.Logger
    23  	validator         hotstuff.Validator
    24  	finalizationLogic forks.Finalizer
    25  }
    26  
    27  // New creates a new FollowerLogic instance
    28  func New(
    29  	log zerolog.Logger,
    30  	validator hotstuff.Validator,
    31  	finalizationLogic forks.Finalizer,
    32  ) (*FollowerLogic, error) {
    33  	return &FollowerLogic{
    34  		log:               log.With().Str("hotstuff", "follower").Logger(),
    35  		validator:         validator,
    36  		finalizationLogic: finalizationLogic,
    37  	}, nil
    38  }
    39  
    40  // FinalizedBlock returns the latest finalized block
    41  func (f *FollowerLogic) FinalizedBlock() *model.Block {
    42  	return f.finalizationLogic.FinalizedBlock()
    43  }
    44  
    45  // AddBlock processes the given block proposal
    46  func (f *FollowerLogic) AddBlock(blockProposal *model.Proposal) error {
    47  	// validate the block. skip if the proposal is invalid
    48  	err := f.validator.ValidateProposal(blockProposal)
    49  	if model.IsInvalidBlockError(err) {
    50  		f.log.Warn().Err(err).Hex("block_id", logging.ID(blockProposal.Block.BlockID)).
    51  			Msg("invalid proposal")
    52  		return nil
    53  	}
    54  	if errors.Is(err, model.ErrUnverifiableBlock) {
    55  		f.log.Warn().
    56  			Hex("block_id", logging.ID(blockProposal.Block.BlockID)).
    57  			Hex("qc_block_id", logging.ID(blockProposal.Block.QC.BlockID)).
    58  			Msg("unverifiable proposal")
    59  
    60  		// even if the block is unverifiable because the QC has been
    61  		// pruned, it still needs to be added to the forks, otherwise,
    62  		// a new block with a QC to this block will fail to be added
    63  		// to forks and crash the event loop.
    64  	} else if err != nil {
    65  		return fmt.Errorf("cannot validate block proposal %x: %w", blockProposal.Block.BlockID, err)
    66  	}
    67  
    68  	// as a sanity check, we run the finalization logic's internal validation on the block
    69  	if err := f.finalizationLogic.VerifyBlock(blockProposal.Block); err != nil {
    70  		// this should never happen: the block was found to be valid by the validator
    71  		// if the finalization logic's internal validation errors, we have a bug
    72  		return fmt.Errorf("invaid block passed validation: %w", err)
    73  	}
    74  	err = f.finalizationLogic.AddBlock(blockProposal.Block)
    75  	if err != nil {
    76  		return fmt.Errorf("finalization logic cannot process block proposal %x: %w", blockProposal.Block.BlockID, err)
    77  	}
    78  
    79  	return nil
    80  }