Skip to content

Commit 220894b

Browse files
authored
Merge pull request #8215 from onflow/UlianaAndrukhiv/8178-get-transaction-result-by-block-id
[Access] Fix error codes for transactions endpoints during HCU #8178
2 parents 117b91d + 2c29deb commit 220894b

File tree

2 files changed

+171
-32
lines changed

2 files changed

+171
-32
lines changed

engine/access/rpc/backend/transactions/provider/execution_node.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ func (e *ENTransactionProvider) TransactionResult(
128128
}, nil
129129
}
130130

131+
// TransactionsByBlockID returns the transaction for the given block ID.
132+
//
133+
// Expected error returns during normal operation:
134+
// - [codes.NotFound]: If the requested data is not found.
135+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
136+
// - [codes.Internal]: If the system collection cannot be constructed.
131137
func (e *ENTransactionProvider) TransactionsByBlockID(
132138
ctx context.Context,
133139
block *flow.Block,
@@ -153,7 +159,7 @@ func (e *ENTransactionProvider) TransactionsByBlockID(
153159
ByHeight(block.Height).
154160
SystemCollection(e.chainID.Chain(), eventProvider)
155161
if err != nil {
156-
return nil, status.Errorf(codes.Internal, "could not construct system collection: %v", err)
162+
return nil, rpc.ConvertError(err, "could not construct system collection", codes.Internal)
157163
}
158164

159165
return append(transactions, systemCollection.Transactions...), nil
@@ -221,6 +227,12 @@ func (e *ENTransactionProvider) TransactionResultByIndex(
221227
}, nil
222228
}
223229

230+
// TransactionResultsByBlockID get the transaction results by block ID.
231+
//
232+
// Expected error returns during normal operation:
233+
// - [codes.NotFound]: If the requested data is not found.
234+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
235+
// - [codes.Internal]: For internal execution node failures.
224236
func (e *ENTransactionProvider) TransactionResultsByBlockID(
225237
ctx context.Context,
226238
block *flow.Block,
@@ -295,8 +307,9 @@ func (e *ENTransactionProvider) TransactionResultsByBlockID(
295307
// execution node response.
296308
//
297309
// Expected error returns during normal operation:
298-
// - [codes.Internal]: if the scheduled transactions cannot be constructed
299-
// - [status.Error]: for any error returned by the execution node
310+
// - [codes.Internal]: If the scheduled transactions cannot be constructed.
311+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
312+
// - [status.Error]: For any error returned by the execution node.
300313
func (e *ENTransactionProvider) ScheduledTransactionsByBlockID(
301314
ctx context.Context,
302315
header *flow.Header,
@@ -443,7 +456,7 @@ func (e *ENTransactionProvider) systemTransactionIDs(
443456
ByHeight(blockHeight).
444457
SystemCollection(e.chainID.Chain(), eventProvider)
445458
if err != nil {
446-
return nil, rpc.ConvertError(err, "failed to construct system collection", codes.Internal)
459+
return nil, rpc.ConvertError(err, "could not construct system collection", codes.Internal)
447460
}
448461

449462
var systemTxIDs []flow.Identifier
@@ -454,6 +467,12 @@ func (e *ENTransactionProvider) systemTransactionIDs(
454467
return systemTxIDs, nil
455468
}
456469

470+
// getBlockEvents returns all events by the given block ID.
471+
//
472+
// Expected error returns during normal operation:
473+
// - [codes.NotFound]: If the requested data is not found.
474+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
475+
// - [codes.Internal]: For internal execution node failures.
457476
func (e *ENTransactionProvider) getBlockEvents(
458477
ctx context.Context,
459478
blockID flow.Identifier,
@@ -609,6 +628,11 @@ func (e *ENTransactionProvider) getTransactionResultByIndexFromAnyExeNode(
609628
return resp, errToReturn
610629
}
611630

631+
// getBlockEventsByBlockIDsFromAnyExeNode get events by block ID from the execution node.
632+
//
633+
// Expected error returns during normal operation:
634+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
635+
// - [status.Error]: If the execution node returns a gRPC error.
612636
func (e *ENTransactionProvider) getBlockEventsByBlockIDsFromAnyExeNode(
613637
ctx context.Context,
614638
execNodes flow.IdentitySkeletonList,
@@ -696,6 +720,11 @@ func (e *ENTransactionProvider) tryGetTransactionResultByIndex(
696720
return resp, nil
697721
}
698722

723+
// tryGetBlockEventsByBlockIDs attempts to get events by block ID from the given execution node.
724+
//
725+
// Expected error returns during normal operation:
726+
// - [codes.Unavailable]: If a connection to an execution node cannot be established.
727+
// - [status.Error]: If the execution node returns a gRPC error.
699728
func (e *ENTransactionProvider) tryGetBlockEventsByBlockIDs(
700729
ctx context.Context,
701730
execNode *flow.IdentitySkeleton,
@@ -720,6 +749,7 @@ func (e *ENTransactionProvider) tryGetBlockEventsByBlockIDs(
720749
//
721750
// Expected error returns during normal operation:
722751
// - [codes.NotFound]: if the transaction is not found in the block.
752+
// - [codes.Unavailable]: If no nodes are available or a connection to an execution node cannot be established.
723753
// - [codes.Internal]: if the system collection cannot be constructed.
724754
func (e *ENTransactionProvider) getTransactionIDByIndex(ctx context.Context, block *flow.Block, index uint32) (flow.Identifier, error) {
725755
i := uint32(0)
@@ -747,7 +777,7 @@ func (e *ENTransactionProvider) getTransactionIDByIndex(ctx context.Context, blo
747777
ByHeight(block.Height).
748778
SystemCollection(e.chainID.Chain(), eventProvider)
749779
if err != nil {
750-
return flow.ZeroID, status.Errorf(codes.Internal, "could not construct system collection: %v", err)
780+
return flow.ZeroID, rpc.ConvertError(err, "could not construct system collection", codes.Internal)
751781
}
752782

753783
for _, tx := range systemCollection.Transactions {

engine/access/rpc/backend/transactions/transactions_functional_test.go

Lines changed: 136 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/rs/zerolog"
1212
"github.com/stretchr/testify/mock"
1313
"github.com/stretchr/testify/suite"
14+
"google.golang.org/grpc/codes"
15+
"google.golang.org/grpc/status"
1416

1517
"github.com/onflow/flow/protobuf/go/flow/entities"
1618
"github.com/onflow/flow/protobuf/go/flow/execution"
@@ -35,7 +37,7 @@ import (
3537
"github.com/onflow/flow-go/module"
3638
"github.com/onflow/flow-go/module/counters"
3739
execmock "github.com/onflow/flow-go/module/execution/mock"
38-
testutil "github.com/onflow/flow-go/module/executiondatasync/testutil"
40+
"github.com/onflow/flow-go/module/executiondatasync/testutil"
3941
"github.com/onflow/flow-go/module/metrics"
4042
syncmock "github.com/onflow/flow-go/module/state_synchronization/mock"
4143
protocol "github.com/onflow/flow-go/state/protocol/badger"
@@ -648,6 +650,33 @@ func (s *TransactionsFunctionalSuite) TestTransactionResultByIndex_ExecutionNode
648650
s.Require().Equal(expectedResult, result)
649651
}
650652

653+
func (s *TransactionsFunctionalSuite) TestTransactionResultByIndex_ExecutionNode_Errors() {
654+
s.T().Run("failed to get events from EN", func(t *testing.T) {
655+
blockID := s.tf.Block.ID()
656+
env := systemcontracts.SystemContractsForChain(s.g.ChainID()).AsTemplateEnv()
657+
pendingExecuteEventType := blueprints.PendingExecutionEventType(env)
658+
659+
eventsExpectedErr := status.Error(
660+
codes.Unavailable,
661+
"there are no available nodes",
662+
)
663+
s.setupExecutionGetEventsRequestFailed(blockID, pendingExecuteEventType, eventsExpectedErr)
664+
665+
params := s.defaultExecutionNodeParams()
666+
txBackend, err := NewTransactionsBackend(params)
667+
s.Require().NoError(err)
668+
669+
expectedErr := fmt.Errorf("failed to get process transactions events: rpc error: code = Unavailable desc = failed to retrieve result from any execution node: 1 error occurred:\n\t* %w\n\n", eventsExpectedErr)
670+
index := uint32(20) // case when user transactions is not within the guarantees
671+
result, err := txBackend.GetTransactionResultByIndex(context.Background(), blockID, index, entities.EventEncodingVersion_JSON_CDC_V0)
672+
s.Require().Error(err)
673+
s.Require().Nil(result)
674+
675+
s.Require().Equal(codes.Unavailable, status.Code(err))
676+
s.Require().Equal(expectedErr.Error(), err.Error())
677+
})
678+
}
679+
651680
func (s *TransactionsFunctionalSuite) TestTransactionResultsByBlockID_ExecutionNode() {
652681
blockID := s.tf.Block.ID()
653682

@@ -717,17 +746,19 @@ func (s *TransactionsFunctionalSuite) TestTransactionsByBlockID_ExecutionNode()
717746
}
718747

719748
nodeResponse := &execution.GetEventsForBlockIDsResponse{
720-
Results: []*execution.GetEventsForBlockIDsResponse_Result{{
721-
BlockId: blockID[:],
722-
BlockHeight: block.Height,
723-
Events: events,
724-
}},
749+
Results: []*execution.GetEventsForBlockIDsResponse_Result{
750+
{
751+
BlockId: blockID[:],
752+
BlockHeight: block.Height,
753+
Events: events,
754+
},
755+
},
725756
EventEncodingVersion: entities.EventEncodingVersion_CCF_V0,
726757
}
727758

728759
s.execClient.
729760
On("GetEventsForBlockIDs", mock.Anything, expectedRequest).
730-
Return(nodeResponse, nil)
761+
Return(nodeResponse, nil).Once()
731762

732763
params := s.defaultExecutionNodeParams()
733764
txBackend, err := NewTransactionsBackend(params)
@@ -738,37 +769,46 @@ func (s *TransactionsFunctionalSuite) TestTransactionsByBlockID_ExecutionNode()
738769
s.Require().Equal(expectedTransactions, results)
739770
}
740771

772+
func (s *TransactionsFunctionalSuite) TestTransactionsByBlockID_ExecutionNode_Errors() {
773+
s.T().Run("failed to get events from EN", func(t *testing.T) {
774+
blockID := s.tf.Block.ID()
775+
env := systemcontracts.SystemContractsForChain(s.g.ChainID()).AsTemplateEnv()
776+
pendingExecuteEventType := blueprints.PendingExecutionEventType(env)
777+
778+
eventsExpectedErr := status.Error(
779+
codes.Unavailable,
780+
"there are no available nodes",
781+
)
782+
s.setupExecutionGetEventsRequestFailed(blockID, pendingExecuteEventType, eventsExpectedErr)
783+
784+
params := s.defaultExecutionNodeParams()
785+
txBackend, err := NewTransactionsBackend(params)
786+
s.Require().NoError(err)
787+
788+
expectedErr := fmt.Errorf("failed to get process transactions events: rpc error: code = Unavailable desc = failed to retrieve result from any execution node: 1 error occurred:\n\t* %w\n\n", eventsExpectedErr)
789+
results, err := txBackend.GetTransactionsByBlockID(context.Background(), blockID)
790+
s.Require().Error(err)
791+
s.Require().Nil(results)
792+
793+
s.Require().Equal(codes.Unavailable, status.Code(err))
794+
s.Require().Equal(expectedErr.Error(), err.Error())
795+
})
796+
}
797+
741798
func (s *TransactionsFunctionalSuite) TestScheduledTransactionsByBlockID_ExecutionNode() {
742799
block := s.tf.Block
743800
blockID := block.ID()
744801

745802
env := systemcontracts.SystemContractsForChain(s.g.ChainID()).AsTemplateEnv()
746803
pendingExecuteEventType := blueprints.PendingExecutionEventType(env)
747804

748-
expectedRequest := &execproto.GetEventsForBlockIDsRequest{
749-
Type: string(pendingExecuteEventType),
750-
BlockIds: [][]byte{blockID[:]},
751-
}
752-
753-
events := make([]*entities.Event, 0)
805+
events := make([]flow.Event, 0)
754806
for _, event := range s.tf.ExpectedEvents {
755807
if blueprints.IsPendingExecutionEvent(env, event) {
756-
events = append(events, convert.EventToMessage(event))
808+
events = append(events, event)
757809
}
758810
}
759-
760-
nodeResponse := &execution.GetEventsForBlockIDsResponse{
761-
Results: []*execution.GetEventsForBlockIDsResponse_Result{{
762-
BlockId: blockID[:],
763-
BlockHeight: block.Height,
764-
Events: events,
765-
}},
766-
EventEncodingVersion: entities.EventEncodingVersion_CCF_V0,
767-
}
768-
769-
s.execClient.
770-
On("GetEventsForBlockIDs", mock.Anything, expectedRequest).
771-
Return(nodeResponse, nil)
811+
s.setupExecutionGetEventsRequest(blockID, pendingExecuteEventType, block.Height, events)
772812

773813
params := s.defaultExecutionNodeParams()
774814
txBackend, err := NewTransactionsBackend(params)
@@ -785,3 +825,72 @@ func (s *TransactionsFunctionalSuite) TestScheduledTransactionsByBlockID_Executi
785825
break // call for the first scheduled transaction iterated
786826
}
787827
}
828+
829+
func (s *TransactionsFunctionalSuite) TestScheduledTransactionsByBlockID_ExecutionNode_Errors() {
830+
s.T().Run("failed to get events from EN", func(t *testing.T) {
831+
block := s.tf.Block
832+
blockID := block.ID()
833+
env := systemcontracts.SystemContractsForChain(s.g.ChainID()).AsTemplateEnv()
834+
pendingExecuteEventType := blueprints.PendingExecutionEventType(env)
835+
836+
eventsExpectedErr := status.Error(
837+
codes.Unavailable,
838+
"there are no available nodes",
839+
)
840+
s.setupExecutionGetEventsRequestFailed(blockID, pendingExecuteEventType, eventsExpectedErr)
841+
842+
params := s.defaultExecutionNodeParams()
843+
txBackend, err := NewTransactionsBackend(params)
844+
s.Require().NoError(err)
845+
846+
expectedErr := fmt.Errorf("rpc error: code = Unavailable desc = failed to retrieve result from any execution node: 1 error occurred:\n\t* %w\n\n", eventsExpectedErr)
847+
for _, scheduledTxID := range s.tf.ExpectedScheduledTransactions {
848+
results, err := txBackend.GetScheduledTransaction(context.Background(), scheduledTxID)
849+
s.Require().Error(err)
850+
s.Require().Nil(results)
851+
852+
s.Require().Equal(codes.Unavailable, status.Code(err))
853+
s.Require().Equal(expectedErr.Error(), err.Error())
854+
855+
break // call for the first scheduled transaction iterated
856+
}
857+
})
858+
}
859+
860+
func (s *TransactionsFunctionalSuite) setupExecutionGetEventsRequest(blockID flow.Identifier, eventType flow.EventType, blockHeight uint64, events []flow.Event) {
861+
eventMessages := make([]*entities.Event, len(events))
862+
for i, event := range events {
863+
eventMessages[i] = convert.EventToMessage(event)
864+
}
865+
866+
request := &execproto.GetEventsForBlockIDsRequest{
867+
Type: string(eventType),
868+
BlockIds: [][]byte{blockID[:]},
869+
}
870+
expectedResponse := &execproto.GetEventsForBlockIDsResponse{
871+
Results: []*execproto.GetEventsForBlockIDsResponse_Result{
872+
{
873+
BlockId: blockID[:],
874+
BlockHeight: blockHeight,
875+
Events: eventMessages,
876+
},
877+
},
878+
EventEncodingVersion: entities.EventEncodingVersion_CCF_V0,
879+
}
880+
881+
s.execClient.
882+
On("GetEventsForBlockIDs", mock.Anything, request).
883+
Return(expectedResponse, nil).
884+
Once()
885+
}
886+
887+
func (s *TransactionsFunctionalSuite) setupExecutionGetEventsRequestFailed(blockID flow.Identifier, eventType flow.EventType, expectedErr error) {
888+
expectedRequest := &execproto.GetEventsForBlockIDsRequest{
889+
Type: string(eventType),
890+
BlockIds: [][]byte{blockID[:]},
891+
}
892+
893+
s.execClient.
894+
On("GetEventsForBlockIDs", mock.Anything, expectedRequest).
895+
Return(nil, expectedErr).Once()
896+
}

0 commit comments

Comments
 (0)