github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/state_stream/backend/backend_executiondata.go (about) 1 package backend 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/rs/zerolog" 10 "google.golang.org/grpc/codes" 11 "google.golang.org/grpc/status" 12 13 "github.com/onflow/flow-go/engine/access/subscription" 14 "github.com/onflow/flow-go/engine/common/rpc" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/module/executiondatasync/execution_data" 17 "github.com/onflow/flow-go/storage" 18 ) 19 20 type ExecutionDataResponse struct { 21 Height uint64 22 ExecutionData *execution_data.BlockExecutionData 23 BlockTimestamp time.Time 24 } 25 26 type ExecutionDataBackend struct { 27 log zerolog.Logger 28 headers storage.Headers 29 30 getExecutionData GetExecutionDataFunc 31 32 subscriptionHandler *subscription.SubscriptionHandler 33 executionDataTracker subscription.ExecutionDataTracker 34 } 35 36 func (b *ExecutionDataBackend) GetExecutionDataByBlockID(ctx context.Context, blockID flow.Identifier) (*execution_data.BlockExecutionData, error) { 37 header, err := b.headers.ByBlockID(blockID) 38 if err != nil { 39 return nil, fmt.Errorf("could not get block header for %s: %w", blockID, err) 40 } 41 42 executionData, err := b.getExecutionData(ctx, header.Height) 43 44 if err != nil { 45 // need custom not found handler due to blob not found error 46 if errors.Is(err, storage.ErrNotFound) || execution_data.IsBlobNotFoundError(err) { 47 return nil, status.Errorf(codes.NotFound, "could not find execution data: %v", err) 48 } 49 50 return nil, rpc.ConvertError(err, "could not get execution data", codes.Internal) 51 } 52 53 return executionData.BlockExecutionData, nil 54 } 55 56 // SubscribeExecutionData is deprecated and will be removed in future versions. 57 // Use SubscribeExecutionDataFromStartBlockID, SubscribeExecutionDataFromStartBlockHeight or SubscribeExecutionDataFromLatest. 58 // 59 // SubscribeExecutionData streams execution data for all blocks starting at the specified block ID or block height 60 // up until the latest available block. Once the latest is reached, the stream will remain open and responses 61 // are sent for each new block as it becomes available. 62 // 63 // Only one of startBlockID and startHeight may be set. If neither startBlockID nor startHeight is provided, 64 // the latest sealed block is used. 65 // 66 // Parameters: 67 // - ctx: Context for the operation. 68 // - startBlockID: The identifier of the starting block. If provided, startHeight should be 0. 69 // - startHeight: The height of the starting block. If provided, startBlockID should be flow.ZeroID. 70 // 71 // If invalid parameters are provided, failed subscription will be returned. 72 func (b *ExecutionDataBackend) SubscribeExecutionData(ctx context.Context, startBlockID flow.Identifier, startHeight uint64) subscription.Subscription { 73 nextHeight, err := b.executionDataTracker.GetStartHeight(ctx, startBlockID, startHeight) 74 if err != nil { 75 return subscription.NewFailedSubscription(err, "could not get start block height") 76 } 77 78 return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponse) 79 } 80 81 // SubscribeExecutionDataFromStartBlockID streams execution data for all blocks starting at the specified block ID 82 // up until the latest available block. Once the latest is reached, the stream will remain open and responses 83 // are sent for each new block as it becomes available. 84 // 85 // Parameters: 86 // - ctx: Context for the operation. 87 // - startBlockID: The identifier of the starting block. 88 // 89 // If invalid parameters are provided, failed subscription will be returned. 90 func (b *ExecutionDataBackend) SubscribeExecutionDataFromStartBlockID(ctx context.Context, startBlockID flow.Identifier) subscription.Subscription { 91 nextHeight, err := b.executionDataTracker.GetStartHeightFromBlockID(startBlockID) 92 if err != nil { 93 return subscription.NewFailedSubscription(err, "could not get start block height") 94 } 95 96 return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponse) 97 } 98 99 // SubscribeExecutionDataFromStartBlockHeight streams execution data for all blocks starting at the specified block height 100 // up until the latest available block. Once the latest is reached, the stream will remain open and responses 101 // are sent for each new block as it becomes available. 102 // 103 // Parameters: 104 // - ctx: Context for the operation. 105 // - startHeight: The height of the starting block. 106 // 107 // If invalid parameters are provided, failed subscription will be returned. 108 func (b *ExecutionDataBackend) SubscribeExecutionDataFromStartBlockHeight(ctx context.Context, startBlockHeight uint64) subscription.Subscription { 109 nextHeight, err := b.executionDataTracker.GetStartHeightFromHeight(startBlockHeight) 110 if err != nil { 111 return subscription.NewFailedSubscription(err, "could not get start block height") 112 } 113 114 return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponse) 115 } 116 117 // SubscribeExecutionDataFromLatest streams execution data starting at the latest block. 118 // Once the latest is reached, the stream will remain open and responses are sent for each new 119 // block as it becomes available. 120 // 121 // Parameters: 122 // - ctx: Context for the operation. 123 // 124 // If invalid parameters are provided, failed subscription will be returned. 125 func (b *ExecutionDataBackend) SubscribeExecutionDataFromLatest(ctx context.Context) subscription.Subscription { 126 nextHeight, err := b.executionDataTracker.GetStartHeightFromLatest(ctx) 127 if err != nil { 128 return subscription.NewFailedSubscription(err, "could not get start block height") 129 } 130 131 return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponse) 132 } 133 134 func (b *ExecutionDataBackend) getResponse(ctx context.Context, height uint64) (interface{}, error) { 135 executionData, err := b.getExecutionData(ctx, height) 136 if err != nil { 137 return nil, fmt.Errorf("could not get execution data for block %d: %w", height, err) 138 } 139 140 return &ExecutionDataResponse{ 141 Height: height, 142 ExecutionData: executionData.BlockExecutionData, 143 }, nil 144 }