github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/state_stream/backend/backend_account_statuses.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  
    12  	"github.com/onflow/flow-go/fvm/errors"
    13  	"github.com/onflow/flow-go/model/flow"
    14  	"github.com/onflow/flow-go/storage"
    15  )
    16  
    17  type AccountStatusesResponse struct {
    18  	BlockID       flow.Identifier
    19  	Height        uint64
    20  	AccountEvents map[string]flow.EventsList
    21  }
    22  
    23  // AccountStatusesBackend is a struct representing a backend implementation for subscribing to account statuses changes.
    24  type AccountStatusesBackend struct {
    25  	log                 zerolog.Logger
    26  	subscriptionHandler *subscription.SubscriptionHandler
    27  
    28  	executionDataTracker subscription.ExecutionDataTracker
    29  	eventsRetriever      EventsRetriever
    30  }
    31  
    32  // subscribe creates and returns a subscription to receive account status updates starting from the specified height.
    33  func (b *AccountStatusesBackend) subscribe(
    34  	ctx context.Context,
    35  	nextHeight uint64,
    36  	filter state_stream.AccountStatusFilter,
    37  ) subscription.Subscription {
    38  	return b.subscriptionHandler.Subscribe(ctx, nextHeight, b.getAccountStatusResponseFactory(filter))
    39  }
    40  
    41  // SubscribeAccountStatusesFromStartBlockID subscribes to the streaming of account status changes starting from
    42  // a specific block ID with an optional status filter.
    43  // Errors:
    44  // - codes.ErrNotFound if could not get block by start blockID.
    45  // - codes.Internal if there is an internal error.
    46  func (b *AccountStatusesBackend) SubscribeAccountStatusesFromStartBlockID(
    47  	ctx context.Context,
    48  	startBlockID flow.Identifier,
    49  	filter state_stream.AccountStatusFilter,
    50  ) subscription.Subscription {
    51  	nextHeight, err := b.executionDataTracker.GetStartHeightFromBlockID(startBlockID)
    52  	if err != nil {
    53  		return subscription.NewFailedSubscription(err, "could not get start height from block id")
    54  	}
    55  	return b.subscribe(ctx, nextHeight, filter)
    56  }
    57  
    58  // SubscribeAccountStatusesFromStartHeight subscribes to the streaming of account status changes starting from
    59  // a specific block height, with an optional status filter.
    60  // Errors:
    61  // - codes.ErrNotFound if could not get block by start height.
    62  // - codes.Internal if there is an internal error.
    63  func (b *AccountStatusesBackend) SubscribeAccountStatusesFromStartHeight(
    64  	ctx context.Context,
    65  	startHeight uint64,
    66  	filter state_stream.AccountStatusFilter,
    67  ) subscription.Subscription {
    68  	nextHeight, err := b.executionDataTracker.GetStartHeightFromHeight(startHeight)
    69  	if err != nil {
    70  		return subscription.NewFailedSubscription(err, "could not get start height from block height")
    71  	}
    72  	return b.subscribe(ctx, nextHeight, filter)
    73  }
    74  
    75  // SubscribeAccountStatusesFromLatestBlock subscribes to the streaming of account status changes starting from a
    76  // latest sealed block, with an optional status filter.
    77  //
    78  // No errors are expected during normal operation.
    79  func (b *AccountStatusesBackend) SubscribeAccountStatusesFromLatestBlock(
    80  	ctx context.Context,
    81  	filter state_stream.AccountStatusFilter,
    82  ) subscription.Subscription {
    83  	nextHeight, err := b.executionDataTracker.GetStartHeightFromLatest(ctx)
    84  	if err != nil {
    85  		return subscription.NewFailedSubscription(err, "could not get start height from latest")
    86  	}
    87  	return b.subscribe(ctx, nextHeight, filter)
    88  }
    89  
    90  // getAccountStatusResponseFactory returns a function that returns the account statuses response for a given height.
    91  //
    92  // Errors:
    93  // - subscription.ErrBlockNotReady: If block header for the specified block height is not found.
    94  // - error: An error, if any, encountered during getting events from storage or execution data.
    95  func (b *AccountStatusesBackend) getAccountStatusResponseFactory(
    96  	filter state_stream.AccountStatusFilter,
    97  ) subscription.GetDataByHeightFunc {
    98  	return func(ctx context.Context, height uint64) (interface{}, error) {
    99  		eventsResponse, err := b.eventsRetriever.GetAllEventsResponse(ctx, height)
   100  		if err != nil {
   101  			if errors.Is(err, storage.ErrNotFound) ||
   102  				errors.Is(err, storage.ErrHeightNotIndexed) {
   103  				return nil, fmt.Errorf("block %d is not available yet: %w", height, subscription.ErrBlockNotReady)
   104  			}
   105  			return nil, err
   106  		}
   107  		filteredProtocolEvents := filter.Filter(eventsResponse.Events)
   108  		allAccountProtocolEvents := filter.GroupCoreEventsByAccountAddress(filteredProtocolEvents, b.log)
   109  
   110  		return &AccountStatusesResponse{
   111  			BlockID:       eventsResponse.BlockID,
   112  			Height:        eventsResponse.Height,
   113  			AccountEvents: allAccountProtocolEvents,
   114  		}, nil
   115  	}
   116  }