github.com/onflow/flow-go@v0.33.17/engine/access/rpc/backend/backend_test.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/dgraph-io/badger/v2"
    10  	accessproto "github.com/onflow/flow/protobuf/go/flow/access"
    11  	"github.com/onflow/flow/protobuf/go/flow/entities"
    12  	entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities"
    13  	execproto "github.com/onflow/flow/protobuf/go/flow/execution"
    14  	"github.com/rs/zerolog"
    15  	"github.com/sony/gobreaker"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/mock"
    18  	"github.com/stretchr/testify/require"
    19  	"github.com/stretchr/testify/suite"
    20  	"google.golang.org/grpc/codes"
    21  	"google.golang.org/grpc/status"
    22  
    23  	accessflow "github.com/onflow/flow-go/access"
    24  	"github.com/onflow/flow-go/cmd/build"
    25  	access "github.com/onflow/flow-go/engine/access/mock"
    26  	backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock"
    27  	"github.com/onflow/flow-go/engine/access/rpc/connection"
    28  	connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock"
    29  	"github.com/onflow/flow-go/engine/common/rpc/convert"
    30  	"github.com/onflow/flow-go/fvm/blueprints"
    31  	"github.com/onflow/flow-go/model/flow"
    32  	"github.com/onflow/flow-go/module/irrecoverable"
    33  	"github.com/onflow/flow-go/module/metrics"
    34  	realstate "github.com/onflow/flow-go/state"
    35  	bprotocol "github.com/onflow/flow-go/state/protocol/badger"
    36  	"github.com/onflow/flow-go/state/protocol/invalid"
    37  	protocol "github.com/onflow/flow-go/state/protocol/mock"
    38  	"github.com/onflow/flow-go/state/protocol/snapshots"
    39  	"github.com/onflow/flow-go/state/protocol/util"
    40  	"github.com/onflow/flow-go/storage"
    41  	storagemock "github.com/onflow/flow-go/storage/mock"
    42  	"github.com/onflow/flow-go/utils/unittest"
    43  	"github.com/onflow/flow-go/utils/unittest/generator"
    44  )
    45  
    46  const TEST_MAX_HEIGHT = 100
    47  
    48  var eventEncodingVersions = []entitiesproto.EventEncodingVersion{
    49  	entitiesproto.EventEncodingVersion_JSON_CDC_V0,
    50  	entitiesproto.EventEncodingVersion_CCF_V0,
    51  }
    52  
    53  type Suite struct {
    54  	suite.Suite
    55  
    56  	state    *protocol.State
    57  	snapshot *protocol.Snapshot
    58  	log      zerolog.Logger
    59  
    60  	blocks             *storagemock.Blocks
    61  	headers            *storagemock.Headers
    62  	collections        *storagemock.Collections
    63  	transactions       *storagemock.Transactions
    64  	receipts           *storagemock.ExecutionReceipts
    65  	results            *storagemock.ExecutionResults
    66  	transactionResults *storagemock.LightTransactionResults
    67  	events             *storagemock.Events
    68  
    69  	colClient              *access.AccessAPIClient
    70  	execClient             *access.ExecutionAPIClient
    71  	historicalAccessClient *access.AccessAPIClient
    72  
    73  	connectionFactory *connectionmock.ConnectionFactory
    74  	communicator      *backendmock.Communicator
    75  
    76  	chainID  flow.ChainID
    77  	systemTx *flow.TransactionBody
    78  }
    79  
    80  func TestHandler(t *testing.T) {
    81  	suite.Run(t, new(Suite))
    82  }
    83  
    84  func (suite *Suite) SetupTest() {
    85  	suite.log = zerolog.New(zerolog.NewConsoleWriter())
    86  	suite.state = new(protocol.State)
    87  	suite.snapshot = new(protocol.Snapshot)
    88  	header := unittest.BlockHeaderFixture()
    89  	params := new(protocol.Params)
    90  	params.On("FinalizedRoot").Return(header, nil)
    91  	params.On("SporkID").Return(unittest.IdentifierFixture(), nil)
    92  	params.On("ProtocolVersion").Return(uint(unittest.Uint64InRange(10, 30)), nil)
    93  	params.On("SporkRootBlockHeight").Return(header.Height, nil)
    94  	params.On("SealedRoot").Return(header, nil)
    95  	suite.state.On("Params").Return(params)
    96  
    97  	suite.blocks = new(storagemock.Blocks)
    98  	suite.headers = new(storagemock.Headers)
    99  	suite.transactions = new(storagemock.Transactions)
   100  	suite.collections = new(storagemock.Collections)
   101  	suite.receipts = new(storagemock.ExecutionReceipts)
   102  	suite.results = new(storagemock.ExecutionResults)
   103  	suite.colClient = new(access.AccessAPIClient)
   104  	suite.execClient = new(access.ExecutionAPIClient)
   105  	suite.transactionResults = storagemock.NewLightTransactionResults(suite.T())
   106  	suite.events = storagemock.NewEvents(suite.T())
   107  	suite.chainID = flow.Testnet
   108  	suite.historicalAccessClient = new(access.AccessAPIClient)
   109  	suite.connectionFactory = connectionmock.NewConnectionFactory(suite.T())
   110  
   111  	suite.communicator = new(backendmock.Communicator)
   112  
   113  	var err error
   114  	suite.systemTx, err = blueprints.SystemChunkTransaction(flow.Testnet.Chain())
   115  	suite.Require().NoError(err)
   116  }
   117  
   118  func (suite *Suite) TestPing() {
   119  	suite.colClient.
   120  		On("Ping", mock.Anything, &accessproto.PingRequest{}).
   121  		Return(&accessproto.PingResponse{}, nil)
   122  
   123  	suite.execClient.
   124  		On("Ping", mock.Anything, &execproto.PingRequest{}).
   125  		Return(&execproto.PingResponse{}, nil)
   126  
   127  	params := suite.defaultBackendParams()
   128  
   129  	backend, err := New(params)
   130  	suite.Require().NoError(err)
   131  
   132  	err = backend.Ping(context.Background())
   133  	suite.Require().NoError(err)
   134  }
   135  
   136  func (suite *Suite) TestGetLatestFinalizedBlockHeader() {
   137  	// setup the mocks
   138  	block := unittest.BlockHeaderFixture()
   139  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
   140  	suite.snapshot.On("Head").Return(block, nil).Once()
   141  	suite.state.On("Sealed").Return(suite.snapshot, nil)
   142  	suite.snapshot.On("Head").Return(block, nil).Once()
   143  
   144  	params := suite.defaultBackendParams()
   145  
   146  	backend, err := New(params)
   147  	suite.Require().NoError(err)
   148  
   149  	// query the handler for the latest finalized block
   150  	header, stat, err := backend.GetLatestBlockHeader(context.Background(), false)
   151  	suite.checkResponse(header, err)
   152  
   153  	// make sure we got the latest block
   154  	suite.Require().Equal(block.ID(), header.ID())
   155  	suite.Require().Equal(block.Height, header.Height)
   156  	suite.Require().Equal(block.ParentID, header.ParentID)
   157  	suite.Require().Equal(stat, flow.BlockStatusSealed)
   158  
   159  	suite.assertAllExpectations()
   160  }
   161  
   162  // TestGetLatestProtocolStateSnapshot_NoTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint
   163  // where the sealing segment for the State requested at latest finalized  block does not contain any Blocks that
   164  // spans an epoch or epoch phase transition.
   165  func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() {
   166  	identities := unittest.CompleteIdentitySet()
   167  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   168  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   169  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   170  		// build epoch 1
   171  		// Blocks in current State
   172  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   173  		epochBuilder.
   174  			BuildEpoch().
   175  			CompleteEpoch()
   176  
   177  		// get heights of each phase in built epochs
   178  		epoch1, ok := epochBuilder.EpochHeights(1)
   179  		require.True(suite.T(), ok)
   180  
   181  		// setup AtBlockID mock returns for State
   182  		for _, height := range epoch1.Range() {
   183  			suite.state.On("AtHeight", height).Return(state.AtHeight(height)).Once()
   184  		}
   185  
   186  		// Take snapshot at height of block D (epoch1.heights[2]) for valid segment and valid snapshot
   187  		// where its sealing segment is A <- B <- C
   188  		snap := state.AtHeight(epoch1.Range()[2])
   189  		suite.state.On("Final").Return(snap).Once()
   190  
   191  		params := suite.defaultBackendParams()
   192  		params.MaxHeightRange = TEST_MAX_HEIGHT
   193  
   194  		backend, err := New(params)
   195  		suite.Require().NoError(err)
   196  
   197  		// query the handler for the latest finalized snapshot
   198  		bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background())
   199  		suite.Require().NoError(err)
   200  
   201  		// we expect the endpoint to return the snapshot at the same height we requested
   202  		// because it has a valid sealing segment with no Blocks spanning an epoch or phase transition
   203  		expectedSnapshotBytes, err := convert.SnapshotToBytes(snap)
   204  		suite.Require().NoError(err)
   205  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   206  	})
   207  }
   208  
   209  // TestGetLatestProtocolStateSnapshot_TransitionSpans tests our GetLatestProtocolStateSnapshot RPC endpoint
   210  // where the sealing segment for the State requested for latest finalized block  contains a block that
   211  // spans an epoch transition and Blocks that span epoch phase transitions.
   212  func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() {
   213  	identities := unittest.CompleteIdentitySet()
   214  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   215  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   216  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   217  
   218  		// building 2 epochs allows us to take a snapshot at a point in time where
   219  		// an epoch transition happens
   220  		epochBuilder.
   221  			BuildEpoch().
   222  			CompleteEpoch()
   223  
   224  		epochBuilder.
   225  			BuildEpoch().
   226  			CompleteEpoch()
   227  
   228  		// get heights of each phase in built epochs
   229  		epoch1, ok := epochBuilder.EpochHeights(1)
   230  		require.True(suite.T(), ok)
   231  		epoch2, ok := epochBuilder.EpochHeights(2)
   232  		require.True(suite.T(), ok)
   233  
   234  		// setup AtHeight mock returns for State
   235  		for _, height := range append(epoch1.Range(), epoch2.Range()...) {
   236  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   237  		}
   238  
   239  		// Take snapshot at height of the first block of epoch2, the sealing segment of this snapshot
   240  		// will have contain block spanning an epoch transition as well as an epoch phase transition.
   241  		// This will cause our GetLatestProtocolStateSnapshot func to return a snapshot
   242  		// at block with height 3, the first block of the staking phase of epoch1.
   243  
   244  		snap := state.AtHeight(epoch2.Range()[0])
   245  		suite.state.On("Final").Return(snap).Once()
   246  
   247  		params := suite.defaultBackendParams()
   248  		params.MaxHeightRange = TEST_MAX_HEIGHT
   249  
   250  		backend, err := New(params)
   251  		suite.Require().NoError(err)
   252  
   253  		// query the handler for the latest finalized snapshot
   254  		bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background())
   255  		suite.Require().NoError(err)
   256  		fmt.Println()
   257  
   258  		// we expect the endpoint to return last valid snapshot which is the snapshot at block C (height 2)
   259  		expectedSnapshotBytes, err := convert.SnapshotToBytes(state.AtHeight(epoch1.Range()[2]))
   260  		suite.Require().NoError(err)
   261  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   262  	})
   263  }
   264  
   265  // TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint
   266  // where the sealing segment for the State requested at latest finalized  block contains a Blocks that
   267  // spans an epoch phase transition.
   268  func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() {
   269  	identities := unittest.CompleteIdentitySet()
   270  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   271  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   272  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   273  		// build epoch 1
   274  		// Blocks in current State
   275  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   276  		epochBuilder.
   277  			BuildEpoch().
   278  			CompleteEpoch()
   279  
   280  		// get heights of each phase in built epochs
   281  		epoch1, ok := epochBuilder.EpochHeights(1)
   282  		require.True(suite.T(), ok)
   283  
   284  		// setup AtBlockID mock returns for State
   285  		for _, height := range epoch1.Range() {
   286  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   287  		}
   288  
   289  		// Take snapshot at height of block D (epoch1.heights[3]) the sealing segment for this snapshot
   290  		// is C(S_A) <- D(S_B) |setup|) which spans the epoch setup phase. This will force
   291  		// our RPC endpoint to return a snapshot at block D which is the snapshot at the boundary where the phase
   292  		// transition happens.
   293  		snap := state.AtHeight(epoch1.Range()[3])
   294  		suite.state.On("Final").Return(snap).Once()
   295  
   296  		params := suite.defaultBackendParams()
   297  		params.MaxHeightRange = TEST_MAX_HEIGHT
   298  
   299  		backend, err := New(params)
   300  		suite.Require().NoError(err)
   301  
   302  		// query the handler for the latest finalized snapshot
   303  		bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background())
   304  		suite.Require().NoError(err)
   305  
   306  		// we expect the endpoint to return last valid snapshot which is the snapshot at block C (height 2)
   307  		expectedSnapshotBytes, err := convert.SnapshotToBytes(state.AtHeight(epoch1.Range()[2]))
   308  		suite.Require().NoError(err)
   309  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   310  	})
   311  }
   312  
   313  // TestGetLatestProtocolStateSnapshot_EpochTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint
   314  // where the sealing segment for the State requested at latest finalized  block contains a Blocks that
   315  // spans an epoch transition.
   316  func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() {
   317  	identities := unittest.CompleteIdentitySet()
   318  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   319  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   320  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   321  		// build epoch 1
   322  		// Blocks in current State
   323  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   324  		epochBuilder.BuildEpoch()
   325  
   326  		// add more Blocks to our State in the commit phase, this will allow
   327  		// us to take a snapshot at the height where the epoch1 -> epoch2 transition
   328  		// and no block spans an epoch phase transition. The third block added will
   329  		// have a seal for the first block in the commit phase allowing us to avoid
   330  		// spanning an epoch phase transition.
   331  		epochBuilder.AddBlocksWithSeals(3, 1)
   332  		epochBuilder.CompleteEpoch()
   333  
   334  		// Now we build our second epoch
   335  		epochBuilder.
   336  			BuildEpoch().
   337  			CompleteEpoch()
   338  
   339  		// get heights of each phase in built epochs
   340  		epoch1, ok := epochBuilder.EpochHeights(1)
   341  		require.True(suite.T(), ok)
   342  		epoch2, ok := epochBuilder.EpochHeights(2)
   343  		require.True(suite.T(), ok)
   344  
   345  		// setup AtHeight mock returns for State
   346  		for _, height := range append(epoch1.Range(), epoch2.Range()...) {
   347  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   348  		}
   349  
   350  		// Take snapshot at the first block of epoch2 . The sealing segment
   351  		// for this snapshot contains a block (highest) that spans the epoch1 -> epoch2
   352  		// transition.
   353  		snap := state.AtHeight(epoch2.Range()[0])
   354  		suite.state.On("Final").Return(snap).Once()
   355  
   356  		params := suite.defaultBackendParams()
   357  		params.MaxHeightRange = TEST_MAX_HEIGHT
   358  
   359  		backend, err := New(params)
   360  		suite.Require().NoError(err)
   361  
   362  		// query the handler for the latest finalized snapshot
   363  		bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background())
   364  		suite.Require().NoError(err)
   365  
   366  		// we expect the endpoint to return last valid snapshot which is the snapshot at the final block
   367  		// of the previous epoch
   368  		expectedSnapshotBytes, err := convert.SnapshotToBytes(state.AtHeight(epoch1.Range()[len(epoch1.Range())-1]))
   369  		suite.Require().NoError(err)
   370  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   371  	})
   372  }
   373  
   374  // TestGetLatestProtocolStateSnapshot_EpochTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint
   375  // where the length of the sealing segment is greater than the configured SnapshotHistoryLimit.
   376  func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() {
   377  	identities := unittest.CompleteIdentitySet()
   378  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   379  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   380  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state).BuildEpoch().CompleteEpoch()
   381  
   382  		// get heights of each phase in built epochs
   383  		epoch1, ok := epochBuilder.EpochHeights(1)
   384  		require.True(suite.T(), ok)
   385  
   386  		// setup AtBlockID mock returns for State
   387  		for _, height := range epoch1.Range() {
   388  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   389  		}
   390  
   391  		// Take snapshot at height of block E (epoch1.heights[4]) the sealing segment for this snapshot
   392  		// is C(S_A) <- D(S_B) |setup| <- E(S_C) which spans the epoch setup phase. This will force
   393  		// our RPC endpoint to return a snapshot at block D which is the snapshot at the boundary where a phase
   394  		// transition happens.
   395  		snap := state.AtHeight(epoch1.Range()[4])
   396  		suite.state.On("Final").Return(snap).Once()
   397  
   398  		params := suite.defaultBackendParams()
   399  		// very short history limit, any segment with any Blocks spanning any transition should force the endpoint to return a history limit error
   400  		params.SnapshotHistoryLimit = 1
   401  
   402  		backend, err := New(params)
   403  		suite.Require().NoError(err)
   404  
   405  		// the handler should return a snapshot history limit error
   406  		_, err = backend.GetLatestProtocolStateSnapshot(context.Background())
   407  		suite.Require().ErrorIs(err, snapshots.ErrSnapshotHistoryLimit)
   408  	})
   409  }
   410  
   411  // TestGetProtocolStateSnapshotByBlockID tests our GetProtocolStateSnapshotByBlockID RPC endpoint.
   412  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID() {
   413  	identities := unittest.CompleteIdentitySet()
   414  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   415  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   416  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   417  		// build epoch 1
   418  		// Blocks in current State
   419  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   420  		epochBuilder.
   421  			BuildEpoch().
   422  			CompleteEpoch()
   423  
   424  		// get heights of each phase in built epochs
   425  		epoch1, ok := epochBuilder.EpochHeights(1)
   426  		require.True(suite.T(), ok)
   427  
   428  		// setup AtBlockID, AtHeight and BlockIDByHeight mock returns for State
   429  		for _, height := range epoch1.Range() {
   430  			snap := state.AtHeight(height)
   431  			blockHead, err := snap.Head()
   432  			suite.Require().NoError(err)
   433  
   434  			suite.state.On("AtHeight", height).Return(snap)
   435  			suite.state.On("AtBlockID", blockHead.ID()).Return(snap)
   436  			suite.headers.On("BlockIDByHeight", height).Return(blockHead.ID(), nil)
   437  		}
   438  
   439  		// Take snapshot at height of block D (epoch1.heights[2]) for valid segment and valid snapshot
   440  		snap := state.AtHeight(epoch1.Range()[2])
   441  		blockHead, err := snap.Head()
   442  		suite.Require().NoError(err)
   443  
   444  		params := suite.defaultBackendParams()
   445  		params.MaxHeightRange = TEST_MAX_HEIGHT
   446  
   447  		backend, err := New(params)
   448  		suite.Require().NoError(err)
   449  
   450  		// query the handler for the latest finalized snapshot
   451  		bytes, err := backend.GetProtocolStateSnapshotByBlockID(context.Background(), blockHead.ID())
   452  		suite.Require().NoError(err)
   453  
   454  		// we expect the endpoint to return the snapshot at the same height we requested
   455  		expectedSnapshotBytes, err := convert.SnapshotToBytes(snap)
   456  		suite.Require().NoError(err)
   457  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   458  	})
   459  }
   460  
   461  // TestGetProtocolStateSnapshotByBlockID_NonQueryBlock tests our GetProtocolStateSnapshotByBlockID RPC endpoint
   462  // where no block with the given ID was found.
   463  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_UnknownQueryBlock() {
   464  	identities := unittest.CompleteIdentitySet()
   465  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   466  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   467  		rootBlock, err := rootSnapshot.Head()
   468  		suite.Require().NoError(err)
   469  
   470  		params := suite.defaultBackendParams()
   471  		params.MaxHeightRange = TEST_MAX_HEIGHT
   472  
   473  		backend, err := New(params)
   474  		suite.Require().NoError(err)
   475  
   476  		// create a new block with root block as parent
   477  		newBlock := unittest.BlockWithParentFixture(rootBlock)
   478  		ctx := context.Background()
   479  
   480  		suite.state.On("AtBlockID", newBlock.ID()).Return(unittest.StateSnapshotForUnknownBlock())
   481  
   482  		// query the handler for the snapshot for non existing block
   483  		snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, newBlock.ID())
   484  		suite.Require().Nil(snapshotBytes)
   485  		suite.Require().Error(err)
   486  		suite.Require().Equal(codes.NotFound, status.Code(err))
   487  		suite.Require().Equal(status.Errorf(codes.NotFound, "failed to get a valid snapshot: block not found").Error(),
   488  			err.Error())
   489  		suite.assertAllExpectations()
   490  	})
   491  }
   492  
   493  // TestGetProtocolStateSnapshotByBlockID_AtBlockIDInternalError tests our GetProtocolStateSnapshotByBlockID RPC endpoint
   494  // where unexpected error from snapshot.AtBlockID appear because of closed database.
   495  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_AtBlockIDInternalError() {
   496  	identities := unittest.CompleteIdentitySet()
   497  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   498  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   499  		params := suite.defaultBackendParams()
   500  		params.MaxHeightRange = TEST_MAX_HEIGHT
   501  
   502  		backend, err := New(params)
   503  		suite.Require().NoError(err)
   504  
   505  		ctx := context.Background()
   506  		newBlock := unittest.BlockFixture()
   507  
   508  		expectedError := errors.New("runtime-error")
   509  		suite.state.On("AtBlockID", newBlock.ID()).Return(invalid.NewSnapshot(expectedError))
   510  
   511  		// query the handler for the snapshot
   512  		snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, newBlock.ID())
   513  		suite.Require().Nil(snapshotBytes)
   514  		suite.Require().Error(err)
   515  		suite.Require().ErrorAs(err, &expectedError)
   516  		suite.Require().Equal(codes.Internal, status.Code(err))
   517  		suite.assertAllExpectations()
   518  	})
   519  }
   520  
   521  // TestGetProtocolStateSnapshotByBlockID_BlockNotFinalizedAtHeight tests our GetProtocolStateSnapshotByBlockID RPC endpoint
   522  // where The block exists, but no block has been finalized at its height.
   523  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_BlockNotFinalizedAtHeight() {
   524  	identities := unittest.CompleteIdentitySet()
   525  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   526  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   527  		rootBlock, err := rootSnapshot.Head()
   528  		suite.Require().NoError(err)
   529  
   530  		params := suite.defaultBackendParams()
   531  		params.MaxHeightRange = TEST_MAX_HEIGHT
   532  
   533  		backend, err := New(params)
   534  		suite.Require().NoError(err)
   535  
   536  		// create a new block with root block as parent
   537  		newBlock := unittest.BlockWithParentFixture(rootBlock)
   538  		ctx := context.Background()
   539  		// add new block to the chain state
   540  		err = state.Extend(ctx, newBlock)
   541  		suite.Require().NoError(err)
   542  
   543  		// since block was added to the block tree it must be queryable by block ID
   544  		suite.state.On("AtBlockID", newBlock.ID()).Return(state.AtBlockID(newBlock.ID()))
   545  		suite.headers.On("BlockIDByHeight", newBlock.Header.Height).Return(flow.ZeroID, storage.ErrNotFound)
   546  
   547  		// query the handler for the snapshot for non finalized block
   548  		snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, newBlock.ID())
   549  		suite.Require().Nil(snapshotBytes)
   550  		suite.Require().Error(err)
   551  		suite.Require().Equal(codes.FailedPrecondition, status.Code(err))
   552  		suite.assertAllExpectations()
   553  	})
   554  }
   555  
   556  // TestGetProtocolStateSnapshotByBlockID_DifferentBlockFinalizedAtHeight tests our GetProtocolStateSnapshotByBlockID RPC
   557  // endpoint where a different block than what was queried has been finalized at this height.
   558  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_DifferentBlockFinalizedAtHeight() {
   559  	identities := unittest.CompleteIdentitySet()
   560  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   561  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   562  		rootBlock, err := rootSnapshot.Head()
   563  		suite.Require().NoError(err)
   564  
   565  		params := suite.defaultBackendParams()
   566  		params.MaxHeightRange = TEST_MAX_HEIGHT
   567  
   568  		backend, err := New(params)
   569  		suite.Require().NoError(err)
   570  
   571  		// create a new block with root block as parent
   572  		finalizedBlock := unittest.BlockWithParentFixture(rootBlock)
   573  		orphanBlock := unittest.BlockWithParentFixture(rootBlock)
   574  		ctx := context.Background()
   575  
   576  		// add new block to the chain state
   577  		err = state.Extend(ctx, finalizedBlock)
   578  		suite.Require().NoError(err)
   579  
   580  		// add orphan block to the chain state as well
   581  		err = state.Extend(ctx, orphanBlock)
   582  		suite.Require().NoError(err)
   583  
   584  		suite.Equal(finalizedBlock.Header.Height, orphanBlock.Header.Height,
   585  			"expect both blocks to have same height to have a fork")
   586  
   587  		// since block was added to the block tree it must be queryable by block ID
   588  		suite.state.On("AtBlockID", orphanBlock.ID()).Return(state.AtBlockID(orphanBlock.ID()))
   589  
   590  		// since there are two candidate blocks with the same height, we will return the one that was finalized
   591  		suite.headers.On("BlockIDByHeight", finalizedBlock.Header.Height).Return(finalizedBlock.ID(), nil)
   592  
   593  		// query the handler for the snapshot for non finalized block
   594  		snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, orphanBlock.ID())
   595  		suite.Require().Nil(snapshotBytes)
   596  		suite.Require().Error(err)
   597  		suite.Require().Equal(codes.InvalidArgument, status.Code(err))
   598  		suite.assertAllExpectations()
   599  	})
   600  }
   601  
   602  // TestGetProtocolStateSnapshotByBlockID_UnexpectedErrorBlockIDByHeight tests our GetProtocolStateSnapshotByBlockID RPC
   603  // endpoint where a unexpected error from BlockIDByHeight appear.
   604  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_UnexpectedErrorBlockIDByHeight() {
   605  	identities := unittest.CompleteIdentitySet()
   606  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   607  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   608  		rootBlock, err := rootSnapshot.Head()
   609  		suite.Require().NoError(err)
   610  
   611  		params := suite.defaultBackendParams()
   612  		params.MaxHeightRange = TEST_MAX_HEIGHT
   613  
   614  		backend, err := New(params)
   615  		suite.Require().NoError(err)
   616  
   617  		// create a new block with root block as parent
   618  		newBlock := unittest.BlockWithParentFixture(rootBlock)
   619  		ctx := context.Background()
   620  		// add new block to the chain state
   621  		err = state.Extend(ctx, newBlock)
   622  		suite.Require().NoError(err)
   623  
   624  		// since block was added to the block tree it must be queryable by block ID
   625  		suite.state.On("AtBlockID", newBlock.ID()).Return(state.AtBlockID(newBlock.ID()))
   626  		//expectedError := errors.New("runtime-error")
   627  		suite.headers.On("BlockIDByHeight", newBlock.Header.Height).Return(flow.ZeroID,
   628  			status.Errorf(codes.Internal, "failed to lookup block id by height %d", newBlock.Header.Height))
   629  
   630  		// query the handler for the snapshot
   631  		snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, newBlock.ID())
   632  		suite.Require().Nil(snapshotBytes)
   633  		suite.Require().Error(err)
   634  		suite.Require().Equal(codes.Internal, status.Code(err))
   635  		suite.assertAllExpectations()
   636  	})
   637  }
   638  
   639  // TestGetProtocolStateSnapshotByBlockID_InvalidSegment tests our GetProtocolStateSnapshotByBlockID RPC endpoint
   640  // for invalid segment between phases and between epochs
   641  func (suite *Suite) TestGetProtocolStateSnapshotByBlockID_InvalidSegment() {
   642  	identities := unittest.CompleteIdentitySet()
   643  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   644  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   645  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   646  		// build epoch 1
   647  		// Blocks in current State
   648  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   649  		epochBuilder.
   650  			BuildEpoch().
   651  			CompleteEpoch()
   652  
   653  		// get heights of each phase in built epochs
   654  		epoch1, ok := epochBuilder.EpochHeights(1)
   655  		require.True(suite.T(), ok)
   656  
   657  		// setup AtBlockID and AtHeight mock returns for State
   658  		for _, height := range epoch1.Range() {
   659  			snap := state.AtHeight(height)
   660  			blockHead, err := snap.Head()
   661  			suite.Require().NoError(err)
   662  
   663  			suite.state.On("AtHeight", height).Return(snap)
   664  			suite.state.On("AtBlockID", blockHead.ID()).Return(snap)
   665  			suite.headers.On("BlockIDByHeight", height).Return(blockHead.ID(), nil)
   666  		}
   667  
   668  		backend, err := New(suite.defaultBackendParams())
   669  		suite.Require().NoError(err)
   670  
   671  		suite.T().Run("sealing segment between phases", func(t *testing.T) {
   672  			// Take snapshot at height of first block of setup phase
   673  			snap := state.AtHeight(epoch1.SetupRange()[0])
   674  			block, err := snap.Head()
   675  			suite.Require().NoError(err)
   676  
   677  			bytes, err := backend.GetProtocolStateSnapshotByBlockID(context.Background(), block.ID())
   678  			suite.Require().Error(err)
   679  			suite.Require().Empty(bytes)
   680  			suite.Require().Equal(status.Errorf(codes.InvalidArgument, "failed to retrieve snapshot for block, try again with different block: %v",
   681  				snapshots.ErrSnapshotPhaseMismatch).Error(),
   682  				err.Error())
   683  		})
   684  
   685  		suite.T().Run("sealing segment between epochs", func(t *testing.T) {
   686  			// Take snapshot at height of latest finalized block
   687  			snap := state.Final()
   688  			epochCounter, err := snap.Epochs().Current().Counter()
   689  			suite.Require().NoError(err)
   690  			suite.Require().Equal(epoch1.Counter+1, epochCounter, "expect to be in next epoch")
   691  			block, err := snap.Head()
   692  			suite.Require().NoError(err)
   693  
   694  			suite.state.On("AtBlockID", block.ID()).Return(snap)
   695  			suite.state.On("AtHeight", block.Height).Return(snap)
   696  			suite.headers.On("BlockIDByHeight", block.Height).Return(block.ID(), nil)
   697  
   698  			bytes, err := backend.GetProtocolStateSnapshotByBlockID(context.Background(), block.ID())
   699  			suite.Require().Error(err)
   700  			suite.Require().Empty(bytes)
   701  			suite.Require().Equal(status.Errorf(codes.InvalidArgument, "failed to retrieve snapshot for block, try again with different block: %v",
   702  				snapshots.ErrSnapshotPhaseMismatch).Error(),
   703  				err.Error())
   704  		})
   705  	})
   706  }
   707  
   708  // TestGetProtocolStateSnapshotByHeight tests our GetProtocolStateSnapshotByHeight RPC endpoint
   709  // where non finalized block is added to state
   710  func (suite *Suite) TestGetProtocolStateSnapshotByHeight() {
   711  	identities := unittest.CompleteIdentitySet()
   712  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   713  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   714  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   715  		// build epoch 1
   716  		// Blocks in current State
   717  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   718  		epochBuilder.
   719  			BuildEpoch().
   720  			CompleteEpoch()
   721  
   722  		// get heights of each phase in built epochs
   723  		epoch1, ok := epochBuilder.EpochHeights(1)
   724  		require.True(suite.T(), ok)
   725  
   726  		// setup AtHeight mock returns for State
   727  		for _, height := range epoch1.Range() {
   728  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   729  		}
   730  
   731  		// Take snapshot at height of block D (epoch1.heights[2]) for valid segment and valid snapshot
   732  		snap := state.AtHeight(epoch1.Range()[2])
   733  
   734  		params := suite.defaultBackendParams()
   735  		params.MaxHeightRange = TEST_MAX_HEIGHT
   736  
   737  		backend, err := New(params)
   738  		suite.Require().NoError(err)
   739  
   740  		// query the handler for the latest finalized snapshot
   741  		bytes, err := backend.GetProtocolStateSnapshotByHeight(context.Background(), epoch1.Range()[2])
   742  		suite.Require().NoError(err)
   743  
   744  		// we expect the endpoint to return the snapshot at the same height we requested
   745  		expectedSnapshotBytes, err := convert.SnapshotToBytes(snap)
   746  		suite.Require().NoError(err)
   747  		suite.Require().Equal(expectedSnapshotBytes, bytes)
   748  	})
   749  }
   750  
   751  // TestGetProtocolStateSnapshotByHeight_NonFinalizedBlocks tests our GetProtocolStateSnapshotByHeight RPC endpoint
   752  // where non finalized block is added to state
   753  func (suite *Suite) TestGetProtocolStateSnapshotByHeight_NonFinalizedBlocks() {
   754  	identities := unittest.CompleteIdentitySet()
   755  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   756  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   757  		rootBlock, err := rootSnapshot.Head()
   758  		suite.Require().NoError(err)
   759  		// create a new block with root block as parent
   760  		newBlock := unittest.BlockWithParentFixture(rootBlock)
   761  		ctx := context.Background()
   762  		// add new block to the chain state
   763  		err = state.Extend(ctx, newBlock)
   764  		suite.Require().NoError(err)
   765  
   766  		// since block was not yet finalized AtHeight must return an invalid snapshot
   767  		suite.state.On("AtHeight", newBlock.Header.Height).Return(invalid.NewSnapshot(realstate.ErrUnknownSnapshotReference))
   768  
   769  		params := suite.defaultBackendParams()
   770  		params.MaxHeightRange = TEST_MAX_HEIGHT
   771  
   772  		backend, err := New(params)
   773  		suite.Require().NoError(err)
   774  
   775  		// query the handler for the snapshot for non finalized block
   776  		bytes, err := backend.GetProtocolStateSnapshotByHeight(context.Background(), newBlock.Header.Height)
   777  
   778  		suite.Require().Nil(bytes)
   779  		suite.Require().Error(err)
   780  		suite.Require().Equal(status.Errorf(codes.NotFound, "failed to find snapshot: %v",
   781  			realstate.ErrUnknownSnapshotReference).Error(),
   782  			err.Error())
   783  	})
   784  }
   785  
   786  // TestGetProtocolStateSnapshotByHeight_InvalidSegment tests our GetProtocolStateSnapshotByHeight RPC endpoint
   787  // for invalid segment
   788  func (suite *Suite) TestGetProtocolStateSnapshotByHeight_InvalidSegment() {
   789  	identities := unittest.CompleteIdentitySet()
   790  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   791  	util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   792  		epochBuilder := unittest.NewEpochBuilder(suite.T(), state)
   793  		// build epoch 1
   794  		// Blocks in current State
   795  		// P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit|
   796  		epochBuilder.
   797  			BuildEpoch().
   798  			CompleteEpoch()
   799  
   800  		// get heights of each phase in built epochs
   801  		epoch1, ok := epochBuilder.EpochHeights(1)
   802  		require.True(suite.T(), ok)
   803  
   804  		// setup AtHeight mock returns for State
   805  		for _, height := range epoch1.Range() {
   806  			suite.state.On("AtHeight", height).Return(state.AtHeight(height))
   807  		}
   808  
   809  		backend, err := New(suite.defaultBackendParams())
   810  		suite.Require().NoError(err)
   811  
   812  		// query the handler for the snapshot with invalid segment
   813  		bytes, err := backend.GetProtocolStateSnapshotByHeight(context.Background(), epoch1.SetupRange()[0])
   814  
   815  		suite.Require().Nil(bytes)
   816  		suite.Require().Error(err)
   817  		suite.Require().Equal(status.Errorf(codes.InvalidArgument, "failed to retrieve snapshot for block, try "+
   818  			"again with different block: %v",
   819  			snapshots.ErrSnapshotPhaseMismatch).Error(),
   820  			err.Error())
   821  	})
   822  }
   823  
   824  func (suite *Suite) TestGetLatestSealedBlockHeader() {
   825  	// setup the mocks
   826  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   827  	suite.state.On("Sealed").Return(suite.snapshot, nil)
   828  
   829  	params := suite.defaultBackendParams()
   830  
   831  	backend, err := New(params)
   832  	suite.Require().NoError(err)
   833  
   834  	suite.Run("GetLatestSealedBlockHeader - happy path", func() {
   835  		block := unittest.BlockHeaderFixture()
   836  		suite.snapshot.On("Head").Return(block, nil).Once()
   837  		suite.snapshot.On("Head").Return(block, nil).Once()
   838  
   839  		// query the handler for the latest sealed block
   840  		header, stat, err := backend.GetLatestBlockHeader(context.Background(), true)
   841  		suite.checkResponse(header, err)
   842  
   843  		// make sure we got the latest sealed block
   844  		suite.Require().Equal(block.ID(), header.ID())
   845  		suite.Require().Equal(block.Height, header.Height)
   846  		suite.Require().Equal(block.ParentID, header.ParentID)
   847  		suite.Require().Equal(stat, flow.BlockStatusSealed)
   848  
   849  		suite.assertAllExpectations()
   850  	})
   851  
   852  	// tests that signaler context received error when node state is inconsistent
   853  	suite.Run("GetLatestSealedBlockHeader - fails with inconsistent node's state", func() {
   854  		err := fmt.Errorf("inconsistent node's state")
   855  		suite.snapshot.On("Head").Return(nil, err)
   856  
   857  		// mock signaler context expect an error
   858  		signCtxErr := irrecoverable.NewExceptionf("failed to lookup sealed header: %w", err)
   859  		signalerCtx := irrecoverable.WithSignalerContext(context.Background(),
   860  			irrecoverable.NewMockSignalerContextExpectError(suite.T(), context.Background(), signCtxErr))
   861  
   862  		actualHeader, actualStatus, err := backend.GetLatestBlockHeader(signalerCtx, true)
   863  		suite.Require().Error(err)
   864  		suite.Require().Nil(actualHeader)
   865  		suite.Require().Equal(flow.BlockStatusUnknown, actualStatus)
   866  	})
   867  }
   868  
   869  func (suite *Suite) TestGetTransaction() {
   870  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   871  
   872  	transaction := unittest.TransactionFixture()
   873  	expected := transaction.TransactionBody
   874  
   875  	suite.transactions.
   876  		On("ByID", transaction.ID()).
   877  		Return(&expected, nil).
   878  		Once()
   879  
   880  	params := suite.defaultBackendParams()
   881  
   882  	backend, err := New(params)
   883  	suite.Require().NoError(err)
   884  
   885  	actual, err := backend.GetTransaction(context.Background(), transaction.ID())
   886  	suite.checkResponse(actual, err)
   887  
   888  	suite.Require().Equal(expected, *actual)
   889  
   890  	suite.assertAllExpectations()
   891  }
   892  
   893  func (suite *Suite) TestGetCollection() {
   894  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   895  
   896  	expected := unittest.CollectionFixture(1).Light()
   897  
   898  	suite.collections.
   899  		On("LightByID", expected.ID()).
   900  		Return(&expected, nil).
   901  		Once()
   902  
   903  	params := suite.defaultBackendParams()
   904  
   905  	backend, err := New(params)
   906  	suite.Require().NoError(err)
   907  
   908  	actual, err := backend.GetCollectionByID(context.Background(), expected.ID())
   909  	suite.transactions.AssertExpectations(suite.T())
   910  	suite.checkResponse(actual, err)
   911  
   912  	suite.Equal(expected, *actual)
   913  	suite.assertAllExpectations()
   914  }
   915  
   916  // TestGetTransactionResultByIndex tests that the request is forwarded to EN
   917  func (suite *Suite) TestGetTransactionResultByIndex() {
   918  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   919  
   920  	ctx := context.Background()
   921  	block := unittest.BlockFixture()
   922  	blockId := block.ID()
   923  	index := uint32(0)
   924  
   925  	// block storage returns the corresponding block
   926  	suite.blocks.
   927  		On("ByID", blockId).
   928  		Return(&block, nil)
   929  
   930  	_, fixedENIDs := suite.setupReceipts(&block)
   931  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
   932  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
   933  
   934  	// create a mock connection factory
   935  	connFactory := connectionmock.NewConnectionFactory(suite.T())
   936  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
   937  
   938  	exeEventReq := &execproto.GetTransactionByIndexRequest{
   939  		BlockId: blockId[:],
   940  		Index:   index,
   941  	}
   942  
   943  	exeEventResp := &execproto.GetTransactionResultResponse{
   944  		Events: nil,
   945  	}
   946  
   947  	params := suite.defaultBackendParams()
   948  	// the connection factory should be used to get the execution node client
   949  	params.ConnFactory = connFactory
   950  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
   951  
   952  	backend, err := New(params)
   953  	suite.Require().NoError(err)
   954  
   955  	suite.execClient.
   956  		On("GetTransactionResultByIndex", mock.Anything, exeEventReq).
   957  		Return(exeEventResp, nil)
   958  
   959  	suite.Run("TestGetTransactionResultByIndex - happy path", func() {
   960  		suite.snapshot.On("Head").Return(block.Header, nil).Once()
   961  		result, err := backend.GetTransactionResultByIndex(ctx, blockId, index, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
   962  		suite.checkResponse(result, err)
   963  		suite.Assert().Equal(result.BlockHeight, block.Header.Height)
   964  
   965  		suite.assertAllExpectations()
   966  	})
   967  
   968  	// tests that signaler context received error when node state is inconsistent
   969  	suite.Run("TestGetTransactionResultByIndex - fails with inconsistent node's state", func() {
   970  		err := fmt.Errorf("inconsistent node's state")
   971  		suite.snapshot.On("Head").Return(nil, err).Once()
   972  
   973  		// mock signaler context expect an error
   974  		signCtxErr := irrecoverable.NewExceptionf("failed to lookup sealed header: %w", err)
   975  		signalerCtx := irrecoverable.WithSignalerContext(context.Background(),
   976  			irrecoverable.NewMockSignalerContextExpectError(suite.T(), context.Background(), signCtxErr))
   977  
   978  		actual, err := backend.GetTransactionResultByIndex(signalerCtx, blockId, index, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
   979  		suite.Require().Error(err)
   980  		suite.Require().Nil(actual)
   981  	})
   982  }
   983  
   984  func (suite *Suite) TestGetTransactionResultsByBlockID() {
   985  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   986  
   987  	ctx := context.Background()
   988  	params := suite.defaultBackendParams()
   989  
   990  	block := unittest.BlockFixture()
   991  	sporkRootBlockHeight, err := suite.state.Params().SporkRootBlockHeight()
   992  	suite.Require().NoError(err)
   993  	block.Header.Height = sporkRootBlockHeight + 1
   994  	blockId := block.ID()
   995  
   996  	// block storage returns the corresponding block
   997  	suite.blocks.
   998  		On("ByID", blockId).
   999  		Return(&block, nil)
  1000  
  1001  	_, fixedENIDs := suite.setupReceipts(&block)
  1002  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1003  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
  1004  
  1005  	// create a mock connection factory
  1006  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  1007  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  1008  
  1009  	exeEventReq := &execproto.GetTransactionsByBlockIDRequest{
  1010  		BlockId: blockId[:],
  1011  	}
  1012  
  1013  	exeEventResp := &execproto.GetTransactionResultsResponse{
  1014  		TransactionResults: []*execproto.GetTransactionResultResponse{{}},
  1015  	}
  1016  
  1017  	// the connection factory should be used to get the execution node client
  1018  	params.ConnFactory = connFactory
  1019  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
  1020  
  1021  	backend, err := New(params)
  1022  	suite.Require().NoError(err)
  1023  
  1024  	suite.execClient.
  1025  		On("GetTransactionResultsByBlockID", mock.Anything, exeEventReq).
  1026  		Return(exeEventResp, nil)
  1027  
  1028  	suite.Run("GetTransactionResultsByBlockID - happy path", func() {
  1029  		suite.snapshot.On("Head").Return(block.Header, nil).Once()
  1030  
  1031  		result, err := backend.GetTransactionResultsByBlockID(ctx, blockId, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1032  		suite.checkResponse(result, err)
  1033  
  1034  		suite.assertAllExpectations()
  1035  	})
  1036  
  1037  	//tests that signaler context received error when node state is inconsistent
  1038  	suite.Run("GetTransactionResultsByBlockID - fails with inconsistent node's state", func() {
  1039  		err := fmt.Errorf("inconsistent node's state")
  1040  		suite.snapshot.On("Head").Return(nil, err).Once()
  1041  
  1042  		// mock signaler context expect an error
  1043  		signCtxErr := irrecoverable.NewExceptionf("failed to lookup sealed header: %w", err)
  1044  		signalerCtx := irrecoverable.WithSignalerContext(context.Background(),
  1045  			irrecoverable.NewMockSignalerContextExpectError(suite.T(), context.Background(), signCtxErr))
  1046  
  1047  		actual, err := backend.GetTransactionResultsByBlockID(signalerCtx, blockId, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1048  		suite.Require().Error(err)
  1049  		suite.Require().Nil(actual)
  1050  	})
  1051  }
  1052  
  1053  // TestTransactionStatusTransition tests that the status of transaction changes from Finalized to Sealed
  1054  // when the protocol State is updated
  1055  func (suite *Suite) TestTransactionStatusTransition() {
  1056  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1057  
  1058  	ctx := context.Background()
  1059  	collection := unittest.CollectionFixture(1)
  1060  	transactionBody := collection.Transactions[0]
  1061  	block := unittest.BlockFixture()
  1062  	block.Header.Height = 2
  1063  	headBlock := unittest.BlockFixture()
  1064  	headBlock.Header.Height = block.Header.Height - 1 // head is behind the current block
  1065  	block.SetPayload(
  1066  		unittest.PayloadFixture(
  1067  			unittest.WithGuarantees(
  1068  				unittest.CollectionGuaranteesWithCollectionIDFixture([]*flow.Collection{&collection})...)))
  1069  
  1070  	suite.snapshot.
  1071  		On("Head").
  1072  		Return(headBlock.Header, nil)
  1073  
  1074  	light := collection.Light()
  1075  	suite.collections.On("LightByID", light.ID()).Return(&light, nil)
  1076  
  1077  	// transaction storage returns the corresponding transaction
  1078  	suite.transactions.
  1079  		On("ByID", transactionBody.ID()).
  1080  		Return(transactionBody, nil)
  1081  
  1082  	// collection storage returns the corresponding collection
  1083  	suite.collections.
  1084  		On("LightByTransactionID", transactionBody.ID()).
  1085  		Return(&light, nil)
  1086  
  1087  	// block storage returns the corresponding block
  1088  	suite.blocks.
  1089  		On("ByCollectionID", collection.ID()).
  1090  		Return(&block, nil)
  1091  
  1092  	txID := transactionBody.ID()
  1093  	blockID := block.ID()
  1094  	_, fixedENIDs := suite.setupReceipts(&block)
  1095  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1096  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
  1097  
  1098  	// create a mock connection factory
  1099  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  1100  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  1101  
  1102  	exeEventReq := &execproto.GetTransactionResultRequest{
  1103  		BlockId:       blockID[:],
  1104  		TransactionId: txID[:],
  1105  	}
  1106  
  1107  	exeEventResp := &execproto.GetTransactionResultResponse{
  1108  		Events: nil,
  1109  	}
  1110  
  1111  	params := suite.defaultBackendParams()
  1112  	// the connection factory should be used to get the execution node client
  1113  	params.ConnFactory = connFactory
  1114  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
  1115  
  1116  	backend, err := New(params)
  1117  	suite.Require().NoError(err)
  1118  
  1119  	// Successfully return empty event list
  1120  	suite.execClient.
  1121  		On("GetTransactionResult", ctx, exeEventReq).
  1122  		Return(exeEventResp, status.Errorf(codes.NotFound, "not found")).
  1123  		Times(len(fixedENIDs)) // should call each EN once
  1124  
  1125  	// first call - when block under test is greater height than the sealed head, but execution node does not know about Tx
  1126  	result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1127  	suite.checkResponse(result, err)
  1128  
  1129  	// status should be finalized since the sealed Blocks is smaller in height
  1130  	suite.Assert().Equal(flow.TransactionStatusFinalized, result.Status)
  1131  
  1132  	// block ID should be included in the response
  1133  	suite.Assert().Equal(blockID, result.BlockID)
  1134  
  1135  	// Successfully return empty event list from here on
  1136  	suite.execClient.
  1137  		On("GetTransactionResult", ctx, exeEventReq).
  1138  		Return(exeEventResp, nil)
  1139  
  1140  	// second call - when block under test's height is greater height than the sealed head
  1141  	result, err = backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1142  	suite.checkResponse(result, err)
  1143  
  1144  	// status should be executed since no `NotFound` error in the `GetTransactionResult` call
  1145  	suite.Assert().Equal(flow.TransactionStatusExecuted, result.Status)
  1146  
  1147  	// now let the head block be finalized
  1148  	headBlock.Header.Height = block.Header.Height + 1
  1149  
  1150  	// third call - when block under test's height is less than sealed head's height
  1151  	result, err = backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1152  	suite.checkResponse(result, err)
  1153  
  1154  	// status should be sealed since the sealed Blocks is greater in height
  1155  	suite.Assert().Equal(flow.TransactionStatusSealed, result.Status)
  1156  
  1157  	// now go far into the future
  1158  	headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry + 1
  1159  
  1160  	// fourth call - when block under test's height so much less than the head's height that it's considered expired,
  1161  	// but since there is a execution result, means it should retain it's sealed status
  1162  	result, err = backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1163  	suite.checkResponse(result, err)
  1164  
  1165  	// status should be expired since
  1166  	suite.Assert().Equal(flow.TransactionStatusSealed, result.Status)
  1167  
  1168  	suite.assertAllExpectations()
  1169  }
  1170  
  1171  // TestTransactionExpiredStatusTransition tests that the status
  1172  // of transaction changes from Pending to Expired when enough Blocks pass
  1173  func (suite *Suite) TestTransactionExpiredStatusTransition() {
  1174  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1175  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1176  
  1177  	ctx := context.Background()
  1178  	collection := unittest.CollectionFixture(1)
  1179  	transactionBody := collection.Transactions[0]
  1180  	block := unittest.BlockFixture()
  1181  	block.Header.Height = 2
  1182  	transactionBody.SetReferenceBlockID(block.ID())
  1183  
  1184  	headBlock := unittest.BlockFixture()
  1185  	headBlock.Header.Height = block.Header.Height - 1 // head is behind the current block
  1186  
  1187  	// set up GetLastFullBlockHeight mock
  1188  	fullHeight := headBlock.Header.Height
  1189  	suite.blocks.On("GetLastFullBlockHeight").Return(
  1190  		func() uint64 { return fullHeight },
  1191  		func() error { return nil },
  1192  	)
  1193  
  1194  	suite.snapshot.
  1195  		On("Head").
  1196  		Return(headBlock.Header, nil)
  1197  
  1198  	snapshotAtBlock := new(protocol.Snapshot)
  1199  	snapshotAtBlock.On("Head").Return(block.Header, nil)
  1200  
  1201  	suite.state.
  1202  		On("AtBlockID", block.ID()).
  1203  		Return(snapshotAtBlock, nil)
  1204  
  1205  	// transaction storage returns the corresponding transaction
  1206  	suite.transactions.
  1207  		On("ByID", transactionBody.ID()).
  1208  		Return(transactionBody, nil)
  1209  
  1210  	// collection storage returns a not found error
  1211  	suite.collections.
  1212  		On("LightByTransactionID", transactionBody.ID()).
  1213  		Return(nil, storage.ErrNotFound)
  1214  
  1215  	txID := transactionBody.ID()
  1216  
  1217  	params := suite.defaultBackendParams()
  1218  
  1219  	backend, err := New(params)
  1220  	suite.Require().NoError(err)
  1221  
  1222  	// should return pending status when we have not observed an expiry block
  1223  	suite.Run("pending", func() {
  1224  		// referenced block isn't known yet, so should return pending status
  1225  		result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1226  		suite.checkResponse(result, err)
  1227  
  1228  		suite.Assert().Equal(flow.TransactionStatusPending, result.Status)
  1229  	})
  1230  
  1231  	// should return pending status when we have observed an expiry block but
  1232  	// have not observed all intermediary Collections
  1233  	suite.Run("expiry un-confirmed", func() {
  1234  
  1235  		suite.Run("ONLY finalized expiry block", func() {
  1236  			// we have finalized an expiry block
  1237  			headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry + 1
  1238  			// we have NOT observed all intermediary Collections
  1239  			fullHeight = block.Header.Height + flow.DefaultTransactionExpiry/2
  1240  
  1241  			result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1242  			suite.checkResponse(result, err)
  1243  			suite.Assert().Equal(flow.TransactionStatusPending, result.Status)
  1244  		})
  1245  		suite.Run("ONLY observed intermediary Collections", func() {
  1246  			// we have NOT finalized an expiry block
  1247  			headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry/2
  1248  			// we have observed all intermediary Collections
  1249  			fullHeight = block.Header.Height + flow.DefaultTransactionExpiry + 1
  1250  
  1251  			result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1252  			suite.checkResponse(result, err)
  1253  			suite.Assert().Equal(flow.TransactionStatusPending, result.Status)
  1254  		})
  1255  
  1256  	})
  1257  
  1258  	// should return expired status only when we have observed an expiry block
  1259  	// and have observed all intermediary Collections
  1260  	suite.Run("expired", func() {
  1261  		// we have finalized an expiry block
  1262  		headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry + 1
  1263  		// we have observed all intermediary Collections
  1264  		fullHeight = block.Header.Height + flow.DefaultTransactionExpiry + 1
  1265  
  1266  		result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1267  		suite.checkResponse(result, err)
  1268  		suite.Assert().Equal(flow.TransactionStatusExpired, result.Status)
  1269  	})
  1270  
  1271  	suite.assertAllExpectations()
  1272  }
  1273  
  1274  // TestTransactionPendingToFinalizedStatusTransition tests that the status of transaction changes from Finalized to Expired
  1275  func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() {
  1276  
  1277  	ctx := context.Background()
  1278  	collection := unittest.CollectionFixture(1)
  1279  	transactionBody := collection.Transactions[0]
  1280  	// block which will eventually contain the transaction
  1281  	block := unittest.BlockFixture()
  1282  	block.SetPayload(
  1283  		unittest.PayloadFixture(
  1284  			unittest.WithGuarantees(
  1285  				unittest.CollectionGuaranteesWithCollectionIDFixture([]*flow.Collection{&collection})...)))
  1286  	blockID := block.ID()
  1287  
  1288  	// reference block to which the transaction points to
  1289  	refBlock := unittest.BlockFixture()
  1290  	refBlockID := refBlock.ID()
  1291  	refBlock.Header.Height = 2
  1292  	transactionBody.SetReferenceBlockID(refBlockID)
  1293  	txID := transactionBody.ID()
  1294  
  1295  	headBlock := unittest.BlockFixture()
  1296  	headBlock.Header.Height = refBlock.Header.Height - 1 // head is behind the current refBlock
  1297  
  1298  	suite.snapshot.
  1299  		On("Head").
  1300  		Return(headBlock.Header, nil)
  1301  
  1302  	snapshotAtBlock := new(protocol.Snapshot)
  1303  	snapshotAtBlock.On("Head").Return(refBlock.Header, nil)
  1304  
  1305  	_, enIDs := suite.setupReceipts(&block)
  1306  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1307  
  1308  	suite.snapshot.On("Identities", mock.Anything).Return(enIDs, nil)
  1309  
  1310  	suite.state.
  1311  		On("AtBlockID", refBlockID).
  1312  		Return(snapshotAtBlock, nil)
  1313  
  1314  	suite.state.On("Final").Return(snapshotAtBlock, nil).Maybe()
  1315  
  1316  	// transaction storage returns the corresponding transaction
  1317  	suite.transactions.
  1318  		On("ByID", txID).
  1319  		Return(transactionBody, nil)
  1320  
  1321  	currentState := flow.TransactionStatusPending // marker for the current State
  1322  	// collection storage returns a not found error if tx is pending, else it returns the collection light reference
  1323  	suite.collections.
  1324  		On("LightByTransactionID", txID).
  1325  		Return(func(txID flow.Identifier) *flow.LightCollection {
  1326  			if currentState == flow.TransactionStatusPending {
  1327  				return nil
  1328  			}
  1329  			collLight := collection.Light()
  1330  			return &collLight
  1331  		},
  1332  			func(txID flow.Identifier) error {
  1333  				if currentState == flow.TransactionStatusPending {
  1334  					return storage.ErrNotFound
  1335  				}
  1336  				return nil
  1337  			})
  1338  
  1339  	light := collection.Light()
  1340  	suite.collections.On("LightByID", mock.Anything).Return(&light, nil)
  1341  
  1342  	// refBlock storage returns the corresponding refBlock
  1343  	suite.blocks.
  1344  		On("ByCollectionID", collection.ID()).
  1345  		Return(&block, nil)
  1346  
  1347  	receipts, _ := suite.setupReceipts(&block)
  1348  
  1349  	exeEventReq := &execproto.GetTransactionResultRequest{
  1350  		BlockId:       blockID[:],
  1351  		TransactionId: txID[:],
  1352  	}
  1353  
  1354  	exeEventResp := &execproto.GetTransactionResultResponse{
  1355  		Events: nil,
  1356  	}
  1357  
  1358  	// simulate that the execution node has not yet executed the transaction
  1359  	suite.execClient.
  1360  		On("GetTransactionResult", ctx, exeEventReq).
  1361  		Return(exeEventResp, status.Errorf(codes.NotFound, "not found")).
  1362  		Times(len(enIDs)) // should call each EN once
  1363  
  1364  	// create a mock connection factory
  1365  	connFactory := suite.setupConnectionFactory()
  1366  
  1367  	params := suite.defaultBackendParams()
  1368  	params.ConnFactory = connFactory
  1369  	params.MaxHeightRange = TEST_MAX_HEIGHT
  1370  
  1371  	backend, err := New(params)
  1372  	suite.Require().NoError(err)
  1373  
  1374  	preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID}
  1375  
  1376  	// should return pending status when we have not observed collection for the transaction
  1377  	suite.Run("pending", func() {
  1378  		currentState = flow.TransactionStatusPending
  1379  		result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1380  		suite.checkResponse(result, err)
  1381  		suite.Assert().Equal(flow.TransactionStatusPending, result.Status)
  1382  		// assert that no call to an execution node is made
  1383  		suite.execClient.AssertNotCalled(suite.T(), "GetTransactionResult", mock.Anything, mock.Anything)
  1384  	})
  1385  
  1386  	// should return finalized status when we have observed collection for the transaction (after observing the
  1387  	// preceding sealed refBlock)
  1388  	suite.Run("finalized", func() {
  1389  		currentState = flow.TransactionStatusFinalized
  1390  		result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1391  		suite.checkResponse(result, err)
  1392  		suite.Assert().Equal(flow.TransactionStatusFinalized, result.Status)
  1393  	})
  1394  
  1395  	suite.assertAllExpectations()
  1396  }
  1397  
  1398  // TestTransactionResultUnknown tests that the status of transaction is reported as unknown when it is not found in the
  1399  // local storage
  1400  func (suite *Suite) TestTransactionResultUnknown() {
  1401  	ctx := context.Background()
  1402  	txID := unittest.IdentifierFixture()
  1403  
  1404  	// transaction storage returns an error
  1405  	suite.transactions.
  1406  		On("ByID", txID).
  1407  		Return(nil, storage.ErrNotFound)
  1408  
  1409  	params := suite.defaultBackendParams()
  1410  
  1411  	backend, err := New(params)
  1412  	suite.Require().NoError(err)
  1413  
  1414  	// first call - when block under test is greater height than the sealed head, but execution node does not know about Tx
  1415  	result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  1416  	suite.checkResponse(result, err)
  1417  
  1418  	// status should be reported as unknown
  1419  	suite.Assert().Equal(flow.TransactionStatusUnknown, result.Status)
  1420  }
  1421  
  1422  func (suite *Suite) TestGetLatestFinalizedBlock() {
  1423  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1424  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1425  
  1426  	params := suite.defaultBackendParams()
  1427  
  1428  	backend, err := New(params)
  1429  	suite.Require().NoError(err)
  1430  
  1431  	suite.Run("GetLatestFinalizedBlock - happy path", func() {
  1432  		// setup the mocks
  1433  		expected := unittest.BlockFixture()
  1434  		header := expected.Header
  1435  
  1436  		suite.snapshot.
  1437  			On("Head").
  1438  			Return(header, nil).Once()
  1439  
  1440  		headerClone := *header
  1441  		headerClone.Height = 0
  1442  
  1443  		suite.snapshot.
  1444  			On("Head").
  1445  			Return(&headerClone, nil).
  1446  			Once()
  1447  
  1448  		suite.blocks.
  1449  			On("ByHeight", header.Height).
  1450  			Return(&expected, nil)
  1451  
  1452  		// query the handler for the latest finalized header
  1453  		actual, stat, err := backend.GetLatestBlock(context.Background(), false)
  1454  		suite.checkResponse(actual, err)
  1455  
  1456  		// make sure we got the latest header
  1457  		suite.Require().Equal(expected, *actual)
  1458  		suite.Assert().Equal(stat, flow.BlockStatusFinalized)
  1459  
  1460  		suite.assertAllExpectations()
  1461  	})
  1462  
  1463  	// tests that signaler context received error when node state is inconsistent
  1464  	suite.Run("GetLatestFinalizedBlock - fails with inconsistent node's state", func() {
  1465  		err := fmt.Errorf("inconsistent node's state")
  1466  		suite.snapshot.On("Head").Return(nil, err)
  1467  
  1468  		// mock signaler context expect an error
  1469  		signCtxErr := irrecoverable.NewExceptionf("failed to lookup final header: %w", err)
  1470  		signalerCtx := irrecoverable.WithSignalerContext(context.Background(),
  1471  			irrecoverable.NewMockSignalerContextExpectError(suite.T(), context.Background(), signCtxErr))
  1472  
  1473  		actualBlock, actualStatus, err := backend.GetLatestBlock(signalerCtx, false)
  1474  		suite.Require().Error(err)
  1475  		suite.Require().Nil(actualBlock)
  1476  		suite.Require().Equal(flow.BlockStatusUnknown, actualStatus)
  1477  	})
  1478  }
  1479  
  1480  type mockCloser struct{}
  1481  
  1482  func (mc *mockCloser) Close() error { return nil }
  1483  
  1484  func (suite *Suite) TestGetExecutionResultByID() {
  1485  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1486  
  1487  	validExecutorIdentities := flow.IdentityList{}
  1488  	validENIDs := flow.IdentifierList(validExecutorIdentities.NodeIDs())
  1489  
  1490  	// create a mock connection factory
  1491  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  1492  
  1493  	nonexistingID := unittest.IdentifierFixture()
  1494  	blockID := unittest.IdentifierFixture()
  1495  	executionResult := unittest.ExecutionResultFixture(
  1496  		unittest.WithExecutionResultBlockID(blockID))
  1497  
  1498  	ctx := context.Background()
  1499  
  1500  	results := new(storagemock.ExecutionResults)
  1501  	results.
  1502  		On("ByID", nonexistingID).
  1503  		Return(nil, storage.ErrNotFound)
  1504  
  1505  	results.
  1506  		On("ByID", executionResult.ID()).
  1507  		Return(executionResult, nil)
  1508  
  1509  	suite.Run("nonexisting execution result for id", func() {
  1510  		params := suite.defaultBackendParams()
  1511  		params.ExecutionResults = results
  1512  		params.ConnFactory = connFactory
  1513  		params.FixedExecutionNodeIDs = validENIDs.Strings()
  1514  
  1515  		backend, err := New(params)
  1516  		suite.Require().NoError(err)
  1517  
  1518  		// execute request
  1519  		_, err = backend.GetExecutionResultByID(ctx, nonexistingID)
  1520  
  1521  		assert.Error(suite.T(), err)
  1522  	})
  1523  
  1524  	suite.Run("existing execution result id", func() {
  1525  		params := suite.defaultBackendParams()
  1526  		params.ExecutionResults = results
  1527  		params.ConnFactory = connFactory
  1528  		params.FixedExecutionNodeIDs = validENIDs.Strings()
  1529  
  1530  		backend, err := New(params)
  1531  		suite.Require().NoError(err)
  1532  
  1533  		// execute request
  1534  		er, err := backend.GetExecutionResultByID(ctx, executionResult.ID())
  1535  		suite.checkResponse(er, err)
  1536  
  1537  		require.Equal(suite.T(), executionResult, er)
  1538  	})
  1539  
  1540  	results.AssertExpectations(suite.T())
  1541  	suite.assertAllExpectations()
  1542  }
  1543  
  1544  func (suite *Suite) TestGetExecutionResultByBlockID() {
  1545  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1546  
  1547  	validExecutorIdentities := flow.IdentityList{}
  1548  	validENIDs := flow.IdentifierList(validExecutorIdentities.NodeIDs())
  1549  
  1550  	// create a mock connection factory
  1551  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  1552  
  1553  	blockID := unittest.IdentifierFixture()
  1554  	executionResult := unittest.ExecutionResultFixture(
  1555  		unittest.WithExecutionResultBlockID(blockID),
  1556  		unittest.WithServiceEvents(2))
  1557  
  1558  	ctx := context.Background()
  1559  
  1560  	nonexistingBlockID := unittest.IdentifierFixture()
  1561  
  1562  	results := new(storagemock.ExecutionResults)
  1563  	results.
  1564  		On("ByBlockID", nonexistingBlockID).
  1565  		Return(nil, storage.ErrNotFound)
  1566  
  1567  	results.
  1568  		On("ByBlockID", blockID).
  1569  		Return(executionResult, nil)
  1570  
  1571  	suite.Run("nonexisting execution results", func() {
  1572  		params := suite.defaultBackendParams()
  1573  		params.ExecutionResults = results
  1574  		params.ConnFactory = connFactory
  1575  		params.FixedExecutionNodeIDs = validENIDs.Strings()
  1576  
  1577  		backend, err := New(params)
  1578  		suite.Require().NoError(err)
  1579  
  1580  		// execute request
  1581  		_, err = backend.GetExecutionResultForBlockID(ctx, nonexistingBlockID)
  1582  
  1583  		assert.Error(suite.T(), err)
  1584  	})
  1585  
  1586  	suite.Run("existing execution results", func() {
  1587  		params := suite.defaultBackendParams()
  1588  		params.ExecutionResults = results
  1589  		params.ConnFactory = connFactory
  1590  		params.FixedExecutionNodeIDs = validENIDs.Strings()
  1591  
  1592  		backend, err := New(params)
  1593  		suite.Require().NoError(err)
  1594  
  1595  		// execute request
  1596  		er, err := backend.GetExecutionResultForBlockID(ctx, blockID)
  1597  		suite.checkResponse(er, err)
  1598  
  1599  		require.Equal(suite.T(), executionResult, er)
  1600  	})
  1601  
  1602  	results.AssertExpectations(suite.T())
  1603  	suite.assertAllExpectations()
  1604  }
  1605  
  1606  func (suite *Suite) TestGetNodeVersionInfo() {
  1607  	sporkRootBlock := unittest.BlockHeaderFixture()
  1608  	nodeRootBlock := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(sporkRootBlock.Height + 100))
  1609  	sporkID := unittest.IdentifierFixture()
  1610  	protocolVersion := uint(1234)
  1611  
  1612  	suite.Run("happy path", func() {
  1613  		stateParams := protocol.NewParams(suite.T())
  1614  		stateParams.On("SporkID").Return(sporkID, nil)
  1615  		stateParams.On("ProtocolVersion").Return(protocolVersion, nil)
  1616  		stateParams.On("SporkRootBlockHeight").Return(sporkRootBlock.Height, nil)
  1617  		stateParams.On("SealedRoot").Return(nodeRootBlock, nil)
  1618  
  1619  		state := protocol.NewState(suite.T())
  1620  		state.On("Params").Return(stateParams, nil).Maybe()
  1621  
  1622  		expected := &accessflow.NodeVersionInfo{
  1623  			Semver:               build.Version(),
  1624  			Commit:               build.Commit(),
  1625  			SporkId:              sporkID,
  1626  			ProtocolVersion:      uint64(protocolVersion),
  1627  			SporkRootBlockHeight: sporkRootBlock.Height,
  1628  			NodeRootBlockHeight:  nodeRootBlock.Height,
  1629  		}
  1630  
  1631  		params := suite.defaultBackendParams()
  1632  		params.State = state
  1633  
  1634  		backend, err := New(params)
  1635  		suite.Require().NoError(err)
  1636  
  1637  		actual, err := backend.GetNodeVersionInfo(context.Background())
  1638  		suite.Require().NoError(err)
  1639  
  1640  		suite.Require().Equal(expected, actual)
  1641  	})
  1642  
  1643  	suite.Run("backend construct fails when SporkID lookup fails", func() {
  1644  		stateParams := protocol.NewParams(suite.T())
  1645  		stateParams.On("SporkID").Return(flow.ZeroID, fmt.Errorf("fail"))
  1646  
  1647  		state := protocol.NewState(suite.T())
  1648  		state.On("Params").Return(stateParams, nil).Maybe()
  1649  
  1650  		params := suite.defaultBackendParams()
  1651  		params.State = state
  1652  
  1653  		backend, err := New(params)
  1654  		suite.Require().Error(err)
  1655  		suite.Require().Nil(backend)
  1656  	})
  1657  
  1658  	suite.Run("backend construct fails when ProtocolVersion lookup fails", func() {
  1659  		stateParams := protocol.NewParams(suite.T())
  1660  		stateParams.On("SporkID").Return(sporkID, nil)
  1661  		stateParams.On("ProtocolVersion").Return(uint(0), fmt.Errorf("fail"))
  1662  
  1663  		state := protocol.NewState(suite.T())
  1664  		state.On("Params").Return(stateParams, nil).Maybe()
  1665  
  1666  		params := suite.defaultBackendParams()
  1667  		params.State = state
  1668  
  1669  		backend, err := New(params)
  1670  		suite.Require().Error(err)
  1671  		suite.Require().Nil(backend)
  1672  	})
  1673  
  1674  	suite.Run("backend construct fails when SporkRootBlockHeight lookup fails", func() {
  1675  		stateParams := protocol.NewParams(suite.T())
  1676  		stateParams.On("SporkID").Return(sporkID, nil)
  1677  		stateParams.On("ProtocolVersion").Return(protocolVersion, nil)
  1678  		stateParams.On("SporkRootBlockHeight").Return(uint64(0), fmt.Errorf("fail"))
  1679  
  1680  		state := protocol.NewState(suite.T())
  1681  		state.On("Params").Return(stateParams, nil).Maybe()
  1682  
  1683  		params := suite.defaultBackendParams()
  1684  		params.State = state
  1685  
  1686  		backend, err := New(params)
  1687  		suite.Require().Error(err)
  1688  		suite.Require().Nil(backend)
  1689  	})
  1690  
  1691  	suite.Run("backend construct fails when SealedRoot lookup fails", func() {
  1692  		stateParams := protocol.NewParams(suite.T())
  1693  		stateParams.On("SporkID").Return(sporkID, nil)
  1694  		stateParams.On("ProtocolVersion").Return(protocolVersion, nil)
  1695  		stateParams.On("SporkRootBlockHeight").Return(sporkRootBlock.Height, nil)
  1696  		stateParams.On("SealedRoot").Return(nil, fmt.Errorf("fail"))
  1697  
  1698  		state := protocol.NewState(suite.T())
  1699  		state.On("Params").Return(stateParams, nil).Maybe()
  1700  
  1701  		params := suite.defaultBackendParams()
  1702  		params.State = state
  1703  
  1704  		backend, err := New(params)
  1705  		suite.Require().Error(err)
  1706  		suite.Require().Nil(backend)
  1707  	})
  1708  }
  1709  
  1710  func (suite *Suite) TestGetNetworkParameters() {
  1711  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1712  
  1713  	expectedChainID := flow.Mainnet
  1714  
  1715  	params := suite.defaultBackendParams()
  1716  	params.ChainID = expectedChainID
  1717  
  1718  	backend, err := New(params)
  1719  	suite.Require().NoError(err)
  1720  
  1721  	actual := backend.GetNetworkParameters(context.Background())
  1722  
  1723  	suite.Require().Equal(expectedChainID, actual.ChainID)
  1724  }
  1725  
  1726  // TestExecutionNodesForBlockID tests the common method backend.executionNodesForBlockID used for serving all API calls
  1727  // that need to talk to an execution node.
  1728  func (suite *Suite) TestExecutionNodesForBlockID() {
  1729  
  1730  	totalReceipts := 5
  1731  
  1732  	block := unittest.BlockFixture()
  1733  
  1734  	// generate one execution node identities for each receipt assuming that each ER is generated by a unique exec node
  1735  	allExecutionNodes := unittest.IdentityListFixture(totalReceipts, unittest.WithRole(flow.RoleExecution))
  1736  
  1737  	// one execution result for all receipts for this block
  1738  	executionResult := unittest.ExecutionResultFixture()
  1739  
  1740  	// generate execution receipts
  1741  	receipts := make(flow.ExecutionReceiptList, totalReceipts)
  1742  	for j := 0; j < totalReceipts; j++ {
  1743  		r := unittest.ReceiptForBlockFixture(&block)
  1744  		r.ExecutorID = allExecutionNodes[j].NodeID
  1745  		er := *executionResult
  1746  		r.ExecutionResult = er
  1747  		receipts[j] = r
  1748  	}
  1749  
  1750  	currentAttempt := 0
  1751  	attempt1Receipts, attempt2Receipts, attempt3Receipts := receipts, receipts, receipts
  1752  
  1753  	// setup receipts storage mock to return different list of receipts on each call
  1754  	suite.receipts.
  1755  		On("ByBlockID", block.ID()).Return(
  1756  		func(id flow.Identifier) flow.ExecutionReceiptList {
  1757  			switch currentAttempt {
  1758  			case 0:
  1759  				currentAttempt++
  1760  				return attempt1Receipts
  1761  			case 1:
  1762  				currentAttempt++
  1763  				return attempt2Receipts
  1764  			default:
  1765  				currentAttempt = 0
  1766  				return attempt3Receipts
  1767  			}
  1768  		},
  1769  		func(id flow.Identifier) error { return nil })
  1770  
  1771  	suite.snapshot.On("Identities", mock.Anything).Return(
  1772  		func(filter flow.IdentityFilter) flow.IdentityList {
  1773  			// apply the filter passed in to the list of all the execution nodes
  1774  			return allExecutionNodes.Filter(filter)
  1775  		},
  1776  		func(flow.IdentityFilter) error { return nil })
  1777  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1778  
  1779  	testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) {
  1780  
  1781  		if preferredENs != nil {
  1782  			preferredENIdentifiers = preferredENs.NodeIDs()
  1783  		}
  1784  		if fixedENs != nil {
  1785  			fixedENIdentifiers = fixedENs.NodeIDs()
  1786  		}
  1787  
  1788  		if expectedENs == nil {
  1789  			expectedENs = flow.IdentityList{}
  1790  		}
  1791  
  1792  		allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log)
  1793  		require.NoError(suite.T(), err)
  1794  
  1795  		execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false}
  1796  		execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes)
  1797  		require.NoError(suite.T(), err)
  1798  
  1799  		actualList := flow.IdentityList{}
  1800  		for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() {
  1801  			actualList = append(actualList, actual)
  1802  		}
  1803  
  1804  		if len(expectedENs) > maxNodesCnt {
  1805  			for _, actual := range actualList {
  1806  				require.Contains(suite.T(), expectedENs, actual)
  1807  			}
  1808  		} else {
  1809  			require.ElementsMatch(suite.T(), actualList, expectedENs)
  1810  		}
  1811  	}
  1812  	// if we don't find sufficient receipts, executionNodesForBlockID should return a list of random ENs
  1813  	suite.Run("insufficient receipts return random ENs in State", func() {
  1814  		// return no receipts at all attempts
  1815  		attempt1Receipts = flow.ExecutionReceiptList{}
  1816  		attempt2Receipts = flow.ExecutionReceiptList{}
  1817  		attempt3Receipts = flow.ExecutionReceiptList{}
  1818  		suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot)
  1819  
  1820  		allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log)
  1821  		require.NoError(suite.T(), err)
  1822  
  1823  		execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false}
  1824  		execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes)
  1825  		require.NoError(suite.T(), err)
  1826  
  1827  		actualList := flow.IdentityList{}
  1828  		for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() {
  1829  			actualList = append(actualList, actual)
  1830  		}
  1831  
  1832  		require.Equal(suite.T(), len(actualList), maxNodesCnt)
  1833  	})
  1834  
  1835  	// if no preferred or fixed ENs are specified, the ExecutionNodesForBlockID function should
  1836  	// return the exe node list without a filter
  1837  	suite.Run("no preferred or fixed ENs", func() {
  1838  		testExecutionNodesForBlockID(nil, nil, allExecutionNodes)
  1839  	})
  1840  	// if only preferred ENs are specified, the ExecutionNodesForBlockID function should
  1841  	// return the preferred ENs list
  1842  	suite.Run("two preferred ENs with zero fixed EN", func() {
  1843  		// mark the first two ENs as preferred
  1844  		preferredENs := allExecutionNodes[0:2]
  1845  		expectedList := preferredENs
  1846  		testExecutionNodesForBlockID(preferredENs, nil, expectedList)
  1847  	})
  1848  	// if only fixed ENs are specified, the ExecutionNodesForBlockID function should
  1849  	// return the fixed ENs list
  1850  	suite.Run("two fixed ENs with zero preferred EN", func() {
  1851  		// mark the first two ENs as fixed
  1852  		fixedENs := allExecutionNodes[0:2]
  1853  		expectedList := fixedENs
  1854  		testExecutionNodesForBlockID(nil, fixedENs, expectedList)
  1855  	})
  1856  	// if both are specified, the ExecutionNodesForBlockID function should
  1857  	// return the preferred ENs list
  1858  	suite.Run("four fixed ENs of which two are preferred ENs", func() {
  1859  		// mark the first four ENs as fixed
  1860  		fixedENs := allExecutionNodes[0:5]
  1861  		// mark the first two of the fixed ENs as preferred ENs
  1862  		preferredENs := fixedENs[0:2]
  1863  		expectedList := preferredENs
  1864  		testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList)
  1865  	})
  1866  	// if both are specified, but the preferred ENs don't match the ExecutorIDs in the ER,
  1867  	// the ExecutionNodesForBlockID function should return the fixed ENs list
  1868  	suite.Run("four fixed ENs of which two are preferred ENs but have not generated the ER", func() {
  1869  		// mark the first two ENs as fixed
  1870  		fixedENs := allExecutionNodes[0:2]
  1871  		// specify two ENs not specified in the ERs as preferred
  1872  		preferredENs := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution))
  1873  		expectedList := fixedENs
  1874  		testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList)
  1875  	})
  1876  	// if execution receipts are not yet available, the ExecutionNodesForBlockID function should retry twice
  1877  	suite.Run("retry execution receipt query", func() {
  1878  		// on first attempt, no execution receipts are available
  1879  		attempt1Receipts = flow.ExecutionReceiptList{}
  1880  		// on second attempt ony one is available
  1881  		attempt2Receipts = flow.ExecutionReceiptList{receipts[0]}
  1882  		// on third attempt all receipts are available
  1883  		attempt3Receipts = receipts
  1884  		currentAttempt = 0
  1885  		// mark the first two ENs as preferred
  1886  		preferredENs := allExecutionNodes[0:2]
  1887  		expectedList := preferredENs
  1888  		testExecutionNodesForBlockID(preferredENs, nil, expectedList)
  1889  	})
  1890  }
  1891  
  1892  // TestGetTransactionResultEventEncodingVersion tests the GetTransactionResult function with different event encoding versions.
  1893  func (suite *Suite) TestGetTransactionResultEventEncodingVersion() {
  1894  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1895  
  1896  	ctx := context.Background()
  1897  
  1898  	collection := unittest.CollectionFixture(1)
  1899  	transactionBody := collection.Transactions[0]
  1900  	// block which will eventually contain the transaction
  1901  	block := unittest.BlockFixture()
  1902  	block.SetPayload(
  1903  		unittest.PayloadFixture(
  1904  			unittest.WithGuarantees(
  1905  				unittest.CollectionGuaranteesWithCollectionIDFixture([]*flow.Collection{&collection})...)))
  1906  	blockId := block.ID()
  1907  
  1908  	// reference block to which the transaction points to
  1909  	refBlock := unittest.BlockFixture()
  1910  	refBlockID := refBlock.ID()
  1911  	refBlock.Header.Height = 2
  1912  	transactionBody.SetReferenceBlockID(refBlockID)
  1913  	txId := transactionBody.ID()
  1914  
  1915  	// transaction storage returns the corresponding transaction
  1916  	suite.transactions.
  1917  		On("ByID", txId).
  1918  		Return(transactionBody, nil)
  1919  
  1920  	light := collection.Light()
  1921  	suite.collections.On("LightByID", mock.Anything).Return(&light, nil)
  1922  
  1923  	suite.snapshot.On("Head").Return(block.Header, nil)
  1924  
  1925  	// block storage returns the corresponding block
  1926  	suite.blocks.
  1927  		On("ByID", blockId).
  1928  		Return(&block, nil)
  1929  
  1930  	_, fixedENIDs := suite.setupReceipts(&block)
  1931  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1932  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
  1933  
  1934  	// create a mock connection factory
  1935  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  1936  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  1937  
  1938  	params := suite.defaultBackendParams()
  1939  	// the connection factory should be used to get the execution node client
  1940  	params.ConnFactory = connFactory
  1941  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
  1942  
  1943  	backend, err := New(params)
  1944  	suite.Require().NoError(err)
  1945  
  1946  	ccfEvents, jsoncdcEvents := generateEncodedEvents(suite.T(), 1)
  1947  	eventMessages := convert.EventsToMessages(ccfEvents)
  1948  
  1949  	for _, version := range eventEncodingVersions {
  1950  		suite.Run(fmt.Sprintf("test %s event encoding version for GetTransactionResult", version.String()), func() {
  1951  			exeEventResp := &execproto.GetTransactionResultResponse{
  1952  				Events:               eventMessages,
  1953  				EventEncodingVersion: entitiesproto.EventEncodingVersion_CCF_V0,
  1954  			}
  1955  
  1956  			suite.execClient.
  1957  				On("GetTransactionResult", ctx, &execproto.GetTransactionResultRequest{
  1958  					BlockId:       blockId[:],
  1959  					TransactionId: txId[:],
  1960  				}).
  1961  				Return(exeEventResp, nil).
  1962  				Once()
  1963  
  1964  			result, err := backend.GetTransactionResult(ctx, txId, blockId, flow.ZeroID, version)
  1965  			suite.checkResponse(result, err)
  1966  
  1967  			var expectedResult []flow.Event
  1968  			switch version {
  1969  			case entitiesproto.EventEncodingVersion_CCF_V0:
  1970  				expectedResult = append(expectedResult, ccfEvents...)
  1971  			case entitiesproto.EventEncodingVersion_JSON_CDC_V0:
  1972  				expectedResult = append(expectedResult, jsoncdcEvents...)
  1973  			}
  1974  
  1975  			suite.Assert().Equal(result.Events, expectedResult)
  1976  		})
  1977  	}
  1978  }
  1979  
  1980  // TestGetTransactionResultEventEncodingVersion tests the GetTransactionResult function with different event encoding versions.
  1981  func (suite *Suite) TestGetTransactionResultByIndexAndBlockIdEventEncodingVersion() {
  1982  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  1983  
  1984  	ctx := context.Background()
  1985  	block := unittest.BlockFixture()
  1986  	blockId := block.ID()
  1987  	index := uint32(0)
  1988  
  1989  	suite.snapshot.On("Head").Return(block.Header, nil)
  1990  
  1991  	// block storage returns the corresponding block
  1992  	suite.blocks.
  1993  		On("ByID", blockId).
  1994  		Return(&block, nil)
  1995  
  1996  	_, fixedENIDs := suite.setupReceipts(&block)
  1997  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  1998  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
  1999  
  2000  	// create a mock connection factory
  2001  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  2002  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  2003  
  2004  	params := suite.defaultBackendParams()
  2005  	// the connection factory should be used to get the execution node client
  2006  	params.ConnFactory = connFactory
  2007  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
  2008  
  2009  	backend, err := New(params)
  2010  	suite.Require().NoError(err)
  2011  
  2012  	exeNodeEventEncodingVersion := entitiesproto.EventEncodingVersion_CCF_V0
  2013  	ccfEvents, jsoncdcEvents := generateEncodedEvents(suite.T(), 1)
  2014  	eventMessages := convert.EventsToMessages(ccfEvents)
  2015  
  2016  	for _, version := range eventEncodingVersions {
  2017  		suite.Run(fmt.Sprintf("test %s event encoding version for GetTransactionResultByIndex", version.String()), func() {
  2018  			exeEventResp := &execproto.GetTransactionResultResponse{
  2019  				Events:               eventMessages,
  2020  				EventEncodingVersion: exeNodeEventEncodingVersion,
  2021  			}
  2022  
  2023  			suite.execClient.
  2024  				On("GetTransactionResultByIndex", ctx, &execproto.GetTransactionByIndexRequest{
  2025  					BlockId: blockId[:],
  2026  					Index:   index,
  2027  				}).
  2028  				Return(exeEventResp, nil).
  2029  				Once()
  2030  
  2031  			result, err := backend.GetTransactionResultByIndex(ctx, blockId, index, version)
  2032  			suite.checkResponse(result, err)
  2033  
  2034  			var expectedResult []flow.Event
  2035  			switch version {
  2036  			case entitiesproto.EventEncodingVersion_CCF_V0:
  2037  				expectedResult = append(expectedResult, ccfEvents...)
  2038  			case entitiesproto.EventEncodingVersion_JSON_CDC_V0:
  2039  				expectedResult = append(expectedResult, jsoncdcEvents...)
  2040  			}
  2041  
  2042  			suite.Assert().Equal(expectedResult, result.Events)
  2043  		})
  2044  
  2045  		suite.Run(fmt.Sprintf("test %s event encoding version for GetTransactionResultsByBlockID", version.String()), func() {
  2046  			exeEventResp := &execproto.GetTransactionResultsResponse{
  2047  				TransactionResults: []*execproto.GetTransactionResultResponse{
  2048  					{
  2049  						Events:               eventMessages,
  2050  						EventEncodingVersion: exeNodeEventEncodingVersion,
  2051  					}},
  2052  				EventEncodingVersion: exeNodeEventEncodingVersion,
  2053  			}
  2054  
  2055  			suite.execClient.
  2056  				On("GetTransactionResultsByBlockID", ctx, &execproto.GetTransactionsByBlockIDRequest{
  2057  					BlockId: blockId[:],
  2058  				}).
  2059  				Return(exeEventResp, nil).
  2060  				Once()
  2061  
  2062  			results, err := backend.GetTransactionResultsByBlockID(ctx, blockId, version)
  2063  			suite.checkResponse(results, err)
  2064  
  2065  			var expectedResult []flow.Event
  2066  			switch version {
  2067  			case entitiesproto.EventEncodingVersion_CCF_V0:
  2068  				expectedResult = append(expectedResult, ccfEvents...)
  2069  			case entitiesproto.EventEncodingVersion_JSON_CDC_V0:
  2070  				expectedResult = append(expectedResult, jsoncdcEvents...)
  2071  			}
  2072  
  2073  			for _, result := range results {
  2074  				suite.Assert().Equal(result.Events, expectedResult)
  2075  			}
  2076  		})
  2077  	}
  2078  }
  2079  
  2080  // TestNodeCommunicator tests special case for node communicator, when only one node available and communicator gets
  2081  // gobreaker.ErrOpenState
  2082  func (suite *Suite) TestNodeCommunicator() {
  2083  	head := unittest.BlockHeaderFixture()
  2084  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
  2085  	suite.snapshot.On("Head").Return(head, nil).Maybe()
  2086  
  2087  	ctx := context.Background()
  2088  	block := unittest.BlockFixture()
  2089  	blockId := block.ID()
  2090  
  2091  	// block storage returns the corresponding block
  2092  	suite.blocks.
  2093  		On("ByID", blockId).
  2094  		Return(&block, nil)
  2095  
  2096  	_, fixedENIDs := suite.setupReceipts(&block)
  2097  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
  2098  	suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil)
  2099  
  2100  	// create a mock connection factory
  2101  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  2102  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  2103  
  2104  	exeEventReq := &execproto.GetTransactionsByBlockIDRequest{
  2105  		BlockId: blockId[:],
  2106  	}
  2107  
  2108  	params := suite.defaultBackendParams()
  2109  	// the connection factory should be used to get the execution node client
  2110  	params.ConnFactory = connFactory
  2111  	params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings()
  2112  	// Left only one preferred execution node
  2113  	params.PreferredExecutionNodeIDs = []string{fixedENIDs[0].NodeID.String()}
  2114  
  2115  	backend, err := New(params)
  2116  	suite.Require().NoError(err)
  2117  
  2118  	// Simulate closed circuit breaker error
  2119  	suite.execClient.
  2120  		On("GetTransactionResultsByBlockID", ctx, exeEventReq).
  2121  		Return(nil, gobreaker.ErrOpenState).
  2122  		Once()
  2123  
  2124  	result, err := backend.GetTransactionResultsByBlockID(ctx, blockId, entitiesproto.EventEncodingVersion_JSON_CDC_V0)
  2125  	suite.Assert().Nil(result)
  2126  	suite.Assert().Error(err)
  2127  	suite.Assert().Equal(codes.Unavailable, status.Code(err))
  2128  }
  2129  
  2130  func (suite *Suite) assertAllExpectations() {
  2131  	suite.snapshot.AssertExpectations(suite.T())
  2132  	suite.state.AssertExpectations(suite.T())
  2133  	suite.blocks.AssertExpectations(suite.T())
  2134  	suite.headers.AssertExpectations(suite.T())
  2135  	suite.collections.AssertExpectations(suite.T())
  2136  	suite.transactions.AssertExpectations(suite.T())
  2137  	suite.execClient.AssertExpectations(suite.T())
  2138  }
  2139  
  2140  func (suite *Suite) checkResponse(resp interface{}, err error) {
  2141  	suite.Require().NoError(err)
  2142  	suite.Require().NotNil(resp)
  2143  }
  2144  
  2145  func (suite *Suite) setupReceipts(block *flow.Block) ([]*flow.ExecutionReceipt, flow.IdentityList) {
  2146  	ids := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution))
  2147  	receipt1 := unittest.ReceiptForBlockFixture(block)
  2148  	receipt1.ExecutorID = ids[0].NodeID
  2149  	receipt2 := unittest.ReceiptForBlockFixture(block)
  2150  	receipt2.ExecutorID = ids[1].NodeID
  2151  	receipt1.ExecutionResult = receipt2.ExecutionResult
  2152  
  2153  	receipts := flow.ExecutionReceiptList{receipt1, receipt2}
  2154  	suite.receipts.
  2155  		On("ByBlockID", block.ID()).
  2156  		Return(receipts, nil)
  2157  
  2158  	return receipts, ids
  2159  }
  2160  
  2161  func (suite *Suite) setupConnectionFactory() connection.ConnectionFactory {
  2162  	// create a mock connection factory
  2163  	connFactory := connectionmock.NewConnectionFactory(suite.T())
  2164  	connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
  2165  	return connFactory
  2166  }
  2167  
  2168  func getEvents(n int) []flow.Event {
  2169  	events := make([]flow.Event, n)
  2170  	for i := range events {
  2171  		events[i] = flow.Event{Type: flow.EventAccountCreated}
  2172  	}
  2173  	return events
  2174  }
  2175  
  2176  func generateEncodedEvents(t *testing.T, n int) ([]flow.Event, []flow.Event) {
  2177  	ccfEvents := generator.GetEventsWithEncoding(n, entities.EventEncodingVersion_CCF_V0)
  2178  	jsonEvents := make([]flow.Event, n)
  2179  	for i, e := range ccfEvents {
  2180  		jsonEvent, err := convert.CcfEventToJsonEvent(e)
  2181  		require.NoError(t, err)
  2182  		jsonEvents[i] = *jsonEvent
  2183  	}
  2184  	return ccfEvents, jsonEvents
  2185  }
  2186  
  2187  func (suite *Suite) defaultBackendParams() Params {
  2188  	return Params{
  2189  		State:                    suite.state,
  2190  		Blocks:                   suite.blocks,
  2191  		Headers:                  suite.headers,
  2192  		Collections:              suite.collections,
  2193  		Transactions:             suite.transactions,
  2194  		ExecutionReceipts:        suite.receipts,
  2195  		ExecutionResults:         suite.results,
  2196  		ChainID:                  suite.chainID,
  2197  		CollectionRPC:            suite.colClient,
  2198  		MaxHeightRange:           DefaultMaxHeightRange,
  2199  		SnapshotHistoryLimit:     DefaultSnapshotHistoryLimit,
  2200  		Communicator:             NewNodeCommunicator(false),
  2201  		AccessMetrics:            metrics.NewNoopCollector(),
  2202  		Log:                      suite.log,
  2203  		TxErrorMessagesCacheSize: 1000,
  2204  		TxResultQueryMode:        IndexQueryModeExecutionNodesOnly,
  2205  	}
  2206  }