github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/backend/backend_test.go (about)

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