-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Access] SendAndSubscribeTransactionStatuses endpoint implementation for Access Streaming API #5310
Merged
peterargue
merged 59 commits into
onflow:master
from
Guitarheroua:guitarheroua/send-and-subscribe-transaction_statuses
Mar 21, 2024
Merged
Changes from 54 commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
b9c1537
Implemented transaction status streaming
Guitarheroua 56b9f5b
Fixed backend and tests
Guitarheroua 117b881
Fixed tests
Guitarheroua 62c09c0
Fixed comments
Guitarheroua 08e3de0
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua 87f04d5
Fixed issue with multiple subscribtion.
Guitarheroua b9bf00c
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua 2594bf5
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua fd4bf03
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua 51ef06a
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua 5ffeae7
Make changes for Chain State tracker
Guitarheroua 59817d4
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
Guitarheroua 9cc36df
Linted
Guitarheroua c402ec2
Merge branch 'guitarheroua/send-and-subscribe-transaction_statuses' o…
Guitarheroua 78e2ad1
Merge branch 'master' of github.com:Guitarheroua/flow-go into guitarh…
Guitarheroua a77f107
Added godoc
Guitarheroua 48db475
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua 8defed0
Added comments
Guitarheroua 087ba90
Fixed conflicts
Guitarheroua ea30dd4
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
franklywatson 6bbba07
Fixed subscribtion and tests.
Guitarheroua 1579d57
Merge branch 'guitarheroua/send-and-subscribe-transaction_statuses' o…
Guitarheroua d625f4a
Merge branch 'master' of github.com:Guitarheroua/flow-go into guitarh…
Guitarheroua 67b9ff2
Fixed issue with finalized tx status
Guitarheroua 38b9779
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
franklywatson 41c0446
Fixed remarks
Guitarheroua bbffca9
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
franklywatson d6ac926
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
Guitarheroua 557029b
Fixed remarks
Guitarheroua 96d0fe8
Renamed SubscribeTransactionStatuses
Guitarheroua 8a1081d
Fixed exe results
Guitarheroua 890ae5b
fixed remarks
Guitarheroua eb16e71
Merge remote-tracking branch 'UlyanaAndrukhiv/UlyanaAndrukhiv/subscri…
Guitarheroua c74a3b6
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
peterargue 27fadb9
Updated flow-emulator
Guitarheroua 33a9f8e
Apply suggestions from code review
Guitarheroua 44ea43f
Revert unnecessary changes
Guitarheroua 61ad359
Merge branch 'guitarheroua/send-and-subscribe-transaction_statuses' o…
Guitarheroua 86b74e4
Fixed remarks
Guitarheroua 3f231cb
Fixed test
Guitarheroua 37014a7
Fixed remarks
Guitarheroua 638d451
Apply suggestions from code review
Guitarheroua cd6b28c
Fixed issue with leak on sub cancel
Guitarheroua 87aab74
Add integration test
Guitarheroua 78a4097
Fixed integration test
Guitarheroua d600685
Linted
Guitarheroua 85be1bd
Fixed logs
Guitarheroua d80b1b5
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
Guitarheroua 3679c76
Fixed incorrect tx subscribtion behaviour
Guitarheroua 16c9318
Fixed remarks
Guitarheroua a321f35
Removed comments
Guitarheroua bcca0aa
Fixed integration test
Guitarheroua 9d6484c
Removed sentinel error for result not available
Guitarheroua 471fa6c
Added graceful close. Fixed integration test
Guitarheroua 81465f2
Change to ctx without cancel
Guitarheroua a2eb6f7
Merge branch 'master' into guitarheroua/send-and-subscribe-transactio…
Guitarheroua 6c092e1
Update access/handler.go
Guitarheroua c48a862
Updated flow-emulator version
Guitarheroua 49a6c68
Updated flow-emulator version
Guitarheroua File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
195 changes: 195 additions & 0 deletions
195
engine/access/rpc/backend/backend_stream_transactions.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package backend | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/onflow/flow-go/module/irrecoverable" | ||
"github.com/onflow/flow-go/state" | ||
|
||
"github.com/onflow/flow-go/engine/common/rpc/convert" | ||
"github.com/onflow/flow-go/module/counters" | ||
|
||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
|
||
"github.com/rs/zerolog" | ||
|
||
"github.com/onflow/flow-go/engine" | ||
"github.com/onflow/flow-go/engine/access/subscription" | ||
"github.com/onflow/flow-go/engine/common/rpc" | ||
"github.com/onflow/flow-go/model/flow" | ||
"github.com/onflow/flow-go/storage" | ||
) | ||
|
||
// backendSubscribeTransactions handles transaction subscriptions. | ||
type backendSubscribeTransactions struct { | ||
txLocalDataProvider *TransactionsLocalDataProvider | ||
executionResults storage.ExecutionResults | ||
log zerolog.Logger | ||
broadcaster *engine.Broadcaster | ||
sendTimeout time.Duration | ||
responseLimit float64 | ||
sendBufferSize int | ||
|
||
blockTracker subscription.BlockTracker | ||
} | ||
|
||
// TransactionSubscriptionMetadata holds data representing the status state for each transaction subscription. | ||
type TransactionSubscriptionMetadata struct { | ||
txID flow.Identifier | ||
txReferenceBlockID flow.Identifier | ||
messageIndex counters.StrictMonotonousCounter | ||
blockWithTx *flow.Header | ||
blockID flow.Identifier | ||
txExecuted bool | ||
lastTxStatus flow.TransactionStatus | ||
} | ||
|
||
// SubscribeTransactionStatuses subscribes to transaction status changes starting from the transaction reference block ID. | ||
// If invalid tx parameters will be supplied SubscribeTransactionStatuses will return a failed subscription. | ||
func (b *backendSubscribeTransactions) SubscribeTransactionStatuses(ctx context.Context, tx *flow.TransactionBody) subscription.Subscription { | ||
nextHeight, err := b.blockTracker.GetStartHeightFromBlockID(tx.ReferenceBlockID) | ||
if err != nil { | ||
return subscription.NewFailedSubscription(err, "could not get start height") | ||
} | ||
|
||
txInfo := TransactionSubscriptionMetadata{ | ||
txID: tx.ID(), | ||
txReferenceBlockID: tx.ReferenceBlockID, | ||
messageIndex: counters.NewMonotonousCounter(0), | ||
blockWithTx: nil, | ||
blockID: flow.ZeroID, | ||
lastTxStatus: flow.TransactionStatusUnknown, | ||
} | ||
|
||
sub := subscription.NewHeightBasedSubscription( | ||
b.sendBufferSize, | ||
nextHeight, | ||
b.getTransactionStatusResponse(&txInfo), | ||
) | ||
|
||
go subscription.NewStreamer(b.log, b.broadcaster, b.sendTimeout, b.responseLimit, sub).Stream(ctx) | ||
|
||
return sub | ||
} | ||
|
||
// getTransactionStatusResponse returns a callback function that produces transaction status | ||
// subscription responses based on new blocks. | ||
func (b *backendSubscribeTransactions) getTransactionStatusResponse(txInfo *TransactionSubscriptionMetadata) func(context.Context, uint64) (interface{}, error) { | ||
return func(ctx context.Context, height uint64) (interface{}, error) { | ||
highestHeight, err := b.blockTracker.GetHighestHeight(flow.BlockStatusFinalized) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not get highest height for block %d: %w", height, err) | ||
} | ||
|
||
// Fail early if no block finalized notification has been received for the given height. | ||
// Note: It's possible that the block is locally finalized before the notification is | ||
// received. This ensures a consistent view is available to all streams. | ||
if height > highestHeight { | ||
return nil, fmt.Errorf("block %d is not available yet: %w", height, subscription.ErrBlockNotReady) | ||
} | ||
|
||
if txInfo.lastTxStatus == flow.TransactionStatusSealed || txInfo.lastTxStatus == flow.TransactionStatusExpired { | ||
return nil, fmt.Errorf("transaction final status %s was already reported: %w", txInfo.lastTxStatus.String(), subscription.ErrEndOfData) | ||
} | ||
|
||
if txInfo.blockWithTx == nil { | ||
// Check if block contains transaction. | ||
txInfo.blockWithTx, txInfo.blockID, err = b.searchForTransactionBlock(height, txInfo) | ||
if err != nil { | ||
if errors.Is(err, storage.ErrNotFound) { | ||
return nil, fmt.Errorf("could not find block %d in storage: %w", height, subscription.ErrBlockNotReady) | ||
} | ||
|
||
if !errors.Is(err, ErrTransactionNotInBlock) { | ||
return nil, status.Errorf(codes.Internal, "could not get block %d: %v", height, err) | ||
} | ||
} | ||
} | ||
|
||
// Find the transaction status. | ||
var txStatus flow.TransactionStatus | ||
if txInfo.blockWithTx == nil { | ||
txStatus, err = b.txLocalDataProvider.DeriveUnknownTransactionStatus(txInfo.txReferenceBlockID) | ||
} else { | ||
if !txInfo.txExecuted { | ||
// Check if transaction was executed. | ||
txInfo.txExecuted, err = b.searchForExecutionResult(txInfo.blockID) | ||
if err != nil { | ||
return nil, status.Errorf(codes.Internal, "failed to get execution result for block %s: %v", txInfo.blockID, err) | ||
} | ||
} | ||
|
||
txStatus, err = b.txLocalDataProvider.DeriveTransactionStatus(txInfo.blockID, txInfo.blockWithTx.Height, txInfo.txExecuted) | ||
} | ||
if err != nil { | ||
if !errors.Is(err, state.ErrUnknownSnapshotReference) { | ||
irrecoverable.Throw(ctx, err) | ||
} | ||
return nil, rpc.ConvertStorageError(err) | ||
Guitarheroua marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// The same transaction status should not be reported, so return here with no response | ||
if txInfo.lastTxStatus == txStatus { | ||
return nil, nil | ||
} | ||
txInfo.lastTxStatus = txStatus | ||
|
||
messageIndex := txInfo.messageIndex.Value() | ||
if ok := txInfo.messageIndex.Set(messageIndex + 1); !ok { | ||
return nil, status.Errorf(codes.Internal, "the message index has already been incremented to %d", txInfo.messageIndex.Value()) | ||
} | ||
|
||
return &convert.TransactionSubscribeInfo{ | ||
ID: txInfo.txID, | ||
Status: txInfo.lastTxStatus, | ||
MessageIndex: messageIndex, | ||
}, nil | ||
} | ||
} | ||
|
||
// searchForTransactionBlock searches for the block containing the specified transaction. | ||
// It retrieves the block at the given height and checks if the transaction is included in that block. | ||
// Expected errors: | ||
// - subscription.ErrBlockNotReady when unable to retrieve the block or collection ID | ||
// - codes.Internal when other errors occur during block or collection lookup | ||
func (b *backendSubscribeTransactions) searchForTransactionBlock( | ||
Guitarheroua marked this conversation as resolved.
Show resolved
Hide resolved
|
||
height uint64, | ||
txInfo *TransactionSubscriptionMetadata, | ||
) (*flow.Header, flow.Identifier, error) { | ||
block, err := b.txLocalDataProvider.blocks.ByHeight(height) | ||
if err != nil { | ||
return nil, flow.ZeroID, fmt.Errorf("error looking up block: %w", err) | ||
} | ||
|
||
collectionID, err := b.txLocalDataProvider.LookupCollectionIDInBlock(block, txInfo.txID) | ||
Guitarheroua marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, flow.ZeroID, fmt.Errorf("error looking up transaction in block: %w", err) | ||
} | ||
|
||
if collectionID != flow.ZeroID { | ||
return block.Header, block.ID(), nil | ||
} | ||
|
||
return nil, flow.ZeroID, nil | ||
} | ||
|
||
// searchForExecutionResult searches for the execution result of a block. It retrieves the execution result for the specified block ID. | ||
// Expected errors: | ||
// - codes.Internal if an internal error occurs while retrieving execution result. | ||
func (b *backendSubscribeTransactions) searchForExecutionResult( | ||
Guitarheroua marked this conversation as resolved.
Show resolved
Hide resolved
|
||
blockID flow.Identifier, | ||
) (bool, error) { | ||
_, err := b.executionResults.ByBlockID(blockID) | ||
if err != nil { | ||
if errors.Is(err, storage.ErrNotFound) { | ||
return false, nil | ||
} | ||
return false, fmt.Errorf("failed to get execution result for block %s: %w", blockID, err) | ||
} | ||
|
||
return true, nil | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we're not using cancel anymore, can you remove it