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

     1  package recovery
     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/model"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/utils/logging"
    13  )
    14  
    15  // Recover implements the core logic for recovering HotStuff state after a restart.
    16  // It accepts the finalized block and a list of pending blocks that have been
    17  // received but not finalized, and that share the latest finalized block as a common
    18  // ancestor.
    19  func Recover(log zerolog.Logger, finalized *flow.Header, pending []*flow.Header, validator hotstuff.Validator, onProposal func(*model.Proposal) error) error {
    20  	blocks := make(map[flow.Identifier]*flow.Header, len(pending)+1)
    21  
    22  	// finalized is the root
    23  	blocks[finalized.ID()] = finalized
    24  
    25  	log.Info().Int("total", len(pending)).Msgf("recovery started")
    26  
    27  	// add all pending blocks to forks
    28  	for _, header := range pending {
    29  		blocks[header.ID()] = header
    30  
    31  		// parent must exist in storage, because the index has the parent ID
    32  		parent, ok := blocks[header.ParentID]
    33  		if !ok {
    34  			return fmt.Errorf("could not find the parent block %x for header %x", header.ParentID, header.ID())
    35  		}
    36  
    37  		// convert the header into a proposal
    38  		proposal := model.ProposalFromFlow(header, parent.View)
    39  
    40  		// verify the proposal
    41  		err := validator.ValidateProposal(proposal)
    42  		if model.IsInvalidBlockError(err) {
    43  			log.Warn().
    44  				Hex("block_id", logging.ID(proposal.Block.BlockID)).
    45  				Err(err).
    46  				Msg("invalid proposal")
    47  			continue
    48  		}
    49  		if errors.Is(err, model.ErrUnverifiableBlock) {
    50  			log.Warn().
    51  				Hex("block_id", logging.ID(proposal.Block.BlockID)).
    52  				Hex("qc_block_id", logging.ID(proposal.Block.QC.BlockID)).
    53  				Msg("unverifiable proposal")
    54  
    55  			// even if the block is unverifiable because the QC has been
    56  			// pruned, it still needs to be added to the forks, otherwise,
    57  			// a new block with a QC to this block will fail to be added
    58  			// to forks and crash the event loop.
    59  		} else if err != nil {
    60  			return fmt.Errorf("cannot validate proposal (%x): %w", proposal.Block.BlockID, err)
    61  		}
    62  
    63  		err = onProposal(proposal)
    64  		if err != nil {
    65  			return fmt.Errorf("cannot recover proposal: %w", err)
    66  		}
    67  	}
    68  
    69  	log.Info().Msgf("recovery completed")
    70  
    71  	return nil
    72  }