github.com/onflow/flow-go@v0.33.17/engine/execution/checker/core.go (about)

     1  package checker
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/onflow/flow-go/engine/execution/state"
    11  	"github.com/onflow/flow-go/model/flow"
    12  	"github.com/onflow/flow-go/state/protocol"
    13  	"github.com/onflow/flow-go/storage"
    14  )
    15  
    16  // Core is the core logic of the checker engine that checks if the execution result matches the sealed result.
    17  type Core struct {
    18  	log       zerolog.Logger
    19  	state     protocol.State
    20  	execState state.ExecutionState
    21  }
    22  
    23  func NewCore(
    24  	logger zerolog.Logger,
    25  	state protocol.State,
    26  	execState state.ExecutionState,
    27  ) *Core {
    28  	e := &Core{
    29  		log:       logger.With().Str("engine", "checker").Logger(),
    30  		state:     state,
    31  		execState: execState,
    32  	}
    33  
    34  	return e
    35  }
    36  
    37  // checkMyCommitWithSealedCommit is the main check of the checker engine
    38  func checkMyCommitWithSealedCommit(
    39  	executedBlock *flow.Header,
    40  	myCommit flow.StateCommitment,
    41  	sealedCommit flow.StateCommitment,
    42  ) error {
    43  	if myCommit != sealedCommit {
    44  		// mismatch
    45  		return fmt.Errorf("execution result is different from the sealed result, height: %v, block_id: %v, sealed_commit: %v, my_commit: %v",
    46  			executedBlock.Height,
    47  			executedBlock.ID(),
    48  			sealedCommit,
    49  			myCommit,
    50  		)
    51  	}
    52  
    53  	// match
    54  	return nil
    55  }
    56  
    57  // RunCheck skips when the last sealed has not been executed, and last executed has not been finalized.
    58  func (c *Core) RunCheck() error {
    59  	// find last sealed block
    60  	lastSealedBlock, lastFinal, seal, err := c.findLastSealedBlock()
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	mycommitAtLastSealed, err := c.execState.StateCommitmentByBlockID(lastSealedBlock.ID())
    66  	if err == nil {
    67  		// if last sealed block has been executed, then check if they match
    68  		return checkMyCommitWithSealedCommit(lastSealedBlock, mycommitAtLastSealed, seal.FinalState)
    69  	}
    70  
    71  	// if last sealed block has not been executed, then check if recent executed block has
    72  	// been sealed already, if yes, check if they match.
    73  	lastExecutedHeight, err := c.findLastExecutedBlockHeight()
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	if lastExecutedHeight > lastFinal.Height {
    79  		// last executed block has not been finalized yet,
    80  		// can't check since unfinalized block is also unsealed, skip
    81  		return nil
    82  	}
    83  
    84  	// TODO: better to query seals from protocol state,
    85  	// switch to state.Final().LastSealed() when available
    86  	sealedExecuted, seal, err := c.findLatestSealedAtHeight(lastExecutedHeight)
    87  	if err != nil {
    88  		return fmt.Errorf("could not get the last sealed block at height: %v, err: %w", lastExecutedHeight, err)
    89  	}
    90  
    91  	sealedCommit := seal.FinalState
    92  
    93  	mycommit, err := c.execState.StateCommitmentByBlockID(seal.BlockID)
    94  	if errors.Is(err, storage.ErrNotFound) {
    95  		// have not executed the sealed block yet
    96  		// in other words, this can't detect execution fork, if the execution is behind
    97  		// the sealing
    98  		return nil
    99  	}
   100  
   101  	if err != nil {
   102  		return fmt.Errorf("could not get my state commitment OnFinalizedBlock, blockID: %v", seal.BlockID)
   103  	}
   104  
   105  	return checkMyCommitWithSealedCommit(sealedExecuted, mycommit, sealedCommit)
   106  }
   107  
   108  // findLastSealedBlock finds the last sealed block
   109  func (c *Core) findLastSealedBlock() (*flow.Header, *flow.Header, *flow.Seal, error) {
   110  	finalized := c.state.Final()
   111  	lastFinal, err := finalized.Head()
   112  	if err != nil {
   113  		return nil, nil, nil, err
   114  	}
   115  
   116  	_, lastSeal, err := finalized.SealedResult()
   117  	if err != nil {
   118  		return nil, nil, nil, fmt.Errorf("could not get the last sealed for the finalized block: %w", err)
   119  	}
   120  
   121  	lastSealed, err := c.state.AtBlockID(lastSeal.BlockID).Head()
   122  	if err != nil {
   123  		return nil, nil, nil, fmt.Errorf("could not get the last sealed block: %w", err)
   124  	}
   125  
   126  	return lastSealed, lastFinal, lastSeal, nil
   127  }
   128  
   129  // findLastExecutedBlockHeight finds the last executed block height
   130  func (c *Core) findLastExecutedBlockHeight() (uint64, error) {
   131  	height, _, err := c.execState.GetHighestExecutedBlockID(context.Background())
   132  	if err != nil {
   133  		return 0, fmt.Errorf("could not get the last executed block: %w", err)
   134  	}
   135  	return height, nil
   136  }
   137  
   138  // findLatestSealedAtHeight finds the latest sealed block at the given height
   139  func (c *Core) findLatestSealedAtHeight(finalizedHeight uint64) (*flow.Header, *flow.Seal, error) {
   140  	_, seal, err := c.state.AtHeight(finalizedHeight).SealedResult()
   141  	if err != nil {
   142  		return nil, nil, fmt.Errorf("could not get the last sealed for the finalized block: %w", err)
   143  	}
   144  
   145  	sealed, err := c.state.AtBlockID(seal.BlockID).Head()
   146  	if err != nil {
   147  		return nil, nil, fmt.Errorf("could not get the last sealed block: %w", err)
   148  	}
   149  	return sealed, seal, nil
   150  }