github.com/koko1123/flow-go-1@v0.29.6/engine/execution/checker/engine.go (about)

     1  package checker
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/rs/zerolog"
     8  
     9  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff/notifications"
    11  	"github.com/koko1123/flow-go-1/engine"
    12  	"github.com/koko1123/flow-go-1/engine/execution/state"
    13  	"github.com/koko1123/flow-go-1/model/flow"
    14  	"github.com/koko1123/flow-go-1/state/protocol"
    15  	"github.com/koko1123/flow-go-1/storage"
    16  )
    17  
    18  type Engine struct {
    19  	notifications.NoopConsumer // satisfy the FinalizationConsumer interface
    20  
    21  	unit      *engine.Unit
    22  	log       zerolog.Logger
    23  	state     protocol.State
    24  	execState state.ExecutionState
    25  	sealsDB   storage.Seals
    26  }
    27  
    28  func New(
    29  	logger zerolog.Logger,
    30  	state protocol.State,
    31  	execState state.ExecutionState,
    32  	sealsDB storage.Seals,
    33  ) *Engine {
    34  	return &Engine{
    35  		unit:      engine.NewUnit(),
    36  		log:       logger.With().Str("engine", "checker").Logger(),
    37  		state:     state,
    38  		execState: execState,
    39  		sealsDB:   sealsDB,
    40  	}
    41  }
    42  
    43  func (e *Engine) Ready() <-chan struct{} {
    44  	// make sure we will run into a crashloop if result gets inconsistent
    45  	// with sealed result.
    46  
    47  	finalized, err := e.state.Final().Head()
    48  
    49  	if err != nil {
    50  		e.log.Fatal().Err(err).Msg("could not get finalized block on startup")
    51  	}
    52  
    53  	err = e.checkLastSealed(finalized.ID())
    54  	if err != nil {
    55  		e.log.Fatal().Err(err).Msg("execution consistency check failed on startup")
    56  	}
    57  	return e.unit.Ready()
    58  }
    59  
    60  func (e *Engine) Done() <-chan struct{} {
    61  	return e.unit.Done()
    62  }
    63  
    64  // when a block is finalized check if the last sealed has been executed,
    65  // if it has been executed, check whether if the sealed result is consistent
    66  // with the executed result
    67  func (e *Engine) OnFinalizedBlock(block *model.Block) {
    68  	err := e.checkLastSealed(block.BlockID)
    69  	if err != nil {
    70  		e.log.Fatal().Err(err).Msg("execution consistency check failed")
    71  	}
    72  }
    73  
    74  func (e *Engine) checkLastSealed(finalizedID flow.Identifier) error {
    75  	// TODO: better to query seals from protocol state,
    76  	// switch to state.Final().LastSealed() when available
    77  	seal, err := e.sealsDB.HighestInFork(finalizedID)
    78  	if err != nil {
    79  		return fmt.Errorf("could not get the last sealed for the finalized block: %w", err)
    80  	}
    81  
    82  	blockID := seal.BlockID
    83  	sealedCommit := seal.FinalState
    84  
    85  	mycommit, err := e.execState.StateCommitmentByBlockID(e.unit.Ctx(), blockID)
    86  	if errors.Is(err, storage.ErrNotFound) {
    87  		// have not executed the sealed block yet
    88  		// in other words, this can't detect execution fork, if the execution is behind
    89  		// the sealing
    90  		return nil
    91  	}
    92  
    93  	if err != nil {
    94  		return fmt.Errorf("could not get my state commitment OnFinalizedBlock, blockID: %v", blockID)
    95  	}
    96  
    97  	if mycommit != sealedCommit {
    98  		sealed, err := e.state.AtBlockID(blockID).Head()
    99  		if err != nil {
   100  			return fmt.Errorf("could not get sealed block when checkLastSealed: %v, err: %w", blockID, err)
   101  		}
   102  
   103  		return fmt.Errorf("execution result is different from the sealed result, height: %v, block_id: %v, sealed_commit: %x, my_commit: %x",
   104  			sealed.Height, blockID, sealedCommit, mycommit)
   105  	}
   106  
   107  	return nil
   108  }