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  }