github.com/onflow/flow-go@v0.33.17/engine/execution/computation/computer/computer_test.go (about)

     1  package computer_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"sync/atomic"
     8  	"testing"
     9  
    10  	"github.com/onflow/cadence"
    11  	"github.com/onflow/cadence/encoding/ccf"
    12  	"github.com/onflow/cadence/runtime"
    13  	"github.com/onflow/cadence/runtime/common"
    14  	"github.com/onflow/cadence/runtime/interpreter"
    15  	"github.com/onflow/cadence/runtime/sema"
    16  	"github.com/onflow/cadence/runtime/stdlib"
    17  
    18  	"github.com/ipfs/go-datastore"
    19  	dssync "github.com/ipfs/go-datastore/sync"
    20  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    21  	"github.com/rs/zerolog"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/mock"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"github.com/onflow/flow-go/engine/execution"
    27  	"github.com/onflow/flow-go/engine/execution/computation/committer"
    28  	"github.com/onflow/flow-go/engine/execution/computation/computer"
    29  	computermock "github.com/onflow/flow-go/engine/execution/computation/computer/mock"
    30  	"github.com/onflow/flow-go/engine/execution/storehouse"
    31  	"github.com/onflow/flow-go/engine/execution/testutil"
    32  	"github.com/onflow/flow-go/fvm"
    33  	"github.com/onflow/flow-go/fvm/environment"
    34  	fvmErrors "github.com/onflow/flow-go/fvm/errors"
    35  	fvmmock "github.com/onflow/flow-go/fvm/mock"
    36  	reusableRuntime "github.com/onflow/flow-go/fvm/runtime"
    37  	"github.com/onflow/flow-go/fvm/storage"
    38  	"github.com/onflow/flow-go/fvm/storage/derived"
    39  	"github.com/onflow/flow-go/fvm/storage/logical"
    40  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    41  	"github.com/onflow/flow-go/fvm/storage/state"
    42  	"github.com/onflow/flow-go/fvm/systemcontracts"
    43  	"github.com/onflow/flow-go/ledger"
    44  	"github.com/onflow/flow-go/ledger/common/convert"
    45  	"github.com/onflow/flow-go/ledger/common/pathfinder"
    46  	"github.com/onflow/flow-go/ledger/complete"
    47  	"github.com/onflow/flow-go/model/flow"
    48  	"github.com/onflow/flow-go/module/epochs"
    49  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    50  	"github.com/onflow/flow-go/module/executiondatasync/provider"
    51  	mocktracker "github.com/onflow/flow-go/module/executiondatasync/tracker/mock"
    52  	"github.com/onflow/flow-go/module/mempool/entity"
    53  	"github.com/onflow/flow-go/module/metrics"
    54  	modulemock "github.com/onflow/flow-go/module/mock"
    55  	requesterunit "github.com/onflow/flow-go/module/state_synchronization/requester/unittest"
    56  	"github.com/onflow/flow-go/module/trace"
    57  	"github.com/onflow/flow-go/utils/unittest"
    58  )
    59  
    60  const (
    61  	testMaxConcurrency = 2
    62  )
    63  
    64  func incStateCommitment(startState flow.StateCommitment) flow.StateCommitment {
    65  	endState := flow.StateCommitment(startState)
    66  	endState[0] += 1
    67  	return endState
    68  }
    69  
    70  type fakeCommitter struct {
    71  	callCount int
    72  }
    73  
    74  func (committer *fakeCommitter) CommitView(
    75  	view *snapshot.ExecutionSnapshot,
    76  	baseStorageSnapshot execution.ExtendableStorageSnapshot,
    77  ) (
    78  	flow.StateCommitment,
    79  	[]byte,
    80  	*ledger.TrieUpdate,
    81  	execution.ExtendableStorageSnapshot,
    82  	error,
    83  ) {
    84  	committer.callCount++
    85  
    86  	startState := baseStorageSnapshot.Commitment()
    87  	endState := incStateCommitment(startState)
    88  
    89  	reg := unittest.MakeOwnerReg("key", fmt.Sprintf("%v", committer.callCount))
    90  	regKey := convert.RegisterIDToLedgerKey(reg.Key)
    91  	path, err := pathfinder.KeyToPath(
    92  		regKey,
    93  		complete.DefaultPathFinderVersion,
    94  	)
    95  	if err != nil {
    96  		return flow.DummyStateCommitment, nil, nil, nil, err
    97  	}
    98  	trieUpdate := &ledger.TrieUpdate{
    99  		RootHash: ledger.RootHash(startState),
   100  		Paths: []ledger.Path{
   101  			path,
   102  		},
   103  		Payloads: []*ledger.Payload{
   104  			ledger.NewPayload(regKey, reg.Value),
   105  		},
   106  	}
   107  
   108  	newStorageSnapshot := baseStorageSnapshot.Extend(endState, map[flow.RegisterID]flow.RegisterValue{
   109  		reg.Key: reg.Value,
   110  	})
   111  
   112  	return newStorageSnapshot.Commitment(),
   113  		[]byte{byte(committer.callCount)},
   114  		trieUpdate,
   115  		newStorageSnapshot,
   116  		nil
   117  }
   118  
   119  func TestBlockExecutor_ExecuteBlock(t *testing.T) {
   120  
   121  	rag := &RandomAddressGenerator{}
   122  
   123  	executorID := unittest.IdentifierFixture()
   124  
   125  	me := new(modulemock.Local)
   126  	me.On("NodeID").Return(executorID)
   127  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   128  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   129  		Return(nil, nil)
   130  
   131  	t.Run("single collection", func(t *testing.T) {
   132  
   133  		execCtx := fvm.NewContext()
   134  
   135  		vm := &testVM{
   136  			t:                    t,
   137  			eventsPerTransaction: 1,
   138  		}
   139  
   140  		committer := &fakeCommitter{
   141  			callCount: 0,
   142  		}
   143  
   144  		exemetrics := new(modulemock.ExecutionMetrics)
   145  		exemetrics.On("ExecutionBlockExecuted",
   146  			mock.Anything,  // duration
   147  			mock.Anything). // stats
   148  			Return(nil).
   149  			Times(1)
   150  
   151  		exemetrics.On("ExecutionCollectionExecuted",
   152  			mock.Anything,  // duration
   153  			mock.Anything). // stats
   154  			Return(nil).
   155  			Times(2) // 1 collection + system collection
   156  
   157  		exemetrics.On("ExecutionTransactionExecuted",
   158  			mock.Anything, // duration
   159  			mock.Anything, // conflict retry count
   160  			mock.Anything, // computation used
   161  			mock.Anything, // memory used
   162  			mock.Anything, // number of events
   163  			mock.Anything, // size of events
   164  			false).        // no failure
   165  			Return(nil).
   166  			Times(2 + 1) // 2 txs in collection + system chunk tx
   167  
   168  		exemetrics.On(
   169  			"ExecutionChunkDataPackGenerated",
   170  			mock.Anything,
   171  			mock.Anything).
   172  			Return(nil).
   173  			Times(2) // 1 collection + system collection
   174  
   175  		expectedProgramsInCache := 1 // we set one program in the cache
   176  		exemetrics.On(
   177  			"ExecutionBlockCachedPrograms",
   178  			expectedProgramsInCache).
   179  			Return(nil).
   180  			Times(1) // 1 block
   181  
   182  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   183  		trackerStorage := mocktracker.NewMockStorage()
   184  
   185  		prov := provider.NewProvider(
   186  			zerolog.Nop(),
   187  			metrics.NewNoopCollector(),
   188  			execution_data.DefaultSerializer,
   189  			bservice,
   190  			trackerStorage,
   191  		)
   192  
   193  		exe, err := computer.NewBlockComputer(
   194  			vm,
   195  			execCtx,
   196  			exemetrics,
   197  			trace.NewNoopTracer(),
   198  			zerolog.Nop(),
   199  			committer,
   200  			me,
   201  			prov,
   202  			nil,
   203  			testutil.ProtocolStateWithSourceFixture(nil),
   204  			testMaxConcurrency)
   205  		require.NoError(t, err)
   206  
   207  		// create a block with 1 collection with 2 transactions
   208  		block := generateBlock(1, 2, rag)
   209  
   210  		parentBlockExecutionResultID := unittest.IdentifierFixture()
   211  		result, err := exe.ExecuteBlock(
   212  			context.Background(),
   213  			parentBlockExecutionResultID,
   214  			block,
   215  			nil,
   216  			derived.NewEmptyDerivedBlockData(0))
   217  		assert.NoError(t, err)
   218  		assert.Len(t, result.AllExecutionSnapshots(), 1+1) // +1 system chunk
   219  
   220  		require.Equal(t, 2, committer.callCount)
   221  
   222  		assert.Equal(t, block.ID(), result.BlockExecutionData.BlockID)
   223  
   224  		expectedChunk1EndState := incStateCommitment(*block.StartState)
   225  		expectedChunk2EndState := incStateCommitment(expectedChunk1EndState)
   226  
   227  		assert.Equal(t, expectedChunk2EndState, result.CurrentEndState())
   228  
   229  		assertEventHashesMatch(t, 1+1, result)
   230  
   231  		// Verify ExecutionReceipt
   232  		receipt := result.ExecutionReceipt
   233  
   234  		assert.Equal(t, executorID, receipt.ExecutorID)
   235  		assert.Equal(
   236  			t,
   237  			parentBlockExecutionResultID,
   238  			receipt.PreviousResultID)
   239  		assert.Equal(t, block.ID(), receipt.BlockID)
   240  		assert.NotEqual(t, flow.ZeroID, receipt.ExecutionDataID)
   241  
   242  		assert.Len(t, receipt.Chunks, 1+1) // +1 system chunk
   243  
   244  		chunk1 := receipt.Chunks[0]
   245  
   246  		eventCommits := result.AllEventCommitments()
   247  		assert.Equal(t, block.ID(), chunk1.BlockID)
   248  		assert.Equal(t, uint(0), chunk1.CollectionIndex)
   249  		assert.Equal(t, uint64(2), chunk1.NumberOfTransactions)
   250  		assert.Equal(t, eventCommits[0], chunk1.EventCollection)
   251  
   252  		assert.Equal(t, *block.StartState, chunk1.StartState)
   253  
   254  		assert.NotEqual(t, *block.StartState, chunk1.EndState)
   255  		assert.NotEqual(t, flow.DummyStateCommitment, chunk1.EndState)
   256  		assert.Equal(t, expectedChunk1EndState, chunk1.EndState)
   257  
   258  		chunk2 := receipt.Chunks[1]
   259  		assert.Equal(t, block.ID(), chunk2.BlockID)
   260  		assert.Equal(t, uint(1), chunk2.CollectionIndex)
   261  		assert.Equal(t, uint64(1), chunk2.NumberOfTransactions)
   262  		assert.Equal(t, eventCommits[1], chunk2.EventCollection)
   263  
   264  		assert.Equal(t, expectedChunk1EndState, chunk2.StartState)
   265  
   266  		assert.NotEqual(t, *block.StartState, chunk2.EndState)
   267  		assert.NotEqual(t, flow.DummyStateCommitment, chunk2.EndState)
   268  		assert.NotEqual(t, expectedChunk1EndState, chunk2.EndState)
   269  		assert.Equal(t, expectedChunk2EndState, chunk2.EndState)
   270  
   271  		// Verify ChunkDataPacks
   272  
   273  		chunkDataPacks := result.AllChunkDataPacks()
   274  		assert.Len(t, chunkDataPacks, 1+1) // +1 system chunk
   275  
   276  		chunkDataPack1 := chunkDataPacks[0]
   277  
   278  		assert.Equal(t, chunk1.ID(), chunkDataPack1.ChunkID)
   279  		assert.Equal(t, *block.StartState, chunkDataPack1.StartState)
   280  		assert.Equal(t, []byte{1}, chunkDataPack1.Proof)
   281  		assert.NotNil(t, chunkDataPack1.Collection)
   282  
   283  		chunkDataPack2 := chunkDataPacks[1]
   284  
   285  		assert.Equal(t, chunk2.ID(), chunkDataPack2.ChunkID)
   286  		assert.Equal(t, chunk2.StartState, chunkDataPack2.StartState)
   287  		assert.Equal(t, []byte{2}, chunkDataPack2.Proof)
   288  		assert.Nil(t, chunkDataPack2.Collection)
   289  
   290  		// Verify BlockExecutionData
   291  
   292  		assert.Len(t, result.ChunkExecutionDatas, 1+1) // +1 system chunk
   293  
   294  		chunkExecutionData1 := result.ChunkExecutionDatas[0]
   295  		assert.Equal(
   296  			t,
   297  			chunkDataPack1.Collection,
   298  			chunkExecutionData1.Collection)
   299  		assert.NotNil(t, chunkExecutionData1.TrieUpdate)
   300  		assert.Equal(t, ledger.RootHash(chunk1.StartState), chunkExecutionData1.TrieUpdate.RootHash)
   301  
   302  		chunkExecutionData2 := result.ChunkExecutionDatas[1]
   303  		assert.NotNil(t, chunkExecutionData2.Collection)
   304  		assert.NotNil(t, chunkExecutionData2.TrieUpdate)
   305  		assert.Equal(t, ledger.RootHash(chunk2.StartState), chunkExecutionData2.TrieUpdate.RootHash)
   306  
   307  		assert.GreaterOrEqual(t, vm.CallCount(), 3)
   308  		// if every transaction is retried once, then the call count should be
   309  		// (1+totalTransactionCount) /2 * totalTransactionCount
   310  		assert.LessOrEqual(t, vm.CallCount(), (1+3)/2*3)
   311  	})
   312  
   313  	t.Run("empty block still computes system chunk", func(t *testing.T) {
   314  
   315  		execCtx := fvm.NewContext()
   316  
   317  		vm := new(fvmmock.VM)
   318  		committer := new(computermock.ViewCommitter)
   319  
   320  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   321  		trackerStorage := mocktracker.NewMockStorage()
   322  
   323  		prov := provider.NewProvider(
   324  			zerolog.Nop(),
   325  			metrics.NewNoopCollector(),
   326  			execution_data.DefaultSerializer,
   327  			bservice,
   328  			trackerStorage,
   329  		)
   330  
   331  		exe, err := computer.NewBlockComputer(
   332  			vm,
   333  			execCtx,
   334  			metrics.NewNoopCollector(),
   335  			trace.NewNoopTracer(),
   336  			zerolog.Nop(),
   337  			committer,
   338  			me,
   339  			prov,
   340  			nil,
   341  			testutil.ProtocolStateWithSourceFixture(nil),
   342  			testMaxConcurrency)
   343  		require.NoError(t, err)
   344  
   345  		// create an empty block
   346  		block := generateBlock(0, 0, rag)
   347  		derivedBlockData := derived.NewEmptyDerivedBlockData(0)
   348  
   349  		vm.On("NewExecutor", mock.Anything, mock.Anything, mock.Anything).
   350  			Return(noOpExecutor{}).
   351  			Once() // just system chunk
   352  
   353  		snapshot := storehouse.NewExecutingBlockSnapshot(
   354  			snapshot.MapStorageSnapshot{},
   355  			unittest.StateCommitmentFixture(),
   356  		)
   357  
   358  		committer.On("CommitView", mock.Anything, mock.Anything).
   359  			Return(nil, nil, nil, snapshot, nil).
   360  			Once() // just system chunk
   361  
   362  		result, err := exe.ExecuteBlock(
   363  			context.Background(),
   364  			unittest.IdentifierFixture(),
   365  			block,
   366  			nil,
   367  			derivedBlockData)
   368  		assert.NoError(t, err)
   369  		assert.Len(t, result.AllExecutionSnapshots(), 1)
   370  		assert.Len(t, result.AllTransactionResults(), 1)
   371  		assert.Len(t, result.ChunkExecutionDatas, 1)
   372  
   373  		assertEventHashesMatch(t, 1, result)
   374  
   375  		vm.AssertExpectations(t)
   376  	})
   377  
   378  	t.Run("system chunk transaction should not fail", func(t *testing.T) {
   379  
   380  		// include all fees. System chunk should ignore them
   381  		contextOptions := []fvm.Option{
   382  			fvm.WithTransactionFeesEnabled(true),
   383  			fvm.WithAccountStorageLimit(true),
   384  			fvm.WithBlocks(&environment.NoopBlockFinder{}),
   385  		}
   386  		// set 0 clusters to pass n_collectors >= n_clusters check
   387  		epochConfig := epochs.DefaultEpochConfig()
   388  		epochConfig.NumCollectorClusters = 0
   389  		bootstrapOptions := []fvm.BootstrapProcedureOption{
   390  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   391  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   392  			fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
   393  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   394  			fvm.WithEpochConfig(epochConfig),
   395  		}
   396  
   397  		chain := flow.Localnet.Chain()
   398  		vm := fvm.NewVirtualMachine()
   399  		derivedBlockData := derived.NewEmptyDerivedBlockData(0)
   400  		baseOpts := []fvm.Option{
   401  			fvm.WithChain(chain),
   402  			fvm.WithDerivedBlockData(derivedBlockData),
   403  		}
   404  
   405  		opts := append(baseOpts, contextOptions...)
   406  		ctx := fvm.NewContext(opts...)
   407  		snapshotTree := snapshot.NewSnapshotTree(nil)
   408  
   409  		baseBootstrapOpts := []fvm.BootstrapProcedureOption{
   410  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   411  		}
   412  		bootstrapOpts := append(baseBootstrapOpts, bootstrapOptions...)
   413  		executionSnapshot, _, err := vm.Run(
   414  			ctx,
   415  			fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...),
   416  			snapshotTree)
   417  		require.NoError(t, err)
   418  
   419  		snapshotTree = snapshotTree.Append(executionSnapshot)
   420  
   421  		comm := new(computermock.ViewCommitter)
   422  
   423  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   424  		trackerStorage := mocktracker.NewMockStorage()
   425  
   426  		prov := provider.NewProvider(
   427  			zerolog.Nop(),
   428  			metrics.NewNoopCollector(),
   429  			execution_data.DefaultSerializer,
   430  			bservice,
   431  			trackerStorage,
   432  		)
   433  
   434  		exe, err := computer.NewBlockComputer(
   435  			vm,
   436  			ctx,
   437  			metrics.NewNoopCollector(),
   438  			trace.NewNoopTracer(),
   439  			zerolog.Nop(),
   440  			comm,
   441  			me,
   442  			prov,
   443  			nil,
   444  			testutil.ProtocolStateWithSourceFixture(nil),
   445  			testMaxConcurrency)
   446  		require.NoError(t, err)
   447  
   448  		// create an empty block
   449  		block := generateBlock(0, 0, rag)
   450  
   451  		snapshot := storehouse.NewExecutingBlockSnapshot(
   452  			snapshot.MapStorageSnapshot{},
   453  			unittest.StateCommitmentFixture(),
   454  		)
   455  
   456  		comm.On("CommitView", mock.Anything, mock.Anything).
   457  			Return(nil, nil, nil, snapshot, nil).
   458  			Once() // just system chunk
   459  
   460  		result, err := exe.ExecuteBlock(
   461  			context.Background(),
   462  			unittest.IdentifierFixture(),
   463  			block,
   464  			snapshotTree,
   465  			derivedBlockData.NewChildDerivedBlockData())
   466  		assert.NoError(t, err)
   467  		assert.Len(t, result.AllExecutionSnapshots(), 1)
   468  		assert.Len(t, result.AllTransactionResults(), 1)
   469  		assert.Len(t, result.ChunkExecutionDatas, 1)
   470  
   471  		assert.Empty(t, result.AllTransactionResults()[0].ErrorMessage)
   472  	})
   473  
   474  	t.Run("multiple collections", func(t *testing.T) {
   475  		execCtx := fvm.NewContext()
   476  
   477  		committer := new(computermock.ViewCommitter)
   478  
   479  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   480  		trackerStorage := mocktracker.NewMockStorage()
   481  
   482  		prov := provider.NewProvider(
   483  			zerolog.Nop(),
   484  			metrics.NewNoopCollector(),
   485  			execution_data.DefaultSerializer,
   486  			bservice,
   487  			trackerStorage,
   488  		)
   489  
   490  		eventsPerTransaction := 2
   491  		vm := &testVM{
   492  			t:                    t,
   493  			eventsPerTransaction: eventsPerTransaction,
   494  			err: fvmErrors.NewInvalidAddressErrorf(
   495  				flow.EmptyAddress,
   496  				"no payer address provided"),
   497  		}
   498  
   499  		exe, err := computer.NewBlockComputer(
   500  			vm,
   501  			execCtx,
   502  			metrics.NewNoopCollector(),
   503  			trace.NewNoopTracer(),
   504  			zerolog.Nop(),
   505  			committer,
   506  			me,
   507  			prov,
   508  			nil,
   509  			testutil.ProtocolStateWithSourceFixture(nil),
   510  			testMaxConcurrency)
   511  		require.NoError(t, err)
   512  
   513  		collectionCount := 2
   514  		transactionsPerCollection := 2
   515  		eventsPerCollection := eventsPerTransaction * transactionsPerCollection
   516  		totalTransactionCount := (collectionCount * transactionsPerCollection) + 1 // +1 for system chunk
   517  		// totalEventCount := eventsPerTransaction * totalTransactionCount
   518  
   519  		// create a block with 2 collections with 2 transactions each
   520  		block := generateBlock(collectionCount, transactionsPerCollection, rag)
   521  		derivedBlockData := derived.NewEmptyDerivedBlockData(0)
   522  
   523  		snapshot := storehouse.NewExecutingBlockSnapshot(
   524  			snapshot.MapStorageSnapshot{},
   525  			unittest.StateCommitmentFixture(),
   526  		)
   527  
   528  		committer.On("CommitView", mock.Anything, mock.Anything).
   529  			Return(nil, nil, nil, snapshot, nil).
   530  			Times(collectionCount + 1)
   531  
   532  		result, err := exe.ExecuteBlock(
   533  			context.Background(),
   534  			unittest.IdentifierFixture(),
   535  			block,
   536  			nil,
   537  			derivedBlockData)
   538  		assert.NoError(t, err)
   539  
   540  		// chunk count should match collection count
   541  		assert.Equal(t, result.BlockExecutionResult.Size(), collectionCount+1) // system chunk
   542  
   543  		// all events should have been collected
   544  		for i := 0; i < collectionCount; i++ {
   545  			events := result.CollectionExecutionResultAt(i).Events()
   546  			assert.Len(t, events, eventsPerCollection)
   547  		}
   548  
   549  		// system chunk
   550  		assert.Len(t, result.CollectionExecutionResultAt(collectionCount).Events(), eventsPerTransaction)
   551  
   552  		events := result.AllEvents()
   553  
   554  		// events should have been indexed by transaction and event
   555  		k := 0
   556  		for expectedTxIndex := 0; expectedTxIndex < totalTransactionCount; expectedTxIndex++ {
   557  			for expectedEventIndex := 0; expectedEventIndex < eventsPerTransaction; expectedEventIndex++ {
   558  				e := events[k]
   559  				assert.EqualValues(t, expectedEventIndex, int(e.EventIndex))
   560  				assert.EqualValues(t, expectedTxIndex, e.TransactionIndex)
   561  				k++
   562  			}
   563  		}
   564  
   565  		expectedResults := make([]flow.TransactionResult, 0)
   566  		for _, c := range block.CompleteCollections {
   567  			for _, t := range c.Transactions {
   568  				txResult := flow.TransactionResult{
   569  					TransactionID: t.ID(),
   570  					ErrorMessage: fvmErrors.NewInvalidAddressErrorf(
   571  						flow.EmptyAddress,
   572  						"no payer address provided").Error(),
   573  				}
   574  				expectedResults = append(expectedResults, txResult)
   575  			}
   576  		}
   577  		txResults := result.AllTransactionResults()
   578  		assert.ElementsMatch(t, expectedResults, txResults[0:len(txResults)-1]) // strip system chunk
   579  
   580  		assertEventHashesMatch(t, collectionCount+1, result)
   581  
   582  		assert.GreaterOrEqual(t, vm.CallCount(), totalTransactionCount)
   583  		// if every transaction is retried once, then the call count should be
   584  		// (1+totalTransactionCount) /2 * totalTransactionCount
   585  		assert.LessOrEqual(t, vm.CallCount(), (1+totalTransactionCount)/2*totalTransactionCount)
   586  	})
   587  
   588  	t.Run(
   589  		"service events are emitted", func(t *testing.T) {
   590  			execCtx := fvm.NewContext(
   591  				fvm.WithServiceEventCollectionEnabled(),
   592  				fvm.WithAuthorizationChecksEnabled(false),
   593  				fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   594  			)
   595  
   596  			collectionCount := 2
   597  			transactionsPerCollection := 2
   598  
   599  			// create a block with 2 collections with 2 transactions each
   600  			block := generateBlock(collectionCount, transactionsPerCollection, rag)
   601  
   602  			serviceEvents := systemcontracts.ServiceEventsForChain(execCtx.Chain.ChainID())
   603  
   604  			randomSource := unittest.EpochSetupRandomSourceFixture()
   605  			payload, err := ccf.Decode(nil, unittest.EpochSetupFixtureCCF(randomSource))
   606  			require.NoError(t, err)
   607  
   608  			serviceEventA, ok := payload.(cadence.Event)
   609  			require.True(t, ok)
   610  
   611  			serviceEventA.EventType.Location = common.AddressLocation{
   612  				Address: common.Address(serviceEvents.EpochSetup.Address),
   613  			}
   614  			serviceEventA.EventType.QualifiedIdentifier = serviceEvents.EpochSetup.QualifiedIdentifier()
   615  
   616  			payload, err = ccf.Decode(nil, unittest.EpochCommitFixtureCCF)
   617  			require.NoError(t, err)
   618  
   619  			serviceEventB, ok := payload.(cadence.Event)
   620  			require.True(t, ok)
   621  
   622  			serviceEventB.EventType.Location = common.AddressLocation{
   623  				Address: common.Address(serviceEvents.EpochCommit.Address),
   624  			}
   625  			serviceEventB.EventType.QualifiedIdentifier = serviceEvents.EpochCommit.QualifiedIdentifier()
   626  
   627  			payload, err = ccf.Decode(nil, unittest.VersionBeaconFixtureCCF)
   628  			require.NoError(t, err)
   629  
   630  			serviceEventC, ok := payload.(cadence.Event)
   631  			require.True(t, ok)
   632  
   633  			serviceEventC.EventType.Location = common.AddressLocation{
   634  				Address: common.Address(serviceEvents.VersionBeacon.Address),
   635  			}
   636  			serviceEventC.EventType.QualifiedIdentifier = serviceEvents.VersionBeacon.QualifiedIdentifier()
   637  
   638  			transactions := []*flow.TransactionBody{}
   639  			for _, col := range block.Collections() {
   640  				transactions = append(transactions, col.Transactions...)
   641  			}
   642  
   643  			// events to emit for each iteration/transaction
   644  			events := map[common.Location][]cadence.Event{
   645  				common.TransactionLocation(transactions[0].ID()): nil,
   646  				common.TransactionLocation(transactions[1].ID()): {
   647  					serviceEventA,
   648  					{
   649  						EventType: &cadence.EventType{
   650  							Location:            stdlib.FlowLocation{},
   651  							QualifiedIdentifier: "what.ever",
   652  						},
   653  					},
   654  				},
   655  				common.TransactionLocation(transactions[2].ID()): {
   656  					{
   657  						EventType: &cadence.EventType{
   658  							Location:            stdlib.FlowLocation{},
   659  							QualifiedIdentifier: "what.ever",
   660  						},
   661  					},
   662  				},
   663  				common.TransactionLocation(transactions[3].ID()): nil,
   664  			}
   665  
   666  			systemTransactionEvents := []cadence.Event{
   667  				serviceEventB,
   668  				serviceEventC,
   669  			}
   670  
   671  			emittingRuntime := &testRuntime{
   672  				executeTransaction: func(
   673  					script runtime.Script,
   674  					context runtime.Context,
   675  				) error {
   676  					scriptEvents, ok := events[context.Location]
   677  					if !ok {
   678  						scriptEvents = systemTransactionEvents
   679  					}
   680  
   681  					for _, e := range scriptEvents {
   682  						err := context.Interface.EmitEvent(e)
   683  						if err != nil {
   684  							return err
   685  						}
   686  					}
   687  					return nil
   688  				},
   689  				readStored: func(
   690  					address common.Address,
   691  					path cadence.Path,
   692  					r runtime.Context,
   693  				) (cadence.Value, error) {
   694  					return nil, nil
   695  				},
   696  			}
   697  
   698  			execCtx = fvm.NewContextFromParent(
   699  				execCtx,
   700  				fvm.WithReusableCadenceRuntimePool(
   701  					reusableRuntime.NewCustomReusableCadenceRuntimePool(
   702  						0,
   703  						runtime.Config{},
   704  						func(_ runtime.Config) runtime.Runtime {
   705  							return emittingRuntime
   706  						},
   707  					),
   708  				),
   709  			)
   710  
   711  			vm := fvm.NewVirtualMachine()
   712  
   713  			bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   714  			trackerStorage := mocktracker.NewMockStorage()
   715  
   716  			prov := provider.NewProvider(
   717  				zerolog.Nop(),
   718  				metrics.NewNoopCollector(),
   719  				execution_data.DefaultSerializer,
   720  				bservice,
   721  				trackerStorage,
   722  			)
   723  
   724  			exe, err := computer.NewBlockComputer(
   725  				vm,
   726  				execCtx,
   727  				metrics.NewNoopCollector(),
   728  				trace.NewNoopTracer(),
   729  				zerolog.Nop(),
   730  				committer.NewNoopViewCommitter(),
   731  				me,
   732  				prov,
   733  				nil,
   734  				testutil.ProtocolStateWithSourceFixture(nil),
   735  				testMaxConcurrency)
   736  			require.NoError(t, err)
   737  
   738  			result, err := exe.ExecuteBlock(
   739  				context.Background(),
   740  				unittest.IdentifierFixture(),
   741  				block,
   742  				nil,
   743  				derived.NewEmptyDerivedBlockData(0),
   744  			)
   745  			require.NoError(t, err)
   746  
   747  			// make sure event index sequence are valid
   748  			for i := 0; i < result.BlockExecutionResult.Size(); i++ {
   749  				collectionResult := result.CollectionExecutionResultAt(i)
   750  				unittest.EnsureEventsIndexSeq(t, collectionResult.Events(), execCtx.Chain.ChainID())
   751  			}
   752  
   753  			sEvents := result.AllServiceEvents() // all events should have been collected
   754  			require.Len(t, sEvents, 3)
   755  
   756  			// events are ordered
   757  			require.Equal(
   758  				t,
   759  				serviceEventA.EventType.ID(),
   760  				string(sEvents[0].Type),
   761  			)
   762  			require.Equal(
   763  				t,
   764  				serviceEventB.EventType.ID(),
   765  				string(sEvents[1].Type),
   766  			)
   767  
   768  			require.Equal(
   769  				t,
   770  				serviceEventC.EventType.ID(),
   771  				string(sEvents[2].Type),
   772  			)
   773  
   774  			assertEventHashesMatch(t, collectionCount+1, result)
   775  		},
   776  	)
   777  
   778  	t.Run("succeeding transactions store programs", func(t *testing.T) {
   779  
   780  		execCtx := fvm.NewContext()
   781  
   782  		address := common.Address{0x1}
   783  		contractLocation := common.AddressLocation{
   784  			Address: address,
   785  			Name:    "Test",
   786  		}
   787  
   788  		contractProgram := &interpreter.Program{}
   789  
   790  		rt := &testRuntime{
   791  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   792  
   793  				_, err := r.Interface.GetOrLoadProgram(
   794  					contractLocation,
   795  					func() (*interpreter.Program, error) {
   796  						return contractProgram, nil
   797  					},
   798  				)
   799  				require.NoError(t, err)
   800  
   801  				return nil
   802  			},
   803  			readStored: func(
   804  				address common.Address,
   805  				path cadence.Path,
   806  				r runtime.Context,
   807  			) (cadence.Value, error) {
   808  				return nil, nil
   809  			},
   810  		}
   811  
   812  		execCtx = fvm.NewContextFromParent(
   813  			execCtx,
   814  			fvm.WithReusableCadenceRuntimePool(
   815  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   816  					0,
   817  					runtime.Config{},
   818  					func(_ runtime.Config) runtime.Runtime {
   819  						return rt
   820  					})))
   821  
   822  		vm := fvm.NewVirtualMachine()
   823  
   824  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   825  		trackerStorage := mocktracker.NewMockStorage()
   826  
   827  		prov := provider.NewProvider(
   828  			zerolog.Nop(),
   829  			metrics.NewNoopCollector(),
   830  			execution_data.DefaultSerializer,
   831  			bservice,
   832  			trackerStorage,
   833  		)
   834  
   835  		exe, err := computer.NewBlockComputer(
   836  			vm,
   837  			execCtx,
   838  			metrics.NewNoopCollector(),
   839  			trace.NewNoopTracer(),
   840  			zerolog.Nop(),
   841  			committer.NewNoopViewCommitter(),
   842  			me,
   843  			prov,
   844  			nil,
   845  			testutil.ProtocolStateWithSourceFixture(nil),
   846  			testMaxConcurrency)
   847  		require.NoError(t, err)
   848  
   849  		const collectionCount = 2
   850  		const transactionCount = 2
   851  		block := generateBlock(collectionCount, transactionCount, rag)
   852  
   853  		key := flow.AccountStatusRegisterID(
   854  			flow.BytesToAddress(address.Bytes()))
   855  		value := environment.NewAccountStatus().ToBytes()
   856  
   857  		result, err := exe.ExecuteBlock(
   858  			context.Background(),
   859  			unittest.IdentifierFixture(),
   860  			block,
   861  			snapshot.MapStorageSnapshot{key: value},
   862  			derived.NewEmptyDerivedBlockData(0))
   863  		assert.NoError(t, err)
   864  		assert.Len(t, result.AllExecutionSnapshots(), collectionCount+1) // +1 system chunk
   865  	})
   866  
   867  	t.Run("failing transactions do not store programs", func(t *testing.T) {
   868  		execCtx := fvm.NewContext(
   869  			fvm.WithAuthorizationChecksEnabled(false),
   870  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   871  		)
   872  
   873  		address := common.Address{0x1}
   874  
   875  		contractLocation := common.AddressLocation{
   876  			Address: address,
   877  			Name:    "Test",
   878  		}
   879  
   880  		contractProgram := &interpreter.Program{}
   881  
   882  		const collectionCount = 2
   883  		const transactionCount = 2
   884  		block := generateBlock(collectionCount, transactionCount, rag)
   885  
   886  		normalTransactions := map[common.Location]struct{}{}
   887  		for _, col := range block.Collections() {
   888  			for _, txn := range col.Transactions {
   889  				loc := common.TransactionLocation(txn.ID())
   890  				normalTransactions[loc] = struct{}{}
   891  			}
   892  		}
   893  
   894  		rt := &testRuntime{
   895  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   896  
   897  				// NOTE: set a program and revert all transactions but the
   898  				// system chunk transaction
   899  				_, err := r.Interface.GetOrLoadProgram(
   900  					contractLocation,
   901  					func() (*interpreter.Program, error) {
   902  						return contractProgram, nil
   903  					},
   904  				)
   905  				require.NoError(t, err)
   906  
   907  				_, ok := normalTransactions[r.Location]
   908  				if ok {
   909  					return runtime.Error{
   910  						Err: fmt.Errorf("TX reverted"),
   911  					}
   912  				}
   913  
   914  				return nil
   915  			},
   916  			readStored: func(
   917  				address common.Address,
   918  				path cadence.Path,
   919  				r runtime.Context,
   920  			) (cadence.Value, error) {
   921  				return nil, nil
   922  			},
   923  		}
   924  
   925  		execCtx = fvm.NewContextFromParent(
   926  			execCtx,
   927  			fvm.WithReusableCadenceRuntimePool(
   928  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   929  					0,
   930  					runtime.Config{},
   931  					func(_ runtime.Config) runtime.Runtime {
   932  						return rt
   933  					})))
   934  
   935  		vm := fvm.NewVirtualMachine()
   936  
   937  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   938  		trackerStorage := mocktracker.NewMockStorage()
   939  
   940  		prov := provider.NewProvider(
   941  			zerolog.Nop(),
   942  			metrics.NewNoopCollector(),
   943  			execution_data.DefaultSerializer,
   944  			bservice,
   945  			trackerStorage,
   946  		)
   947  
   948  		exe, err := computer.NewBlockComputer(
   949  			vm,
   950  			execCtx,
   951  			metrics.NewNoopCollector(),
   952  			trace.NewNoopTracer(),
   953  			zerolog.Nop(),
   954  			committer.NewNoopViewCommitter(),
   955  			me,
   956  			prov,
   957  			nil,
   958  			testutil.ProtocolStateWithSourceFixture(nil),
   959  			testMaxConcurrency)
   960  		require.NoError(t, err)
   961  
   962  		key := flow.AccountStatusRegisterID(
   963  			flow.BytesToAddress(address.Bytes()))
   964  		value := environment.NewAccountStatus().ToBytes()
   965  
   966  		result, err := exe.ExecuteBlock(
   967  			context.Background(),
   968  			unittest.IdentifierFixture(),
   969  			block,
   970  			snapshot.MapStorageSnapshot{key: value},
   971  			derived.NewEmptyDerivedBlockData(0))
   972  		require.NoError(t, err)
   973  		assert.Len(t, result.AllExecutionSnapshots(), collectionCount+1) // +1 system chunk
   974  	})
   975  
   976  	t.Run("internal error", func(t *testing.T) {
   977  		execCtx := fvm.NewContext()
   978  
   979  		committer := new(computermock.ViewCommitter)
   980  
   981  		bservice := requesterunit.MockBlobService(
   982  			blockstore.NewBlockstore(
   983  				dssync.MutexWrap(datastore.NewMapDatastore())))
   984  		trackerStorage := mocktracker.NewMockStorage()
   985  
   986  		prov := provider.NewProvider(
   987  			zerolog.Nop(),
   988  			metrics.NewNoopCollector(),
   989  			execution_data.DefaultSerializer,
   990  			bservice,
   991  			trackerStorage)
   992  
   993  		exe, err := computer.NewBlockComputer(
   994  			errorVM{errorAt: 5},
   995  			execCtx,
   996  			metrics.NewNoopCollector(),
   997  			trace.NewNoopTracer(),
   998  			zerolog.Nop(),
   999  			committer,
  1000  			me,
  1001  			prov,
  1002  			nil,
  1003  			testutil.ProtocolStateWithSourceFixture(nil),
  1004  			testMaxConcurrency)
  1005  		require.NoError(t, err)
  1006  
  1007  		collectionCount := 5
  1008  		transactionsPerCollection := 3
  1009  		block := generateBlock(collectionCount, transactionsPerCollection, rag)
  1010  
  1011  		snapshot := storehouse.NewExecutingBlockSnapshot(
  1012  			snapshot.MapStorageSnapshot{},
  1013  			unittest.StateCommitmentFixture(),
  1014  		)
  1015  
  1016  		committer.On("CommitView", mock.Anything, mock.Anything).
  1017  			Return(nil, nil, nil, snapshot, nil).
  1018  			Times(collectionCount + 1)
  1019  
  1020  		_, err = exe.ExecuteBlock(
  1021  			context.Background(),
  1022  			unittest.IdentifierFixture(),
  1023  			block,
  1024  			nil,
  1025  			derived.NewEmptyDerivedBlockData(0))
  1026  		assert.ErrorContains(t, err, "boom - internal error")
  1027  	})
  1028  
  1029  }
  1030  
  1031  func assertEventHashesMatch(
  1032  	t *testing.T,
  1033  	expectedNoOfChunks int,
  1034  	result *execution.ComputationResult,
  1035  ) {
  1036  	execResSize := result.BlockExecutionResult.Size()
  1037  	attestResSize := result.BlockAttestationResult.Size()
  1038  	require.Equal(t, execResSize, expectedNoOfChunks)
  1039  	require.Equal(t, execResSize, attestResSize)
  1040  
  1041  	for i := 0; i < expectedNoOfChunks; i++ {
  1042  		events := result.CollectionExecutionResultAt(i).Events()
  1043  		calculatedHash, err := flow.EventsMerkleRootHash(events)
  1044  		require.NoError(t, err)
  1045  		require.Equal(t, calculatedHash, result.CollectionAttestationResultAt(i).EventCommitment())
  1046  	}
  1047  }
  1048  
  1049  type testTransactionExecutor struct {
  1050  	executeTransaction func(runtime.Script, runtime.Context) error
  1051  
  1052  	script  runtime.Script
  1053  	context runtime.Context
  1054  }
  1055  
  1056  func (executor *testTransactionExecutor) Preprocess() error {
  1057  	// Do nothing.
  1058  	return nil
  1059  }
  1060  
  1061  func (executor *testTransactionExecutor) Execute() error {
  1062  	return executor.executeTransaction(executor.script, executor.context)
  1063  }
  1064  
  1065  func (executor *testTransactionExecutor) Result() (cadence.Value, error) {
  1066  	panic("Result not expected")
  1067  }
  1068  
  1069  type testRuntime struct {
  1070  	executeScript      func(runtime.Script, runtime.Context) (cadence.Value, error)
  1071  	executeTransaction func(runtime.Script, runtime.Context) error
  1072  	readStored         func(common.Address, cadence.Path, runtime.Context) (
  1073  		cadence.Value,
  1074  		error,
  1075  	)
  1076  }
  1077  
  1078  var _ runtime.Runtime = &testRuntime{}
  1079  
  1080  func (e *testRuntime) Config() runtime.Config {
  1081  	panic("Config not expected")
  1082  }
  1083  
  1084  func (e *testRuntime) NewScriptExecutor(
  1085  	script runtime.Script,
  1086  	c runtime.Context,
  1087  ) runtime.Executor {
  1088  	panic("NewScriptExecutor not expected")
  1089  }
  1090  
  1091  func (e *testRuntime) NewTransactionExecutor(
  1092  	script runtime.Script,
  1093  	c runtime.Context,
  1094  ) runtime.Executor {
  1095  	return &testTransactionExecutor{
  1096  		executeTransaction: e.executeTransaction,
  1097  		script:             script,
  1098  		context:            c,
  1099  	}
  1100  }
  1101  
  1102  func (e *testRuntime) NewContractFunctionExecutor(
  1103  	contractLocation common.AddressLocation,
  1104  	functionName string,
  1105  	arguments []cadence.Value,
  1106  	argumentTypes []sema.Type,
  1107  	context runtime.Context,
  1108  ) runtime.Executor {
  1109  	panic("NewContractFunctionExecutor not expected")
  1110  }
  1111  
  1112  func (e *testRuntime) SetInvalidatedResourceValidationEnabled(_ bool) {
  1113  	panic("SetInvalidatedResourceValidationEnabled not expected")
  1114  }
  1115  
  1116  func (e *testRuntime) SetTracingEnabled(_ bool) {
  1117  	panic("SetTracingEnabled not expected")
  1118  }
  1119  
  1120  func (e *testRuntime) SetResourceOwnerChangeHandlerEnabled(_ bool) {
  1121  	panic("SetResourceOwnerChangeHandlerEnabled not expected")
  1122  }
  1123  
  1124  func (e *testRuntime) InvokeContractFunction(
  1125  	_ common.AddressLocation,
  1126  	_ string,
  1127  	_ []cadence.Value,
  1128  	_ []sema.Type,
  1129  	_ runtime.Context,
  1130  ) (cadence.Value, error) {
  1131  	panic("InvokeContractFunction not expected")
  1132  }
  1133  
  1134  func (e *testRuntime) ExecuteScript(
  1135  	script runtime.Script,
  1136  	context runtime.Context,
  1137  ) (cadence.Value, error) {
  1138  	return e.executeScript(script, context)
  1139  }
  1140  
  1141  func (e *testRuntime) ExecuteTransaction(
  1142  	script runtime.Script,
  1143  	context runtime.Context,
  1144  ) error {
  1145  	return e.executeTransaction(script, context)
  1146  }
  1147  
  1148  func (*testRuntime) ParseAndCheckProgram(
  1149  	_ []byte,
  1150  	_ runtime.Context,
  1151  ) (*interpreter.Program, error) {
  1152  	panic("ParseAndCheckProgram not expected")
  1153  }
  1154  
  1155  func (*testRuntime) SetCoverageReport(_ *runtime.CoverageReport) {
  1156  	panic("SetCoverageReport not expected")
  1157  }
  1158  
  1159  func (*testRuntime) SetContractUpdateValidationEnabled(_ bool) {
  1160  	panic("SetContractUpdateValidationEnabled not expected")
  1161  }
  1162  
  1163  func (*testRuntime) SetAtreeValidationEnabled(_ bool) {
  1164  	panic("SetAtreeValidationEnabled not expected")
  1165  }
  1166  
  1167  func (e *testRuntime) ReadStored(
  1168  	a common.Address,
  1169  	p cadence.Path,
  1170  	c runtime.Context,
  1171  ) (cadence.Value, error) {
  1172  	return e.readStored(a, p, c)
  1173  }
  1174  
  1175  func (*testRuntime) ReadLinked(
  1176  	_ common.Address,
  1177  	_ cadence.Path,
  1178  	_ runtime.Context,
  1179  ) (cadence.Value, error) {
  1180  	panic("ReadLinked not expected")
  1181  }
  1182  
  1183  func (*testRuntime) SetDebugger(_ *interpreter.Debugger) {
  1184  	panic("SetDebugger not expected")
  1185  }
  1186  
  1187  type RandomAddressGenerator struct{}
  1188  
  1189  func (r *RandomAddressGenerator) NextAddress() (flow.Address, error) {
  1190  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000))), nil
  1191  }
  1192  
  1193  func (r *RandomAddressGenerator) CurrentAddress() flow.Address {
  1194  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000)))
  1195  }
  1196  
  1197  func (r *RandomAddressGenerator) Bytes() []byte {
  1198  	panic("not implemented")
  1199  }
  1200  
  1201  func (r *RandomAddressGenerator) AddressCount() uint64 {
  1202  	panic("not implemented")
  1203  }
  1204  
  1205  func (testRuntime) Storage(runtime.Context) (
  1206  	*runtime.Storage,
  1207  	*interpreter.Interpreter,
  1208  	error,
  1209  ) {
  1210  	panic("Storage not expected")
  1211  }
  1212  
  1213  type FixedAddressGenerator struct {
  1214  	Address flow.Address
  1215  }
  1216  
  1217  func (f *FixedAddressGenerator) NextAddress() (flow.Address, error) {
  1218  	return f.Address, nil
  1219  }
  1220  
  1221  func (f *FixedAddressGenerator) CurrentAddress() flow.Address {
  1222  	return f.Address
  1223  }
  1224  
  1225  func (f *FixedAddressGenerator) Bytes() []byte {
  1226  	panic("not implemented")
  1227  }
  1228  
  1229  func (f *FixedAddressGenerator) AddressCount() uint64 {
  1230  	panic("not implemented")
  1231  }
  1232  
  1233  func Test_ExecutingSystemCollection(t *testing.T) {
  1234  
  1235  	execCtx := fvm.NewContext(
  1236  		fvm.WithChain(flow.Localnet.Chain()),
  1237  		fvm.WithBlocks(&environment.NoopBlockFinder{}),
  1238  	)
  1239  
  1240  	vm := fvm.NewVirtualMachine()
  1241  
  1242  	rag := &RandomAddressGenerator{}
  1243  
  1244  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
  1245  
  1246  	committer := new(computermock.ViewCommitter)
  1247  	snapshot := storehouse.NewExecutingBlockSnapshot(
  1248  		snapshot.MapStorageSnapshot{},
  1249  		unittest.StateCommitmentFixture(),
  1250  	)
  1251  
  1252  	committer.On("CommitView", mock.Anything, mock.Anything).
  1253  		Return(nil, nil, nil, snapshot, nil).
  1254  		Times(1) // only system chunk
  1255  
  1256  	noopCollector := metrics.NewNoopCollector()
  1257  
  1258  	expectedNumberOfEvents := 3
  1259  	expectedEventSize := 1493
  1260  	// bootstrapping does not cache programs
  1261  	expectedCachedPrograms := 0
  1262  
  1263  	metrics := new(modulemock.ExecutionMetrics)
  1264  	metrics.On("ExecutionBlockExecuted",
  1265  		mock.Anything,  // duration
  1266  		mock.Anything). // stats
  1267  		Return(nil).
  1268  		Times(1)
  1269  
  1270  	metrics.On("ExecutionCollectionExecuted",
  1271  		mock.Anything,  // duration
  1272  		mock.Anything). // stats
  1273  		Return(nil).
  1274  		Times(1) // system collection
  1275  
  1276  	metrics.On("ExecutionTransactionExecuted",
  1277  		mock.Anything, // duration
  1278  		mock.Anything, // conflict retry count
  1279  		mock.Anything, // computation used
  1280  		mock.Anything, // memory used
  1281  		expectedNumberOfEvents,
  1282  		expectedEventSize,
  1283  		false).
  1284  		Return(nil).
  1285  		Times(1) // system chunk tx
  1286  
  1287  	metrics.On(
  1288  		"ExecutionChunkDataPackGenerated",
  1289  		mock.Anything,
  1290  		mock.Anything).
  1291  		Return(nil).
  1292  		Times(1) // system collection
  1293  
  1294  	metrics.On(
  1295  		"ExecutionBlockCachedPrograms",
  1296  		expectedCachedPrograms).
  1297  		Return(nil).
  1298  		Times(1) // block
  1299  
  1300  	metrics.On(
  1301  		"ExecutionBlockExecutionEffortVectorComponent",
  1302  		mock.Anything,
  1303  		mock.Anything).
  1304  		Return(nil)
  1305  
  1306  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
  1307  	trackerStorage := mocktracker.NewMockStorage()
  1308  
  1309  	prov := provider.NewProvider(
  1310  		zerolog.Nop(),
  1311  		noopCollector,
  1312  		execution_data.DefaultSerializer,
  1313  		bservice,
  1314  		trackerStorage,
  1315  	)
  1316  
  1317  	me := new(modulemock.Local)
  1318  	me.On("NodeID").Return(unittest.IdentifierFixture())
  1319  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
  1320  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
  1321  		Return(nil, nil)
  1322  
  1323  	constRandomSource := make([]byte, 32)
  1324  
  1325  	exe, err := computer.NewBlockComputer(
  1326  		vm,
  1327  		execCtx,
  1328  		metrics,
  1329  		trace.NewNoopTracer(),
  1330  		zerolog.Nop(),
  1331  		committer,
  1332  		me,
  1333  		prov,
  1334  		nil,
  1335  		testutil.ProtocolStateWithSourceFixture(constRandomSource),
  1336  		testMaxConcurrency)
  1337  	require.NoError(t, err)
  1338  
  1339  	// create empty block, it will have system collection attached while executing
  1340  	block := generateBlock(0, 0, rag)
  1341  
  1342  	result, err := exe.ExecuteBlock(
  1343  		context.Background(),
  1344  		unittest.IdentifierFixture(),
  1345  		block,
  1346  		ledger,
  1347  		derived.NewEmptyDerivedBlockData(0))
  1348  	assert.NoError(t, err)
  1349  	assert.Len(t, result.AllExecutionSnapshots(), 1) // +1 system chunk
  1350  	assert.Len(t, result.AllTransactionResults(), 1)
  1351  
  1352  	assert.Empty(t, result.AllTransactionResults()[0].ErrorMessage)
  1353  
  1354  	committer.AssertExpectations(t)
  1355  }
  1356  
  1357  func generateBlock(
  1358  	collectionCount, transactionCount int,
  1359  	addressGenerator flow.AddressGenerator,
  1360  ) *entity.ExecutableBlock {
  1361  	return generateBlockWithVisitor(collectionCount, transactionCount, addressGenerator, nil)
  1362  }
  1363  
  1364  func generateBlockWithVisitor(
  1365  	collectionCount, transactionCount int,
  1366  	addressGenerator flow.AddressGenerator,
  1367  	visitor func(body *flow.TransactionBody),
  1368  ) *entity.ExecutableBlock {
  1369  	collections := make([]*entity.CompleteCollection, collectionCount)
  1370  	guarantees := make([]*flow.CollectionGuarantee, collectionCount)
  1371  	completeCollections := make(map[flow.Identifier]*entity.CompleteCollection)
  1372  
  1373  	for i := 0; i < collectionCount; i++ {
  1374  		collection := generateCollection(transactionCount, addressGenerator, visitor)
  1375  		collections[i] = collection
  1376  		guarantees[i] = collection.Guarantee
  1377  		completeCollections[collection.Guarantee.ID()] = collection
  1378  	}
  1379  
  1380  	block := flow.Block{
  1381  		Header: &flow.Header{
  1382  			Timestamp: flow.GenesisTime,
  1383  			Height:    42,
  1384  			View:      42,
  1385  		},
  1386  		Payload: &flow.Payload{
  1387  			Guarantees: guarantees,
  1388  		},
  1389  	}
  1390  
  1391  	return &entity.ExecutableBlock{
  1392  		Block:               &block,
  1393  		CompleteCollections: completeCollections,
  1394  		StartState:          unittest.StateCommitmentPointerFixture(),
  1395  	}
  1396  }
  1397  
  1398  func generateCollection(
  1399  	transactionCount int,
  1400  	addressGenerator flow.AddressGenerator,
  1401  	visitor func(body *flow.TransactionBody),
  1402  ) *entity.CompleteCollection {
  1403  	transactions := make([]*flow.TransactionBody, transactionCount)
  1404  
  1405  	for i := 0; i < transactionCount; i++ {
  1406  		nextAddress, err := addressGenerator.NextAddress()
  1407  		if err != nil {
  1408  			panic(fmt.Errorf("cannot generate next address in test: %w", err))
  1409  		}
  1410  		txBody := &flow.TransactionBody{
  1411  			Payer:  nextAddress, // a unique payer for each tx to generate a unique id
  1412  			Script: []byte("transaction { execute {} }"),
  1413  		}
  1414  		if visitor != nil {
  1415  			visitor(txBody)
  1416  		}
  1417  		transactions[i] = txBody
  1418  	}
  1419  
  1420  	collection := flow.Collection{Transactions: transactions}
  1421  
  1422  	guarantee := &flow.CollectionGuarantee{CollectionID: collection.ID()}
  1423  
  1424  	return &entity.CompleteCollection{
  1425  		Guarantee:    guarantee,
  1426  		Transactions: transactions,
  1427  	}
  1428  }
  1429  
  1430  type noOpExecutor struct{}
  1431  
  1432  func (noOpExecutor) Cleanup() {}
  1433  
  1434  func (noOpExecutor) Preprocess() error {
  1435  	return nil
  1436  }
  1437  
  1438  func (noOpExecutor) Execute() error {
  1439  	return nil
  1440  }
  1441  
  1442  func (noOpExecutor) Output() fvm.ProcedureOutput {
  1443  	return fvm.ProcedureOutput{}
  1444  }
  1445  
  1446  type testVM struct {
  1447  	t                    *testing.T
  1448  	eventsPerTransaction int
  1449  
  1450  	callCount int32 // atomic variable
  1451  	err       fvmErrors.CodedError
  1452  }
  1453  
  1454  type testExecutor struct {
  1455  	*testVM
  1456  
  1457  	ctx      fvm.Context
  1458  	proc     fvm.Procedure
  1459  	txnState storage.TransactionPreparer
  1460  }
  1461  
  1462  func (testExecutor) Cleanup() {
  1463  }
  1464  
  1465  func (testExecutor) Preprocess() error {
  1466  	return nil
  1467  }
  1468  
  1469  func (executor *testExecutor) Execute() error {
  1470  	atomic.AddInt32(&executor.callCount, 1)
  1471  
  1472  	getSetAProgram(executor.t, executor.txnState)
  1473  
  1474  	return nil
  1475  }
  1476  
  1477  func (executor *testExecutor) Output() fvm.ProcedureOutput {
  1478  	txn := executor.proc.(*fvm.TransactionProcedure)
  1479  
  1480  	return fvm.ProcedureOutput{
  1481  		Events: generateEvents(executor.eventsPerTransaction, txn.TxIndex),
  1482  		Err:    executor.err,
  1483  	}
  1484  }
  1485  
  1486  func (vm *testVM) NewExecutor(
  1487  	ctx fvm.Context,
  1488  	proc fvm.Procedure,
  1489  	txnState storage.TransactionPreparer,
  1490  ) fvm.ProcedureExecutor {
  1491  	return &testExecutor{
  1492  		testVM:   vm,
  1493  		proc:     proc,
  1494  		ctx:      ctx,
  1495  		txnState: txnState,
  1496  	}
  1497  }
  1498  
  1499  func (vm *testVM) CallCount() int {
  1500  	return int(atomic.LoadInt32(&vm.callCount))
  1501  }
  1502  
  1503  func (vm *testVM) Run(
  1504  	ctx fvm.Context,
  1505  	proc fvm.Procedure,
  1506  	storageSnapshot snapshot.StorageSnapshot,
  1507  ) (
  1508  	*snapshot.ExecutionSnapshot,
  1509  	fvm.ProcedureOutput,
  1510  	error,
  1511  ) {
  1512  	database := storage.NewBlockDatabase(
  1513  		storageSnapshot,
  1514  		proc.ExecutionTime(),
  1515  		ctx.DerivedBlockData)
  1516  
  1517  	txn, err := database.NewTransaction(
  1518  		proc.ExecutionTime(),
  1519  		state.DefaultParameters())
  1520  	require.NoError(vm.t, err)
  1521  
  1522  	executor := vm.NewExecutor(ctx, proc, txn)
  1523  	err = fvm.Run(executor)
  1524  	require.NoError(vm.t, err)
  1525  
  1526  	err = txn.Finalize()
  1527  	require.NoError(vm.t, err)
  1528  
  1529  	executionSnapshot, err := txn.Commit()
  1530  	require.NoError(vm.t, err)
  1531  
  1532  	return executionSnapshot, executor.Output(), nil
  1533  }
  1534  
  1535  func (testVM) GetAccount(
  1536  	_ fvm.Context,
  1537  	_ flow.Address,
  1538  	_ snapshot.StorageSnapshot,
  1539  ) (
  1540  	*flow.Account,
  1541  	error,
  1542  ) {
  1543  	panic("not implemented")
  1544  }
  1545  
  1546  func generateEvents(eventCount int, txIndex uint32) []flow.Event {
  1547  	events := make([]flow.Event, eventCount)
  1548  	for i := 0; i < eventCount; i++ {
  1549  		// creating some dummy event
  1550  		event := flow.Event{
  1551  			Type:             "whatever",
  1552  			EventIndex:       uint32(i),
  1553  			TransactionIndex: txIndex,
  1554  		}
  1555  		events[i] = event
  1556  	}
  1557  	return events
  1558  }
  1559  
  1560  type errorVM struct {
  1561  	errorAt logical.Time
  1562  }
  1563  
  1564  type errorExecutor struct {
  1565  	err error
  1566  }
  1567  
  1568  func (errorExecutor) Cleanup() {}
  1569  
  1570  func (errorExecutor) Preprocess() error {
  1571  	return nil
  1572  }
  1573  
  1574  func (e errorExecutor) Execute() error {
  1575  	return e.err
  1576  }
  1577  
  1578  func (errorExecutor) Output() fvm.ProcedureOutput {
  1579  	return fvm.ProcedureOutput{}
  1580  }
  1581  
  1582  func (vm errorVM) NewExecutor(
  1583  	ctx fvm.Context,
  1584  	proc fvm.Procedure,
  1585  	txn storage.TransactionPreparer,
  1586  ) fvm.ProcedureExecutor {
  1587  	var err error
  1588  	if proc.ExecutionTime() == vm.errorAt {
  1589  		err = fmt.Errorf("boom - internal error")
  1590  	}
  1591  
  1592  	return errorExecutor{err: err}
  1593  }
  1594  
  1595  func (vm errorVM) Run(
  1596  	ctx fvm.Context,
  1597  	proc fvm.Procedure,
  1598  	storageSnapshot snapshot.StorageSnapshot,
  1599  ) (
  1600  	*snapshot.ExecutionSnapshot,
  1601  	fvm.ProcedureOutput,
  1602  	error,
  1603  ) {
  1604  	var err error
  1605  	if proc.ExecutionTime() == vm.errorAt {
  1606  		err = fmt.Errorf("boom - internal error")
  1607  	}
  1608  	return &snapshot.ExecutionSnapshot{}, fvm.ProcedureOutput{}, err
  1609  }
  1610  
  1611  func (errorVM) GetAccount(
  1612  	ctx fvm.Context,
  1613  	addr flow.Address,
  1614  	storageSnapshot snapshot.StorageSnapshot,
  1615  ) (
  1616  	*flow.Account,
  1617  	error,
  1618  ) {
  1619  	panic("not implemented")
  1620  }
  1621  
  1622  func getSetAProgram(
  1623  	t *testing.T,
  1624  	txnState storage.TransactionPreparer,
  1625  ) {
  1626  	loc := common.AddressLocation{
  1627  		Name:    "SomeContract",
  1628  		Address: common.MustBytesToAddress([]byte{0x1}),
  1629  	}
  1630  	_, err := txnState.GetOrComputeProgram(
  1631  		txnState,
  1632  		loc,
  1633  		&programLoader{
  1634  			load: func() (*derived.Program, error) {
  1635  				return &derived.Program{}, nil
  1636  			},
  1637  		},
  1638  	)
  1639  	require.NoError(t, err)
  1640  }
  1641  
  1642  type programLoader struct {
  1643  	load func() (*derived.Program, error)
  1644  }
  1645  
  1646  func (p *programLoader) Compute(
  1647  	_ state.NestedTransactionPreparer,
  1648  	_ common.AddressLocation,
  1649  ) (
  1650  	*derived.Program,
  1651  	error,
  1652  ) {
  1653  	return p.load()
  1654  }