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 }