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  }