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 }