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