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

     1  package ingestion
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/rs/zerolog"
    12  	"github.com/rs/zerolog/log"
    13  
    14  	"github.com/koko1123/flow-go-1/engine"
    15  	"github.com/koko1123/flow-go-1/engine/execution"
    16  	"github.com/koko1123/flow-go-1/engine/execution/computation"
    17  	"github.com/koko1123/flow-go-1/engine/execution/ingestion/uploader"
    18  	"github.com/koko1123/flow-go-1/engine/execution/provider"
    19  	"github.com/koko1123/flow-go-1/engine/execution/state"
    20  	"github.com/koko1123/flow-go-1/engine/execution/utils"
    21  	"github.com/koko1123/flow-go-1/model/flow"
    22  	"github.com/koko1123/flow-go-1/model/flow/filter"
    23  	"github.com/koko1123/flow-go-1/module"
    24  	"github.com/koko1123/flow-go-1/module/executiondatasync/pruner"
    25  	"github.com/koko1123/flow-go-1/module/mempool"
    26  	"github.com/koko1123/flow-go-1/module/mempool/entity"
    27  	"github.com/koko1123/flow-go-1/module/mempool/queue"
    28  	"github.com/koko1123/flow-go-1/module/mempool/stdmap"
    29  	"github.com/koko1123/flow-go-1/module/trace"
    30  	"github.com/koko1123/flow-go-1/network"
    31  	"github.com/koko1123/flow-go-1/network/channels"
    32  	"github.com/koko1123/flow-go-1/state/protocol"
    33  	psEvents "github.com/koko1123/flow-go-1/state/protocol/events"
    34  	"github.com/koko1123/flow-go-1/storage"
    35  	"github.com/koko1123/flow-go-1/utils/logging"
    36  	"github.com/onflow/flow-go/crypto"
    37  	"github.com/onflow/flow-go/crypto/hash"
    38  )
    39  
    40  // An Engine receives and saves incoming blocks.
    41  type Engine struct {
    42  	psEvents.Noop // satisfy protocol events consumer interface
    43  
    44  	unit                   *engine.Unit
    45  	log                    zerolog.Logger
    46  	me                     module.Local
    47  	request                module.Requester // used to request collections
    48  	state                  protocol.State
    49  	receiptHasher          hash.Hasher // used as hasher to sign the execution receipt
    50  	blocks                 storage.Blocks
    51  	collections            storage.Collections
    52  	events                 storage.Events
    53  	serviceEvents          storage.ServiceEvents
    54  	transactionResults     storage.TransactionResults
    55  	computationManager     computation.ComputationManager
    56  	providerEngine         provider.ProviderEngine
    57  	mempool                *Mempool
    58  	execState              state.ExecutionState
    59  	metrics                module.ExecutionMetrics
    60  	maxCollectionHeight    uint64
    61  	tracer                 module.Tracer
    62  	extensiveLogging       bool
    63  	syncThreshold          int                 // the threshold for how many sealed unexecuted blocks to trigger state syncing.
    64  	syncFilter             flow.IdentityFilter // specify the filter to sync state from
    65  	syncConduit            network.Conduit     // sending state syncing requests
    66  	syncDeltas             mempool.Deltas      // storing the synced state deltas
    67  	syncFast               bool                // sync fast allows execution node to skip fetching collection during state syncing, and rely on state syncing to catch up
    68  	checkAuthorizedAtBlock func(blockID flow.Identifier) (bool, error)
    69  	executionDataPruner    *pruner.Pruner
    70  	uploader               *uploader.Manager
    71  	stopControl            *StopControl
    72  }
    73  
    74  func New(
    75  	logger zerolog.Logger,
    76  	net network.Network,
    77  	me module.Local,
    78  	request module.Requester,
    79  	state protocol.State,
    80  	blocks storage.Blocks,
    81  	collections storage.Collections,
    82  	events storage.Events,
    83  	serviceEvents storage.ServiceEvents,
    84  	transactionResults storage.TransactionResults,
    85  	executionEngine computation.ComputationManager,
    86  	providerEngine provider.ProviderEngine,
    87  	execState state.ExecutionState,
    88  	metrics module.ExecutionMetrics,
    89  	tracer module.Tracer,
    90  	extLog bool,
    91  	syncFilter flow.IdentityFilter,
    92  	syncDeltas mempool.Deltas,
    93  	syncThreshold int,
    94  	syncFast bool,
    95  	checkAuthorizedAtBlock func(blockID flow.Identifier) (bool, error),
    96  	pruner *pruner.Pruner,
    97  	uploader *uploader.Manager,
    98  	stopControl *StopControl,
    99  ) (*Engine, error) {
   100  	log := logger.With().Str("engine", "ingestion").Logger()
   101  
   102  	mempool := newMempool()
   103  
   104  	eng := Engine{
   105  		unit:                   engine.NewUnit(),
   106  		log:                    log,
   107  		me:                     me,
   108  		request:                request,
   109  		state:                  state,
   110  		receiptHasher:          utils.NewExecutionReceiptHasher(),
   111  		blocks:                 blocks,
   112  		collections:            collections,
   113  		events:                 events,
   114  		serviceEvents:          serviceEvents,
   115  		transactionResults:     transactionResults,
   116  		computationManager:     executionEngine,
   117  		providerEngine:         providerEngine,
   118  		mempool:                mempool,
   119  		execState:              execState,
   120  		metrics:                metrics,
   121  		maxCollectionHeight:    0,
   122  		tracer:                 tracer,
   123  		extensiveLogging:       extLog,
   124  		syncFilter:             syncFilter,
   125  		syncThreshold:          syncThreshold,
   126  		syncDeltas:             syncDeltas,
   127  		syncFast:               syncFast,
   128  		checkAuthorizedAtBlock: checkAuthorizedAtBlock,
   129  		executionDataPruner:    pruner,
   130  		uploader:               uploader,
   131  		stopControl:            stopControl,
   132  	}
   133  
   134  	// move to state syncing engine
   135  	syncConduit, err := net.Register(channels.SyncExecution, &eng)
   136  	if err != nil {
   137  		return nil, fmt.Errorf("could not register execution blockSync engine: %w", err)
   138  	}
   139  
   140  	eng.syncConduit = syncConduit
   141  
   142  	return &eng, nil
   143  }
   144  
   145  // Ready returns a channel that will close when the engine has
   146  // successfully started.
   147  func (e *Engine) Ready() <-chan struct{} {
   148  	if !e.stopControl.IsPaused() {
   149  		if e.uploader.Enabled() {
   150  			if err := e.uploader.RetryUploads(); err != nil {
   151  				e.log.Warn().Msg("failed to re-upload all ComputationResults")
   152  			}
   153  		}
   154  
   155  		err := e.reloadUnexecutedBlocks()
   156  		if err != nil {
   157  			e.log.Fatal().Err(err).Msg("failed to load all unexecuted blocks")
   158  		}
   159  	}
   160  
   161  	return e.unit.Ready()
   162  }
   163  
   164  // Done returns a channel that will close when the engine has
   165  // successfully stopped.
   166  func (e *Engine) Done() <-chan struct{} {
   167  	return e.unit.Done()
   168  }
   169  
   170  // SubmitLocal submits an event originating on the local node.
   171  func (e *Engine) SubmitLocal(event interface{}) {
   172  	e.unit.Launch(func() {
   173  		err := e.process(e.me.NodeID(), event)
   174  		if err != nil {
   175  			engine.LogError(e.log, err)
   176  		}
   177  	})
   178  }
   179  
   180  // Submit submits the given event from the node with the given origin ID
   181  // for processing in a non-blocking manner. It returns instantly and logs
   182  // a potential processing error internally when done.
   183  func (e *Engine) Submit(channel channels.Channel, originID flow.Identifier, event interface{}) {
   184  	e.unit.Launch(func() {
   185  		err := e.process(originID, event)
   186  		if err != nil {
   187  			engine.LogError(e.log, err)
   188  		}
   189  	})
   190  }
   191  
   192  // ProcessLocal processes an event originating on the local node.
   193  func (e *Engine) ProcessLocal(event interface{}) error {
   194  	return fmt.Errorf("ingestion error does not process local events")
   195  }
   196  
   197  func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, event interface{}) error {
   198  	return e.unit.Do(func() error {
   199  		return e.process(originID, event)
   200  	})
   201  }
   202  
   203  func (e *Engine) process(originID flow.Identifier, event interface{}) error {
   204  	return nil
   205  }
   206  
   207  func (e *Engine) finalizedUnexecutedBlocks(finalized protocol.Snapshot) ([]flow.Identifier, error) {
   208  	// get finalized height
   209  	final, err := finalized.Head()
   210  	if err != nil {
   211  		return nil, fmt.Errorf("could not get finalized block: %w", err)
   212  	}
   213  
   214  	// find the first unexecuted and finalized block
   215  	// We iterate from the last finalized, check if it has been executed,
   216  	// if not, keep going to the lower height, until we find an executed
   217  	// block, and then the next height is the first unexecuted.
   218  	// If there is only one finalized, and it's executed (i.e. root block),
   219  	// then the firstUnexecuted is a unfinalized block, which is ok,
   220  	// because the next loop will ensure it only iterates through finalized
   221  	// blocks.
   222  	lastExecuted := final.Height
   223  
   224  	rootBlock, err := e.state.Params().Root()
   225  	if err != nil {
   226  		return nil, fmt.Errorf("failed to retrieve root block: %w", err)
   227  	}
   228  
   229  	for ; lastExecuted > rootBlock.Height; lastExecuted-- {
   230  		header, err := e.state.AtHeight(lastExecuted).Head()
   231  		if err != nil {
   232  			return nil, fmt.Errorf("could not get header at height: %v, %w", lastExecuted, err)
   233  		}
   234  
   235  		executed, err := state.IsBlockExecuted(e.unit.Ctx(), e.execState, header.ID())
   236  		if err != nil {
   237  			return nil, fmt.Errorf("could not check whether block is executed: %w", err)
   238  		}
   239  
   240  		if executed {
   241  			break
   242  		}
   243  	}
   244  
   245  	firstUnexecuted := lastExecuted + 1
   246  
   247  	e.log.Info().Msgf("last finalized and executed height: %v", lastExecuted)
   248  
   249  	unexecuted := make([]flow.Identifier, 0)
   250  
   251  	// starting from the first unexecuted block, go through each unexecuted and finalized block
   252  	// reload its block to execution queues
   253  	for height := firstUnexecuted; height <= final.Height; height++ {
   254  		header, err := e.state.AtHeight(height).Head()
   255  		if err != nil {
   256  			return nil, fmt.Errorf("could not get header at height: %v, %w", height, err)
   257  		}
   258  
   259  		unexecuted = append(unexecuted, header.ID())
   260  	}
   261  
   262  	return unexecuted, nil
   263  }
   264  
   265  func (e *Engine) pendingUnexecutedBlocks(finalized protocol.Snapshot) ([]flow.Identifier, error) {
   266  	pendings, err := finalized.ValidDescendants()
   267  	if err != nil {
   268  		return nil, fmt.Errorf("could not get pending blocks: %w", err)
   269  	}
   270  
   271  	unexecuted := make([]flow.Identifier, 0)
   272  
   273  	for _, pending := range pendings {
   274  		executed, err := state.IsBlockExecuted(e.unit.Ctx(), e.execState, pending)
   275  		if err != nil {
   276  			return nil, fmt.Errorf("could not check block executed or not: %w", err)
   277  		}
   278  
   279  		if !executed {
   280  			unexecuted = append(unexecuted, pending)
   281  		}
   282  	}
   283  
   284  	return unexecuted, nil
   285  }
   286  
   287  func (e *Engine) unexecutedBlocks() (finalized []flow.Identifier, pending []flow.Identifier, err error) {
   288  	// pin the snapshot so that finalizedUnexecutedBlocks and pendingUnexecutedBlocks are based
   289  	// on the same snapshot.
   290  	snapshot := e.state.Final()
   291  
   292  	finalized, err = e.finalizedUnexecutedBlocks(snapshot)
   293  	if err != nil {
   294  		return nil, nil, fmt.Errorf("could not read finalized unexecuted blocks")
   295  	}
   296  
   297  	pending, err = e.pendingUnexecutedBlocks(snapshot)
   298  	if err != nil {
   299  		return nil, nil, fmt.Errorf("could not read pending unexecuted blocks")
   300  	}
   301  
   302  	return finalized, pending, nil
   303  }
   304  
   305  // on nodes startup, we need to load all the unexecuted blocks to the execution queues.
   306  // blocks have to be loaded in the way that the parent has been loaded before loading its children
   307  func (e *Engine) reloadUnexecutedBlocks() error {
   308  	// it's possible the BlockProcessable is called during the reloading, as the follower engine
   309  	// will receive blocks before ingestion engine is ready.
   310  	// The problem with that is, since the reloading hasn't finished yet, enqueuing the new block from
   311  	// the BlockProcessable callback will fail, because its parent block might have not been reloaded
   312  	// to the queues yet.
   313  	// So one solution here is to lock the execution queues during reloading, so that if BlockProcessable
   314  	// is called before reloading is finished, it will be blocked, which will avoid that edge case.
   315  	return e.mempool.Run(func(
   316  		blockByCollection *stdmap.BlockByCollectionBackdata,
   317  		executionQueues *stdmap.QueuesBackdata) error {
   318  
   319  		// saving an executed block is currently not transactional, so it's possible
   320  		// the block is marked as executed but the receipt might not be saved during a crash.
   321  		// in order to mitigate this problem, we always re-execute the last executed and finalized
   322  		// block.
   323  		// there is an exception, if the last executed block is a root block, then don't execute it,
   324  		// because the root has already been executed during bootstrapping phase. And re-executing
   325  		// a root block will fail, because the root block doesn't have a parent block, and could not
   326  		// get the result of it.
   327  		// TODO: remove this, when saving a executed block is transactional
   328  		lastExecutedHeight, lastExecutedID, err := e.execState.GetHighestExecutedBlockID(e.unit.Ctx())
   329  		if err != nil {
   330  			return fmt.Errorf("could not get last executed: %w", err)
   331  		}
   332  
   333  		last, err := e.state.AtBlockID(lastExecutedID).Head()
   334  		if err != nil {
   335  			return fmt.Errorf("could not get last executed final by ID: %w", err)
   336  		}
   337  
   338  		// don't reload root block
   339  		rootBlock, err := e.state.Params().Root()
   340  		if err != nil {
   341  			return fmt.Errorf("failed to retrieve root block: %w", err)
   342  		}
   343  
   344  		isRoot := rootBlock.ID() == last.ID()
   345  		if !isRoot {
   346  			executed, err := state.IsBlockExecuted(e.unit.Ctx(), e.execState, lastExecutedID)
   347  			if err != nil {
   348  				return fmt.Errorf("cannot check is last exeucted final block has been executed %v: %w", lastExecutedID, err)
   349  			}
   350  			if !executed {
   351  				// this should not happen, but if it does, execution should still work
   352  				e.log.Warn().
   353  					Hex("block_id", lastExecutedID[:]).
   354  					Msg("block marked as highest executed one, but not executable - internal inconsistency")
   355  
   356  				err = e.reloadBlock(blockByCollection, executionQueues, lastExecutedID)
   357  				if err != nil {
   358  					return fmt.Errorf("could not reload the last executed final block: %v, %w", lastExecutedID, err)
   359  				}
   360  			}
   361  		}
   362  
   363  		finalized, pending, err := e.unexecutedBlocks()
   364  		if err != nil {
   365  			return fmt.Errorf("could not reload unexecuted blocks: %w", err)
   366  		}
   367  
   368  		unexecuted := append(finalized, pending...)
   369  
   370  		log := e.log.With().
   371  			Int("total", len(unexecuted)).
   372  			Int("finalized", len(finalized)).
   373  			Int("pending", len(pending)).
   374  			Uint64("last_executed", lastExecutedHeight).
   375  			Hex("last_executed_id", lastExecutedID[:]).
   376  			Logger()
   377  
   378  		log.Info().Msg("reloading unexecuted blocks")
   379  
   380  		for _, blockID := range unexecuted {
   381  			err := e.reloadBlock(blockByCollection, executionQueues, blockID)
   382  			if err != nil {
   383  				return fmt.Errorf("could not reload block: %v, %w", blockID, err)
   384  			}
   385  
   386  			e.log.Debug().Hex("block_id", blockID[:]).Msg("reloaded block")
   387  		}
   388  
   389  		log.Info().Msg("all unexecuted have been successfully reloaded")
   390  
   391  		return nil
   392  	})
   393  }
   394  
   395  func (e *Engine) reloadBlock(
   396  	blockByCollection *stdmap.BlockByCollectionBackdata,
   397  	executionQueues *stdmap.QueuesBackdata,
   398  	blockID flow.Identifier) error {
   399  	block, err := e.blocks.ByID(blockID)
   400  	if err != nil {
   401  		return fmt.Errorf("could not get block by ID: %v %w", blockID, err)
   402  	}
   403  
   404  	// enqueue the block and check if there is any missing collections
   405  	missingCollections, err := e.enqueueBlockAndCheckExecutable(blockByCollection, executionQueues, block, false)
   406  
   407  	if err != nil {
   408  		return fmt.Errorf("could not enqueue block %x on reloading: %w", blockID, err)
   409  	}
   410  
   411  	// forward the missing collections to requester engine for requesting them from collection nodes,
   412  	// adding the missing collections to mempool in order to trigger the block execution as soon as
   413  	// all missing collections are received.
   414  	err = e.fetchAndHandleCollection(blockID, block.Header.Height, missingCollections, func(collection *flow.Collection) error {
   415  		err := e.addCollectionToMempool(collection, blockByCollection)
   416  
   417  		if err != nil {
   418  			return fmt.Errorf("could not add collection to mempool: %w", err)
   419  		}
   420  		return nil
   421  	})
   422  
   423  	if err != nil {
   424  		return fmt.Errorf("could not fetch or handle collection %w", err)
   425  	}
   426  	return nil
   427  }
   428  
   429  // BlockProcessable handles the new verified blocks (blocks that
   430  // have passed consensus validation) received from the consensus nodes
   431  // Note: BlockProcessable might be called multiple times for the same block.
   432  func (e *Engine) BlockProcessable(b *flow.Header) {
   433  
   434  	// skip if stopControl tells to skip
   435  	if !e.stopControl.blockProcessable(b) {
   436  		return
   437  	}
   438  
   439  	blockID := b.ID()
   440  	newBlock, err := e.blocks.ByID(blockID)
   441  	if err != nil {
   442  		e.log.Fatal().Err(err).Msgf("could not get incorporated block(%v): %v", blockID, err)
   443  	}
   444  
   445  	e.log.Info().Hex("block_id", blockID[:]).
   446  		Uint64("height", b.Height).
   447  		Msg("handling new block")
   448  
   449  	err = e.handleBlock(e.unit.Ctx(), newBlock)
   450  	if err != nil {
   451  		e.log.Error().Err(err).Hex("block_id", blockID[:]).Msg("failed to handle block")
   452  	}
   453  }
   454  
   455  // BlockFinalized implements part of state.protocol.Consumer interface.
   456  // Method gets called for every finalized block
   457  func (e *Engine) BlockFinalized(h *flow.Header) {
   458  	e.stopControl.blockFinalized(e.unit.Ctx(), e.execState, h)
   459  }
   460  
   461  // Main handling
   462  
   463  // handle block will process the incoming block.
   464  // the block has passed the consensus validation.
   465  func (e *Engine) handleBlock(ctx context.Context, block *flow.Block) error {
   466  
   467  	blockID := block.ID()
   468  	log := e.log.With().Hex("block_id", blockID[:]).Logger()
   469  
   470  	span, _ := e.tracer.StartBlockSpan(ctx, blockID, trace.EXEHandleBlock)
   471  	defer span.End()
   472  
   473  	executed, err := state.IsBlockExecuted(e.unit.Ctx(), e.execState, blockID)
   474  	if err != nil {
   475  		return fmt.Errorf("could not check whether block is executed: %w", err)
   476  	}
   477  
   478  	if executed {
   479  		log.Debug().Msg("block has been executed already")
   480  		return nil
   481  	}
   482  
   483  	var missingCollections []*flow.CollectionGuarantee
   484  	// unexecuted block
   485  	// acquiring the lock so that there is only one process modifying the queue
   486  	err = e.mempool.Run(func(
   487  		blockByCollection *stdmap.BlockByCollectionBackdata,
   488  		executionQueues *stdmap.QueuesBackdata,
   489  	) error {
   490  		missing, err := e.enqueueBlockAndCheckExecutable(blockByCollection, executionQueues, block, false)
   491  		if err != nil {
   492  			return err
   493  		}
   494  		missingCollections = missing
   495  		return nil
   496  	})
   497  
   498  	if err != nil {
   499  		return fmt.Errorf("could not enqueue block %v: %w", blockID, err)
   500  	}
   501  
   502  	return e.addOrFetch(blockID, block.Header.Height, missingCollections)
   503  }
   504  
   505  func (e *Engine) enqueueBlockAndCheckExecutable(
   506  	blockByCollection *stdmap.BlockByCollectionBackdata,
   507  	executionQueues *stdmap.QueuesBackdata,
   508  	block *flow.Block,
   509  	checkStateSync bool) ([]*flow.CollectionGuarantee, error) {
   510  	executableBlock := &entity.ExecutableBlock{
   511  		Block:               block,
   512  		CompleteCollections: make(map[flow.Identifier]*entity.CompleteCollection),
   513  	}
   514  
   515  	blockID := executableBlock.ID()
   516  
   517  	lg := e.log.With().
   518  		Hex("block_id", blockID[:]).
   519  		Uint64("block_height", executableBlock.Block.Header.Height).
   520  		Logger()
   521  
   522  	// adding the block to the queue,
   523  	queue, added, head := enqueue(executableBlock, executionQueues)
   524  
   525  	// if it's not added, it means the block is not a new block, it already
   526  	// exists in the queue, then bail
   527  	if !added {
   528  		log.Debug().Hex("block_id", logging.Entity(executableBlock)).
   529  			Int("block_height", int(executableBlock.Height())).
   530  			Msg("block already exists in the execution queue")
   531  		return nil, nil
   532  	}
   533  
   534  	firstUnexecutedHeight := queue.Head.Item.Height()
   535  	// disable state syncing for now
   536  	// if checkStateSync {
   537  	// 	// whenever the queue grows, we need to check whether the state sync should be
   538  	// 	// triggered.
   539  	// 	e.unit.Launch(func() {
   540  	// 		e.checkStateSyncStart(firstUnexecutedHeight)
   541  	// 	})
   542  	// }
   543  
   544  	// check if a block is executable.
   545  	// a block is executable if the following conditions are all true
   546  	// 1) the parent state commitment is ready
   547  	// 2) the collections for the block payload are ready
   548  	// 3) the child block is ready for querying the randomness
   549  
   550  	// check if the block's parent has been executed. (we can't execute the block if the parent has
   551  	// not been executed yet)
   552  	// check if there is a statecommitment for the parent block
   553  	parentCommitment, err := e.execState.StateCommitmentByBlockID(e.unit.Ctx(), block.Header.ParentID)
   554  
   555  	// if we found the statecommitment for the parent block, then add it to the executable block.
   556  	if err == nil {
   557  		executableBlock.StartState = &parentCommitment
   558  	} else if errors.Is(err, storage.ErrNotFound) {
   559  		// the parent block is an unexecuted block.
   560  		// if the queue only has one block, and its parent doesn't
   561  		// exist in the queue, then we need to load the block from the storage.
   562  		_, ok := queue.Nodes[blockID]
   563  		if !ok {
   564  			lg.Error().Msgf("an unexecuted parent block is missing in the queue")
   565  		}
   566  	} else {
   567  		// if there is exception, then crash
   568  		lg.Fatal().Err(err).Msg("unexpected error while accessing storage, shutting down")
   569  	}
   570  
   571  	// check if we have all the collections for the block, and request them if there is missing.
   572  	missingCollections, err := e.matchAndFindMissingCollections(executableBlock, blockByCollection)
   573  	if err != nil {
   574  		return nil, fmt.Errorf("cannot send collection requests: %w", err)
   575  	}
   576  
   577  	complete := false
   578  
   579  	// if newly enqueued block is inside any existing queue, we should skip now and wait
   580  	// for parent to finish execution
   581  	if head {
   582  		// execute the block if the block is ready to be executed
   583  		complete = e.executeBlockIfComplete(executableBlock)
   584  	}
   585  
   586  	lg.Info().
   587  		// if the execution is halt, but the queue keeps growing, we could check which block
   588  		// hasn't been executed.
   589  		Uint64("first_unexecuted_in_queue", firstUnexecutedHeight).
   590  		Bool("complete", complete).
   591  		Bool("head_of_queue", head).
   592  		Msg("block is enqueued")
   593  
   594  	return missingCollections, nil
   595  }
   596  
   597  // executeBlock will execute the block.
   598  // When finish executing, it will check if the children becomes executable and execute them if yes.
   599  func (e *Engine) executeBlock(ctx context.Context, executableBlock *entity.ExecutableBlock) {
   600  	lg := e.log.With().
   601  		Hex("block_id", logging.Entity(executableBlock)).
   602  		Uint64("height", executableBlock.Block.Header.Height).
   603  		Logger()
   604  
   605  	lg.Info().Msg("executing block")
   606  
   607  	startedAt := time.Now()
   608  
   609  	e.stopControl.executingBlockHeight(executableBlock.Block.Header.Height)
   610  
   611  	span, ctx := e.tracer.StartSpanFromContext(ctx, trace.EXEExecuteBlock)
   612  	defer span.End()
   613  
   614  	view := e.execState.NewView(*executableBlock.StartState)
   615  
   616  	computationResult, err := e.computationManager.ComputeBlock(ctx, executableBlock, view)
   617  	if err != nil {
   618  		lg.Err(err).Msg("error while computing block")
   619  		return
   620  	}
   621  
   622  	if e.uploader.Enabled() {
   623  		err := e.uploader.Upload(ctx, computationResult)
   624  		if err != nil {
   625  			lg.Err(err).Msg("error while uploading block")
   626  			// continue processing. uploads should not block execution
   627  		}
   628  	}
   629  
   630  	finalState, receipt, err := e.handleComputationResult(ctx, computationResult, *executableBlock.StartState)
   631  	if errors.Is(err, storage.ErrDataMismatch) {
   632  		lg.Fatal().Err(err).Msg("fatal: trying to store different results for the same block")
   633  	}
   634  
   635  	if err != nil {
   636  		lg.Err(err).Msg("error while handing computation results")
   637  		return
   638  	}
   639  
   640  	// if the receipt is for a sealed block, then no need to broadcast it.
   641  	lastSealed, err := e.state.Sealed().Head()
   642  	if err != nil {
   643  		lg.Fatal().Err(err).Msg("could not get sealed block before broadcasting")
   644  	}
   645  
   646  	isExecutedBlockSealed := executableBlock.Block.Header.Height <= lastSealed.Height
   647  	broadcasted := false
   648  
   649  	if !isExecutedBlockSealed {
   650  		authorizedAtBlock, err := e.checkAuthorizedAtBlock(executableBlock.ID())
   651  		if err != nil {
   652  			lg.Fatal().Err(err).Msg("could not check staking status")
   653  		}
   654  		if authorizedAtBlock {
   655  			err = e.providerEngine.BroadcastExecutionReceipt(ctx, receipt)
   656  			if err != nil {
   657  				lg.Err(err).Msg("critical: failed to broadcast the receipt")
   658  			} else {
   659  				broadcasted = true
   660  			}
   661  		}
   662  	}
   663  
   664  	lg.Info().
   665  		Hex("parent_block", executableBlock.Block.Header.ParentID[:]).
   666  		Int("collections", len(executableBlock.Block.Payload.Guarantees)).
   667  		Hex("start_state", executableBlock.StartState[:]).
   668  		Hex("final_state", finalState[:]).
   669  		Hex("receipt_id", logging.Entity(receipt)).
   670  		Hex("result_id", logging.Entity(receipt.ExecutionResult)).
   671  		Hex("execution_data_id", receipt.ExecutionResult.ExecutionDataID[:]).
   672  		Bool("sealed", isExecutedBlockSealed).
   673  		Bool("broadcasted", broadcasted).
   674  		Int64("timeSpentInMS", time.Since(startedAt).Milliseconds()).
   675  		Msg("block executed")
   676  
   677  	e.metrics.ExecutionBlockExecuted(
   678  		time.Since(startedAt),
   679  		computationResult.BlockStats())
   680  
   681  	for computationKind, intensity := range computationResult.ComputationIntensities {
   682  		e.metrics.ExecutionBlockExecutionEffortVectorComponent(computationKind.String(), intensity)
   683  	}
   684  
   685  	err = e.onBlockExecuted(executableBlock, finalState)
   686  	if err != nil {
   687  		lg.Err(err).Msg("failed in process block's children")
   688  	}
   689  
   690  	if e.executionDataPruner != nil {
   691  		e.executionDataPruner.NotifyFulfilledHeight(executableBlock.Height())
   692  	}
   693  
   694  	e.unit.Ctx()
   695  
   696  	e.stopControl.blockExecuted(executableBlock.Block.Header)
   697  }
   698  
   699  // we've executed the block, now we need to check:
   700  // 1. whether the state syncing can be turned off
   701  // 2. whether its children can be executed
   702  //   the executionQueues stores blocks as a tree:
   703  //
   704  //   10 <- 11 <- 12
   705  //   	 ^-- 13
   706  //   14 <- 15 <- 16
   707  //
   708  //   if block 10 is the one just executed, then we will remove it from the queue, and add
   709  //   its children back, meaning the tree will become:
   710  //
   711  //   11 <- 12
   712  //   13
   713  //   14 <- 15 <- 16
   714  
   715  func (e *Engine) onBlockExecuted(executed *entity.ExecutableBlock, finalState flow.StateCommitment) error {
   716  
   717  	e.metrics.ExecutionStorageStateCommitment(int64(len(finalState)))
   718  	e.metrics.ExecutionLastExecutedBlockHeight(executed.Block.Header.Height)
   719  
   720  	// e.checkStateSyncStop(executed.Block.Header.Height)
   721  
   722  	missingCollections := make(map[*entity.ExecutableBlock][]*flow.CollectionGuarantee)
   723  	err := e.mempool.Run(
   724  		func(
   725  			blockByCollection *stdmap.BlockByCollectionBackdata,
   726  			executionQueues *stdmap.QueuesBackdata,
   727  		) error {
   728  			// find the block that was just executed
   729  			executionQueue, exists := executionQueues.ByID(executed.ID())
   730  			if !exists {
   731  				// when the block no longer exists in the queue, it means there was a race condition that
   732  				// two onBlockExecuted was called for the same block, and one process has already removed the
   733  				// block from the queue, so we will print an error here
   734  				return fmt.Errorf("block has been executed already, no longer exists in the queue")
   735  			}
   736  
   737  			// dismount the executed block and all its children
   738  			_, newQueues := executionQueue.Dismount()
   739  
   740  			// go through each children, add them back to the queue, and check
   741  			// if the children is executable
   742  			for _, queue := range newQueues {
   743  				queueID := queue.ID()
   744  				added := executionQueues.Add(queueID, queue)
   745  				if !added {
   746  					// blocks should be unique in execution queues, if we dismount all the children blocks, then
   747  					// add it back to the queues, then it should always be able to add.
   748  					// If not, then there is a bug that the queues have duplicated blocks
   749  					return fmt.Errorf("fatal error - child block already in execution queue")
   750  				}
   751  
   752  				// the parent block has been executed, update the StartState of
   753  				// each child block.
   754  				child := queue.Head.Item.(*entity.ExecutableBlock)
   755  				child.StartState = &finalState
   756  
   757  				missing, err := e.matchAndFindMissingCollections(child, blockByCollection)
   758  				if err != nil {
   759  					return fmt.Errorf("cannot send collection requests: %w", err)
   760  				}
   761  				if len(missing) > 0 {
   762  					missingCollections[child] = append(missingCollections[child], missing...)
   763  				}
   764  
   765  				completed := e.executeBlockIfComplete(child)
   766  				if !completed {
   767  					e.log.Debug().
   768  						Hex("executed_block", logging.Entity(executed)).
   769  						Hex("child_block", logging.Entity(child)).
   770  						Msg("child block is not ready to be executed yet")
   771  				} else {
   772  					e.log.Debug().
   773  						Hex("executed_block", logging.Entity(executed)).
   774  						Hex("child_block", logging.Entity(child)).
   775  						Msg("child block is ready to be executed")
   776  				}
   777  			}
   778  
   779  			// remove the executed block
   780  			executionQueues.Remove(executed.ID())
   781  
   782  			return nil
   783  		})
   784  
   785  	if err != nil {
   786  		e.log.Err(err).
   787  			Hex("block", logging.Entity(executed)).
   788  			Msg("error while requeueing blocks after execution")
   789  	}
   790  
   791  	for child, missing := range missingCollections {
   792  		err := e.addOrFetch(child.ID(), child.Block.Header.Height, missing)
   793  		if err != nil {
   794  			return fmt.Errorf("fail to add missing collections: %w", err)
   795  		}
   796  	}
   797  
   798  	return nil
   799  }
   800  
   801  // executeBlockIfComplete checks whether the block is ready to be executed.
   802  // if yes, execute the block
   803  // return a bool indicates whether the block was completed
   804  func (e *Engine) executeBlockIfComplete(eb *entity.ExecutableBlock) bool {
   805  
   806  	if eb.Executing {
   807  		return false
   808  	}
   809  
   810  	// if the eb has parent statecommitment, and we have the delta for this block
   811  	// then apply the delta
   812  	// note the block ID is the delta's ID
   813  	// delta, found := e.syncDeltas.ByBlockID(eb.Block.ID())
   814  	// if found {
   815  	// 	// double check before applying the state delta
   816  	// 	if bytes.Equal(eb.StartState, delta.ExecutableBlock.StartState) {
   817  	// 		e.unit.Launch(func() {
   818  	// 			e.applyStateDelta(delta)
   819  	// 		})
   820  	// 		return true
   821  	// 	}
   822  	//
   823  	// 	// if state delta is invalid, remove the delta and log error
   824  	// 	e.log.Error().
   825  	// 		Hex("block_start_state", eb.StartState).
   826  	// 		Hex("delta_start_state", delta.ExecutableBlock.StartState).
   827  	// 		Msg("can not apply the state delta, the start state does not match")
   828  	//
   829  	// 	e.syncDeltas.Remove(eb.Block.ID())
   830  	// }
   831  
   832  	// if don't have the delta, then check if everything is ready for executing
   833  	// the block
   834  	if eb.IsComplete() {
   835  
   836  		if e.extensiveLogging {
   837  			e.logExecutableBlock(eb)
   838  		}
   839  
   840  		// no external synchronisation is used because this method must be run in a thread-safe context
   841  		eb.Executing = true
   842  
   843  		e.unit.Launch(func() {
   844  			e.executeBlock(e.unit.Ctx(), eb)
   845  		})
   846  		return true
   847  	}
   848  	return false
   849  }
   850  
   851  // OnCollection is a callback for handling the collections requested by the
   852  // collection requester.
   853  func (e *Engine) OnCollection(originID flow.Identifier, entity flow.Entity) {
   854  	// convert entity to strongly typed collection
   855  	collection, ok := entity.(*flow.Collection)
   856  	if !ok {
   857  		e.log.Error().Msgf("invalid entity type (%T)", entity)
   858  		return
   859  	}
   860  
   861  	// no need to validate the origin ID, since the collection requester has
   862  	// checked the origin must be a collection node.
   863  
   864  	err := e.handleCollection(originID, collection)
   865  	if err != nil {
   866  		e.log.Error().Err(err).Msg("could not handle collection")
   867  	}
   868  }
   869  
   870  // a block can't be executed if its collection is missing.
   871  // since a collection can belong to multiple blocks, we need to
   872  // find all the blocks that are needing this collection, and then
   873  // check if any of these block becomes executable and execute it if
   874  // is.
   875  func (e *Engine) handleCollection(originID flow.Identifier, collection *flow.Collection) error {
   876  	collID := collection.ID()
   877  
   878  	span, _ := e.tracer.StartCollectionSpan(context.Background(), collID, trace.EXEHandleCollection)
   879  	defer span.End()
   880  
   881  	lg := e.log.With().Hex("collection_id", collID[:]).Logger()
   882  
   883  	lg.Info().Hex("sender", originID[:]).Msg("handle collection")
   884  	defer func(startTime time.Time) {
   885  		lg.Info().TimeDiff("duration", time.Now(), startTime).Msg("collection handled")
   886  	}(time.Now())
   887  
   888  	// TODO: bail if have seen this collection before.
   889  	err := e.collections.Store(collection)
   890  	if err != nil {
   891  		return fmt.Errorf("cannot store collection: %w", err)
   892  	}
   893  
   894  	return e.mempool.BlockByCollection.Run(
   895  		func(backdata *stdmap.BlockByCollectionBackdata) error {
   896  			return e.addCollectionToMempool(collection, backdata)
   897  		},
   898  	)
   899  }
   900  
   901  func (e *Engine) addCollectionToMempool(collection *flow.Collection, backdata *stdmap.BlockByCollectionBackdata) error {
   902  	collID := collection.ID()
   903  	blockByCollectionID, exists := backdata.ByID(collID)
   904  
   905  	// if we don't find any block for this collection, then
   906  	// means we don't need this collection any more.
   907  	// or it was ejected from the mempool when it was full.
   908  	// either way, we will return
   909  	if !exists {
   910  		return nil
   911  	}
   912  
   913  	for _, executableBlock := range blockByCollectionID.ExecutableBlocks {
   914  		blockID := executableBlock.ID()
   915  
   916  		completeCollection, ok := executableBlock.CompleteCollections[collID]
   917  		if !ok {
   918  			return fmt.Errorf("cannot handle collection: internal inconsistency - collection pointing to block %v which does not contain said collection",
   919  				blockID)
   920  		}
   921  
   922  		// record collection max height metrics
   923  		blockHeight := executableBlock.Block.Header.Height
   924  		if blockHeight > e.maxCollectionHeight {
   925  			e.metrics.UpdateCollectionMaxHeight(blockHeight)
   926  			e.maxCollectionHeight = blockHeight
   927  		}
   928  
   929  		if completeCollection.IsCompleted() {
   930  			// already received transactions for this collection
   931  			continue
   932  		}
   933  
   934  		// update the transactions of the collection
   935  		// Note: it's guaranteed the transactions are for this collection, because
   936  		// the collection id matches with the CollectionID from the collection guarantee
   937  		completeCollection.Transactions = collection.Transactions
   938  
   939  		// check if the block becomes executable
   940  		_ = e.executeBlockIfComplete(executableBlock)
   941  	}
   942  
   943  	// since we've received this collection, remove it from the index
   944  	// this also prevents from executing the same block twice, because the second
   945  	// time when the collection arrives, it will not be found in the blockByCollectionID
   946  	// index.
   947  	backdata.Remove(collID)
   948  
   949  	return nil
   950  }
   951  
   952  func newQueue(blockify queue.Blockify, queues *stdmap.QueuesBackdata) (*queue.Queue, bool) {
   953  	q := queue.NewQueue(blockify)
   954  	qID := q.ID()
   955  	return q, queues.Add(qID, q)
   956  }
   957  
   958  // enqueue adds a block to the queues, return the queue that includes the block and booleans
   959  // * is block new one (it's not already enqueued, not a duplicate)
   960  // * is head of the queue (new queue has been created)
   961  //
   962  // Queues are chained blocks. Since a block can't be executable until its parent has been
   963  // executed, the chained structure allows us to only check the head of each queue to see if
   964  // any block becomes executable.
   965  // for instance we have one queue whose head is A:
   966  //
   967  //	A <- B <- C
   968  //	  ^- D <- E
   969  //
   970  // If we receive E <- F, then we will add it to the queue:
   971  //
   972  //	A <- B <- C
   973  //	  ^- D <- E <- F
   974  //
   975  // Even through there are 6 blocks, we only need to check if block A becomes executable.
   976  // when the parent block isn't in the queue, we add it as a new queue. for instance, if
   977  // we receive H <- G, then the queues will become:
   978  //
   979  //	A <- B <- C
   980  //	  ^- D <- E
   981  //	G
   982  func enqueue(blockify queue.Blockify, queues *stdmap.QueuesBackdata) (*queue.Queue, bool, bool) {
   983  	for _, queue := range queues.All() {
   984  		if stored, isNew := queue.TryAdd(blockify); stored {
   985  			return queue, isNew, false
   986  		}
   987  	}
   988  	queue, isNew := newQueue(blockify, queues)
   989  	return queue, isNew, true
   990  }
   991  
   992  // check if the block's collections have been received,
   993  // if yes, add the collection to the executable block
   994  // if no, fetch the collection.
   995  // if a block has 3 collection, it would be 3 reqs to fetch them.
   996  // mark the collection belongs to the block,
   997  // mark the block contains this collection.
   998  // It returns the missing collections to be fetched
   999  // TODO: to rename
  1000  func (e *Engine) matchAndFindMissingCollections(
  1001  	executableBlock *entity.ExecutableBlock,
  1002  	collectionsBackdata *stdmap.BlockByCollectionBackdata,
  1003  ) ([]*flow.CollectionGuarantee, error) {
  1004  	// if the state syncing is on, it will fetch deltas for sealed and
  1005  	// unexecuted blocks. However, for any new blocks, we are still fetching
  1006  	// collections for them, which is not necessary, because the state deltas
  1007  	// will include the collection.
  1008  	// Fetching those collections will introduce load to collection nodes,
  1009  	// and handling them would increase memory usage and network bandwidth.
  1010  	// Therefore, we introduced this "sync-fast" mode.
  1011  	// The sync-fast mode can be turned on by the `sync-fast=true` flag.
  1012  	// When it's turned on, it will skip fetching collections, and will
  1013  	// rely on the state syncing to catch up.
  1014  	// if e.syncFast {
  1015  	// 	isSyncing := e.isSyncingState()
  1016  	// 	if isSyncing {
  1017  	// 		return nil
  1018  	// 	}
  1019  	// }
  1020  
  1021  	missingCollections := make([]*flow.CollectionGuarantee, 0, len(executableBlock.Block.Payload.Guarantees))
  1022  
  1023  	for _, guarantee := range executableBlock.Block.Payload.Guarantees {
  1024  		coll := &entity.CompleteCollection{
  1025  			Guarantee: guarantee,
  1026  		}
  1027  		executableBlock.CompleteCollections[guarantee.ID()] = coll
  1028  
  1029  		// check if we have requested this collection before.
  1030  		// blocksNeedingCollection stores all the blocks that contain this collection
  1031  
  1032  		if blocksNeedingCollection, exists := collectionsBackdata.ByID(guarantee.ID()); exists {
  1033  			// if we've requested this collection, it means other block might also contain this collection.
  1034  			// in this case, add this block to the map so that when the collection is received,
  1035  			// we could update the executable block
  1036  			blocksNeedingCollection.ExecutableBlocks[executableBlock.ID()] = executableBlock
  1037  
  1038  			// since the collection is still being requested, we don't have the transactions
  1039  			// yet, so exit
  1040  			continue
  1041  		}
  1042  
  1043  		// the storage doesn't have this collection, meaning this is our first time seeing this
  1044  		// collection guarantee, create an entry to store in collectionsBackdata in order to
  1045  		// update the executable blocks when the collection is received.
  1046  		blocksNeedingCollection := &entity.BlocksByCollection{
  1047  			CollectionID:     guarantee.ID(),
  1048  			ExecutableBlocks: map[flow.Identifier]*entity.ExecutableBlock{executableBlock.ID(): executableBlock},
  1049  		}
  1050  
  1051  		added := collectionsBackdata.Add(blocksNeedingCollection.ID(), blocksNeedingCollection)
  1052  		if !added {
  1053  			// sanity check, should not happen, unless mempool implementation has a bug
  1054  			return nil, fmt.Errorf("collection already mapped to block")
  1055  		}
  1056  
  1057  		missingCollections = append(missingCollections, guarantee)
  1058  	}
  1059  
  1060  	return missingCollections, nil
  1061  }
  1062  
  1063  func (e *Engine) ExecuteScriptAtBlockID(ctx context.Context, script []byte, arguments [][]byte, blockID flow.Identifier) ([]byte, error) {
  1064  
  1065  	stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
  1066  	if err != nil {
  1067  		return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
  1068  	}
  1069  
  1070  	// return early if state with the given state commitment is not in memory
  1071  	// and already purged. This reduces allocations for scripts targeting old blocks.
  1072  	if !e.execState.HasState(stateCommit) {
  1073  		return nil, fmt.Errorf("failed to execute script at block (%s): state commitment not found (%s). this error usually happens if the reference block for this script is not set to a recent block", blockID.String(), hex.EncodeToString(stateCommit[:]))
  1074  	}
  1075  
  1076  	block, err := e.state.AtBlockID(blockID).Head()
  1077  	if err != nil {
  1078  		return nil, fmt.Errorf("failed to get block (%s): %w", blockID, err)
  1079  	}
  1080  
  1081  	blockView := e.execState.NewView(stateCommit)
  1082  
  1083  	if e.extensiveLogging {
  1084  		args := make([]string, 0)
  1085  		for _, a := range arguments {
  1086  			args = append(args, hex.EncodeToString(a))
  1087  		}
  1088  		e.log.Debug().
  1089  			Hex("block_id", logging.ID(blockID)).
  1090  			Uint64("block_height", block.Height).
  1091  			Hex("state_commitment", stateCommit[:]).
  1092  			Hex("script_hex", script).
  1093  			Str("args", strings.Join(args[:], ",")).
  1094  			Msg("extensive log: executed script content")
  1095  	}
  1096  	return e.computationManager.ExecuteScript(ctx, script, arguments, block, blockView)
  1097  }
  1098  
  1099  func (e *Engine) GetRegisterAtBlockID(ctx context.Context, owner, key []byte, blockID flow.Identifier) ([]byte, error) {
  1100  
  1101  	stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
  1102  	if err != nil {
  1103  		return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
  1104  	}
  1105  
  1106  	blockView := e.execState.NewView(stateCommit)
  1107  
  1108  	data, err := blockView.Get(string(owner), string(key))
  1109  	if err != nil {
  1110  		return nil, fmt.Errorf("failed to get the register (owner : %s, key: %s): %w", hex.EncodeToString(owner), string(key), err)
  1111  	}
  1112  
  1113  	return data, nil
  1114  }
  1115  
  1116  func (e *Engine) GetAccount(ctx context.Context, addr flow.Address, blockID flow.Identifier) (*flow.Account, error) {
  1117  	stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
  1118  	if err != nil {
  1119  		return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
  1120  	}
  1121  
  1122  	// return early if state with the given state commitment is not in memory
  1123  	// and already purged. This reduces allocations for get accounts targeting old blocks.
  1124  	if !e.execState.HasState(stateCommit) {
  1125  		return nil, fmt.Errorf("failed to get account at block (%s): state commitment not found (%s). this error usually happens if the reference block for this script is not set to a recent block.", blockID.String(), hex.EncodeToString(stateCommit[:]))
  1126  	}
  1127  
  1128  	block, err := e.state.AtBlockID(blockID).Head()
  1129  	if err != nil {
  1130  		return nil, fmt.Errorf("failed to get block (%s): %w", blockID, err)
  1131  	}
  1132  
  1133  	blockView := e.execState.NewView(stateCommit)
  1134  
  1135  	return e.computationManager.GetAccount(addr, block, blockView)
  1136  }
  1137  
  1138  func (e *Engine) handleComputationResult(
  1139  	ctx context.Context,
  1140  	result *execution.ComputationResult,
  1141  	startState flow.StateCommitment,
  1142  ) (flow.StateCommitment, *flow.ExecutionReceipt, error) {
  1143  
  1144  	span, ctx := e.tracer.StartSpanFromContext(ctx, trace.EXEHandleComputationResult)
  1145  	defer span.End()
  1146  
  1147  	e.log.Debug().
  1148  		Hex("block_id", logging.Entity(result.ExecutableBlock)).
  1149  		Msg("received computation result")
  1150  
  1151  	receipt, err := e.saveExecutionResults(
  1152  		ctx,
  1153  		result,
  1154  		startState,
  1155  	)
  1156  	if err != nil {
  1157  		return flow.DummyStateCommitment, nil, fmt.Errorf("could not save execution results: %w", err)
  1158  	}
  1159  
  1160  	finalState, err := receipt.ExecutionResult.FinalStateCommitment()
  1161  	if errors.Is(err, flow.ErrNoChunks) {
  1162  		finalState = startState
  1163  	} else if err != nil {
  1164  		return flow.DummyStateCommitment, nil, fmt.Errorf("unexpected error accessing result's final state commitment: %w", err)
  1165  	}
  1166  	return finalState, receipt, nil
  1167  }
  1168  
  1169  // save the execution result of a block
  1170  func (e *Engine) saveExecutionResults(
  1171  	ctx context.Context,
  1172  	result *execution.ComputationResult,
  1173  	startState flow.StateCommitment,
  1174  ) (*flow.ExecutionReceipt, error) {
  1175  
  1176  	span, childCtx := e.tracer.StartSpanFromContext(ctx, trace.EXESaveExecutionResults)
  1177  	defer span.End()
  1178  
  1179  	originalState := startState
  1180  
  1181  	block := result.ExecutableBlock.Block
  1182  	previousErID, err := e.execState.GetExecutionResultID(ctx, block.Header.ParentID)
  1183  	if err != nil {
  1184  		return nil, fmt.Errorf("could not get execution result ID for parent block (%v): %w",
  1185  			block.Header.ParentID, err)
  1186  	}
  1187  
  1188  	endState, chdps, executionResult, err := execution.GenerateExecutionResultAndChunkDataPacks(e.metrics, previousErID, startState, result)
  1189  	if err != nil {
  1190  		return nil, fmt.Errorf("cannot build chunk data pack: %w", err)
  1191  	}
  1192  	for _, event := range executionResult.ServiceEvents {
  1193  		e.log.Info().
  1194  			Uint64("block_height", result.ExecutableBlock.Height()).
  1195  			Hex("block_id", logging.Entity(result.ExecutableBlock)).
  1196  			Str("event_type", event.Type).
  1197  			Msg("service event emitted")
  1198  	}
  1199  
  1200  	executionReceipt, err := GenerateExecutionReceipt(
  1201  		e.me,
  1202  		e.receiptHasher,
  1203  		executionResult,
  1204  		result.SpockSignatures)
  1205  
  1206  	if err != nil {
  1207  		return nil, fmt.Errorf("could not generate execution receipt: %w", err)
  1208  	}
  1209  
  1210  	err = e.execState.SaveExecutionResults(childCtx,
  1211  		block.Header,
  1212  		endState,
  1213  		chdps,
  1214  		executionReceipt,
  1215  		result.Events,
  1216  		result.ServiceEvents,
  1217  		result.TransactionResults)
  1218  	if err != nil {
  1219  		return nil, fmt.Errorf("cannot persist execution state: %w", err)
  1220  	}
  1221  
  1222  	e.log.Debug().
  1223  		Hex("block_id", logging.Entity(result.ExecutableBlock)).
  1224  		Hex("start_state", originalState[:]).
  1225  		Hex("final_state", endState[:]).
  1226  		Msg("saved computation results")
  1227  
  1228  	return executionReceipt, nil
  1229  }
  1230  
  1231  // logExecutableBlock logs all data about an executable block
  1232  // over time we should skip this
  1233  func (e *Engine) logExecutableBlock(eb *entity.ExecutableBlock) {
  1234  	// log block
  1235  	e.log.Debug().
  1236  		Hex("block_id", logging.Entity(eb)).
  1237  		Hex("prev_block_id", logging.ID(eb.Block.Header.ParentID)).
  1238  		Uint64("block_height", eb.Block.Header.Height).
  1239  		Int("number_of_collections", len(eb.Collections())).
  1240  		RawJSON("block_header", logging.AsJSON(eb.Block.Header)).
  1241  		Msg("extensive log: block header")
  1242  
  1243  	// logs transactions
  1244  	for i, col := range eb.Collections() {
  1245  		for j, tx := range col.Transactions {
  1246  			e.log.Debug().
  1247  				Hex("block_id", logging.Entity(eb)).
  1248  				Int("block_height", int(eb.Block.Header.Height)).
  1249  				Hex("prev_block_id", logging.ID(eb.Block.Header.ParentID)).
  1250  				Int("collection_index", i).
  1251  				Int("tx_index", j).
  1252  				Hex("collection_id", logging.ID(col.Guarantee.CollectionID)).
  1253  				Hex("tx_hash", logging.Entity(tx)).
  1254  				Hex("start_state_commitment", eb.StartState[:]).
  1255  				RawJSON("transaction", logging.AsJSON(tx)).
  1256  				Msg("extensive log: executed tx content")
  1257  		}
  1258  	}
  1259  }
  1260  
  1261  func GenerateExecutionReceipt(
  1262  	me module.Local,
  1263  	receiptHasher hash.Hasher,
  1264  	result *flow.ExecutionResult,
  1265  	spockSignatures []crypto.Signature,
  1266  ) (
  1267  	*flow.ExecutionReceipt,
  1268  	error,
  1269  ) {
  1270  	receipt := &flow.ExecutionReceipt{
  1271  		ExecutionResult:   *result,
  1272  		Spocks:            spockSignatures,
  1273  		ExecutorSignature: crypto.Signature{},
  1274  		ExecutorID:        me.NodeID(),
  1275  	}
  1276  
  1277  	// generates a signature over the execution result
  1278  	id := receipt.ID()
  1279  	sig, err := me.Sign(id[:], receiptHasher)
  1280  	if err != nil {
  1281  		return nil, fmt.Errorf("could not sign execution result: %w", err)
  1282  	}
  1283  
  1284  	receipt.ExecutorSignature = sig
  1285  
  1286  	return receipt, nil
  1287  }
  1288  
  1289  // addOrFetch checks if there are stored collections for the given guarantees, if there is,
  1290  // forward them to mempool to process the collection, otherwise fetch the collections.
  1291  // any error returned are exception
  1292  func (e *Engine) addOrFetch(blockID flow.Identifier, height uint64, guarantees []*flow.CollectionGuarantee) error {
  1293  	return e.fetchAndHandleCollection(blockID, height, guarantees, func(collection *flow.Collection) error {
  1294  		err := e.mempool.BlockByCollection.Run(
  1295  			func(backdata *stdmap.BlockByCollectionBackdata) error {
  1296  				return e.addCollectionToMempool(collection, backdata)
  1297  			})
  1298  
  1299  		if err != nil {
  1300  			return fmt.Errorf("could not add collection to mempool: %w", err)
  1301  		}
  1302  		return nil
  1303  	})
  1304  }
  1305  
  1306  // addOrFetch checks if there are stored collections for the given guarantees, if there is,
  1307  // forward them to the handler to process the collection, otherwise fetch the collections.
  1308  // any error returned are exception
  1309  func (e *Engine) fetchAndHandleCollection(
  1310  	blockID flow.Identifier,
  1311  	height uint64,
  1312  	guarantees []*flow.CollectionGuarantee,
  1313  	handleCollection func(*flow.Collection) error,
  1314  ) error {
  1315  	fetched := false
  1316  	for _, guarantee := range guarantees {
  1317  		// if we've requested this collection, we will store it in the storage,
  1318  		// so check the storage to see whether we've seen it.
  1319  		collection, err := e.collections.ByID(guarantee.CollectionID)
  1320  
  1321  		if err == nil {
  1322  			// we found the collection from storage, forward this collection to handler
  1323  			err = handleCollection(collection)
  1324  			if err != nil {
  1325  				return fmt.Errorf("could not handle collection: %w", err)
  1326  			}
  1327  
  1328  			continue
  1329  		}
  1330  
  1331  		// check if there was exception
  1332  		if !errors.Is(err, storage.ErrNotFound) {
  1333  			return fmt.Errorf("error while querying for collection: %w", err)
  1334  		}
  1335  
  1336  		err = e.fetchCollection(blockID, height, guarantee)
  1337  		if err != nil {
  1338  			return fmt.Errorf("could not fetch collection: %w", err)
  1339  		}
  1340  		fetched = true
  1341  	}
  1342  
  1343  	// make sure that the requests are dispatched immediately by the requester
  1344  	if fetched {
  1345  		e.request.Force()
  1346  		e.metrics.ExecutionCollectionRequestSent()
  1347  	}
  1348  
  1349  	return nil
  1350  }
  1351  
  1352  // fetchCollection takes a guarantee and forwards to requester engine for fetching the collection
  1353  // any error returned are fatal error
  1354  func (e *Engine) fetchCollection(blockID flow.Identifier, height uint64, guarantee *flow.CollectionGuarantee) error {
  1355  	e.log.Debug().
  1356  		Hex("block", blockID[:]).
  1357  		Hex("collection_id", logging.ID(guarantee.ID())).
  1358  		Msg("requesting collection")
  1359  
  1360  	guarantors, err := protocol.FindGuarantors(e.state, guarantee)
  1361  	if err != nil {
  1362  		// execution node executes certified blocks, which means there is a quorum of consensus nodes who
  1363  		// have validated the block payload. And that validation includes checking the guarantors are correct.
  1364  		// Based on that assumption, failing to find guarantors for guarantees contained in an incorporated block
  1365  		// should be treated as fatal error
  1366  		e.log.Fatal().Err(err).Msgf("failed to find guarantors for guarantee %v at block %v, height %v",
  1367  			guarantee.ID(),
  1368  			blockID,
  1369  			height,
  1370  		)
  1371  		return fmt.Errorf("could not find guarantors: %w", err)
  1372  	}
  1373  	// queue the collection to be requested from one of the guarantors
  1374  	e.request.EntityByID(guarantee.ID(), filter.And(
  1375  		filter.HasNodeID(guarantors...),
  1376  	))
  1377  
  1378  	return nil
  1379  }