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

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/rs/zerolog"
     8  
     9  	"github.com/onflow/flow-go/engine/access/state_stream"
    10  	"github.com/onflow/flow-go/engine/access/subscription"
    11  	"github.com/onflow/flow-go/fvm/errors"
    12  	"github.com/onflow/flow-go/model/flow"
    13  	"github.com/onflow/flow-go/storage"
    14  )
    15  
    16  type EventsBackend struct {
    17  	log zerolog.Logger
    18  
    19  	subscriptionHandler  *subscription.SubscriptionHandler
    20  	executionDataTracker subscription.ExecutionDataTracker
    21  	eventsRetriever      EventsRetriever
    22  }
    23  
    24  // SubscribeEvents is deprecated and will be removed in a future version.
    25  // Use SubscribeEventsFromStartBlockID, SubscribeEventsFromStartHeight or SubscribeEventsFromLatest.
    26  //
    27  // SubscribeEvents streams events for all blocks starting at the specified block ID or block height
    28  // up until the latest available block. Once the latest is
    29  // reached, the stream will remain open and responses are sent for each new
    30  // block as it becomes available.
    31  //
    32  // Only one of startBlockID and startHeight may be set. If neither startBlockID nor startHeight is provided,
    33  // the latest sealed block is used.
    34  //
    35  // Events within each block are filtered by the provided EventFilter, and only
    36  // those events that match the filter are returned. If no filter is provided,
    37  // all events are returned.
    38  //
    39  // Parameters:
    40  // - ctx: Context for the operation.
    41  // - startBlockID: The identifier of the starting block. If provided, startHeight should be 0.
    42  // - startHeight: The height of the starting block. If provided, startBlockID should be flow.ZeroID.
    43  // - filter: The event filter used to filter events.
    44  //
    45  // If invalid parameters will be supplied SubscribeEvents will return a failed subscription.
    46  func (b *EventsBackend) SubscribeEvents(ctx context.Context, startBlockID flow.Identifier, startHeight uint64, filter state_stream.EventFilter) subscription.Subscription {
    47  	nextHeight, err := b.executionDataTracker.GetStartHeight(ctx, startBlockID, startHeight)
    48  	if err != nil {
    49  		return subscription.NewFailedSubscription(err, "could not get start height")
    50  	}
    51  
    52  	return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponseFactory(filter))
    53  }
    54  
    55  // SubscribeEventsFromStartBlockID streams events starting at the specified block ID,
    56  // up until the latest available block. Once the latest is
    57  // reached, the stream will remain open and responses are sent for each new
    58  // block as it becomes available.
    59  //
    60  // Events within each block are filtered by the provided EventFilter, and only
    61  // those events that match the filter are returned. If no filter is provided,
    62  // all events are returned.
    63  //
    64  // Parameters:
    65  // - ctx: Context for the operation.
    66  // - startBlockID: The identifier of the starting block.
    67  // - filter: The event filter used to filter events.
    68  //
    69  // If invalid parameters will be supplied SubscribeEventsFromStartBlockID will return a failed subscription.
    70  func (b *EventsBackend) SubscribeEventsFromStartBlockID(ctx context.Context, startBlockID flow.Identifier, filter state_stream.EventFilter) subscription.Subscription {
    71  	nextHeight, err := b.executionDataTracker.GetStartHeightFromBlockID(startBlockID)
    72  	if err != nil {
    73  		return subscription.NewFailedSubscription(err, "could not get start height from block id")
    74  	}
    75  
    76  	return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponseFactory(filter))
    77  }
    78  
    79  // SubscribeEventsFromStartHeight streams events starting at the specified block height,
    80  // up until the latest available block. Once the latest is
    81  // reached, the stream will remain open and responses are sent for each new
    82  // block as it becomes available.
    83  //
    84  // Events within each block are filtered by the provided EventFilter, and only
    85  // those events that match the filter are returned. If no filter is provided,
    86  // all events are returned.
    87  //
    88  // Parameters:
    89  // - ctx: Context for the operation.
    90  // - startHeight: The height of the starting block.
    91  // - filter: The event filter used to filter events.
    92  //
    93  // If invalid parameters will be supplied SubscribeEventsFromStartHeight will return a failed subscription.
    94  func (b *EventsBackend) SubscribeEventsFromStartHeight(ctx context.Context, startHeight uint64, filter state_stream.EventFilter) subscription.Subscription {
    95  	nextHeight, err := b.executionDataTracker.GetStartHeightFromHeight(startHeight)
    96  	if err != nil {
    97  		return subscription.NewFailedSubscription(err, "could not get start height from block height")
    98  	}
    99  
   100  	return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponseFactory(filter))
   101  }
   102  
   103  // SubscribeEventsFromLatest subscribes to events starting at the latest sealed block,
   104  // up until the latest available block. Once the latest is
   105  // reached, the stream will remain open and responses are sent for each new
   106  // block as it becomes available.
   107  //
   108  // Events within each block are filtered by the provided EventFilter, and only
   109  // those events that match the filter are returned. If no filter is provided,
   110  // all events are returned.
   111  //
   112  // Parameters:
   113  // - ctx: Context for the operation.
   114  // - filter: The event filter used to filter events.
   115  //
   116  // If invalid parameters will be supplied SubscribeEventsFromLatest will return a failed subscription.
   117  func (b *EventsBackend) SubscribeEventsFromLatest(ctx context.Context, filter state_stream.EventFilter) subscription.Subscription {
   118  	nextHeight, err := b.executionDataTracker.GetStartHeightFromLatest(ctx)
   119  	if err != nil {
   120  		return subscription.NewFailedSubscription(err, "could not get start height from block height")
   121  	}
   122  
   123  	return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getResponseFactory(filter))
   124  }
   125  
   126  // getResponseFactory returns a function that retrieves the event response for a given height.
   127  //
   128  // Parameters:
   129  // - filter: The event filter used to filter events.
   130  //
   131  // Expected errors during normal operation:
   132  // - subscription.ErrBlockNotReady: execution data for the given block height is not available.
   133  func (b *EventsBackend) getResponseFactory(filter state_stream.EventFilter) subscription.GetDataByHeightFunc {
   134  	return func(ctx context.Context, height uint64) (response interface{}, err error) {
   135  		eventsResponse, err := b.eventsRetriever.GetAllEventsResponse(ctx, height)
   136  		if err != nil {
   137  			if errors.Is(err, storage.ErrNotFound) ||
   138  				errors.Is(err, storage.ErrHeightNotIndexed) {
   139  				return nil, subscription.ErrBlockNotReady
   140  			}
   141  			return nil, fmt.Errorf("block %d is not available yet: %w", height, subscription.ErrBlockNotReady)
   142  		}
   143  
   144  		eventsResponse.Events = filter.Filter(eventsResponse.Events)
   145  
   146  		return eventsResponse, nil
   147  	}
   148  }