github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/ingestion/machine.go (about) 1 package ingestion 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/rs/zerolog" 8 9 "github.com/onflow/flow-go/engine/common/requester" 10 "github.com/onflow/flow-go/engine/execution" 11 "github.com/onflow/flow-go/engine/execution/computation" 12 "github.com/onflow/flow-go/engine/execution/ingestion/stop" 13 "github.com/onflow/flow-go/engine/execution/ingestion/uploader" 14 "github.com/onflow/flow-go/engine/execution/provider" 15 "github.com/onflow/flow-go/engine/execution/state" 16 "github.com/onflow/flow-go/model/flow" 17 "github.com/onflow/flow-go/module" 18 "github.com/onflow/flow-go/module/mempool/entity" 19 "github.com/onflow/flow-go/state/protocol" 20 "github.com/onflow/flow-go/state/protocol/events" 21 "github.com/onflow/flow-go/storage" 22 ) 23 24 // Machine forwards blocks and collections to the core for processing. 25 type Machine struct { 26 events.Noop // satisfy protocol events consumer interface 27 log zerolog.Logger 28 core *Core 29 throttle Throttle 30 broadcaster provider.ProviderEngine 31 uploader *uploader.Manager 32 execState state.ExecutionState 33 computationManager computation.ComputationManager 34 } 35 36 type CollectionRequester interface { 37 WithHandle(requester.HandleFunc) 38 } 39 40 func NewMachine( 41 logger zerolog.Logger, 42 protocolEvents *events.Distributor, 43 collectionRequester CollectionRequester, 44 45 collectionFetcher CollectionFetcher, 46 headers storage.Headers, 47 blocks storage.Blocks, 48 collections storage.Collections, 49 execState state.ExecutionState, 50 state protocol.State, 51 metrics module.ExecutionMetrics, 52 computationManager computation.ComputationManager, 53 broadcaster provider.ProviderEngine, 54 uploader *uploader.Manager, 55 stopControl *stop.StopControl, 56 ) (*Machine, module.ReadyDoneAware, error) { 57 58 e := &Machine{ 59 log: logger.With().Str("engine", "ingestion_machine").Logger(), 60 broadcaster: broadcaster, 61 uploader: uploader, 62 execState: execState, 63 computationManager: computationManager, 64 } 65 66 throttle, err := NewBlockThrottle( 67 logger, 68 state, 69 execState, 70 headers, 71 ) 72 73 if err != nil { 74 return nil, nil, fmt.Errorf("failed to create block throttle: %w", err) 75 } 76 77 core, err := NewCore( 78 logger, 79 throttle, 80 execState, 81 stopControl, 82 blocks, 83 collections, 84 e, 85 collectionFetcher, 86 e, 87 metrics, 88 ) 89 90 if err != nil { 91 return nil, nil, fmt.Errorf("failed to create ingestion core: %w", err) 92 } 93 94 e.throttle = throttle 95 e.core = core 96 97 protocolEvents.AddConsumer(e) 98 collectionRequester.WithHandle(func(originID flow.Identifier, entity flow.Entity) { 99 collection, ok := entity.(*flow.Collection) 100 if !ok { 101 e.log.Error().Msgf("invalid entity type (%T)", entity) 102 return 103 } 104 e.core.OnCollection(collection) 105 }) 106 107 return e, core, nil 108 } 109 110 // Protocol Events implementation 111 func (e *Machine) BlockProcessable(header *flow.Header, qc *flow.QuorumCertificate) { 112 err := e.throttle.OnBlock(qc.BlockID, header.Height) 113 if err != nil { 114 e.log.Fatal().Err(err).Msgf("error processing block %v (qc.BlockID: %v, blockID: %v)", 115 header.Height, qc.BlockID, header.ID()) 116 } 117 } 118 119 func (e *Machine) BlockFinalized(b *flow.Header) { 120 e.throttle.OnBlockFinalized(b.Height) 121 } 122 123 // EventConsumer implementation 124 var _ EventConsumer = (*Machine)(nil) 125 126 func (e *Machine) BeforeComputationResultSaved( 127 ctx context.Context, 128 result *execution.ComputationResult, 129 ) { 130 err := e.uploader.Upload(ctx, result) 131 if err != nil { 132 e.log.Err(err).Msg("error while uploading block") 133 // continue processing. uploads should not block execution 134 } 135 } 136 137 func (e *Machine) OnComputationResultSaved( 138 ctx context.Context, 139 result *execution.ComputationResult, 140 ) string { 141 header := result.BlockExecutionResult.ExecutableBlock.Block.Header 142 broadcasted, err := e.broadcaster.BroadcastExecutionReceipt( 143 ctx, header.Height, result.ExecutionReceipt) 144 if err != nil { 145 e.log.Err(err).Msg("critical: failed to broadcast the receipt") 146 } 147 return fmt.Sprintf("broadcasted: %v", broadcasted) 148 } 149 150 // BlockExecutor implementation 151 var _ BlockExecutor = (*Machine)(nil) 152 153 func (e *Machine) ExecuteBlock(ctx context.Context, executableBlock *entity.ExecutableBlock) (*execution.ComputationResult, error) { 154 parentID := executableBlock.Block.Header.ParentID 155 parentErID, err := e.execState.GetExecutionResultID(ctx, parentID) 156 if err != nil { 157 return nil, fmt.Errorf("failed to get parent execution result ID %v: %w", parentID, err) 158 } 159 160 snapshot := e.execState.NewStorageSnapshot(*executableBlock.StartState, 161 executableBlock.Block.Header.ParentID, 162 executableBlock.Block.Header.Height-1, 163 ) 164 165 computationResult, err := e.computationManager.ComputeBlock( 166 ctx, 167 parentErID, 168 executableBlock, 169 snapshot) 170 if err != nil { 171 return nil, fmt.Errorf("failed to compute block: %w", err) 172 } 173 174 return computationResult, nil 175 }