github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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/boxo/blockstore"
    19  	"github.com/ipfs/go-datastore"
    20  	dssync "github.com/ipfs/go-datastore/sync"
    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.WithAuthorizationChecksEnabled(false),
   592  				fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   593  			)
   594  
   595  			collectionCount := 2
   596  			transactionsPerCollection := 2
   597  
   598  			// create a block with 2 collections with 2 transactions each
   599  			block := generateBlock(collectionCount, transactionsPerCollection, rag)
   600  
   601  			serviceEvents := systemcontracts.ServiceEventsForChain(execCtx.Chain.ChainID())
   602  
   603  			randomSource := unittest.EpochSetupRandomSourceFixture()
   604  			payload, err := ccf.Decode(nil, unittest.EpochSetupFixtureCCF(randomSource))
   605  			require.NoError(t, err)
   606  
   607  			serviceEventA, ok := payload.(cadence.Event)
   608  			require.True(t, ok)
   609  
   610  			serviceEventA.EventType.Location = common.AddressLocation{
   611  				Address: common.Address(serviceEvents.EpochSetup.Address),
   612  			}
   613  			serviceEventA.EventType.QualifiedIdentifier = serviceEvents.EpochSetup.QualifiedIdentifier()
   614  
   615  			payload, err = ccf.Decode(nil, unittest.EpochCommitFixtureCCF)
   616  			require.NoError(t, err)
   617  
   618  			serviceEventB, ok := payload.(cadence.Event)
   619  			require.True(t, ok)
   620  
   621  			serviceEventB.EventType.Location = common.AddressLocation{
   622  				Address: common.Address(serviceEvents.EpochCommit.Address),
   623  			}
   624  			serviceEventB.EventType.QualifiedIdentifier = serviceEvents.EpochCommit.QualifiedIdentifier()
   625  
   626  			payload, err = ccf.Decode(nil, unittest.VersionBeaconFixtureCCF)
   627  			require.NoError(t, err)
   628  
   629  			serviceEventC, ok := payload.(cadence.Event)
   630  			require.True(t, ok)
   631  
   632  			serviceEventC.EventType.Location = common.AddressLocation{
   633  				Address: common.Address(serviceEvents.VersionBeacon.Address),
   634  			}
   635  			serviceEventC.EventType.QualifiedIdentifier = serviceEvents.VersionBeacon.QualifiedIdentifier()
   636  
   637  			transactions := []*flow.TransactionBody{}
   638  			for _, col := range block.Collections() {
   639  				transactions = append(transactions, col.Transactions...)
   640  			}
   641  
   642  			// events to emit for each iteration/transaction
   643  			events := map[common.Location][]cadence.Event{
   644  				common.TransactionLocation(transactions[0].ID()): nil,
   645  				common.TransactionLocation(transactions[1].ID()): {
   646  					serviceEventA,
   647  					{
   648  						EventType: &cadence.EventType{
   649  							Location:            stdlib.FlowLocation{},
   650  							QualifiedIdentifier: "what.ever",
   651  						},
   652  					},
   653  				},
   654  				common.TransactionLocation(transactions[2].ID()): {
   655  					{
   656  						EventType: &cadence.EventType{
   657  							Location:            stdlib.FlowLocation{},
   658  							QualifiedIdentifier: "what.ever",
   659  						},
   660  					},
   661  				},
   662  				common.TransactionLocation(transactions[3].ID()): nil,
   663  			}
   664  
   665  			systemTransactionEvents := []cadence.Event{
   666  				serviceEventB,
   667  				serviceEventC,
   668  			}
   669  
   670  			emittingRuntime := &testRuntime{
   671  				executeTransaction: func(
   672  					script runtime.Script,
   673  					context runtime.Context,
   674  				) error {
   675  					scriptEvents, ok := events[context.Location]
   676  					if !ok {
   677  						scriptEvents = systemTransactionEvents
   678  					}
   679  
   680  					for _, e := range scriptEvents {
   681  						err := context.Interface.EmitEvent(e)
   682  						if err != nil {
   683  							return err
   684  						}
   685  					}
   686  					return nil
   687  				},
   688  				readStored: func(
   689  					address common.Address,
   690  					path cadence.Path,
   691  					r runtime.Context,
   692  				) (cadence.Value, error) {
   693  					return nil, nil
   694  				},
   695  			}
   696  
   697  			execCtx = fvm.NewContextFromParent(
   698  				execCtx,
   699  				fvm.WithReusableCadenceRuntimePool(
   700  					reusableRuntime.NewCustomReusableCadenceRuntimePool(
   701  						0,
   702  						runtime.Config{},
   703  						func(_ runtime.Config) runtime.Runtime {
   704  							return emittingRuntime
   705  						},
   706  					),
   707  				),
   708  			)
   709  
   710  			vm := fvm.NewVirtualMachine()
   711  
   712  			bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   713  			trackerStorage := mocktracker.NewMockStorage()
   714  
   715  			prov := provider.NewProvider(
   716  				zerolog.Nop(),
   717  				metrics.NewNoopCollector(),
   718  				execution_data.DefaultSerializer,
   719  				bservice,
   720  				trackerStorage,
   721  			)
   722  
   723  			exe, err := computer.NewBlockComputer(
   724  				vm,
   725  				execCtx,
   726  				metrics.NewNoopCollector(),
   727  				trace.NewNoopTracer(),
   728  				zerolog.Nop(),
   729  				committer.NewNoopViewCommitter(),
   730  				me,
   731  				prov,
   732  				nil,
   733  				testutil.ProtocolStateWithSourceFixture(nil),
   734  				testMaxConcurrency)
   735  			require.NoError(t, err)
   736  
   737  			result, err := exe.ExecuteBlock(
   738  				context.Background(),
   739  				unittest.IdentifierFixture(),
   740  				block,
   741  				nil,
   742  				derived.NewEmptyDerivedBlockData(0),
   743  			)
   744  			require.NoError(t, err)
   745  
   746  			// make sure event index sequence are valid
   747  			for i := 0; i < result.BlockExecutionResult.Size(); i++ {
   748  				collectionResult := result.CollectionExecutionResultAt(i)
   749  				unittest.EnsureEventsIndexSeq(t, collectionResult.Events(), execCtx.Chain.ChainID())
   750  			}
   751  
   752  			sEvents := result.AllServiceEvents() // all events should have been collected
   753  			require.Len(t, sEvents, 3)
   754  
   755  			// events are ordered
   756  			require.Equal(
   757  				t,
   758  				serviceEventA.EventType.ID(),
   759  				string(sEvents[0].Type),
   760  			)
   761  			require.Equal(
   762  				t,
   763  				serviceEventB.EventType.ID(),
   764  				string(sEvents[1].Type),
   765  			)
   766  
   767  			require.Equal(
   768  				t,
   769  				serviceEventC.EventType.ID(),
   770  				string(sEvents[2].Type),
   771  			)
   772  
   773  			assertEventHashesMatch(t, collectionCount+1, result)
   774  		},
   775  	)
   776  
   777  	t.Run("succeeding transactions store programs", func(t *testing.T) {
   778  
   779  		execCtx := fvm.NewContext()
   780  
   781  		address := common.Address{0x1}
   782  		contractLocation := common.AddressLocation{
   783  			Address: address,
   784  			Name:    "Test",
   785  		}
   786  
   787  		contractProgram := &interpreter.Program{}
   788  
   789  		rt := &testRuntime{
   790  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   791  
   792  				_, err := r.Interface.GetOrLoadProgram(
   793  					contractLocation,
   794  					func() (*interpreter.Program, error) {
   795  						return contractProgram, nil
   796  					},
   797  				)
   798  				require.NoError(t, err)
   799  
   800  				return nil
   801  			},
   802  			readStored: func(
   803  				address common.Address,
   804  				path cadence.Path,
   805  				r runtime.Context,
   806  			) (cadence.Value, error) {
   807  				return nil, nil
   808  			},
   809  		}
   810  
   811  		execCtx = fvm.NewContextFromParent(
   812  			execCtx,
   813  			fvm.WithReusableCadenceRuntimePool(
   814  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   815  					0,
   816  					runtime.Config{},
   817  					func(_ runtime.Config) runtime.Runtime {
   818  						return rt
   819  					})))
   820  
   821  		vm := fvm.NewVirtualMachine()
   822  
   823  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   824  		trackerStorage := mocktracker.NewMockStorage()
   825  
   826  		prov := provider.NewProvider(
   827  			zerolog.Nop(),
   828  			metrics.NewNoopCollector(),
   829  			execution_data.DefaultSerializer,
   830  			bservice,
   831  			trackerStorage,
   832  		)
   833  
   834  		exe, err := computer.NewBlockComputer(
   835  			vm,
   836  			execCtx,
   837  			metrics.NewNoopCollector(),
   838  			trace.NewNoopTracer(),
   839  			zerolog.Nop(),
   840  			committer.NewNoopViewCommitter(),
   841  			me,
   842  			prov,
   843  			nil,
   844  			testutil.ProtocolStateWithSourceFixture(nil),
   845  			testMaxConcurrency)
   846  		require.NoError(t, err)
   847  
   848  		const collectionCount = 2
   849  		const transactionCount = 2
   850  		block := generateBlock(collectionCount, transactionCount, rag)
   851  
   852  		key := flow.AccountStatusRegisterID(
   853  			flow.BytesToAddress(address.Bytes()))
   854  		value := environment.NewAccountStatus().ToBytes()
   855  
   856  		result, err := exe.ExecuteBlock(
   857  			context.Background(),
   858  			unittest.IdentifierFixture(),
   859  			block,
   860  			snapshot.MapStorageSnapshot{key: value},
   861  			derived.NewEmptyDerivedBlockData(0))
   862  		assert.NoError(t, err)
   863  		assert.Len(t, result.AllExecutionSnapshots(), collectionCount+1) // +1 system chunk
   864  	})
   865  
   866  	t.Run("failing transactions do not store programs", func(t *testing.T) {
   867  		execCtx := fvm.NewContext(
   868  			fvm.WithAuthorizationChecksEnabled(false),
   869  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   870  		)
   871  
   872  		address := common.Address{0x1}
   873  
   874  		contractLocation := common.AddressLocation{
   875  			Address: address,
   876  			Name:    "Test",
   877  		}
   878  
   879  		contractProgram := &interpreter.Program{}
   880  
   881  		const collectionCount = 2
   882  		const transactionCount = 2
   883  		block := generateBlock(collectionCount, transactionCount, rag)
   884  
   885  		normalTransactions := map[common.Location]struct{}{}
   886  		for _, col := range block.Collections() {
   887  			for _, txn := range col.Transactions {
   888  				loc := common.TransactionLocation(txn.ID())
   889  				normalTransactions[loc] = struct{}{}
   890  			}
   891  		}
   892  
   893  		rt := &testRuntime{
   894  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   895  
   896  				// NOTE: set a program and revert all transactions but the
   897  				// system chunk transaction
   898  				_, err := r.Interface.GetOrLoadProgram(
   899  					contractLocation,
   900  					func() (*interpreter.Program, error) {
   901  						return contractProgram, nil
   902  					},
   903  				)
   904  				require.NoError(t, err)
   905  
   906  				_, ok := normalTransactions[r.Location]
   907  				if ok {
   908  					return runtime.Error{
   909  						Err: fmt.Errorf("TX reverted"),
   910  					}
   911  				}
   912  
   913  				return nil
   914  			},
   915  			readStored: func(
   916  				address common.Address,
   917  				path cadence.Path,
   918  				r runtime.Context,
   919  			) (cadence.Value, error) {
   920  				return nil, nil
   921  			},
   922  		}
   923  
   924  		execCtx = fvm.NewContextFromParent(
   925  			execCtx,
   926  			fvm.WithReusableCadenceRuntimePool(
   927  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   928  					0,
   929  					runtime.Config{},
   930  					func(_ runtime.Config) runtime.Runtime {
   931  						return rt
   932  					})))
   933  
   934  		vm := fvm.NewVirtualMachine()
   935  
   936  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   937  		trackerStorage := mocktracker.NewMockStorage()
   938  
   939  		prov := provider.NewProvider(
   940  			zerolog.Nop(),
   941  			metrics.NewNoopCollector(),
   942  			execution_data.DefaultSerializer,
   943  			bservice,
   944  			trackerStorage,
   945  		)
   946  
   947  		exe, err := computer.NewBlockComputer(
   948  			vm,
   949  			execCtx,
   950  			metrics.NewNoopCollector(),
   951  			trace.NewNoopTracer(),
   952  			zerolog.Nop(),
   953  			committer.NewNoopViewCommitter(),
   954  			me,
   955  			prov,
   956  			nil,
   957  			testutil.ProtocolStateWithSourceFixture(nil),
   958  			testMaxConcurrency)
   959  		require.NoError(t, err)
   960  
   961  		key := flow.AccountStatusRegisterID(
   962  			flow.BytesToAddress(address.Bytes()))
   963  		value := environment.NewAccountStatus().ToBytes()
   964  
   965  		result, err := exe.ExecuteBlock(
   966  			context.Background(),
   967  			unittest.IdentifierFixture(),
   968  			block,
   969  			snapshot.MapStorageSnapshot{key: value},
   970  			derived.NewEmptyDerivedBlockData(0))
   971  		require.NoError(t, err)
   972  		assert.Len(t, result.AllExecutionSnapshots(), collectionCount+1) // +1 system chunk
   973  	})
   974  
   975  	t.Run("internal error", func(t *testing.T) {
   976  		execCtx := fvm.NewContext()
   977  
   978  		committer := new(computermock.ViewCommitter)
   979  
   980  		bservice := requesterunit.MockBlobService(
   981  			blockstore.NewBlockstore(
   982  				dssync.MutexWrap(datastore.NewMapDatastore())))
   983  		trackerStorage := mocktracker.NewMockStorage()
   984  
   985  		prov := provider.NewProvider(
   986  			zerolog.Nop(),
   987  			metrics.NewNoopCollector(),
   988  			execution_data.DefaultSerializer,
   989  			bservice,
   990  			trackerStorage)
   991  
   992  		exe, err := computer.NewBlockComputer(
   993  			errorVM{errorAt: 5},
   994  			execCtx,
   995  			metrics.NewNoopCollector(),
   996  			trace.NewNoopTracer(),
   997  			zerolog.Nop(),
   998  			committer,
   999  			me,
  1000  			prov,
  1001  			nil,
  1002  			testutil.ProtocolStateWithSourceFixture(nil),
  1003  			testMaxConcurrency)
  1004  		require.NoError(t, err)
  1005  
  1006  		collectionCount := 5
  1007  		transactionsPerCollection := 3
  1008  		block := generateBlock(collectionCount, transactionsPerCollection, rag)
  1009  
  1010  		snapshot := storehouse.NewExecutingBlockSnapshot(
  1011  			snapshot.MapStorageSnapshot{},
  1012  			unittest.StateCommitmentFixture(),
  1013  		)
  1014  
  1015  		committer.On("CommitView", mock.Anything, mock.Anything).
  1016  			Return(nil, nil, nil, snapshot, nil).
  1017  			Times(collectionCount + 1)
  1018  
  1019  		_, err = exe.ExecuteBlock(
  1020  			context.Background(),
  1021  			unittest.IdentifierFixture(),
  1022  			block,
  1023  			nil,
  1024  			derived.NewEmptyDerivedBlockData(0))
  1025  		assert.ErrorContains(t, err, "boom - internal error")
  1026  	})
  1027  
  1028  }
  1029  
  1030  func assertEventHashesMatch(
  1031  	t *testing.T,
  1032  	expectedNoOfChunks int,
  1033  	result *execution.ComputationResult,
  1034  ) {
  1035  	execResSize := result.BlockExecutionResult.Size()
  1036  	attestResSize := result.BlockAttestationResult.Size()
  1037  	require.Equal(t, execResSize, expectedNoOfChunks)
  1038  	require.Equal(t, execResSize, attestResSize)
  1039  
  1040  	for i := 0; i < expectedNoOfChunks; i++ {
  1041  		events := result.CollectionExecutionResultAt(i).Events()
  1042  		calculatedHash, err := flow.EventsMerkleRootHash(events)
  1043  		require.NoError(t, err)
  1044  		require.Equal(t, calculatedHash, result.CollectionAttestationResultAt(i).EventCommitment())
  1045  	}
  1046  }
  1047  
  1048  type testTransactionExecutor struct {
  1049  	executeTransaction func(runtime.Script, runtime.Context) error
  1050  
  1051  	script  runtime.Script
  1052  	context runtime.Context
  1053  }
  1054  
  1055  func (executor *testTransactionExecutor) Preprocess() error {
  1056  	// Do nothing.
  1057  	return nil
  1058  }
  1059  
  1060  func (executor *testTransactionExecutor) Execute() error {
  1061  	return executor.executeTransaction(executor.script, executor.context)
  1062  }
  1063  
  1064  func (executor *testTransactionExecutor) Result() (cadence.Value, error) {
  1065  	panic("Result not expected")
  1066  }
  1067  
  1068  type testRuntime struct {
  1069  	executeScript      func(runtime.Script, runtime.Context) (cadence.Value, error)
  1070  	executeTransaction func(runtime.Script, runtime.Context) error
  1071  	readStored         func(common.Address, cadence.Path, runtime.Context) (
  1072  		cadence.Value,
  1073  		error,
  1074  	)
  1075  }
  1076  
  1077  var _ runtime.Runtime = &testRuntime{}
  1078  
  1079  func (e *testRuntime) Config() runtime.Config {
  1080  	panic("Config not expected")
  1081  }
  1082  
  1083  func (e *testRuntime) NewScriptExecutor(
  1084  	script runtime.Script,
  1085  	c runtime.Context,
  1086  ) runtime.Executor {
  1087  	panic("NewScriptExecutor not expected")
  1088  }
  1089  
  1090  func (e *testRuntime) NewTransactionExecutor(
  1091  	script runtime.Script,
  1092  	c runtime.Context,
  1093  ) runtime.Executor {
  1094  	return &testTransactionExecutor{
  1095  		executeTransaction: e.executeTransaction,
  1096  		script:             script,
  1097  		context:            c,
  1098  	}
  1099  }
  1100  
  1101  func (e *testRuntime) NewContractFunctionExecutor(
  1102  	contractLocation common.AddressLocation,
  1103  	functionName string,
  1104  	arguments []cadence.Value,
  1105  	argumentTypes []sema.Type,
  1106  	context runtime.Context,
  1107  ) runtime.Executor {
  1108  	panic("NewContractFunctionExecutor not expected")
  1109  }
  1110  
  1111  func (e *testRuntime) SetInvalidatedResourceValidationEnabled(_ bool) {
  1112  	panic("SetInvalidatedResourceValidationEnabled not expected")
  1113  }
  1114  
  1115  func (e *testRuntime) SetTracingEnabled(_ bool) {
  1116  	panic("SetTracingEnabled not expected")
  1117  }
  1118  
  1119  func (e *testRuntime) SetResourceOwnerChangeHandlerEnabled(_ bool) {
  1120  	panic("SetResourceOwnerChangeHandlerEnabled not expected")
  1121  }
  1122  
  1123  func (e *testRuntime) InvokeContractFunction(
  1124  	_ common.AddressLocation,
  1125  	_ string,
  1126  	_ []cadence.Value,
  1127  	_ []sema.Type,
  1128  	_ runtime.Context,
  1129  ) (cadence.Value, error) {
  1130  	panic("InvokeContractFunction not expected")
  1131  }
  1132  
  1133  func (e *testRuntime) ExecuteScript(
  1134  	script runtime.Script,
  1135  	context runtime.Context,
  1136  ) (cadence.Value, error) {
  1137  	return e.executeScript(script, context)
  1138  }
  1139  
  1140  func (e *testRuntime) ExecuteTransaction(
  1141  	script runtime.Script,
  1142  	context runtime.Context,
  1143  ) error {
  1144  	return e.executeTransaction(script, context)
  1145  }
  1146  
  1147  func (*testRuntime) ParseAndCheckProgram(
  1148  	_ []byte,
  1149  	_ runtime.Context,
  1150  ) (*interpreter.Program, error) {
  1151  	panic("ParseAndCheckProgram not expected")
  1152  }
  1153  
  1154  func (*testRuntime) SetCoverageReport(_ *runtime.CoverageReport) {
  1155  	panic("SetCoverageReport not expected")
  1156  }
  1157  
  1158  func (*testRuntime) SetContractUpdateValidationEnabled(_ bool) {
  1159  	panic("SetContractUpdateValidationEnabled not expected")
  1160  }
  1161  
  1162  func (*testRuntime) SetAtreeValidationEnabled(_ bool) {
  1163  	panic("SetAtreeValidationEnabled not expected")
  1164  }
  1165  
  1166  func (e *testRuntime) ReadStored(
  1167  	a common.Address,
  1168  	p cadence.Path,
  1169  	c runtime.Context,
  1170  ) (cadence.Value, error) {
  1171  	return e.readStored(a, p, c)
  1172  }
  1173  
  1174  func (*testRuntime) SetDebugger(_ *interpreter.Debugger) {
  1175  	panic("SetDebugger not expected")
  1176  }
  1177  
  1178  type RandomAddressGenerator struct{}
  1179  
  1180  func (r *RandomAddressGenerator) NextAddress() (flow.Address, error) {
  1181  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000))), nil
  1182  }
  1183  
  1184  func (r *RandomAddressGenerator) CurrentAddress() flow.Address {
  1185  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000)))
  1186  }
  1187  
  1188  func (r *RandomAddressGenerator) Bytes() []byte {
  1189  	panic("not implemented")
  1190  }
  1191  
  1192  func (r *RandomAddressGenerator) AddressCount() uint64 {
  1193  	panic("not implemented")
  1194  }
  1195  
  1196  func (testRuntime) Storage(runtime.Context) (
  1197  	*runtime.Storage,
  1198  	*interpreter.Interpreter,
  1199  	error,
  1200  ) {
  1201  	panic("Storage not expected")
  1202  }
  1203  
  1204  type FixedAddressGenerator struct {
  1205  	Address flow.Address
  1206  }
  1207  
  1208  func (f *FixedAddressGenerator) NextAddress() (flow.Address, error) {
  1209  	return f.Address, nil
  1210  }
  1211  
  1212  func (f *FixedAddressGenerator) CurrentAddress() flow.Address {
  1213  	return f.Address
  1214  }
  1215  
  1216  func (f *FixedAddressGenerator) Bytes() []byte {
  1217  	panic("not implemented")
  1218  }
  1219  
  1220  func (f *FixedAddressGenerator) AddressCount() uint64 {
  1221  	panic("not implemented")
  1222  }
  1223  
  1224  func Test_ExecutingSystemCollection(t *testing.T) {
  1225  
  1226  	execCtx := fvm.NewContext(
  1227  		fvm.WithChain(flow.Localnet.Chain()),
  1228  		fvm.WithBlocks(&environment.NoopBlockFinder{}),
  1229  	)
  1230  
  1231  	vm := fvm.NewVirtualMachine()
  1232  
  1233  	rag := &RandomAddressGenerator{}
  1234  
  1235  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
  1236  
  1237  	committer := new(computermock.ViewCommitter)
  1238  	snapshot := storehouse.NewExecutingBlockSnapshot(
  1239  		snapshot.MapStorageSnapshot{},
  1240  		unittest.StateCommitmentFixture(),
  1241  	)
  1242  
  1243  	committer.On("CommitView", mock.Anything, mock.Anything).
  1244  		Return(nil, nil, nil, snapshot, nil).
  1245  		Times(1) // only system chunk
  1246  
  1247  	noopCollector := metrics.NewNoopCollector()
  1248  
  1249  	expectedNumberOfEvents := 3
  1250  	expectedEventSize := 1497
  1251  
  1252  	// bootstrapping does not cache programs
  1253  	expectedCachedPrograms := 0
  1254  
  1255  	metrics := new(modulemock.ExecutionMetrics)
  1256  	metrics.On("ExecutionBlockExecuted",
  1257  		mock.Anything,  // duration
  1258  		mock.Anything). // stats
  1259  		Return(nil).
  1260  		Times(1)
  1261  
  1262  	metrics.On("ExecutionCollectionExecuted",
  1263  		mock.Anything,  // duration
  1264  		mock.Anything). // stats
  1265  		Return(nil).
  1266  		Times(1) // system collection
  1267  
  1268  	metrics.On("ExecutionTransactionExecuted",
  1269  		mock.Anything, // duration
  1270  		mock.Anything, // conflict retry count
  1271  		mock.Anything, // computation used
  1272  		mock.Anything, // memory used
  1273  		expectedNumberOfEvents,
  1274  		expectedEventSize,
  1275  		false).
  1276  		Return(nil).
  1277  		Times(1) // system chunk tx
  1278  
  1279  	metrics.On(
  1280  		"ExecutionChunkDataPackGenerated",
  1281  		mock.Anything,
  1282  		mock.Anything).
  1283  		Return(nil).
  1284  		Times(1) // system collection
  1285  
  1286  	metrics.On(
  1287  		"ExecutionBlockCachedPrograms",
  1288  		expectedCachedPrograms).
  1289  		Return(nil).
  1290  		Times(1) // block
  1291  
  1292  	metrics.On(
  1293  		"ExecutionBlockExecutionEffortVectorComponent",
  1294  		mock.Anything,
  1295  		mock.Anything).
  1296  		Return(nil)
  1297  
  1298  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
  1299  	trackerStorage := mocktracker.NewMockStorage()
  1300  
  1301  	prov := provider.NewProvider(
  1302  		zerolog.Nop(),
  1303  		noopCollector,
  1304  		execution_data.DefaultSerializer,
  1305  		bservice,
  1306  		trackerStorage,
  1307  	)
  1308  
  1309  	me := new(modulemock.Local)
  1310  	me.On("NodeID").Return(unittest.IdentifierFixture())
  1311  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
  1312  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
  1313  		Return(nil, nil)
  1314  
  1315  	constRandomSource := make([]byte, 32)
  1316  
  1317  	exe, err := computer.NewBlockComputer(
  1318  		vm,
  1319  		execCtx,
  1320  		metrics,
  1321  		trace.NewNoopTracer(),
  1322  		zerolog.Nop(),
  1323  		committer,
  1324  		me,
  1325  		prov,
  1326  		nil,
  1327  		testutil.ProtocolStateWithSourceFixture(constRandomSource),
  1328  		testMaxConcurrency)
  1329  	require.NoError(t, err)
  1330  
  1331  	// create empty block, it will have system collection attached while executing
  1332  	block := generateBlock(0, 0, rag)
  1333  
  1334  	result, err := exe.ExecuteBlock(
  1335  		context.Background(),
  1336  		unittest.IdentifierFixture(),
  1337  		block,
  1338  		ledger,
  1339  		derived.NewEmptyDerivedBlockData(0))
  1340  	assert.NoError(t, err)
  1341  	assert.Len(t, result.AllExecutionSnapshots(), 1) // +1 system chunk
  1342  	assert.Len(t, result.AllTransactionResults(), 1)
  1343  
  1344  	assert.Empty(t, result.AllTransactionResults()[0].ErrorMessage)
  1345  
  1346  	committer.AssertExpectations(t)
  1347  }
  1348  
  1349  func generateBlock(
  1350  	collectionCount, transactionCount int,
  1351  	addressGenerator flow.AddressGenerator,
  1352  ) *entity.ExecutableBlock {
  1353  	return generateBlockWithVisitor(collectionCount, transactionCount, addressGenerator, nil)
  1354  }
  1355  
  1356  func generateBlockWithVisitor(
  1357  	collectionCount, transactionCount int,
  1358  	addressGenerator flow.AddressGenerator,
  1359  	visitor func(body *flow.TransactionBody),
  1360  ) *entity.ExecutableBlock {
  1361  	collections := make([]*entity.CompleteCollection, collectionCount)
  1362  	guarantees := make([]*flow.CollectionGuarantee, collectionCount)
  1363  	completeCollections := make(map[flow.Identifier]*entity.CompleteCollection)
  1364  
  1365  	for i := 0; i < collectionCount; i++ {
  1366  		collection := generateCollection(transactionCount, addressGenerator, visitor)
  1367  		collections[i] = collection
  1368  		guarantees[i] = collection.Guarantee
  1369  		completeCollections[collection.Guarantee.ID()] = collection
  1370  	}
  1371  
  1372  	block := flow.Block{
  1373  		Header: &flow.Header{
  1374  			Timestamp: flow.GenesisTime,
  1375  			Height:    42,
  1376  			View:      42,
  1377  		},
  1378  		Payload: &flow.Payload{
  1379  			Guarantees: guarantees,
  1380  		},
  1381  	}
  1382  
  1383  	return &entity.ExecutableBlock{
  1384  		Block:               &block,
  1385  		CompleteCollections: completeCollections,
  1386  		StartState:          unittest.StateCommitmentPointerFixture(),
  1387  	}
  1388  }
  1389  
  1390  func generateCollection(
  1391  	transactionCount int,
  1392  	addressGenerator flow.AddressGenerator,
  1393  	visitor func(body *flow.TransactionBody),
  1394  ) *entity.CompleteCollection {
  1395  	transactions := make([]*flow.TransactionBody, transactionCount)
  1396  
  1397  	for i := 0; i < transactionCount; i++ {
  1398  		nextAddress, err := addressGenerator.NextAddress()
  1399  		if err != nil {
  1400  			panic(fmt.Errorf("cannot generate next address in test: %w", err))
  1401  		}
  1402  		txBody := &flow.TransactionBody{
  1403  			Payer:  nextAddress, // a unique payer for each tx to generate a unique id
  1404  			Script: []byte("transaction { execute {} }"),
  1405  		}
  1406  		if visitor != nil {
  1407  			visitor(txBody)
  1408  		}
  1409  		transactions[i] = txBody
  1410  	}
  1411  
  1412  	collection := flow.Collection{Transactions: transactions}
  1413  
  1414  	guarantee := &flow.CollectionGuarantee{CollectionID: collection.ID()}
  1415  
  1416  	return &entity.CompleteCollection{
  1417  		Guarantee:    guarantee,
  1418  		Transactions: transactions,
  1419  	}
  1420  }
  1421  
  1422  type noOpExecutor struct{}
  1423  
  1424  func (noOpExecutor) Cleanup() {}
  1425  
  1426  func (noOpExecutor) Preprocess() error {
  1427  	return nil
  1428  }
  1429  
  1430  func (noOpExecutor) Execute() error {
  1431  	return nil
  1432  }
  1433  
  1434  func (noOpExecutor) Output() fvm.ProcedureOutput {
  1435  	return fvm.ProcedureOutput{}
  1436  }
  1437  
  1438  type testVM struct {
  1439  	t                    *testing.T
  1440  	eventsPerTransaction int
  1441  
  1442  	callCount int32 // atomic variable
  1443  	err       fvmErrors.CodedError
  1444  }
  1445  
  1446  type testExecutor struct {
  1447  	*testVM
  1448  
  1449  	ctx      fvm.Context
  1450  	proc     fvm.Procedure
  1451  	txnState storage.TransactionPreparer
  1452  }
  1453  
  1454  func (testExecutor) Cleanup() {
  1455  }
  1456  
  1457  func (testExecutor) Preprocess() error {
  1458  	return nil
  1459  }
  1460  
  1461  func (executor *testExecutor) Execute() error {
  1462  	atomic.AddInt32(&executor.callCount, 1)
  1463  
  1464  	getSetAProgram(executor.t, executor.txnState)
  1465  
  1466  	return nil
  1467  }
  1468  
  1469  func (executor *testExecutor) Output() fvm.ProcedureOutput {
  1470  	txn := executor.proc.(*fvm.TransactionProcedure)
  1471  
  1472  	return fvm.ProcedureOutput{
  1473  		Events: generateEvents(executor.eventsPerTransaction, txn.TxIndex),
  1474  		Err:    executor.err,
  1475  	}
  1476  }
  1477  
  1478  func (vm *testVM) NewExecutor(
  1479  	ctx fvm.Context,
  1480  	proc fvm.Procedure,
  1481  	txnState storage.TransactionPreparer,
  1482  ) fvm.ProcedureExecutor {
  1483  	return &testExecutor{
  1484  		testVM:   vm,
  1485  		proc:     proc,
  1486  		ctx:      ctx,
  1487  		txnState: txnState,
  1488  	}
  1489  }
  1490  
  1491  func (vm *testVM) CallCount() int {
  1492  	return int(atomic.LoadInt32(&vm.callCount))
  1493  }
  1494  
  1495  func (vm *testVM) Run(
  1496  	ctx fvm.Context,
  1497  	proc fvm.Procedure,
  1498  	storageSnapshot snapshot.StorageSnapshot,
  1499  ) (
  1500  	*snapshot.ExecutionSnapshot,
  1501  	fvm.ProcedureOutput,
  1502  	error,
  1503  ) {
  1504  	database := storage.NewBlockDatabase(
  1505  		storageSnapshot,
  1506  		proc.ExecutionTime(),
  1507  		ctx.DerivedBlockData)
  1508  
  1509  	txn, err := database.NewTransaction(
  1510  		proc.ExecutionTime(),
  1511  		state.DefaultParameters())
  1512  	require.NoError(vm.t, err)
  1513  
  1514  	executor := vm.NewExecutor(ctx, proc, txn)
  1515  	err = fvm.Run(executor)
  1516  	require.NoError(vm.t, err)
  1517  
  1518  	err = txn.Finalize()
  1519  	require.NoError(vm.t, err)
  1520  
  1521  	executionSnapshot, err := txn.Commit()
  1522  	require.NoError(vm.t, err)
  1523  
  1524  	return executionSnapshot, executor.Output(), nil
  1525  }
  1526  
  1527  func (testVM) GetAccount(
  1528  	_ fvm.Context,
  1529  	_ flow.Address,
  1530  	_ snapshot.StorageSnapshot,
  1531  ) (
  1532  	*flow.Account,
  1533  	error,
  1534  ) {
  1535  	panic("not implemented")
  1536  }
  1537  
  1538  func generateEvents(eventCount int, txIndex uint32) []flow.Event {
  1539  	events := make([]flow.Event, eventCount)
  1540  	for i := 0; i < eventCount; i++ {
  1541  		// creating some dummy event
  1542  		event := flow.Event{
  1543  			Type:             "whatever",
  1544  			EventIndex:       uint32(i),
  1545  			TransactionIndex: txIndex,
  1546  		}
  1547  		events[i] = event
  1548  	}
  1549  	return events
  1550  }
  1551  
  1552  type errorVM struct {
  1553  	errorAt logical.Time
  1554  }
  1555  
  1556  type errorExecutor struct {
  1557  	err error
  1558  }
  1559  
  1560  func (errorExecutor) Cleanup() {}
  1561  
  1562  func (errorExecutor) Preprocess() error {
  1563  	return nil
  1564  }
  1565  
  1566  func (e errorExecutor) Execute() error {
  1567  	return e.err
  1568  }
  1569  
  1570  func (errorExecutor) Output() fvm.ProcedureOutput {
  1571  	return fvm.ProcedureOutput{}
  1572  }
  1573  
  1574  func (vm errorVM) NewExecutor(
  1575  	ctx fvm.Context,
  1576  	proc fvm.Procedure,
  1577  	txn storage.TransactionPreparer,
  1578  ) fvm.ProcedureExecutor {
  1579  	var err error
  1580  	if proc.ExecutionTime() == vm.errorAt {
  1581  		err = fmt.Errorf("boom - internal error")
  1582  	}
  1583  
  1584  	return errorExecutor{err: err}
  1585  }
  1586  
  1587  func (vm errorVM) Run(
  1588  	ctx fvm.Context,
  1589  	proc fvm.Procedure,
  1590  	storageSnapshot snapshot.StorageSnapshot,
  1591  ) (
  1592  	*snapshot.ExecutionSnapshot,
  1593  	fvm.ProcedureOutput,
  1594  	error,
  1595  ) {
  1596  	var err error
  1597  	if proc.ExecutionTime() == vm.errorAt {
  1598  		err = fmt.Errorf("boom - internal error")
  1599  	}
  1600  	return &snapshot.ExecutionSnapshot{}, fvm.ProcedureOutput{}, err
  1601  }
  1602  
  1603  func (errorVM) GetAccount(
  1604  	ctx fvm.Context,
  1605  	addr flow.Address,
  1606  	storageSnapshot snapshot.StorageSnapshot,
  1607  ) (
  1608  	*flow.Account,
  1609  	error,
  1610  ) {
  1611  	panic("not implemented")
  1612  }
  1613  
  1614  func getSetAProgram(
  1615  	t *testing.T,
  1616  	txnState storage.TransactionPreparer,
  1617  ) {
  1618  	loc := common.AddressLocation{
  1619  		Name:    "SomeContract",
  1620  		Address: common.MustBytesToAddress([]byte{0x1}),
  1621  	}
  1622  	_, err := txnState.GetOrComputeProgram(
  1623  		txnState,
  1624  		loc,
  1625  		&programLoader{
  1626  			load: func() (*derived.Program, error) {
  1627  				return &derived.Program{}, nil
  1628  			},
  1629  		},
  1630  	)
  1631  	require.NoError(t, err)
  1632  }
  1633  
  1634  type programLoader struct {
  1635  	load func() (*derived.Program, error)
  1636  }
  1637  
  1638  func (p *programLoader) Compute(
  1639  	_ state.NestedTransactionPreparer,
  1640  	_ common.AddressLocation,
  1641  ) (
  1642  	*derived.Program,
  1643  	error,
  1644  ) {
  1645  	return p.load()
  1646  }