From 2e8d95be4c44defa3b8b2e0d408e2659165f0e0d Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 15 Nov 2024 15:18:38 +0200 Subject: [PATCH 1/8] change tx result retrieving --- .../backend/backend_stream_transactions.go | 25 +++++++------------ .../rpc/backend/backend_transactions.go | 6 ++--- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions.go b/engine/access/rpc/backend/backend_stream_transactions.go index a82b365240e..38302a1ef8a 100644 --- a/engine/access/rpc/backend/backend_stream_transactions.go +++ b/engine/access/rpc/backend/backend_stream_transactions.go @@ -247,29 +247,22 @@ func (b *backendSubscribeTransactions) searchForTransactionBlockInfo( return nil, flow.ZeroID, 0, flow.ZeroID, nil } -// searchForTransactionResult searches for the transaction 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. +// searchForTransactionResult searches for the transaction result of a block. It retrieves the transaction result from +// storage and, in case of failure, attempts to fetch the transaction result directly from the execution node. +// This is necessary to ensure data availability despite sync storage latency. +// +// No errors expected during normal operations. func (b *backendSubscribeTransactions) searchForTransactionResult( ctx context.Context, txInfo *TransactionSubscriptionMetadata, ) (*access.TransactionResult, error) { - _, err := b.executionResults.ByBlockID(txInfo.BlockID) + block, err := b.txLocalDataProvider.blocks.ByHeight(txInfo.BlockHeight) + txResult, err := b.backendTransactions.GetTransactionResultFromStorage(ctx, block, txInfo.TransactionID, txInfo.eventEncodingVersion) if err != nil { - if errors.Is(err, storage.ErrNotFound) { - return nil, nil - } - return nil, fmt.Errorf("failed to get execution result for block %s: %w", txInfo.BlockID, err) + // If any error occurs with local storage - request transaction result from EN + txResult, err = b.backendTransactions.GetTransactionResultFromExecutionNode(ctx, block, txInfo.TransactionID, txInfo.eventEncodingVersion) } - txResult, err := b.backendTransactions.GetTransactionResult( - ctx, - txInfo.TransactionID, - txInfo.BlockID, - txInfo.CollectionID, - txInfo.eventEncodingVersion, - ) - if err != nil { // if either the storage or execution node reported no results or there were not enough execution results if status.Code(err) == codes.NotFound { diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 5ba1cecb038..5fb05c30880 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -651,14 +651,14 @@ func (b *backendTransactions) lookupTransactionResult( var err error switch b.txResultQueryMode { case IndexQueryModeExecutionNodesOnly: - txResult, err = b.getTransactionResultFromExecutionNode(ctx, block, txID, requiredEventEncodingVersion) + txResult, err = b.GetTransactionResultFromExecutionNode(ctx, block, txID, requiredEventEncodingVersion) case IndexQueryModeLocalOnly: txResult, err = b.GetTransactionResultFromStorage(ctx, block, txID, requiredEventEncodingVersion) case IndexQueryModeFailover: txResult, err = b.GetTransactionResultFromStorage(ctx, block, txID, requiredEventEncodingVersion) if err != nil { // If any error occurs with local storage - request transaction result from EN - txResult, err = b.getTransactionResultFromExecutionNode(ctx, block, txID, requiredEventEncodingVersion) + txResult, err = b.GetTransactionResultFromExecutionNode(ctx, block, txID, requiredEventEncodingVersion) } default: return nil, status.Errorf(codes.Internal, "unknown transaction result query mode: %v", b.txResultQueryMode) @@ -742,7 +742,7 @@ func (b *backendTransactions) registerTransactionForRetry(tx *flow.TransactionBo b.retry.RegisterTransaction(referenceBlock.Height, tx) } -func (b *backendTransactions) getTransactionResultFromExecutionNode( +func (b *backendTransactions) GetTransactionResultFromExecutionNode( ctx context.Context, block *flow.Block, transactionID flow.Identifier, From de3851a6974385c6888051106639c89cf2109ec1 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 11:32:20 +0200 Subject: [PATCH 2/8] Fixed unit tests --- .../backend_stream_transactions_test.go | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions_test.go b/engine/access/rpc/backend/backend_stream_transactions_test.go index 24cdf601f17..3d849d328df 100644 --- a/engine/access/rpc/backend/backend_stream_transactions_test.go +++ b/engine/access/rpc/backend/backend_stream_transactions_test.go @@ -7,6 +7,9 @@ import ( "testing" "time" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/dgraph-io/badger/v2" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" @@ -22,6 +25,7 @@ import ( connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" "github.com/onflow/flow-go/engine/access/subscription" subscriptionmock "github.com/onflow/flow-go/engine/access/subscription/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -30,6 +34,7 @@ import ( syncmock "github.com/onflow/flow-go/module/state_synchronization/mock" protocolint "github.com/onflow/flow-go/state/protocol" protocol "github.com/onflow/flow-go/state/protocol/mock" + "github.com/onflow/flow-go/storage" bstorage "github.com/onflow/flow-go/storage/badger" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" @@ -98,9 +103,6 @@ func (s *TransactionStatusSuite) SetupTest() { s.tempSnapshot = &protocol.Snapshot{} s.db, s.dbDir = unittest.TempBadgerDB(s.T()) - params := protocol.NewParams(s.T()) - s.state.On("Params").Return(params) - s.blocks = storagemock.NewBlocks(s.T()) s.headers = storagemock.NewHeaders(s.T()) s.transactions = storagemock.NewTransactions(s.T()) @@ -121,11 +123,26 @@ func (s *TransactionStatusSuite) SetupTest() { s.blockTracker = subscriptionmock.NewBlockTracker(s.T()) s.resultsMap = map[flow.Identifier]*flow.ExecutionResult{} + s.execClient.On("GetTransactionResult", mock.Anything, mock.Anything).Return(nil, status.Error(codes.NotFound, "not found")).Maybe() + s.connectionFactory.On("GetExecutionAPIClient", mock.Anything).Return(s.execClient, &mocks.MockCloser{}, nil).Maybe() + // generate blockCount consecutive blocks with associated seal, result and execution data s.rootBlock = unittest.BlockFixture() rootResult := unittest.ExecutionResultFixture(unittest.WithBlock(&s.rootBlock)) s.resultsMap[s.rootBlock.ID()] = rootResult + params := protocol.NewParams(s.T()) + params.On("FinalizedRoot").Return(s.rootBlock.Header).Maybe() + s.state.On("Params").Return(params).Maybe() + + // this line causes a S1021 lint error because receipts is explicitly declared. this is required + // to ensure the mock library handles the response type correctly + var receipts flow.ExecutionReceiptList //nolint:gosimple + executionNodes := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution)) + receipts = unittest.ReceiptsForBlockFixture(&s.rootBlock, executionNodes.NodeIDs()) + s.receipts.On("ByBlockID", mock.AnythingOfType("flow.Identifier")).Return(receipts, nil).Maybe() + s.finalSnapshot.On("Identities", mock.Anything).Return(executionNodes, nil).Maybe() + var err error s.lastFullBlockHeight, err = counters.NewPersistentStrictMonotonicCounter( bstorage.NewConsumerProgress(s.db, module.ConsumeProgressLastFullBlockHeight), @@ -219,6 +236,14 @@ func (s *TransactionStatusSuite) backendParams() Params { TxResultQueryMode: IndexQueryModeLocalOnly, EventsIndex: index.NewEventsIndex(s.indexReporter, s.events), LastFullBlockHeight: s.lastFullBlockHeight, + ExecNodeIdentitiesProvider: commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.state, + s.receipts, + nil, + nil, + ), + ConnFactory: s.connectionFactory, } } @@ -246,36 +271,22 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { finalizedHeader := s.finalizedBlock.Header return finalizedHeader.Height, nil }, nil) - s.blocks.On("ByID", mock.AnythingOfType("flow.Identifier")).Return(func(blockID flow.Identifier) (*flow.Block, error) { - for _, block := range s.blockMap { - if block.ID() == blockID { - return block, nil - } - } - return nil, nil - }, nil) s.sealedSnapshot.On("Head").Return(func() *flow.Header { return s.sealedBlock.Header }, nil) s.state.On("Sealed").Return(s.sealedSnapshot, nil) - s.results.On("ByBlockID", mock.AnythingOfType("flow.Identifier")).Return(mocks.StorageMapGetter(s.resultsMap)) // Generate sent transaction with ref block of the current finalized block transaction := unittest.TransactionFixture() transaction.SetReferenceBlockID(s.finalizedBlock.ID()) - s.transactions.On("ByID", mock.AnythingOfType("flow.Identifier")).Return(&transaction.TransactionBody, nil) col := flow.CollectionFromTransactions([]*flow.Transaction{&transaction}) guarantee := col.Guarantee() light := col.Light() txId := transaction.ID() - txResult := flow.LightTransactionResult{ - TransactionID: txId, - Failed: false, - ComputationUsed: 0, - } - + var txResult flow.LightTransactionResult + txResultError := storage.ErrNotFound eventsForTx := unittest.EventsFixture(1, flow.EventAccountCreated) eventMessages := make([]*entities.Event, 1) for j, event := range eventsForTx { @@ -292,7 +303,9 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { "ByBlockIDTransactionID", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("flow.Identifier"), - ).Return(&txResult, nil) + ).Return(func(blockID flow.Identifier, transactionID flow.Identifier) (*flow.LightTransactionResult, error) { + return &txResult, txResultError + }, txResultError) // Create a special common function to read subscription messages from the channel and check converting it to transaction info // and check results for correctness @@ -310,7 +323,7 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { result := txResults[0] assert.Equal(s.T(), txId, result.TransactionID) assert.Equal(s.T(), expectedTxStatus, result.Status) - }, time.Second, fmt.Sprintf("timed out waiting for transaction info:\n\t- txID: %x\n\t- blockID: %x", txId, s.finalizedBlock.ID())) + }, 350*time.Second, fmt.Sprintf("timed out waiting for transaction info:\n\t- txID: %x\n\t- blockID: %x", txId, s.finalizedBlock.ID())) } // 1. Subscribe to transaction status and receive the first message with pending status @@ -328,6 +341,12 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { // 3. Add one more finalized block on top of the transaction block and add execution results to storage finalizedResult := unittest.ExecutionResultFixture(unittest.WithBlock(s.finalizedBlock)) s.resultsMap[s.finalizedBlock.ID()] = finalizedResult + txResult = flow.LightTransactionResult{ + TransactionID: txId, + Failed: false, + ComputationUsed: 0, + } + txResultError = nil s.addNewFinalizedBlock(s.finalizedBlock.Header, true) checkNewSubscriptionMessage(sub, flow.TransactionStatusExecuted) From edb06365c835a6ffab943b9d355a21c2ef37f1ea Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 11:49:43 +0200 Subject: [PATCH 3/8] fixed comment --- engine/access/rpc/backend/backend_stream_transactions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions.go b/engine/access/rpc/backend/backend_stream_transactions.go index 38302a1ef8a..33619a9e364 100644 --- a/engine/access/rpc/backend/backend_stream_transactions.go +++ b/engine/access/rpc/backend/backend_stream_transactions.go @@ -264,7 +264,7 @@ func (b *backendSubscribeTransactions) searchForTransactionResult( } if err != nil { - // if either the storage or execution node reported no results or there were not enough execution results + // if either the storage or execution node reported no results if status.Code(err) == codes.NotFound { // No result yet, indicate that it has not been executed return nil, nil From 23a221467683c3da6d82d4f826116c11777b9b4c Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 11:58:10 +0200 Subject: [PATCH 4/8] simplify unit test --- .../backend_stream_transactions_test.go | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions_test.go b/engine/access/rpc/backend/backend_stream_transactions_test.go index 3d849d328df..0847c9f23b9 100644 --- a/engine/access/rpc/backend/backend_stream_transactions_test.go +++ b/engine/access/rpc/backend/backend_stream_transactions_test.go @@ -285,8 +285,6 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { guarantee := col.Guarantee() light := col.Light() txId := transaction.ID() - var txResult flow.LightTransactionResult - txResultError := storage.ErrNotFound eventsForTx := unittest.EventsFixture(1, flow.EventAccountCreated) eventMessages := make([]*entities.Event, 1) for j, event := range eventsForTx { @@ -299,13 +297,21 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { mock.AnythingOfType("flow.Identifier"), ).Return(eventsForTx, nil) + hasTransactionResultInStorage := false s.transactionResults.On( "ByBlockIDTransactionID", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("flow.Identifier"), ).Return(func(blockID flow.Identifier, transactionID flow.Identifier) (*flow.LightTransactionResult, error) { - return &txResult, txResultError - }, txResultError) + if hasTransactionResultInStorage { + return &flow.LightTransactionResult{ + TransactionID: txId, + Failed: false, + ComputationUsed: 0, + }, nil + } + return nil, storage.ErrNotFound + }).Twice() // Create a special common function to read subscription messages from the channel and check converting it to transaction info // and check results for correctness @@ -323,7 +329,7 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { result := txResults[0] assert.Equal(s.T(), txId, result.TransactionID) assert.Equal(s.T(), expectedTxStatus, result.Status) - }, 350*time.Second, fmt.Sprintf("timed out waiting for transaction info:\n\t- txID: %x\n\t- blockID: %x", txId, s.finalizedBlock.ID())) + }, time.Second, fmt.Sprintf("timed out waiting for transaction info:\n\t- txID: %x\n\t- blockID: %x", txId, s.finalizedBlock.ID())) } // 1. Subscribe to transaction status and receive the first message with pending status @@ -341,12 +347,8 @@ func (s *TransactionStatusSuite) TestSubscribeTransactionStatusHappyCase() { // 3. Add one more finalized block on top of the transaction block and add execution results to storage finalizedResult := unittest.ExecutionResultFixture(unittest.WithBlock(s.finalizedBlock)) s.resultsMap[s.finalizedBlock.ID()] = finalizedResult - txResult = flow.LightTransactionResult{ - TransactionID: txId, - Failed: false, - ComputationUsed: 0, - } - txResultError = nil + // init transaction result for storage + hasTransactionResultInStorage = true s.addNewFinalizedBlock(s.finalizedBlock.Header, true) checkNewSubscriptionMessage(sub, flow.TransactionStatusExecuted) From e3a43a053aeae4d47e390391064bdc8ae48d6e58 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 12:10:52 +0200 Subject: [PATCH 5/8] remove second block extraction --- .../rpc/backend/backend_stream_transactions.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions.go b/engine/access/rpc/backend/backend_stream_transactions.go index 33619a9e364..6f848ac5c2c 100644 --- a/engine/access/rpc/backend/backend_stream_transactions.go +++ b/engine/access/rpc/backend/backend_stream_transactions.go @@ -36,7 +36,7 @@ type backendSubscribeTransactions struct { type TransactionSubscriptionMetadata struct { *access.TransactionResult txReferenceBlockID flow.Identifier - blockWithTx *flow.Header + blockWithTx *flow.Block txExecuted bool eventEncodingVersion entities.EventEncodingVersion } @@ -126,7 +126,7 @@ func (b *backendSubscribeTransactions) getTransactionStatusResponse(txInfo *Tran // When a block with the transaction is available, it is possible to receive a new transaction status while // searching for the transaction result. Otherwise, it remains unchanged. So, if the old and new transaction // statuses are the same, the current transaction status should be retrieved. - txInfo.Status, err = b.txLocalDataProvider.DeriveTransactionStatus(txInfo.blockWithTx.Height, txInfo.txExecuted) + txInfo.Status, err = b.txLocalDataProvider.DeriveTransactionStatus(txInfo.BlockHeight, txInfo.txExecuted) } if err != nil { if !errors.Is(err, state.ErrUnknownSnapshotReference) { @@ -229,7 +229,7 @@ func (b *backendSubscribeTransactions) checkBlockReady(height uint64) error { func (b *backendSubscribeTransactions) searchForTransactionBlockInfo( height uint64, txInfo *TransactionSubscriptionMetadata, -) (*flow.Header, flow.Identifier, uint64, flow.Identifier, error) { +) (*flow.Block, flow.Identifier, uint64, flow.Identifier, error) { block, err := b.txLocalDataProvider.blocks.ByHeight(height) if err != nil { return nil, flow.ZeroID, 0, flow.ZeroID, fmt.Errorf("error looking up block: %w", err) @@ -241,7 +241,7 @@ func (b *backendSubscribeTransactions) searchForTransactionBlockInfo( } if collectionID != flow.ZeroID { - return block.Header, block.ID(), height, collectionID, nil + return block, block.ID(), height, collectionID, nil } return nil, flow.ZeroID, 0, flow.ZeroID, nil @@ -256,11 +256,10 @@ func (b *backendSubscribeTransactions) searchForTransactionResult( ctx context.Context, txInfo *TransactionSubscriptionMetadata, ) (*access.TransactionResult, error) { - block, err := b.txLocalDataProvider.blocks.ByHeight(txInfo.BlockHeight) - txResult, err := b.backendTransactions.GetTransactionResultFromStorage(ctx, block, txInfo.TransactionID, txInfo.eventEncodingVersion) + txResult, err := b.backendTransactions.GetTransactionResultFromStorage(ctx, txInfo.blockWithTx, txInfo.TransactionID, txInfo.eventEncodingVersion) if err != nil { // If any error occurs with local storage - request transaction result from EN - txResult, err = b.backendTransactions.GetTransactionResultFromExecutionNode(ctx, block, txInfo.TransactionID, txInfo.eventEncodingVersion) + txResult, err = b.backendTransactions.GetTransactionResultFromExecutionNode(ctx, txInfo.blockWithTx, txInfo.TransactionID, txInfo.eventEncodingVersion) } if err != nil { From ef8136d5849f92a36b7eb4696c9a950a634e8f7d Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 12:15:25 +0200 Subject: [PATCH 6/8] removed unnecessary comment --- engine/access/rpc/backend/backend_stream_transactions_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions_test.go b/engine/access/rpc/backend/backend_stream_transactions_test.go index 0847c9f23b9..bb170fff06b 100644 --- a/engine/access/rpc/backend/backend_stream_transactions_test.go +++ b/engine/access/rpc/backend/backend_stream_transactions_test.go @@ -135,9 +135,7 @@ func (s *TransactionStatusSuite) SetupTest() { params.On("FinalizedRoot").Return(s.rootBlock.Header).Maybe() s.state.On("Params").Return(params).Maybe() - // this line causes a S1021 lint error because receipts is explicitly declared. this is required - // to ensure the mock library handles the response type correctly - var receipts flow.ExecutionReceiptList //nolint:gosimple + var receipts flow.ExecutionReceiptList executionNodes := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution)) receipts = unittest.ReceiptsForBlockFixture(&s.rootBlock, executionNodes.NodeIDs()) s.receipts.On("ByBlockID", mock.AnythingOfType("flow.Identifier")).Return(receipts, nil).Maybe() From 543fb9e4e5d1d8f0936c48223612f3c2d2bd9878 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 19 Nov 2024 12:32:05 +0200 Subject: [PATCH 7/8] added missing documentation --- engine/access/rpc/backend/backend_stream_transactions.go | 7 ++++++- engine/access/rpc/backend/backend_transactions.go | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions.go b/engine/access/rpc/backend/backend_stream_transactions.go index 6f848ac5c2c..f4efbc055da 100644 --- a/engine/access/rpc/backend/backend_stream_transactions.go +++ b/engine/access/rpc/backend/backend_stream_transactions.go @@ -259,7 +259,12 @@ func (b *backendSubscribeTransactions) searchForTransactionResult( txResult, err := b.backendTransactions.GetTransactionResultFromStorage(ctx, txInfo.blockWithTx, txInfo.TransactionID, txInfo.eventEncodingVersion) if err != nil { // If any error occurs with local storage - request transaction result from EN - txResult, err = b.backendTransactions.GetTransactionResultFromExecutionNode(ctx, txInfo.blockWithTx, txInfo.TransactionID, txInfo.eventEncodingVersion) + txResult, err = b.backendTransactions.GetTransactionResultFromExecutionNode( + ctx, + txInfo.blockWithTx, + txInfo.TransactionID, + txInfo.eventEncodingVersion, + ) } if err != nil { diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 5fb05c30880..6dc0892a0ea 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -742,6 +742,12 @@ func (b *backendTransactions) registerTransactionForRetry(tx *flow.TransactionBo b.retry.RegisterTransaction(referenceBlock.Height, tx) } +// GetTransactionResultFromExecutionNode retrieves the result of a specified transaction +// from the execution nodes for a given block. +// +// Error returns: +// - `codes.NotFound` if no execution receipt were found. +// - Returns internal errors if event conversion or status derivation fails. func (b *backendTransactions) GetTransactionResultFromExecutionNode( ctx context.Context, block *flow.Block, From 8c0c2472b815d22dbb4620cc6b9ad8a3d468c9f6 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 4 Dec 2024 16:41:12 +0200 Subject: [PATCH 8/8] Fixed remark --- .../rpc/backend/backend_stream_transactions.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend_stream_transactions.go b/engine/access/rpc/backend/backend_stream_transactions.go index f4efbc055da..b5ed2c249d8 100644 --- a/engine/access/rpc/backend/backend_stream_transactions.go +++ b/engine/access/rpc/backend/backend_stream_transactions.go @@ -265,16 +265,16 @@ func (b *backendSubscribeTransactions) searchForTransactionResult( txInfo.TransactionID, txInfo.eventEncodingVersion, ) - } - if err != nil { - // if either the storage or execution node reported no results - if status.Code(err) == codes.NotFound { - // No result yet, indicate that it has not been executed - return nil, nil + if err != nil { + // if either the execution node reported no results + if status.Code(err) == codes.NotFound { + // No result yet, indicate that it has not been executed + return nil, nil + } + // Other Error trying to retrieve the result, return with err + return nil, err } - // Other Error trying to retrieve the result, return with err - return nil, err } return txResult, nil