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 }