github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/subscription/block_tracker.go (about) 1 package subscription 2 3 import ( 4 "google.golang.org/grpc/codes" 5 "google.golang.org/grpc/status" 6 7 "github.com/onflow/flow-go/engine" 8 "github.com/onflow/flow-go/model/flow" 9 "github.com/onflow/flow-go/module/counters" 10 "github.com/onflow/flow-go/module/irrecoverable" 11 "github.com/onflow/flow-go/state/protocol" 12 "github.com/onflow/flow-go/storage" 13 ) 14 15 // BlockTracker is an interface for tracking blocks and handling block-related operations. 16 type BlockTracker interface { 17 BaseTracker 18 // GetHighestHeight returns the highest height based on the specified block status which could be only BlockStatusSealed 19 // or BlockStatusFinalized. 20 // No errors are expected during normal operation. 21 GetHighestHeight(flow.BlockStatus) (uint64, error) 22 // ProcessOnFinalizedBlock drives the subscription logic when a block is finalized. 23 // The input to this callback is treated as trusted. This method should be executed on 24 // `OnFinalizedBlock` notifications from the node-internal consensus instance. 25 // No errors are expected during normal operation. 26 ProcessOnFinalizedBlock() error 27 } 28 29 var _ BlockTracker = (*BlockTrackerImpl)(nil) 30 31 // BlockTrackerImpl is an implementation of the BlockTracker interface. 32 type BlockTrackerImpl struct { 33 BaseTracker 34 state protocol.State 35 broadcaster *engine.Broadcaster 36 37 // finalizedHighestHeight contains the highest consecutive block height for which we have received a new notification. 38 finalizedHighestHeight counters.StrictMonotonousCounter 39 // sealedHighestHeight contains the highest consecutive block height for which we have received a new notification. 40 sealedHighestHeight counters.StrictMonotonousCounter 41 } 42 43 // NewBlockTracker creates a new BlockTrackerImpl instance. 44 // 45 // Parameters: 46 // - state: The protocol state used for retrieving block information. 47 // - rootHeight: The root block height, serving as the baseline for calculating the start height. 48 // - headers: The storage headers for accessing block headers. 49 // - broadcaster: The engine broadcaster for publishing notifications. 50 // 51 // No errors are expected during normal operation. 52 func NewBlockTracker( 53 state protocol.State, 54 rootHeight uint64, 55 headers storage.Headers, 56 broadcaster *engine.Broadcaster, 57 ) (*BlockTrackerImpl, error) { 58 lastFinalized, err := state.Final().Head() 59 if err != nil { 60 // this header MUST exist in the db, otherwise the node likely has inconsistent state. 61 return nil, irrecoverable.NewExceptionf("could not retrieve last finalized block: %w", err) 62 } 63 64 lastSealed, err := state.Sealed().Head() 65 if err != nil { 66 // this header MUST exist in the db, otherwise the node likely has inconsistent state. 67 return nil, irrecoverable.NewExceptionf("could not retrieve last sealed block: %w", err) 68 } 69 70 return &BlockTrackerImpl{ 71 BaseTracker: NewBaseTrackerImpl(rootHeight, state, headers), 72 state: state, 73 finalizedHighestHeight: counters.NewMonotonousCounter(lastFinalized.Height), 74 sealedHighestHeight: counters.NewMonotonousCounter(lastSealed.Height), 75 broadcaster: broadcaster, 76 }, nil 77 } 78 79 // GetHighestHeight returns the highest height based on the specified block status. 80 // 81 // Parameters: 82 // - blockStatus: The status of the block. It is expected that blockStatus has already been handled for invalid flow.BlockStatusUnknown. 83 // 84 // Expected errors during normal operation: 85 // - codes.InvalidArgument - if block status is flow.BlockStatusUnknown. 86 func (b *BlockTrackerImpl) GetHighestHeight(blockStatus flow.BlockStatus) (uint64, error) { 87 switch blockStatus { 88 case flow.BlockStatusFinalized: 89 return b.finalizedHighestHeight.Value(), nil 90 case flow.BlockStatusSealed: 91 return b.sealedHighestHeight.Value(), nil 92 } 93 return 0, status.Errorf(codes.InvalidArgument, "invalid block status: %s", blockStatus) 94 } 95 96 // ProcessOnFinalizedBlock drives the subscription logic when a block is finalized. 97 // The input to this callback is treated as trusted. This method should be executed on 98 // `OnFinalizedBlock` notifications from the node-internal consensus instance. 99 // No errors are expected during normal operation. Any errors encountered should be 100 // treated as an exception. 101 func (b *BlockTrackerImpl) ProcessOnFinalizedBlock() error { 102 // get the finalized header from state 103 finalizedHeader, err := b.state.Final().Head() 104 if err != nil { 105 return irrecoverable.NewExceptionf("unable to get latest finalized header: %w", err) 106 } 107 108 if !b.finalizedHighestHeight.Set(finalizedHeader.Height) { 109 return nil 110 } 111 112 // get the latest seal header from state 113 sealedHeader, err := b.state.Sealed().Head() 114 if err != nil { 115 return irrecoverable.NewExceptionf("unable to get latest sealed header: %w", err) 116 } 117 118 _ = b.sealedHighestHeight.Set(sealedHeader.Height) 119 // always publish since there is also a new finalized block. 120 b.broadcaster.Publish() 121 122 return nil 123 }