github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/subscription/base_tracker.go (about)

     1  package subscription
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync/atomic"
     7  
     8  	"google.golang.org/grpc/codes"
     9  	"google.golang.org/grpc/status"
    10  
    11  	"github.com/onflow/flow-go/engine/common/rpc"
    12  
    13  	"github.com/onflow/flow-go/model/flow"
    14  	"github.com/onflow/flow-go/module/irrecoverable"
    15  	"github.com/onflow/flow-go/state/protocol"
    16  	"github.com/onflow/flow-go/storage"
    17  )
    18  
    19  // StreamingData represents common streaming data configuration for access and state_stream handlers.
    20  type StreamingData struct {
    21  	MaxStreams  int32
    22  	StreamCount atomic.Int32
    23  }
    24  
    25  func NewStreamingData(maxStreams uint32) StreamingData {
    26  	return StreamingData{
    27  		MaxStreams:  int32(maxStreams),
    28  		StreamCount: atomic.Int32{},
    29  	}
    30  }
    31  
    32  // BaseTracker is an interface for a tracker that provides base GetStartHeight method related to both blocks and execution data tracking.
    33  type BaseTracker interface {
    34  	// GetStartHeightFromBlockID returns the start height based on the provided starting block ID.
    35  	// If the start block is the root block, skip it and begin from the next block.
    36  	//
    37  	// Parameters:
    38  	// - startBlockID: The identifier of the starting block.
    39  	//
    40  	// Returns:
    41  	// - uint64: The start height associated with the provided block ID.
    42  	// - error: An error indicating any issues with retrieving the start height.
    43  	//
    44  	// Expected errors during normal operation:
    45  	// - codes.NotFound - if the block was not found in storage
    46  	// - codes.Internal - for any other error
    47  	GetStartHeightFromBlockID(flow.Identifier) (uint64, error)
    48  	// GetStartHeightFromHeight returns the start height based on the provided starting block height.
    49  	// If the start block is the root block, skip it and begin from the next block.
    50  	//
    51  	// Parameters:
    52  	// - startHeight: The height of the starting block.
    53  	//
    54  	// Returns:
    55  	// - uint64: The start height associated with the provided block height.
    56  	// - error: An error indicating any issues with retrieving the start height.
    57  	//
    58  	// Expected errors during normal operation:
    59  	// - codes.InvalidArgument   - if the start height is less than the root block height.
    60  	// - codes.NotFound  - if the header was not found in storage.
    61  	GetStartHeightFromHeight(uint64) (uint64, error)
    62  	// GetStartHeightFromLatest returns the start height based on the latest sealed block.
    63  	// If the start block is the root block, skip it and begin from the next block.
    64  	//
    65  	// Parameters:
    66  	// - ctx: Context for the operation.
    67  	//
    68  	// No errors are expected during normal operation.
    69  	GetStartHeightFromLatest(context.Context) (uint64, error)
    70  }
    71  
    72  var _ BaseTracker = (*BaseTrackerImpl)(nil)
    73  
    74  // BaseTrackerImpl is an implementation of the BaseTracker interface.
    75  type BaseTrackerImpl struct {
    76  	rootBlockHeight uint64
    77  	state           protocol.State
    78  	headers         storage.Headers
    79  }
    80  
    81  // NewBaseTrackerImpl creates a new instance of BaseTrackerImpl.
    82  //
    83  // Parameters:
    84  // - rootBlockHeight: The root block height, which serves as the baseline for calculating the start height.
    85  // - state: The protocol state used for retrieving block information.
    86  // - headers: The storage headers for accessing block headers.
    87  //
    88  // Returns:
    89  // - *BaseTrackerImpl: A new instance of BaseTrackerImpl.
    90  func NewBaseTrackerImpl(
    91  	rootBlockHeight uint64,
    92  	state protocol.State,
    93  	headers storage.Headers,
    94  ) *BaseTrackerImpl {
    95  	return &BaseTrackerImpl{
    96  		rootBlockHeight: rootBlockHeight,
    97  		state:           state,
    98  		headers:         headers,
    99  	}
   100  }
   101  
   102  // GetStartHeightFromBlockID returns the start height based on the provided starting block ID.
   103  // If the start block is the root block, skip it and begin from the next block.
   104  //
   105  // Parameters:
   106  // - startBlockID: The identifier of the starting block.
   107  //
   108  // Returns:
   109  // - uint64: The start height associated with the provided block ID.
   110  // - error: An error indicating any issues with retrieving the start height.
   111  //
   112  // Expected errors during normal operation:
   113  // - codes.NotFound - if the block was not found in storage
   114  // - codes.Internal - for any other error
   115  func (b *BaseTrackerImpl) GetStartHeightFromBlockID(startBlockID flow.Identifier) (uint64, error) {
   116  	header, err := b.headers.ByBlockID(startBlockID)
   117  	if err != nil {
   118  		return 0, rpc.ConvertStorageError(fmt.Errorf("could not get header for block %v: %w", startBlockID, err))
   119  	}
   120  
   121  	// ensure that the resolved start height is available
   122  	return b.checkStartHeight(header.Height), nil
   123  }
   124  
   125  // GetStartHeightFromHeight returns the start height based on the provided starting block height.
   126  // If the start block is the root block, skip it and begin from the next block.
   127  //
   128  // Parameters:
   129  // - startHeight: The height of the starting block.
   130  //
   131  // Returns:
   132  // - uint64: The start height associated with the provided block height.
   133  // - error: An error indicating any issues with retrieving the start height.
   134  //
   135  // Expected errors during normal operation:
   136  // - codes.InvalidArgument   - if the start height is less than the root block height.
   137  // - codes.NotFound  - if the header was not found in storage.
   138  func (b *BaseTrackerImpl) GetStartHeightFromHeight(startHeight uint64) (uint64, error) {
   139  	if startHeight < b.rootBlockHeight {
   140  		return 0, status.Errorf(codes.InvalidArgument, "start height must be greater than or equal to the root height %d", b.rootBlockHeight)
   141  	}
   142  
   143  	header, err := b.headers.ByHeight(startHeight)
   144  	if err != nil {
   145  		return 0, rpc.ConvertStorageError(fmt.Errorf("could not get header for height %d: %w", startHeight, err))
   146  	}
   147  
   148  	// ensure that the resolved start height is available
   149  	return b.checkStartHeight(header.Height), nil
   150  }
   151  
   152  // GetStartHeightFromLatest returns the start height based on the latest sealed block.
   153  // If the start block is the root block, skip it and begin from the next block.
   154  //
   155  // Parameters:
   156  // - ctx: Context for the operation.
   157  //
   158  // No errors are expected during normal operation.
   159  func (b *BaseTrackerImpl) GetStartHeightFromLatest(ctx context.Context) (uint64, error) {
   160  	// if no start block was provided, use the latest sealed block
   161  	header, err := b.state.Sealed().Head()
   162  	if err != nil {
   163  		// In the RPC engine, if we encounter an error from the protocol state indicating state corruption,
   164  		// we should halt processing requests
   165  		err := irrecoverable.NewExceptionf("failed to lookup sealed header: %w", err)
   166  		irrecoverable.Throw(ctx, err)
   167  		return 0, err
   168  	}
   169  
   170  	return b.checkStartHeight(header.Height), nil
   171  }
   172  
   173  // checkStartHeight validates the provided start height and adjusts it if necessary.
   174  // If the start block is the root block, skip it and begin from the next block.
   175  //
   176  // Parameters:
   177  // - height: The start height to be checked.
   178  //
   179  // Returns:
   180  // - uint64: The adjusted start height.
   181  //
   182  // No errors are expected during normal operation.
   183  func (b *BaseTrackerImpl) checkStartHeight(height uint64) uint64 {
   184  	// if the start block is the root block, skip it and begin from the next block.
   185  	if height == b.rootBlockHeight {
   186  		height = b.rootBlockHeight + 1
   187  	}
   188  
   189  	return height
   190  }