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

     1  package access_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/dgraph-io/badger/v2"
    10  	"github.com/google/go-cmp/cmp"
    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/protobuf/testing/protocmp"
    20  
    21  	"github.com/onflow/flow-go/access"
    22  	"github.com/onflow/flow-go/cmd/build"
    23  	hsmock "github.com/onflow/flow-go/consensus/hotstuff/mocks"
    24  	"github.com/onflow/flow-go/consensus/hotstuff/model"
    25  	"github.com/onflow/flow-go/crypto"
    26  	"github.com/onflow/flow-go/engine/access/ingestion"
    27  	accessmock "github.com/onflow/flow-go/engine/access/mock"
    28  	"github.com/onflow/flow-go/engine/access/rpc/backend"
    29  	connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock"
    30  	"github.com/onflow/flow-go/engine/common/rpc/convert"
    31  	"github.com/onflow/flow-go/model/flow"
    32  	"github.com/onflow/flow-go/model/flow/factory"
    33  	"github.com/onflow/flow-go/model/flow/filter"
    34  	"github.com/onflow/flow-go/module"
    35  	"github.com/onflow/flow-go/module/irrecoverable"
    36  	"github.com/onflow/flow-go/module/mempool/stdmap"
    37  	"github.com/onflow/flow-go/module/metrics"
    38  	mockmodule "github.com/onflow/flow-go/module/mock"
    39  	"github.com/onflow/flow-go/module/signature"
    40  	"github.com/onflow/flow-go/module/state_synchronization/indexer"
    41  	"github.com/onflow/flow-go/network/channels"
    42  	"github.com/onflow/flow-go/network/mocknetwork"
    43  	protocol "github.com/onflow/flow-go/state/protocol/mock"
    44  	"github.com/onflow/flow-go/storage"
    45  	bstorage "github.com/onflow/flow-go/storage/badger"
    46  	"github.com/onflow/flow-go/storage/badger/operation"
    47  	"github.com/onflow/flow-go/storage/util"
    48  	"github.com/onflow/flow-go/utils/unittest"
    49  	"github.com/onflow/flow-go/utils/unittest/mocks"
    50  )
    51  
    52  type Suite struct {
    53  	suite.Suite
    54  	state                *protocol.State
    55  	sealedSnapshot       *protocol.Snapshot
    56  	finalSnapshot        *protocol.Snapshot
    57  	epochQuery           *protocol.EpochQuery
    58  	params               *protocol.Params
    59  	signerIndicesDecoder *hsmock.BlockSignerDecoder
    60  	signerIds            flow.IdentifierList
    61  	log                  zerolog.Logger
    62  	net                  *mocknetwork.Network
    63  	request              *mockmodule.Requester
    64  	collClient           *accessmock.AccessAPIClient
    65  	execClient           *accessmock.ExecutionAPIClient
    66  	me                   *mockmodule.Local
    67  	rootBlock            *flow.Header
    68  	sealedBlock          *flow.Header
    69  	finalizedBlock       *flow.Header
    70  	chainID              flow.ChainID
    71  	metrics              *metrics.NoopCollector
    72  	finalizedHeaderCache module.FinalizedHeaderCache
    73  	backend              *backend.Backend
    74  	sporkID              flow.Identifier
    75  	protocolVersion      uint
    76  }
    77  
    78  // TestAccess tests scenarios which exercise multiple API calls using both the RPC handler and the ingest engine
    79  // and using a real badger storage
    80  func TestAccess(t *testing.T) {
    81  	suite.Run(t, new(Suite))
    82  }
    83  
    84  func (suite *Suite) SetupTest() {
    85  	suite.log = zerolog.New(os.Stderr)
    86  	suite.net = new(mocknetwork.Network)
    87  	suite.state = new(protocol.State)
    88  	suite.finalSnapshot = new(protocol.Snapshot)
    89  	suite.sealedSnapshot = new(protocol.Snapshot)
    90  	suite.sporkID = unittest.IdentifierFixture()
    91  	suite.protocolVersion = uint(unittest.Uint64InRange(10, 30))
    92  
    93  	suite.rootBlock = unittest.BlockHeaderFixture(unittest.WithHeaderHeight(0))
    94  	suite.sealedBlock = suite.rootBlock
    95  	suite.finalizedBlock = unittest.BlockHeaderWithParentFixture(suite.sealedBlock)
    96  
    97  	suite.epochQuery = new(protocol.EpochQuery)
    98  	suite.state.On("Sealed").Return(suite.sealedSnapshot, nil).Maybe()
    99  	suite.state.On("Final").Return(suite.finalSnapshot, nil).Maybe()
   100  	suite.finalSnapshot.On("Epochs").Return(suite.epochQuery).Maybe()
   101  	suite.sealedSnapshot.On("Head").Return(
   102  		func() *flow.Header {
   103  			return suite.sealedBlock
   104  		},
   105  		nil,
   106  	).Maybe()
   107  	suite.finalSnapshot.On("Head").Return(
   108  		func() *flow.Header {
   109  			return suite.finalizedBlock
   110  		},
   111  		nil,
   112  	).Maybe()
   113  
   114  	suite.params = new(protocol.Params)
   115  	suite.params.On("FinalizedRoot").Return(suite.rootBlock, nil)
   116  	suite.params.On("SporkID").Return(suite.sporkID, nil)
   117  	suite.params.On("ProtocolVersion").Return(suite.protocolVersion, nil)
   118  	suite.params.On("SporkRootBlockHeight").Return(suite.rootBlock.Height, nil)
   119  	suite.params.On("SealedRoot").Return(suite.rootBlock, nil)
   120  	suite.state.On("Params").Return(suite.params).Maybe()
   121  	suite.collClient = new(accessmock.AccessAPIClient)
   122  	suite.execClient = new(accessmock.ExecutionAPIClient)
   123  
   124  	suite.request = new(mockmodule.Requester)
   125  	suite.request.On("EntityByID", mock.Anything, mock.Anything)
   126  
   127  	suite.me = new(mockmodule.Local)
   128  
   129  	suite.signerIds = unittest.IdentifierListFixture(4)
   130  	suite.signerIndicesDecoder = new(hsmock.BlockSignerDecoder)
   131  	suite.signerIndicesDecoder.On("DecodeSignerIDs", mock.Anything).Return(suite.signerIds, nil).Maybe()
   132  
   133  	accessIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess))
   134  	suite.me.
   135  		On("NodeID").
   136  		Return(accessIdentity.NodeID)
   137  
   138  	suite.chainID = flow.Testnet
   139  	suite.metrics = metrics.NewNoopCollector()
   140  	suite.finalizedHeaderCache = mocks.NewFinalizedHeaderCache(suite.T(), suite.state)
   141  }
   142  
   143  func (suite *Suite) RunTest(
   144  	f func(handler *access.Handler, db *badger.DB, all *storage.All),
   145  ) {
   146  	unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) {
   147  		all := util.StorageLayer(suite.T(), db)
   148  
   149  		var err error
   150  		suite.backend, err = backend.New(backend.Params{
   151  			State:                suite.state,
   152  			CollectionRPC:        suite.collClient,
   153  			Blocks:               all.Blocks,
   154  			Headers:              all.Headers,
   155  			Collections:          all.Collections,
   156  			Transactions:         all.Transactions,
   157  			ExecutionResults:     all.Results,
   158  			ExecutionReceipts:    all.Receipts,
   159  			ChainID:              suite.chainID,
   160  			AccessMetrics:        suite.metrics,
   161  			MaxHeightRange:       backend.DefaultMaxHeightRange,
   162  			Log:                  suite.log,
   163  			SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit,
   164  			Communicator:         backend.NewNodeCommunicator(false),
   165  		})
   166  		require.NoError(suite.T(), err)
   167  
   168  		handler := access.NewHandler(
   169  			suite.backend,
   170  			suite.chainID.Chain(),
   171  			suite.finalizedHeaderCache,
   172  			suite.me,
   173  			access.WithBlockSignerDecoder(suite.signerIndicesDecoder),
   174  		)
   175  		f(handler, db, all)
   176  	})
   177  }
   178  
   179  func (suite *Suite) TestSendAndGetTransaction() {
   180  	suite.RunTest(func(handler *access.Handler, _ *badger.DB, _ *storage.All) {
   181  		referenceBlock := unittest.BlockHeaderFixture()
   182  		transaction := unittest.TransactionFixture()
   183  		transaction.SetReferenceBlockID(referenceBlock.ID())
   184  
   185  		refSnapshot := new(protocol.Snapshot)
   186  
   187  		suite.state.
   188  			On("AtBlockID", referenceBlock.ID()).
   189  			Return(refSnapshot, nil)
   190  
   191  		refSnapshot.
   192  			On("Head").
   193  			Return(referenceBlock, nil).
   194  			Twice()
   195  
   196  		suite.finalSnapshot.
   197  			On("Head").
   198  			Return(referenceBlock, nil).
   199  			Once()
   200  
   201  		expected := convert.TransactionToMessage(transaction.TransactionBody)
   202  		sendReq := &accessproto.SendTransactionRequest{
   203  			Transaction: expected,
   204  		}
   205  		sendResp := accessproto.SendTransactionResponse{}
   206  
   207  		suite.collClient.
   208  			On("SendTransaction", mock.Anything, mock.Anything).
   209  			Return(&sendResp, nil).
   210  			Once()
   211  
   212  		// Send transaction
   213  		resp, err := handler.SendTransaction(context.Background(), sendReq)
   214  		suite.Require().NoError(err)
   215  		suite.Require().NotNil(resp)
   216  
   217  		id := transaction.ID()
   218  		getReq := &accessproto.GetTransactionRequest{
   219  			Id: id[:],
   220  		}
   221  
   222  		// Get transaction
   223  		gResp, err := handler.GetTransaction(context.Background(), getReq)
   224  		suite.Require().NoError(err)
   225  		suite.Require().NotNil(gResp)
   226  
   227  		actual := gResp.GetTransaction()
   228  		suite.Require().Equal(expected, actual)
   229  	})
   230  }
   231  
   232  func (suite *Suite) TestSendExpiredTransaction() {
   233  	suite.RunTest(func(handler *access.Handler, _ *badger.DB, _ *storage.All) {
   234  		referenceBlock := suite.finalizedBlock
   235  
   236  		transaction := unittest.TransactionFixture()
   237  		transaction.SetReferenceBlockID(referenceBlock.ID())
   238  		// create latest block that is past the expiry window
   239  		latestBlock := unittest.BlockHeaderFixture()
   240  		latestBlock.Height = referenceBlock.Height + flow.DefaultTransactionExpiry*2
   241  
   242  		refSnapshot := new(protocol.Snapshot)
   243  
   244  		suite.state.
   245  			On("AtBlockID", referenceBlock.ID()).
   246  			Return(refSnapshot, nil)
   247  
   248  		refSnapshot.
   249  			On("Head").
   250  			Return(referenceBlock, nil).
   251  			Twice()
   252  
   253  		//Advancing final state to expire ref block
   254  		suite.finalizedBlock = latestBlock
   255  
   256  		req := &accessproto.SendTransactionRequest{
   257  			Transaction: convert.TransactionToMessage(transaction.TransactionBody),
   258  		}
   259  
   260  		_, err := handler.SendTransaction(context.Background(), req)
   261  		suite.Require().Error(err)
   262  	})
   263  }
   264  
   265  type mockCloser struct{}
   266  
   267  func (mc *mockCloser) Close() error { return nil }
   268  
   269  // TestSendTransactionToRandomCollectionNode tests that collection nodes are chosen from the appropriate cluster when
   270  // forwarding transactions by sending two transactions bound for two different collection clusters.
   271  func (suite *Suite) TestSendTransactionToRandomCollectionNode() {
   272  	unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) {
   273  
   274  		// create a transaction
   275  		referenceBlock := unittest.BlockHeaderFixture()
   276  		transaction := unittest.TransactionFixture()
   277  		transaction.SetReferenceBlockID(referenceBlock.ID())
   278  
   279  		// setup the state and finalSnapshot mock expectations
   280  		suite.state.On("AtBlockID", referenceBlock.ID()).Return(suite.finalSnapshot, nil)
   281  		suite.finalSnapshot.On("Head").Return(referenceBlock, nil)
   282  
   283  		// create storage
   284  		metrics := metrics.NewNoopCollector()
   285  		transactions := bstorage.NewTransactions(metrics, db)
   286  		collections := bstorage.NewCollections(db, transactions)
   287  
   288  		// create collection node cluster
   289  		count := 2
   290  		collNodes := unittest.IdentityListFixture(count, unittest.WithRole(flow.RoleCollection))
   291  		assignments := unittest.ClusterAssignment(uint(count), collNodes)
   292  		clusters, err := factory.NewClusterList(assignments, collNodes)
   293  		suite.Require().Nil(err)
   294  		collNode1 := clusters[0][0]
   295  		collNode2 := clusters[1][0]
   296  		epoch := new(protocol.Epoch)
   297  		suite.epochQuery.On("Current").Return(epoch)
   298  		epoch.On("Clustering").Return(clusters, nil)
   299  
   300  		// create two transactions bound for each of the cluster
   301  		cluster1 := clusters[0]
   302  		cluster1tx := unittest.AlterTransactionForCluster(transaction.TransactionBody, clusters, cluster1, func(transaction *flow.TransactionBody) {})
   303  		tx1 := convert.TransactionToMessage(cluster1tx)
   304  		sendReq1 := &accessproto.SendTransactionRequest{
   305  			Transaction: tx1,
   306  		}
   307  		cluster2 := clusters[1]
   308  		cluster2tx := unittest.AlterTransactionForCluster(transaction.TransactionBody, clusters, cluster2, func(transaction *flow.TransactionBody) {})
   309  		tx2 := convert.TransactionToMessage(cluster2tx)
   310  		sendReq2 := &accessproto.SendTransactionRequest{
   311  			Transaction: tx2,
   312  		}
   313  		sendResp := accessproto.SendTransactionResponse{}
   314  
   315  		// create mock access api clients for each of the collection node expecting the correct transaction once
   316  		col1ApiClient := new(accessmock.AccessAPIClient)
   317  		col1ApiClient.On("SendTransaction", mock.Anything, sendReq1).Return(&sendResp, nil).Once()
   318  		col2ApiClient := new(accessmock.AccessAPIClient)
   319  		col2ApiClient.On("SendTransaction", mock.Anything, sendReq2).Return(&sendResp, nil).Once()
   320  
   321  		// create a mock connection factory
   322  		connFactory := connectionmock.NewConnectionFactory(suite.T())
   323  		connFactory.On("GetAccessAPIClient", collNode1.Address, nil).Return(col1ApiClient, &mockCloser{}, nil)
   324  		connFactory.On("GetAccessAPIClient", collNode2.Address, nil).Return(col2ApiClient, &mockCloser{}, nil)
   325  
   326  		bnd, err := backend.New(backend.Params{State: suite.state,
   327  			Collections:              collections,
   328  			Transactions:             transactions,
   329  			ChainID:                  suite.chainID,
   330  			AccessMetrics:            metrics,
   331  			ConnFactory:              connFactory,
   332  			MaxHeightRange:           backend.DefaultMaxHeightRange,
   333  			Log:                      suite.log,
   334  			SnapshotHistoryLimit:     backend.DefaultSnapshotHistoryLimit,
   335  			Communicator:             backend.NewNodeCommunicator(false),
   336  			TxErrorMessagesCacheSize: 1000,
   337  		})
   338  		require.NoError(suite.T(), err)
   339  
   340  		handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me)
   341  
   342  		// Send transaction 1
   343  		resp, err := handler.SendTransaction(context.Background(), sendReq1)
   344  		require.NoError(suite.T(), err)
   345  		require.NotNil(suite.T(), resp)
   346  
   347  		// Send transaction 2
   348  		resp, err = handler.SendTransaction(context.Background(), sendReq2)
   349  		require.NoError(suite.T(), err)
   350  		require.NotNil(suite.T(), resp)
   351  
   352  		// verify that a collection node in the correct cluster was contacted exactly once
   353  		col1ApiClient.AssertExpectations(suite.T())
   354  		col2ApiClient.AssertExpectations(suite.T())
   355  		epoch.AssertNumberOfCalls(suite.T(), "Clustering", 2)
   356  
   357  		// additionally do a GetTransaction request for the two transactions
   358  		getTx := func(tx flow.TransactionBody) {
   359  			id := tx.ID()
   360  			getReq := &accessproto.GetTransactionRequest{
   361  				Id: id[:],
   362  			}
   363  			gResp, err := handler.GetTransaction(context.Background(), getReq)
   364  			require.NoError(suite.T(), err)
   365  			require.NotNil(suite.T(), gResp)
   366  			actual := gResp.GetTransaction()
   367  			expected := convert.TransactionToMessage(tx)
   368  			require.Equal(suite.T(), expected, actual)
   369  		}
   370  
   371  		getTx(cluster1tx)
   372  		getTx(cluster1tx)
   373  	})
   374  }
   375  
   376  func (suite *Suite) TestGetBlockByIDAndHeight() {
   377  	suite.RunTest(func(handler *access.Handler, db *badger.DB, all *storage.All) {
   378  
   379  		// test block1 get by ID
   380  		block1 := unittest.BlockFixture()
   381  		// test block2 get by height
   382  		block2 := unittest.BlockFixture()
   383  		block2.Header.Height = 2
   384  
   385  		require.NoError(suite.T(), all.Blocks.Store(&block1))
   386  		require.NoError(suite.T(), all.Blocks.Store(&block2))
   387  
   388  		// the follower logic should update height index on the block storage when a block is finalized
   389  		err := db.Update(operation.IndexBlockHeight(block2.Header.Height, block2.ID()))
   390  		require.NoError(suite.T(), err)
   391  
   392  		assertHeaderResp := func(
   393  			resp *accessproto.BlockHeaderResponse,
   394  			err error,
   395  			header *flow.Header,
   396  		) {
   397  			require.NoError(suite.T(), err)
   398  			require.NotNil(suite.T(), resp)
   399  			actual := resp.Block
   400  			expectedMessage, err := convert.BlockHeaderToMessage(header, suite.signerIds)
   401  			require.NoError(suite.T(), err)
   402  			require.Empty(suite.T(), cmp.Diff(expectedMessage, actual, protocmp.Transform()))
   403  			expectedBlockHeader, err := convert.MessageToBlockHeader(actual)
   404  			require.NoError(suite.T(), err)
   405  			require.Equal(suite.T(), expectedBlockHeader, header)
   406  		}
   407  
   408  		assertBlockResp := func(
   409  			resp *accessproto.BlockResponse,
   410  			err error,
   411  			block *flow.Block,
   412  		) {
   413  			require.NoError(suite.T(), err)
   414  			require.NotNil(suite.T(), resp)
   415  			actual := resp.Block
   416  			expectedMessage, err := convert.BlockToMessage(block, suite.signerIds)
   417  			require.NoError(suite.T(), err)
   418  			require.Equal(suite.T(), expectedMessage, actual)
   419  			expectedBlock, err := convert.MessageToBlock(resp.Block)
   420  			require.NoError(suite.T(), err)
   421  			require.Equal(suite.T(), expectedBlock.ID(), block.ID())
   422  		}
   423  
   424  		assertLightBlockResp := func(
   425  			resp *accessproto.BlockResponse,
   426  			err error,
   427  			block *flow.Block,
   428  		) {
   429  			require.NoError(suite.T(), err)
   430  			require.NotNil(suite.T(), resp)
   431  			actual := resp.Block
   432  			expectedMessage := convert.BlockToMessageLight(block)
   433  			require.Equal(suite.T(), expectedMessage, actual)
   434  		}
   435  
   436  		suite.finalSnapshot.On("Head").Return(block1.Header, nil)
   437  		suite.Run("get header 1 by ID", func() {
   438  			// get header by ID
   439  			id := block1.ID()
   440  			req := &accessproto.GetBlockHeaderByIDRequest{
   441  				Id: id[:],
   442  			}
   443  
   444  			resp, err := handler.GetBlockHeaderByID(context.Background(), req)
   445  
   446  			// assert it is indeed block1
   447  			assertHeaderResp(resp, err, block1.Header)
   448  		})
   449  
   450  		suite.Run("get block 1 by ID", func() {
   451  			id := block1.ID()
   452  			// get block details by ID
   453  			req := &accessproto.GetBlockByIDRequest{
   454  				Id:                id[:],
   455  				FullBlockResponse: true,
   456  			}
   457  
   458  			resp, err := handler.GetBlockByID(context.Background(), req)
   459  
   460  			assertBlockResp(resp, err, &block1)
   461  		})
   462  
   463  		suite.Run("get block light 1 by ID", func() {
   464  			id := block1.ID()
   465  			// get block details by ID
   466  			req := &accessproto.GetBlockByIDRequest{
   467  				Id: id[:],
   468  			}
   469  
   470  			resp, err := handler.GetBlockByID(context.Background(), req)
   471  
   472  			assertLightBlockResp(resp, err, &block1)
   473  		})
   474  
   475  		suite.Run("get header 2 by height", func() {
   476  
   477  			// get header by height
   478  			req := &accessproto.GetBlockHeaderByHeightRequest{
   479  				Height: block2.Header.Height,
   480  			}
   481  
   482  			resp, err := handler.GetBlockHeaderByHeight(context.Background(), req)
   483  
   484  			assertHeaderResp(resp, err, block2.Header)
   485  		})
   486  
   487  		suite.Run("get block 2 by height", func() {
   488  			// get block details by height
   489  			req := &accessproto.GetBlockByHeightRequest{
   490  				Height:            block2.Header.Height,
   491  				FullBlockResponse: true,
   492  			}
   493  
   494  			resp, err := handler.GetBlockByHeight(context.Background(), req)
   495  
   496  			assertBlockResp(resp, err, &block2)
   497  		})
   498  
   499  		suite.Run("get block 2 by height", func() {
   500  			// get block details by height
   501  			req := &accessproto.GetBlockByHeightRequest{
   502  				Height: block2.Header.Height,
   503  			}
   504  
   505  			resp, err := handler.GetBlockByHeight(context.Background(), req)
   506  
   507  			assertLightBlockResp(resp, err, &block2)
   508  		})
   509  	})
   510  }
   511  
   512  func (suite *Suite) TestGetExecutionResultByBlockID() {
   513  	suite.RunTest(func(handler *access.Handler, db *badger.DB, all *storage.All) {
   514  
   515  		// test block1 get by ID
   516  		nonexistingID := unittest.IdentifierFixture()
   517  		blockID := unittest.IdentifierFixture()
   518  
   519  		er := unittest.ExecutionResultFixture(
   520  			unittest.WithExecutionResultBlockID(blockID),
   521  			unittest.WithServiceEvents(3))
   522  
   523  		require.NoError(suite.T(), all.Results.Store(er))
   524  		require.NoError(suite.T(), all.Results.Index(blockID, er.ID()))
   525  
   526  		assertResp := func(
   527  			resp *accessproto.ExecutionResultForBlockIDResponse,
   528  			err error,
   529  			executionResult *flow.ExecutionResult,
   530  		) {
   531  			require.NoError(suite.T(), err)
   532  			require.NotNil(suite.T(), resp)
   533  			er := resp.ExecutionResult
   534  
   535  			require.Len(suite.T(), er.Chunks, len(executionResult.Chunks))
   536  			require.Len(suite.T(), er.ServiceEvents, len(executionResult.ServiceEvents))
   537  
   538  			assert.Equal(suite.T(), executionResult.BlockID, convert.MessageToIdentifier(er.BlockId))
   539  			assert.Equal(suite.T(), executionResult.PreviousResultID, convert.MessageToIdentifier(er.PreviousResultId))
   540  			assert.Equal(suite.T(), executionResult.ExecutionDataID, convert.MessageToIdentifier(er.ExecutionDataId))
   541  
   542  			for i, chunk := range executionResult.Chunks {
   543  				assert.Equal(suite.T(), chunk.BlockID[:], er.Chunks[i].BlockId)
   544  				assert.Equal(suite.T(), chunk.Index, er.Chunks[i].Index)
   545  				assert.Equal(suite.T(), uint32(chunk.CollectionIndex), er.Chunks[i].CollectionIndex)
   546  				assert.Equal(suite.T(), chunk.StartState[:], er.Chunks[i].StartState)
   547  				assert.Equal(suite.T(), chunk.EventCollection[:], er.Chunks[i].EventCollection)
   548  				assert.Equal(suite.T(), chunk.TotalComputationUsed, er.Chunks[i].TotalComputationUsed)
   549  				assert.Equal(suite.T(), uint32(chunk.NumberOfTransactions), er.Chunks[i].NumberOfTransactions)
   550  				assert.Equal(suite.T(), chunk.EndState[:], er.Chunks[i].EndState)
   551  			}
   552  
   553  			for i, serviceEvent := range executionResult.ServiceEvents {
   554  				assert.Equal(suite.T(), serviceEvent.Type.String(), er.ServiceEvents[i].Type)
   555  				event := serviceEvent.Event
   556  				marshalledEvent, err := json.Marshal(event)
   557  				require.NoError(suite.T(), err)
   558  				assert.Equal(suite.T(), marshalledEvent, er.ServiceEvents[i].Payload)
   559  			}
   560  			parsedExecResult, err := convert.MessageToExecutionResult(resp.ExecutionResult)
   561  			require.NoError(suite.T(), err)
   562  			assert.Equal(suite.T(), parsedExecResult.ID(), executionResult.ID())
   563  		}
   564  
   565  		suite.Run("nonexisting block", func() {
   566  			req := &accessproto.GetExecutionResultForBlockIDRequest{
   567  				BlockId: nonexistingID[:],
   568  			}
   569  
   570  			resp, err := handler.GetExecutionResultForBlockID(context.Background(), req)
   571  
   572  			require.Error(suite.T(), err)
   573  			require.Nil(suite.T(), resp)
   574  		})
   575  
   576  		suite.Run("some block", func() {
   577  			// get header by ID
   578  			req := &accessproto.GetExecutionResultForBlockIDRequest{
   579  				BlockId: blockID[:],
   580  			}
   581  
   582  			resp, err := handler.GetExecutionResultForBlockID(context.Background(), req)
   583  
   584  			require.NoError(suite.T(), err)
   585  
   586  			assertResp(resp, err, er)
   587  		})
   588  
   589  	})
   590  }
   591  
   592  // TestGetSealedTransaction tests that transactions status of transaction that belongs to a sealed block
   593  // is reported as sealed
   594  func (suite *Suite) TestGetSealedTransaction() {
   595  	unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) {
   596  		all := util.StorageLayer(suite.T(), db)
   597  		results := bstorage.NewExecutionResults(suite.metrics, db)
   598  		receipts := bstorage.NewExecutionReceipts(suite.metrics, db, results, bstorage.DefaultCacheSize)
   599  		enIdentities := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution))
   600  		enNodeIDs := enIdentities.NodeIDs()
   601  
   602  		// create block -> collection -> transactions
   603  		block, collection := suite.createChain()
   604  
   605  		// setup mocks
   606  		conduit := new(mocknetwork.Conduit)
   607  		suite.net.On("Register", channels.ReceiveReceipts, mock.Anything).Return(conduit, nil).
   608  			Once()
   609  		suite.request.On("Request", mock.Anything, mock.Anything).Return()
   610  
   611  		colIdentities := unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleCollection))
   612  		allIdentities := append(colIdentities, enIdentities...)
   613  
   614  		suite.finalSnapshot.On("Identities", mock.Anything).Return(allIdentities, nil).Once()
   615  
   616  		exeEventResp := execproto.GetTransactionResultResponse{
   617  			Events: nil,
   618  		}
   619  
   620  		// generate receipts
   621  		executionReceipts := unittest.ReceiptsForBlockFixture(block, enNodeIDs)
   622  
   623  		// assume execution node returns an empty list of events
   624  		suite.execClient.On("GetTransactionResult", mock.Anything, mock.Anything).Return(&exeEventResp, nil)
   625  
   626  		// create a mock connection factory
   627  		connFactory := connectionmock.NewConnectionFactory(suite.T())
   628  		connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
   629  
   630  		// initialize storage
   631  		metrics := metrics.NewNoopCollector()
   632  		transactions := bstorage.NewTransactions(metrics, db)
   633  		collections := bstorage.NewCollections(db, transactions)
   634  		collectionsToMarkFinalized, err := stdmap.NewTimes(100)
   635  		require.NoError(suite.T(), err)
   636  		collectionsToMarkExecuted, err := stdmap.NewTimes(100)
   637  		require.NoError(suite.T(), err)
   638  		blocksToMarkExecuted, err := stdmap.NewTimes(100)
   639  		require.NoError(suite.T(), err)
   640  
   641  		bnd, err := backend.New(backend.Params{State: suite.state,
   642  			CollectionRPC:             suite.collClient,
   643  			Blocks:                    all.Blocks,
   644  			Headers:                   all.Headers,
   645  			Collections:               collections,
   646  			Transactions:              transactions,
   647  			ExecutionReceipts:         receipts,
   648  			ExecutionResults:          results,
   649  			ChainID:                   suite.chainID,
   650  			AccessMetrics:             suite.metrics,
   651  			ConnFactory:               connFactory,
   652  			MaxHeightRange:            backend.DefaultMaxHeightRange,
   653  			PreferredExecutionNodeIDs: enNodeIDs.Strings(),
   654  			Log:                       suite.log,
   655  			SnapshotHistoryLimit:      backend.DefaultSnapshotHistoryLimit,
   656  			Communicator:              backend.NewNodeCommunicator(false),
   657  			TxErrorMessagesCacheSize:  1000,
   658  			TxResultQueryMode:         backend.IndexQueryModeExecutionNodesOnly,
   659  		})
   660  		require.NoError(suite.T(), err)
   661  
   662  		handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me)
   663  
   664  		collectionExecutedMetric, err := indexer.NewCollectionExecutedMetricImpl(
   665  			suite.log,
   666  			metrics,
   667  			collectionsToMarkFinalized,
   668  			collectionsToMarkExecuted,
   669  			blocksToMarkExecuted,
   670  			collections,
   671  			all.Blocks,
   672  		)
   673  		require.NoError(suite.T(), err)
   674  
   675  		// create the ingest engine
   676  		ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections,
   677  			transactions, results, receipts, collectionExecutedMetric)
   678  		require.NoError(suite.T(), err)
   679  
   680  		// 1. Assume that follower engine updated the block storage and the protocol state. The block is reported as sealed
   681  		err = all.Blocks.Store(block)
   682  		require.NoError(suite.T(), err)
   683  		suite.sealedBlock = block.Header
   684  
   685  		background, cancel := context.WithCancel(context.Background())
   686  		defer cancel()
   687  
   688  		ctx, _ := irrecoverable.WithSignaler(background)
   689  		ingestEng.Start(ctx)
   690  		<-ingestEng.Ready()
   691  
   692  		// 2. Ingest engine was notified by the follower engine about a new block.
   693  		// Follower engine --> Ingest engine
   694  		mb := &model.Block{
   695  			BlockID: block.ID(),
   696  		}
   697  		ingestEng.OnFinalizedBlock(mb)
   698  
   699  		// 3. Request engine is used to request missing collection
   700  		suite.request.On("EntityByID", collection.ID(), mock.Anything).Return()
   701  		// 4. Indexer HandleCollection receives the requested collection and all the execution receipts
   702  		err = indexer.HandleCollection(collection, collections, transactions, suite.log, collectionExecutedMetric)
   703  		require.NoError(suite.T(), err)
   704  
   705  		for _, r := range executionReceipts {
   706  			err = ingestEng.Process(channels.ReceiveReceipts, enNodeIDs[0], r)
   707  			require.NoError(suite.T(), err)
   708  		}
   709  
   710  		// 5. Client requests a transaction
   711  		tx := collection.Transactions[0]
   712  		txID := tx.ID()
   713  		getReq := &accessproto.GetTransactionRequest{
   714  			Id: txID[:],
   715  		}
   716  		gResp, err := handler.GetTransactionResult(context.Background(), getReq)
   717  		require.NoError(suite.T(), err)
   718  		// assert that the transaction is reported as Sealed
   719  		require.Equal(suite.T(), entitiesproto.TransactionStatus_SEALED, gResp.GetStatus())
   720  	})
   721  }
   722  
   723  // TestGetTransactionResult tests different approaches to using the GetTransactionResult query, including using
   724  // transaction ID, block ID, and collection ID.
   725  func (suite *Suite) TestGetTransactionResult() {
   726  	unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) {
   727  		all := util.StorageLayer(suite.T(), db)
   728  		results := bstorage.NewExecutionResults(suite.metrics, db)
   729  		receipts := bstorage.NewExecutionReceipts(suite.metrics, db, results, bstorage.DefaultCacheSize)
   730  
   731  		originID := unittest.IdentifierFixture()
   732  
   733  		*suite.state = protocol.State{}
   734  
   735  		// create block -> collection -> transactions
   736  		block, collection := suite.createChain()
   737  		blockNegative, collectionNegative := suite.createChain()
   738  		blockId := block.ID()
   739  		blockNegativeId := blockNegative.ID()
   740  
   741  		finalSnapshot := new(protocol.Snapshot)
   742  		finalSnapshot.On("Head").Return(block.Header, nil)
   743  
   744  		suite.state.On("Params").Return(suite.params)
   745  		suite.state.On("Final").Return(finalSnapshot)
   746  		suite.state.On("Sealed").Return(suite.sealedSnapshot)
   747  		sealedBlock := unittest.GenesisFixture().Header
   748  		// specifically for this test we will consider that sealed block is far behind finalized, so we get EXECUTED status
   749  		suite.sealedSnapshot.On("Head").Return(sealedBlock, nil)
   750  
   751  		err := all.Blocks.Store(block)
   752  		require.NoError(suite.T(), err)
   753  		err = all.Blocks.Store(blockNegative)
   754  		require.NoError(suite.T(), err)
   755  
   756  		suite.state.On("AtBlockID", blockId).Return(suite.sealedSnapshot)
   757  
   758  		colIdentities := unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleCollection))
   759  		enIdentities := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution))
   760  
   761  		enNodeIDs := enIdentities.NodeIDs()
   762  		allIdentities := append(colIdentities, enIdentities...)
   763  		finalSnapshot.On("Identities", mock.Anything).Return(allIdentities, nil)
   764  
   765  		// assume execution node returns an empty list of events
   766  		suite.execClient.On("GetTransactionResult", mock.Anything, mock.Anything).Return(&execproto.GetTransactionResultResponse{
   767  			Events: nil,
   768  		}, nil)
   769  
   770  		// setup mocks
   771  		conduit := new(mocknetwork.Conduit)
   772  		suite.net.On("Register", channels.ReceiveReceipts, mock.Anything).Return(conduit, nil).Once()
   773  		suite.request.On("Request", mock.Anything, mock.Anything).Return()
   774  
   775  		// create a mock connection factory
   776  		connFactory := connectionmock.NewConnectionFactory(suite.T())
   777  		connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
   778  
   779  		// initialize storage
   780  		metrics := metrics.NewNoopCollector()
   781  		transactions := bstorage.NewTransactions(metrics, db)
   782  		collections := bstorage.NewCollections(db, transactions)
   783  		err = collections.Store(collectionNegative)
   784  		require.NoError(suite.T(), err)
   785  		collectionsToMarkFinalized, err := stdmap.NewTimes(100)
   786  		require.NoError(suite.T(), err)
   787  		collectionsToMarkExecuted, err := stdmap.NewTimes(100)
   788  		require.NoError(suite.T(), err)
   789  		blocksToMarkExecuted, err := stdmap.NewTimes(100)
   790  		require.NoError(suite.T(), err)
   791  
   792  		bnd, err := backend.New(backend.Params{State: suite.state,
   793  			CollectionRPC:             suite.collClient,
   794  			Blocks:                    all.Blocks,
   795  			Headers:                   all.Headers,
   796  			Collections:               collections,
   797  			Transactions:              transactions,
   798  			ExecutionReceipts:         receipts,
   799  			ExecutionResults:          results,
   800  			ChainID:                   suite.chainID,
   801  			AccessMetrics:             suite.metrics,
   802  			ConnFactory:               connFactory,
   803  			MaxHeightRange:            backend.DefaultMaxHeightRange,
   804  			PreferredExecutionNodeIDs: enNodeIDs.Strings(),
   805  			Log:                       suite.log,
   806  			SnapshotHistoryLimit:      backend.DefaultSnapshotHistoryLimit,
   807  			Communicator:              backend.NewNodeCommunicator(false),
   808  			TxErrorMessagesCacheSize:  1000,
   809  			TxResultQueryMode:         backend.IndexQueryModeExecutionNodesOnly,
   810  		})
   811  		require.NoError(suite.T(), err)
   812  
   813  		handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me)
   814  
   815  		collectionExecutedMetric, err := indexer.NewCollectionExecutedMetricImpl(
   816  			suite.log,
   817  			metrics,
   818  			collectionsToMarkFinalized,
   819  			collectionsToMarkExecuted,
   820  			blocksToMarkExecuted,
   821  			collections,
   822  			all.Blocks,
   823  		)
   824  		require.NoError(suite.T(), err)
   825  
   826  		// create the ingest engine
   827  		ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections,
   828  			transactions, results, receipts, collectionExecutedMetric)
   829  		require.NoError(suite.T(), err)
   830  
   831  		background, cancel := context.WithCancel(context.Background())
   832  		defer cancel()
   833  
   834  		ctx := irrecoverable.NewMockSignalerContext(suite.T(), background)
   835  		ingestEng.Start(ctx)
   836  		<-ingestEng.Ready()
   837  
   838  		processExecutionReceipts := func(
   839  			block *flow.Block,
   840  			collection *flow.Collection,
   841  			enNodeIDs flow.IdentifierList,
   842  			originID flow.Identifier,
   843  			ingestEng *ingestion.Engine,
   844  		) {
   845  			executionReceipts := unittest.ReceiptsForBlockFixture(block, enNodeIDs)
   846  			// Ingest engine was notified by the follower engine about a new block.
   847  			// Follower engine --> Ingest engine
   848  			mb := &model.Block{
   849  				BlockID: block.ID(),
   850  			}
   851  			ingestEng.OnFinalizedBlock(mb)
   852  
   853  			// Indexer HandleCollection receives the requested collection and all the execution receipts
   854  			err = indexer.HandleCollection(collection, collections, transactions, suite.log, collectionExecutedMetric)
   855  			require.NoError(suite.T(), err)
   856  
   857  			for _, r := range executionReceipts {
   858  				err = ingestEng.Process(channels.ReceiveReceipts, enNodeIDs[0], r)
   859  				require.NoError(suite.T(), err)
   860  			}
   861  		}
   862  		processExecutionReceipts(block, collection, enNodeIDs, originID, ingestEng)
   863  		processExecutionReceipts(blockNegative, collectionNegative, enNodeIDs, originID, ingestEng)
   864  
   865  		txId := collection.Transactions[0].ID()
   866  		collectionId := collection.ID()
   867  		txIdNegative := collectionNegative.Transactions[0].ID()
   868  		collectionIdNegative := collectionNegative.ID()
   869  
   870  		assertTransactionResult := func(
   871  			resp *accessproto.TransactionResultResponse,
   872  			err error,
   873  		) {
   874  			require.NoError(suite.T(), err)
   875  			actualTxId := flow.HashToID(resp.TransactionId)
   876  			require.Equal(suite.T(), txId, actualTxId)
   877  			actualBlockId := flow.HashToID(resp.BlockId)
   878  			require.Equal(suite.T(), blockId, actualBlockId)
   879  			actualCollectionId := flow.HashToID(resp.CollectionId)
   880  			require.Equal(suite.T(), collectionId, actualCollectionId)
   881  		}
   882  
   883  		// Test behaviour with transactionId provided
   884  		// POSITIVE
   885  		suite.Run("Get transaction result by transaction ID", func() {
   886  			getReq := &accessproto.GetTransactionRequest{
   887  				Id: txId[:],
   888  			}
   889  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   890  			assertTransactionResult(resp, err)
   891  		})
   892  
   893  		// Test behaviour with blockId provided
   894  		suite.Run("Get transaction result by block ID", func() {
   895  			getReq := &accessproto.GetTransactionRequest{
   896  				Id:      txId[:],
   897  				BlockId: blockId[:],
   898  			}
   899  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   900  			assertTransactionResult(resp, err)
   901  		})
   902  
   903  		suite.Run("Get transaction result with wrong transaction ID and correct block ID", func() {
   904  			getReq := &accessproto.GetTransactionRequest{
   905  				Id:      txIdNegative[:],
   906  				BlockId: blockId[:],
   907  			}
   908  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   909  			require.Error(suite.T(), err)
   910  			require.Nil(suite.T(), resp)
   911  		})
   912  
   913  		suite.Run("Get transaction result with wrong block ID and correct transaction ID", func() {
   914  			getReq := &accessproto.GetTransactionRequest{
   915  				Id:      txId[:],
   916  				BlockId: blockNegativeId[:],
   917  			}
   918  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   919  			require.Error(suite.T(), err)
   920  			require.Nil(suite.T(), resp)
   921  		})
   922  
   923  		// Test behaviour with collectionId provided
   924  		suite.Run("Get transaction result by collection ID", func() {
   925  			getReq := &accessproto.GetTransactionRequest{
   926  				Id:           txId[:],
   927  				CollectionId: collectionId[:],
   928  			}
   929  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   930  			assertTransactionResult(resp, err)
   931  		})
   932  
   933  		suite.Run("Get transaction result with wrong collection ID but correct transaction ID", func() {
   934  			getReq := &accessproto.GetTransactionRequest{
   935  				Id:           txId[:],
   936  				CollectionId: collectionIdNegative[:],
   937  			}
   938  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   939  			require.Error(suite.T(), err)
   940  			require.Nil(suite.T(), resp)
   941  		})
   942  
   943  		suite.Run("Get transaction result with wrong transaction ID and correct collection ID", func() {
   944  			getReq := &accessproto.GetTransactionRequest{
   945  				Id:           txIdNegative[:],
   946  				CollectionId: collectionId[:],
   947  			}
   948  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   949  			require.Error(suite.T(), err)
   950  			require.Nil(suite.T(), resp)
   951  		})
   952  
   953  		// Test behaviour with blockId and collectionId provided
   954  		suite.Run("Get transaction result by block ID and collection ID", func() {
   955  			getReq := &accessproto.GetTransactionRequest{
   956  				Id:           txId[:],
   957  				BlockId:      blockId[:],
   958  				CollectionId: collectionId[:],
   959  			}
   960  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   961  			assertTransactionResult(resp, err)
   962  		})
   963  
   964  		suite.Run("Get transaction result by block ID with wrong collection ID", func() {
   965  			getReq := &accessproto.GetTransactionRequest{
   966  				Id:           txId[:],
   967  				BlockId:      blockId[:],
   968  				CollectionId: collectionIdNegative[:],
   969  			}
   970  			resp, err := handler.GetTransactionResult(context.Background(), getReq)
   971  			require.Error(suite.T(), err)
   972  			require.Nil(suite.T(), resp)
   973  		})
   974  	})
   975  }
   976  
   977  // TestExecuteScript tests the three execute Script related calls to make sure that the execution api is called with
   978  // the correct block id
   979  func (suite *Suite) TestExecuteScript() {
   980  	unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) {
   981  		all := util.StorageLayer(suite.T(), db)
   982  		transactions := bstorage.NewTransactions(suite.metrics, db)
   983  		collections := bstorage.NewCollections(db, transactions)
   984  		results := bstorage.NewExecutionResults(suite.metrics, db)
   985  		receipts := bstorage.NewExecutionReceipts(suite.metrics, db, results, bstorage.DefaultCacheSize)
   986  
   987  		identities := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution))
   988  		suite.sealedSnapshot.On("Identities", mock.Anything).Return(identities, nil)
   989  		suite.finalSnapshot.On("Identities", mock.Anything).Return(identities, nil)
   990  
   991  		// create a mock connection factory
   992  		connFactory := connectionmock.NewConnectionFactory(suite.T())
   993  		connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil)
   994  
   995  		var err error
   996  		suite.backend, err = backend.New(backend.Params{
   997  			State:                    suite.state,
   998  			CollectionRPC:            suite.collClient,
   999  			Blocks:                   all.Blocks,
  1000  			Headers:                  all.Headers,
  1001  			Collections:              collections,
  1002  			Transactions:             transactions,
  1003  			ExecutionReceipts:        receipts,
  1004  			ExecutionResults:         results,
  1005  			ChainID:                  suite.chainID,
  1006  			AccessMetrics:            suite.metrics,
  1007  			ConnFactory:              connFactory,
  1008  			MaxHeightRange:           backend.DefaultMaxHeightRange,
  1009  			FixedExecutionNodeIDs:    (identities.NodeIDs()).Strings(),
  1010  			Log:                      suite.log,
  1011  			SnapshotHistoryLimit:     backend.DefaultSnapshotHistoryLimit,
  1012  			Communicator:             backend.NewNodeCommunicator(false),
  1013  			ScriptExecutionMode:      backend.IndexQueryModeExecutionNodesOnly,
  1014  			TxErrorMessagesCacheSize: 1000,
  1015  			TxResultQueryMode:        backend.IndexQueryModeExecutionNodesOnly,
  1016  		})
  1017  		require.NoError(suite.T(), err)
  1018  
  1019  		handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me)
  1020  
  1021  		// initialize metrics related storage
  1022  		metrics := metrics.NewNoopCollector()
  1023  		collectionsToMarkFinalized, err := stdmap.NewTimes(100)
  1024  		require.NoError(suite.T(), err)
  1025  		collectionsToMarkExecuted, err := stdmap.NewTimes(100)
  1026  		require.NoError(suite.T(), err)
  1027  		blocksToMarkExecuted, err := stdmap.NewTimes(100)
  1028  		require.NoError(suite.T(), err)
  1029  
  1030  		collectionExecutedMetric, err := indexer.NewCollectionExecutedMetricImpl(
  1031  			suite.log,
  1032  			metrics,
  1033  			collectionsToMarkFinalized,
  1034  			collectionsToMarkExecuted,
  1035  			blocksToMarkExecuted,
  1036  			collections,
  1037  			all.Blocks,
  1038  		)
  1039  		require.NoError(suite.T(), err)
  1040  
  1041  		conduit := new(mocknetwork.Conduit)
  1042  		suite.net.On("Register", channels.ReceiveReceipts, mock.Anything).Return(conduit, nil).
  1043  			Once()
  1044  		// create the ingest engine
  1045  		ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections,
  1046  			transactions, results, receipts, collectionExecutedMetric)
  1047  		require.NoError(suite.T(), err)
  1048  
  1049  		// create another block as a predecessor of the block created earlier
  1050  		prevBlock := unittest.BlockWithParentFixture(suite.finalizedBlock)
  1051  
  1052  		// create a block and a seal pointing to that block
  1053  		lastBlock := unittest.BlockWithParentFixture(prevBlock.Header)
  1054  		err = all.Blocks.Store(lastBlock)
  1055  		require.NoError(suite.T(), err)
  1056  		err = db.Update(operation.IndexBlockHeight(lastBlock.Header.Height, lastBlock.ID()))
  1057  		require.NoError(suite.T(), err)
  1058  		//update latest sealed block
  1059  		suite.sealedBlock = lastBlock.Header
  1060  		// create execution receipts for each of the execution node and the last block
  1061  		executionReceipts := unittest.ReceiptsForBlockFixture(lastBlock, identities.NodeIDs())
  1062  		// notify the ingest engine about the receipts
  1063  		for _, r := range executionReceipts {
  1064  			err = ingestEng.ProcessLocal(r)
  1065  			require.NoError(suite.T(), err)
  1066  		}
  1067  
  1068  		err = all.Blocks.Store(prevBlock)
  1069  		require.NoError(suite.T(), err)
  1070  		err = db.Update(operation.IndexBlockHeight(prevBlock.Header.Height, prevBlock.ID()))
  1071  		require.NoError(suite.T(), err)
  1072  
  1073  		// create execution receipts for each of the execution node and the previous block
  1074  		executionReceipts = unittest.ReceiptsForBlockFixture(prevBlock, identities.NodeIDs())
  1075  		// notify the ingest engine about the receipts
  1076  		for _, r := range executionReceipts {
  1077  			err = ingestEng.ProcessLocal(r)
  1078  			require.NoError(suite.T(), err)
  1079  		}
  1080  
  1081  		ctx := context.Background()
  1082  
  1083  		script := []byte("dummy script")
  1084  
  1085  		// setupExecClientMock sets up the mock the execution client and returns the access response to expect
  1086  		setupExecClientMock := func(blockID flow.Identifier) *accessproto.ExecuteScriptResponse {
  1087  			id := blockID[:]
  1088  			executionReq := execproto.ExecuteScriptAtBlockIDRequest{
  1089  				BlockId: id,
  1090  				Script:  script,
  1091  			}
  1092  			executionResp := execproto.ExecuteScriptAtBlockIDResponse{
  1093  				Value: []byte{9, 10, 11},
  1094  			}
  1095  
  1096  			suite.execClient.On("ExecuteScriptAtBlockID", ctx, &executionReq).Return(&executionResp, nil).Once()
  1097  
  1098  			finalizedHeader := suite.finalizedHeaderCache.Get()
  1099  			finalizedHeaderId := finalizedHeader.ID()
  1100  			nodeId := suite.me.NodeID()
  1101  
  1102  			expectedResp := accessproto.ExecuteScriptResponse{
  1103  				Value: executionResp.GetValue(),
  1104  				Metadata: &entitiesproto.Metadata{
  1105  					LatestFinalizedBlockId: finalizedHeaderId[:],
  1106  					LatestFinalizedHeight:  finalizedHeader.Height,
  1107  					NodeId:                 nodeId[:],
  1108  				},
  1109  			}
  1110  			return &expectedResp
  1111  		}
  1112  
  1113  		assertResult := func(err error, expected interface{}, actual interface{}) {
  1114  			suite.Require().NoError(err)
  1115  			suite.Require().Equal(expected, actual)
  1116  			suite.execClient.AssertExpectations(suite.T())
  1117  		}
  1118  
  1119  		suite.Run("execute script at latest block", func() {
  1120  			suite.state.
  1121  				On("AtBlockID", lastBlock.ID()).
  1122  				Return(suite.sealedSnapshot, nil)
  1123  
  1124  			expectedResp := setupExecClientMock(lastBlock.ID())
  1125  			req := accessproto.ExecuteScriptAtLatestBlockRequest{
  1126  				Script: script,
  1127  			}
  1128  			actualResp, err := handler.ExecuteScriptAtLatestBlock(ctx, &req)
  1129  			assertResult(err, expectedResp, actualResp)
  1130  		})
  1131  
  1132  		suite.Run("execute script at block id", func() {
  1133  			suite.state.
  1134  				On("AtBlockID", prevBlock.ID()).
  1135  				Return(suite.sealedSnapshot, nil)
  1136  
  1137  			expectedResp := setupExecClientMock(prevBlock.ID())
  1138  			id := prevBlock.ID()
  1139  			req := accessproto.ExecuteScriptAtBlockIDRequest{
  1140  				BlockId: id[:],
  1141  				Script:  script,
  1142  			}
  1143  			actualResp, err := handler.ExecuteScriptAtBlockID(ctx, &req)
  1144  			assertResult(err, expectedResp, actualResp)
  1145  		})
  1146  
  1147  		suite.Run("execute script at block height", func() {
  1148  			suite.state.
  1149  				On("AtBlockID", prevBlock.ID()).
  1150  				Return(suite.sealedSnapshot, nil)
  1151  
  1152  			expectedResp := setupExecClientMock(prevBlock.ID())
  1153  			req := accessproto.ExecuteScriptAtBlockHeightRequest{
  1154  				BlockHeight: prevBlock.Header.Height,
  1155  				Script:      script,
  1156  			}
  1157  			actualResp, err := handler.ExecuteScriptAtBlockHeight(ctx, &req)
  1158  			assertResult(err, expectedResp, actualResp)
  1159  		})
  1160  	})
  1161  }
  1162  
  1163  // TestAPICallNodeVersionInfo tests the GetNodeVersionInfo query and check response returns correct node version
  1164  // information
  1165  func (suite *Suite) TestAPICallNodeVersionInfo() {
  1166  	suite.RunTest(func(handler *access.Handler, db *badger.DB, all *storage.All) {
  1167  		req := &accessproto.GetNodeVersionInfoRequest{}
  1168  		resp, err := handler.GetNodeVersionInfo(context.Background(), req)
  1169  		require.NoError(suite.T(), err)
  1170  		require.NotNil(suite.T(), resp)
  1171  
  1172  		respNodeVersionInfo := resp.Info
  1173  		suite.Require().Equal(respNodeVersionInfo, &entitiesproto.NodeVersionInfo{
  1174  			Semver:          build.Version(),
  1175  			Commit:          build.Commit(),
  1176  			SporkId:         suite.sporkID[:],
  1177  			ProtocolVersion: uint64(suite.protocolVersion),
  1178  		})
  1179  	})
  1180  }
  1181  
  1182  // TestLastFinalizedBlockHeightResult tests on example of the GetBlockHeaderByID function that the LastFinalizedBlock
  1183  // field in the response matches the finalized header from cache. It also tests that the LastFinalizedBlock field is
  1184  // updated correctly when a block with a greater height is finalized.
  1185  func (suite *Suite) TestLastFinalizedBlockHeightResult() {
  1186  	suite.RunTest(func(handler *access.Handler, db *badger.DB, all *storage.All) {
  1187  		block := unittest.BlockWithParentFixture(suite.finalizedBlock)
  1188  		newFinalizedBlock := unittest.BlockWithParentFixture(block.Header)
  1189  
  1190  		// store new block
  1191  		require.NoError(suite.T(), all.Blocks.Store(block))
  1192  
  1193  		assertFinalizedBlockHeader := func(resp *accessproto.BlockHeaderResponse, err error) {
  1194  			require.NoError(suite.T(), err)
  1195  			require.NotNil(suite.T(), resp)
  1196  
  1197  			finalizedHeaderId := suite.finalizedBlock.ID()
  1198  			nodeId := suite.me.NodeID()
  1199  
  1200  			require.Equal(suite.T(), &entitiesproto.Metadata{
  1201  				LatestFinalizedBlockId: finalizedHeaderId[:],
  1202  				LatestFinalizedHeight:  suite.finalizedBlock.Height,
  1203  				NodeId:                 nodeId[:],
  1204  			}, resp.Metadata)
  1205  		}
  1206  
  1207  		id := block.ID()
  1208  		req := &accessproto.GetBlockHeaderByIDRequest{
  1209  			Id: id[:],
  1210  		}
  1211  
  1212  		resp, err := handler.GetBlockHeaderByID(context.Background(), req)
  1213  		assertFinalizedBlockHeader(resp, err)
  1214  
  1215  		suite.finalizedBlock = newFinalizedBlock.Header
  1216  
  1217  		resp, err = handler.GetBlockHeaderByID(context.Background(), req)
  1218  		assertFinalizedBlockHeader(resp, err)
  1219  	})
  1220  }
  1221  
  1222  func (suite *Suite) createChain() (*flow.Block, *flow.Collection) {
  1223  	collection := unittest.CollectionFixture(10)
  1224  	refBlockID := unittest.IdentifierFixture()
  1225  	// prepare cluster committee members
  1226  	clusterCommittee := unittest.IdentityListFixture(32 * 4).Filter(filter.HasRole(flow.RoleCollection))
  1227  	// guarantee signers must be cluster committee members, so that access will fetch collection from
  1228  	// the signers that are specified by guarantee.SignerIndices
  1229  	indices, err := signature.EncodeSignersToIndices(clusterCommittee.NodeIDs(), clusterCommittee.NodeIDs())
  1230  	require.NoError(suite.T(), err)
  1231  	guarantee := &flow.CollectionGuarantee{
  1232  		CollectionID:     collection.ID(),
  1233  		Signature:        crypto.Signature([]byte("signature A")),
  1234  		ReferenceBlockID: refBlockID,
  1235  		SignerIndices:    indices,
  1236  	}
  1237  	block := unittest.BlockWithParentFixture(suite.finalizedBlock)
  1238  	block.SetPayload(unittest.PayloadFixture(unittest.WithGuarantees(guarantee)))
  1239  
  1240  	cluster := new(protocol.Cluster)
  1241  	cluster.On("Members").Return(clusterCommittee, nil)
  1242  	epoch := new(protocol.Epoch)
  1243  	epoch.On("ClusterByChainID", mock.Anything).Return(cluster, nil)
  1244  	epochs := new(protocol.EpochQuery)
  1245  	epochs.On("Current").Return(epoch)
  1246  	snap := new(protocol.Snapshot)
  1247  	snap.On("Epochs").Return(epochs).Maybe()
  1248  	snap.On("Params").Return(suite.params).Maybe()
  1249  	snap.On("Head").Return(block.Header, nil).Maybe()
  1250  
  1251  	suite.state.On("AtBlockID", refBlockID).Return(snap)
  1252  
  1253  	return block, &collection
  1254  }