github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/computer/computer_test.go (about)

     1  package computer_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"testing"
     8  
     9  	"github.com/onflow/cadence"
    10  	"github.com/onflow/cadence/runtime"
    11  	"github.com/onflow/cadence/runtime/common"
    12  	"github.com/onflow/cadence/runtime/interpreter"
    13  	"github.com/onflow/cadence/runtime/sema"
    14  	"github.com/onflow/cadence/runtime/stdlib"
    15  
    16  	"github.com/ipfs/go-datastore"
    17  	dssync "github.com/ipfs/go-datastore/sync"
    18  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    19  	"github.com/rs/zerolog"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/mock"
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/koko1123/flow-go-1/engine/execution"
    25  	"github.com/koko1123/flow-go-1/engine/execution/computation/committer"
    26  	"github.com/koko1123/flow-go-1/engine/execution/computation/computer"
    27  	computermock "github.com/koko1123/flow-go-1/engine/execution/computation/computer/mock"
    28  	"github.com/koko1123/flow-go-1/engine/execution/state/delta"
    29  	"github.com/koko1123/flow-go-1/engine/execution/testutil"
    30  	"github.com/koko1123/flow-go-1/fvm"
    31  	"github.com/koko1123/flow-go-1/fvm/derived"
    32  	"github.com/koko1123/flow-go-1/fvm/environment"
    33  	fvmErrors "github.com/koko1123/flow-go-1/fvm/errors"
    34  	reusableRuntime "github.com/koko1123/flow-go-1/fvm/runtime"
    35  	"github.com/koko1123/flow-go-1/fvm/state"
    36  	"github.com/koko1123/flow-go-1/fvm/systemcontracts"
    37  	"github.com/koko1123/flow-go-1/model/flow"
    38  	"github.com/koko1123/flow-go-1/module"
    39  	"github.com/koko1123/flow-go-1/module/epochs"
    40  	"github.com/koko1123/flow-go-1/module/executiondatasync/execution_data"
    41  	"github.com/koko1123/flow-go-1/module/executiondatasync/provider"
    42  	mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock"
    43  	"github.com/koko1123/flow-go-1/module/mempool/entity"
    44  	"github.com/koko1123/flow-go-1/module/metrics"
    45  	modulemock "github.com/koko1123/flow-go-1/module/mock"
    46  	requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest"
    47  	"github.com/koko1123/flow-go-1/module/trace"
    48  	"github.com/koko1123/flow-go-1/utils/unittest"
    49  )
    50  
    51  func TestBlockExecutor_ExecuteBlock(t *testing.T) {
    52  
    53  	rag := &RandomAddressGenerator{}
    54  
    55  	me := new(modulemock.Local)
    56  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
    57  		Return(nil, nil)
    58  
    59  	t.Run("single collection", func(t *testing.T) {
    60  
    61  		execCtx := fvm.NewContext()
    62  
    63  		vm := new(computermock.VirtualMachine)
    64  		vm.On("Run", mock.Anything, mock.Anything, mock.Anything).
    65  			Return(nil).
    66  			Run(func(args mock.Arguments) {
    67  				// ctx := args[0].(fvm.Context)
    68  				tx := args[1].(*fvm.TransactionProcedure)
    69  
    70  				tx.Events = generateEvents(1, tx.TxIndex)
    71  			}).
    72  			Times(2 + 1) // 2 txs in collection + system chunk
    73  
    74  		committer := new(computermock.ViewCommitter)
    75  		committer.On("CommitView", mock.Anything, mock.Anything).
    76  			Return(nil, nil, nil, nil).
    77  			Times(2 + 1) // 2 txs in collection + system chunk
    78  
    79  		exemetrics := new(modulemock.ExecutionMetrics)
    80  		exemetrics.On("ExecutionCollectionExecuted",
    81  			mock.Anything,  // duration
    82  			mock.Anything). // stats
    83  			Return(nil).
    84  			Times(2) // 1 collection + system collection
    85  
    86  		exemetrics.On("ExecutionTransactionExecuted",
    87  			mock.Anything, // duration
    88  			mock.Anything, // computation used
    89  			mock.Anything, // memory used
    90  			mock.Anything, // actual memory used
    91  			mock.Anything, // number of events
    92  			mock.Anything, // size of events
    93  			false).        // no failure
    94  			Return(nil).
    95  			Times(2 + 1) // 2 txs in collection + system chunk tx
    96  
    97  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
    98  		trackerStorage := mocktracker.NewMockStorage()
    99  
   100  		prov := provider.NewProvider(
   101  			zerolog.Nop(),
   102  			metrics.NewNoopCollector(),
   103  			execution_data.DefaultSerializer,
   104  			bservice,
   105  			trackerStorage,
   106  		)
   107  
   108  		exe, err := computer.NewBlockComputer(
   109  			vm,
   110  			execCtx,
   111  			exemetrics,
   112  			trace.NewNoopTracer(),
   113  			zerolog.Nop(),
   114  			committer,
   115  			me,
   116  			prov)
   117  		require.NoError(t, err)
   118  
   119  		// create a block with 1 collection with 2 transactions
   120  		block := generateBlock(1, 2, rag)
   121  
   122  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   123  			return nil, nil
   124  		})
   125  
   126  		result, err := exe.ExecuteBlock(
   127  			context.Background(),
   128  			block,
   129  			view,
   130  			derived.NewEmptyDerivedBlockData())
   131  		assert.NoError(t, err)
   132  		assert.Len(t, result.StateSnapshots, 1+1) // +1 system chunk
   133  		assert.Len(t, result.TrieUpdates, 1+1)    // +1 system chunk
   134  
   135  		assertEventHashesMatch(t, 1+1, result)
   136  
   137  		vm.AssertExpectations(t)
   138  	})
   139  
   140  	t.Run("empty block still computes system chunk", func(t *testing.T) {
   141  
   142  		execCtx := fvm.NewContext()
   143  
   144  		vm := new(computermock.VirtualMachine)
   145  		committer := new(computermock.ViewCommitter)
   146  
   147  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   148  		trackerStorage := mocktracker.NewMockStorage()
   149  
   150  		prov := provider.NewProvider(
   151  			zerolog.Nop(),
   152  			metrics.NewNoopCollector(),
   153  			execution_data.DefaultSerializer,
   154  			bservice,
   155  			trackerStorage,
   156  		)
   157  
   158  		exe, err := computer.NewBlockComputer(
   159  			vm,
   160  			execCtx,
   161  			metrics.NewNoopCollector(),
   162  			trace.NewNoopTracer(),
   163  			zerolog.Nop(),
   164  			committer,
   165  			me,
   166  			prov)
   167  		require.NoError(t, err)
   168  
   169  		// create an empty block
   170  		block := generateBlock(0, 0, rag)
   171  		derivedBlockData := derived.NewEmptyDerivedBlockData()
   172  
   173  		vm.On("Run", mock.Anything, mock.Anything, mock.Anything).
   174  			Return(nil).
   175  			Once() // just system chunk
   176  
   177  		committer.On("CommitView", mock.Anything, mock.Anything).
   178  			Return(nil, nil, nil, nil).
   179  			Once() // just system chunk
   180  
   181  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   182  			return nil, nil
   183  		})
   184  
   185  		result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData)
   186  		assert.NoError(t, err)
   187  		assert.Len(t, result.StateSnapshots, 1)
   188  		assert.Len(t, result.TrieUpdates, 1)
   189  		assert.Len(t, result.TransactionResults, 1)
   190  
   191  		assertEventHashesMatch(t, 1, result)
   192  
   193  		vm.AssertExpectations(t)
   194  	})
   195  
   196  	t.Run("system chunk transaction should not fail", func(t *testing.T) {
   197  
   198  		// include all fees. System chunk should ignore them
   199  		contextOptions := []fvm.Option{
   200  			fvm.WithTransactionFeesEnabled(true),
   201  			fvm.WithAccountStorageLimit(true),
   202  			fvm.WithBlocks(&environment.NoopBlockFinder{}),
   203  		}
   204  		// set 0 clusters to pass n_collectors >= n_clusters check
   205  		epochConfig := epochs.DefaultEpochConfig()
   206  		epochConfig.NumCollectorClusters = 0
   207  		bootstrapOptions := []fvm.BootstrapProcedureOption{
   208  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   209  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   210  			fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
   211  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   212  			fvm.WithEpochConfig(epochConfig),
   213  		}
   214  
   215  		chain := flow.Localnet.Chain()
   216  		vm := fvm.NewVirtualMachine()
   217  		derivedBlockData := derived.NewEmptyDerivedBlockData()
   218  		baseOpts := []fvm.Option{
   219  			fvm.WithChain(chain),
   220  			fvm.WithDerivedBlockData(derivedBlockData),
   221  		}
   222  
   223  		opts := append(baseOpts, contextOptions...)
   224  		ctx := fvm.NewContext(opts...)
   225  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   226  			return nil, nil
   227  		})
   228  
   229  		baseBootstrapOpts := []fvm.BootstrapProcedureOption{
   230  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   231  		}
   232  		bootstrapOpts := append(baseBootstrapOpts, bootstrapOptions...)
   233  		err := vm.Run(ctx, fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), view)
   234  		require.NoError(t, err)
   235  
   236  		comm := new(computermock.ViewCommitter)
   237  
   238  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   239  		trackerStorage := mocktracker.NewMockStorage()
   240  
   241  		prov := provider.NewProvider(
   242  			zerolog.Nop(),
   243  			metrics.NewNoopCollector(),
   244  			execution_data.DefaultSerializer,
   245  			bservice,
   246  			trackerStorage,
   247  		)
   248  
   249  		exe, err := computer.NewBlockComputer(
   250  			vm,
   251  			ctx,
   252  			metrics.NewNoopCollector(),
   253  			trace.NewNoopTracer(),
   254  			zerolog.Nop(),
   255  			comm,
   256  			me,
   257  			prov)
   258  		require.NoError(t, err)
   259  
   260  		// create an empty block
   261  		block := generateBlock(0, 0, rag)
   262  
   263  		comm.On("CommitView", mock.Anything, mock.Anything).
   264  			Return(nil, nil, nil, nil).
   265  			Once() // just system chunk
   266  
   267  		result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData)
   268  		assert.NoError(t, err)
   269  		assert.Len(t, result.StateSnapshots, 1)
   270  		assert.Len(t, result.TrieUpdates, 1)
   271  		assert.Len(t, result.TransactionResults, 1)
   272  
   273  		assert.Empty(t, result.TransactionResults[0].ErrorMessage)
   274  	})
   275  
   276  	t.Run("multiple collections", func(t *testing.T) {
   277  		execCtx := fvm.NewContext()
   278  
   279  		vm := new(computermock.VirtualMachine)
   280  		committer := new(computermock.ViewCommitter)
   281  
   282  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   283  		trackerStorage := mocktracker.NewMockStorage()
   284  
   285  		prov := provider.NewProvider(
   286  			zerolog.Nop(),
   287  			metrics.NewNoopCollector(),
   288  			execution_data.DefaultSerializer,
   289  			bservice,
   290  			trackerStorage,
   291  		)
   292  
   293  		exe, err := computer.NewBlockComputer(
   294  			vm,
   295  			execCtx,
   296  			metrics.NewNoopCollector(),
   297  			trace.NewNoopTracer(),
   298  			zerolog.Nop(),
   299  			committer,
   300  			me,
   301  			prov)
   302  		require.NoError(t, err)
   303  
   304  		collectionCount := 2
   305  		transactionsPerCollection := 2
   306  		eventsPerTransaction := 2
   307  		eventsPerCollection := eventsPerTransaction * transactionsPerCollection
   308  		totalTransactionCount := (collectionCount * transactionsPerCollection) + 1 // +1 for system chunk
   309  		// totalEventCount := eventsPerTransaction * totalTransactionCount
   310  
   311  		// create a block with 2 collections with 2 transactions each
   312  		block := generateBlock(collectionCount, transactionsPerCollection, rag)
   313  		derivedBlockData := derived.NewEmptyDerivedBlockData()
   314  
   315  		vm.On("Run", mock.Anything, mock.Anything, mock.Anything).
   316  			Run(func(args mock.Arguments) {
   317  				tx := args[1].(*fvm.TransactionProcedure)
   318  
   319  				tx.Err = fvmErrors.NewInvalidAddressErrorf(flow.Address{}, "no payer address provided")
   320  				// create dummy events
   321  				tx.Events = generateEvents(eventsPerTransaction, tx.TxIndex)
   322  			}).
   323  			Return(nil).
   324  			Times(totalTransactionCount)
   325  
   326  		committer.On("CommitView", mock.Anything, mock.Anything).
   327  			Return(nil, nil, nil, nil).
   328  			Times(collectionCount + 1)
   329  
   330  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   331  			return nil, nil
   332  		})
   333  
   334  		result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData)
   335  		assert.NoError(t, err)
   336  
   337  		// chunk count should match collection count
   338  		assert.Len(t, result.StateSnapshots, collectionCount+1) // system chunk
   339  
   340  		// all events should have been collected
   341  		assert.Len(t, result.Events, collectionCount+1)
   342  
   343  		for i := 0; i < collectionCount; i++ {
   344  			assert.Len(t, result.Events[i], eventsPerCollection)
   345  		}
   346  
   347  		assert.Len(t, result.Events[len(result.Events)-1], eventsPerTransaction)
   348  
   349  		// events should have been indexed by transaction and event
   350  		k := 0
   351  		for expectedTxIndex := 0; expectedTxIndex < totalTransactionCount; expectedTxIndex++ {
   352  			for expectedEventIndex := 0; expectedEventIndex < eventsPerTransaction; expectedEventIndex++ {
   353  
   354  				chunkIndex := k / eventsPerCollection
   355  				eventIndex := k % eventsPerCollection
   356  
   357  				e := result.Events[chunkIndex][eventIndex]
   358  				assert.EqualValues(t, expectedEventIndex, int(e.EventIndex))
   359  				assert.EqualValues(t, expectedTxIndex, e.TransactionIndex)
   360  				k++
   361  			}
   362  		}
   363  
   364  		expectedResults := make([]flow.TransactionResult, 0)
   365  		for _, c := range block.CompleteCollections {
   366  			for _, t := range c.Transactions {
   367  				txResult := flow.TransactionResult{
   368  					TransactionID: t.ID(),
   369  					ErrorMessage:  fvmErrors.NewInvalidAddressErrorf(flow.Address{}, "no payer address provided").Error(),
   370  				}
   371  				expectedResults = append(expectedResults, txResult)
   372  			}
   373  		}
   374  		assert.ElementsMatch(t, expectedResults, result.TransactionResults[0:len(result.TransactionResults)-1]) // strip system chunk
   375  
   376  		assertEventHashesMatch(t, collectionCount+1, result)
   377  
   378  		vm.AssertExpectations(t)
   379  	})
   380  
   381  	t.Run("service events are emitted", func(t *testing.T) {
   382  		execCtx := fvm.NewContext(
   383  			fvm.WithServiceEventCollectionEnabled(),
   384  			fvm.WithAuthorizationChecksEnabled(false),
   385  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   386  		)
   387  
   388  		collectionCount := 2
   389  		transactionsPerCollection := 2
   390  
   391  		totalTransactionCount := (collectionCount * transactionsPerCollection) + 1 // +1 for system chunk
   392  
   393  		// create a block with 2 collections with 2 transactions each
   394  		block := generateBlock(collectionCount, transactionsPerCollection, rag)
   395  
   396  		ordinaryEvent := cadence.Event{
   397  			EventType: &cadence.EventType{
   398  				Location:            stdlib.FlowLocation{},
   399  				QualifiedIdentifier: "what.ever",
   400  			},
   401  		}
   402  
   403  		serviceEvents, err := systemcontracts.ServiceEventsForChain(execCtx.Chain.ChainID())
   404  		require.NoError(t, err)
   405  
   406  		serviceEventA := cadence.Event{
   407  			EventType: &cadence.EventType{
   408  				Location: common.AddressLocation{
   409  					Address: common.Address(serviceEvents.EpochSetup.Address),
   410  				},
   411  				QualifiedIdentifier: serviceEvents.EpochSetup.QualifiedIdentifier(),
   412  			},
   413  		}
   414  		serviceEventB := cadence.Event{
   415  			EventType: &cadence.EventType{
   416  				Location: common.AddressLocation{
   417  					Address: common.Address(serviceEvents.EpochCommit.Address),
   418  				},
   419  				QualifiedIdentifier: serviceEvents.EpochCommit.QualifiedIdentifier(),
   420  			},
   421  		}
   422  
   423  		// events to emit for each iteration/transaction
   424  		events := make([][]cadence.Event, totalTransactionCount)
   425  		events[0] = nil
   426  		events[1] = []cadence.Event{serviceEventA, ordinaryEvent}
   427  		events[2] = []cadence.Event{ordinaryEvent}
   428  		events[3] = nil
   429  		events[4] = []cadence.Event{serviceEventB}
   430  
   431  		emittingRuntime := &testRuntime{
   432  			executeTransaction: func(script runtime.Script, context runtime.Context) error {
   433  				for _, e := range events[0] {
   434  					err := context.Interface.EmitEvent(e)
   435  					if err != nil {
   436  						return err
   437  					}
   438  				}
   439  				events = events[1:]
   440  				return nil
   441  			},
   442  			readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) {
   443  				return nil, nil
   444  			},
   445  		}
   446  
   447  		execCtx = fvm.NewContextFromParent(
   448  			execCtx,
   449  			fvm.WithReusableCadenceRuntimePool(
   450  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   451  					0,
   452  					func(_ runtime.Config) runtime.Runtime {
   453  						return emittingRuntime
   454  					})))
   455  
   456  		vm := fvm.NewVirtualMachine()
   457  
   458  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   459  		trackerStorage := mocktracker.NewMockStorage()
   460  
   461  		prov := provider.NewProvider(
   462  			zerolog.Nop(),
   463  			metrics.NewNoopCollector(),
   464  			execution_data.DefaultSerializer,
   465  			bservice,
   466  			trackerStorage,
   467  		)
   468  
   469  		exe, err := computer.NewBlockComputer(
   470  			vm,
   471  			execCtx,
   472  			metrics.NewNoopCollector(),
   473  			trace.NewNoopTracer(),
   474  			zerolog.Nop(),
   475  			committer.NewNoopViewCommitter(),
   476  			me,
   477  			prov)
   478  		require.NoError(t, err)
   479  
   480  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   481  			return nil, nil
   482  		})
   483  
   484  		result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData())
   485  		require.NoError(t, err)
   486  
   487  		// make sure event index sequence are valid
   488  		for _, eventsList := range result.Events {
   489  			unittest.EnsureEventsIndexSeq(t, eventsList, execCtx.Chain.ChainID())
   490  		}
   491  
   492  		// all events should have been collected
   493  		require.Len(t, result.ServiceEvents, 2)
   494  
   495  		// events are ordered
   496  		require.Equal(t, serviceEventA.EventType.ID(), string(result.ServiceEvents[0].Type))
   497  		require.Equal(t, serviceEventB.EventType.ID(), string(result.ServiceEvents[1].Type))
   498  
   499  		assertEventHashesMatch(t, collectionCount+1, result)
   500  	})
   501  
   502  	t.Run("succeeding transactions store programs", func(t *testing.T) {
   503  
   504  		execCtx := fvm.NewContext()
   505  
   506  		address := common.Address{0x1}
   507  		contractLocation := common.AddressLocation{
   508  			Address: address,
   509  			Name:    "Test",
   510  		}
   511  
   512  		contractProgram := &interpreter.Program{}
   513  
   514  		rt := &testRuntime{
   515  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   516  
   517  				program, err := r.Interface.GetProgram(contractLocation) //nolint:staticcheck
   518  				require.NoError(t, err)
   519  				require.Nil(t, program)
   520  
   521  				err = r.Interface.SetProgram(
   522  					contractLocation,
   523  					contractProgram,
   524  				)
   525  				require.NoError(t, err)
   526  
   527  				return nil
   528  			},
   529  			readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) {
   530  				return nil, nil
   531  			},
   532  		}
   533  
   534  		execCtx = fvm.NewContextFromParent(
   535  			execCtx,
   536  			fvm.WithReusableCadenceRuntimePool(
   537  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   538  					0,
   539  					func(_ runtime.Config) runtime.Runtime {
   540  						return rt
   541  					})))
   542  
   543  		vm := fvm.NewVirtualMachine()
   544  
   545  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   546  		trackerStorage := mocktracker.NewMockStorage()
   547  
   548  		prov := provider.NewProvider(
   549  			zerolog.Nop(),
   550  			metrics.NewNoopCollector(),
   551  			execution_data.DefaultSerializer,
   552  			bservice,
   553  			trackerStorage,
   554  		)
   555  
   556  		exe, err := computer.NewBlockComputer(
   557  			vm,
   558  			execCtx,
   559  			metrics.NewNoopCollector(),
   560  			trace.NewNoopTracer(),
   561  			zerolog.Nop(),
   562  			committer.NewNoopViewCommitter(),
   563  			me,
   564  			prov)
   565  		require.NoError(t, err)
   566  
   567  		const collectionCount = 2
   568  		const transactionCount = 2
   569  		block := generateBlock(collectionCount, transactionCount, rag)
   570  
   571  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   572  			return nil, nil
   573  		})
   574  
   575  		err = view.Set(string(address.Bytes()), state.AccountStatusKey, environment.NewAccountStatus().ToBytes())
   576  		require.NoError(t, err)
   577  
   578  		result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData())
   579  		assert.NoError(t, err)
   580  		assert.Len(t, result.StateSnapshots, collectionCount+1) // +1 system chunk
   581  	})
   582  
   583  	t.Run("failing transactions do not store programs", func(t *testing.T) {
   584  		execCtx := fvm.NewContext(
   585  			fvm.WithAuthorizationChecksEnabled(false),
   586  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   587  		)
   588  
   589  		address := common.Address{0x1}
   590  
   591  		contractLocation := common.AddressLocation{
   592  			Address: address,
   593  			Name:    "Test",
   594  		}
   595  
   596  		contractProgram := &interpreter.Program{}
   597  
   598  		const collectionCount = 2
   599  		const transactionCount = 2
   600  
   601  		var executionCalls int
   602  
   603  		rt := &testRuntime{
   604  			executeTransaction: func(script runtime.Script, r runtime.Context) error {
   605  
   606  				executionCalls++
   607  
   608  				// NOTE: set a program and revert all transactions but the system chunk transaction
   609  
   610  				program, err := r.Interface.GetProgram(contractLocation) //nolint:staticcheck
   611  				require.NoError(t, err)
   612  
   613  				if executionCalls > collectionCount*transactionCount {
   614  					return nil
   615  				}
   616  				if program == nil {
   617  
   618  					err = r.Interface.SetProgram(
   619  						contractLocation,
   620  						contractProgram,
   621  					)
   622  					require.NoError(t, err)
   623  
   624  				}
   625  				return runtime.Error{
   626  					Err: fmt.Errorf("TX reverted"),
   627  				}
   628  			},
   629  			readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) {
   630  				return nil, nil
   631  			},
   632  		}
   633  
   634  		execCtx = fvm.NewContextFromParent(
   635  			execCtx,
   636  			fvm.WithReusableCadenceRuntimePool(
   637  				reusableRuntime.NewCustomReusableCadenceRuntimePool(
   638  					0,
   639  					func(_ runtime.Config) runtime.Runtime {
   640  						return rt
   641  					})))
   642  
   643  		vm := fvm.NewVirtualMachine()
   644  
   645  		bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   646  		trackerStorage := mocktracker.NewMockStorage()
   647  
   648  		prov := provider.NewProvider(
   649  			zerolog.Nop(),
   650  			metrics.NewNoopCollector(),
   651  			execution_data.DefaultSerializer,
   652  			bservice,
   653  			trackerStorage,
   654  		)
   655  
   656  		exe, err := computer.NewBlockComputer(
   657  			vm,
   658  			execCtx,
   659  			metrics.NewNoopCollector(),
   660  			trace.NewNoopTracer(),
   661  			zerolog.Nop(),
   662  			committer.NewNoopViewCommitter(),
   663  			me,
   664  			prov)
   665  		require.NoError(t, err)
   666  
   667  		block := generateBlock(collectionCount, transactionCount, rag)
   668  
   669  		view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   670  			return nil, nil
   671  		})
   672  
   673  		err = view.Set(string(address.Bytes()), state.AccountStatusKey, environment.NewAccountStatus().ToBytes())
   674  		require.NoError(t, err)
   675  
   676  		result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData())
   677  		require.NoError(t, err)
   678  		assert.Len(t, result.StateSnapshots, collectionCount+1) // +1 system chunk
   679  	})
   680  }
   681  
   682  func assertEventHashesMatch(t *testing.T, expectedNoOfChunks int, result *execution.ComputationResult) {
   683  
   684  	require.Len(t, result.Events, expectedNoOfChunks)
   685  	require.Len(t, result.EventsHashes, expectedNoOfChunks)
   686  
   687  	for i := 0; i < expectedNoOfChunks; i++ {
   688  		calculatedHash, err := flow.EventsMerkleRootHash(result.Events[i])
   689  		require.NoError(t, err)
   690  
   691  		require.Equal(t, calculatedHash, result.EventsHashes[i])
   692  	}
   693  }
   694  
   695  type testTransactionExecutor struct {
   696  	executeTransaction func(runtime.Script, runtime.Context) error
   697  
   698  	script  runtime.Script
   699  	context runtime.Context
   700  }
   701  
   702  func (executor *testTransactionExecutor) Preprocess() error {
   703  	// Do nothing.
   704  	return nil
   705  }
   706  
   707  func (executor *testTransactionExecutor) Execute() error {
   708  	return executor.executeTransaction(executor.script, executor.context)
   709  }
   710  
   711  func (executor *testTransactionExecutor) Result() (cadence.Value, error) {
   712  	panic("Result not expected")
   713  }
   714  
   715  type testRuntime struct {
   716  	executeScript      func(runtime.Script, runtime.Context) (cadence.Value, error)
   717  	executeTransaction func(runtime.Script, runtime.Context) error
   718  	readStored         func(common.Address, cadence.Path, runtime.Context) (cadence.Value, error)
   719  }
   720  
   721  func (e *testRuntime) NewScriptExecutor(script runtime.Script, c runtime.Context) runtime.Executor {
   722  	panic("NewScriptExecutor not expected")
   723  }
   724  
   725  func (e *testRuntime) NewTransactionExecutor(script runtime.Script, c runtime.Context) runtime.Executor {
   726  	return &testTransactionExecutor{
   727  		executeTransaction: e.executeTransaction,
   728  		script:             script,
   729  		context:            c,
   730  	}
   731  }
   732  
   733  func (e *testRuntime) NewContractFunctionExecutor(contractLocation common.AddressLocation, functionName string, arguments []cadence.Value, argumentTypes []sema.Type, context runtime.Context) runtime.Executor {
   734  	panic("NewContractFunctionExecutor not expected")
   735  }
   736  
   737  var _ runtime.Runtime = &testRuntime{}
   738  
   739  func (e *testRuntime) SetInvalidatedResourceValidationEnabled(_ bool) {
   740  	panic("SetInvalidatedResourceValidationEnabled not expected")
   741  }
   742  
   743  func (e *testRuntime) SetTracingEnabled(_ bool) {
   744  	panic("SetTracingEnabled not expected")
   745  }
   746  
   747  func (e *testRuntime) SetResourceOwnerChangeHandlerEnabled(_ bool) {
   748  	panic("SetResourceOwnerChangeHandlerEnabled not expected")
   749  }
   750  
   751  func (e *testRuntime) InvokeContractFunction(_ common.AddressLocation, _ string, _ []cadence.Value, _ []sema.Type, _ runtime.Context) (cadence.Value, error) {
   752  	panic("InvokeContractFunction not expected")
   753  }
   754  
   755  func (e *testRuntime) ExecuteScript(script runtime.Script, context runtime.Context) (cadence.Value, error) {
   756  	return e.executeScript(script, context)
   757  }
   758  
   759  func (e *testRuntime) ExecuteTransaction(script runtime.Script, context runtime.Context) error {
   760  	return e.executeTransaction(script, context)
   761  }
   762  
   763  func (*testRuntime) ParseAndCheckProgram(_ []byte, _ runtime.Context) (*interpreter.Program, error) {
   764  	panic("ParseAndCheckProgram not expected")
   765  }
   766  
   767  func (*testRuntime) SetCoverageReport(_ *runtime.CoverageReport) {
   768  	panic("SetCoverageReport not expected")
   769  }
   770  
   771  func (*testRuntime) SetContractUpdateValidationEnabled(_ bool) {
   772  	panic("SetContractUpdateValidationEnabled not expected")
   773  }
   774  
   775  func (*testRuntime) SetAtreeValidationEnabled(_ bool) {
   776  	panic("SetAtreeValidationEnabled not expected")
   777  }
   778  
   779  func (e *testRuntime) ReadStored(a common.Address, p cadence.Path, c runtime.Context) (cadence.Value, error) {
   780  	return e.readStored(a, p, c)
   781  }
   782  
   783  func (*testRuntime) ReadLinked(_ common.Address, _ cadence.Path, _ runtime.Context) (cadence.Value, error) {
   784  	panic("ReadLinked not expected")
   785  }
   786  
   787  func (*testRuntime) SetDebugger(_ *interpreter.Debugger) {
   788  	panic("SetDebugger not expected")
   789  }
   790  
   791  type RandomAddressGenerator struct{}
   792  
   793  func (r *RandomAddressGenerator) NextAddress() (flow.Address, error) {
   794  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000))), nil
   795  }
   796  
   797  func (r *RandomAddressGenerator) CurrentAddress() flow.Address {
   798  	return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000)))
   799  }
   800  
   801  func (r *RandomAddressGenerator) Bytes() []byte {
   802  	panic("not implemented")
   803  }
   804  
   805  func (r *RandomAddressGenerator) AddressCount() uint64 {
   806  	panic("not implemented")
   807  }
   808  
   809  func (testRuntime) Storage(runtime.Context) (*runtime.Storage, *interpreter.Interpreter, error) {
   810  	panic("Storage not expected")
   811  }
   812  
   813  type FixedAddressGenerator struct {
   814  	Address flow.Address
   815  }
   816  
   817  func (f *FixedAddressGenerator) NextAddress() (flow.Address, error) {
   818  	return f.Address, nil
   819  }
   820  
   821  func (f *FixedAddressGenerator) CurrentAddress() flow.Address {
   822  	return f.Address
   823  }
   824  
   825  func (f *FixedAddressGenerator) Bytes() []byte {
   826  	panic("not implemented")
   827  }
   828  
   829  func (f *FixedAddressGenerator) AddressCount() uint64 {
   830  	panic("not implemented")
   831  }
   832  
   833  func Test_AccountStatusRegistersAreIncluded(t *testing.T) {
   834  
   835  	address := flow.HexToAddress("1234")
   836  	fag := &FixedAddressGenerator{Address: address}
   837  
   838  	vm := fvm.NewVirtualMachine()
   839  	execCtx := fvm.NewContext()
   840  
   841  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
   842  
   843  	key, err := unittest.AccountKeyDefaultFixture()
   844  	require.NoError(t, err)
   845  
   846  	view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   847  		return ledger.Get(owner, key)
   848  	})
   849  	txnState := state.NewTransactionState(view, state.DefaultParameters())
   850  	accounts := environment.NewAccounts(txnState)
   851  
   852  	// account creation, signing of transaction and bootstrapping ledger should not be required for this test
   853  	// as freeze check should happen before a transaction signature is checked
   854  	// but we currently discard all the touches if it fails and any point
   855  	err = accounts.Create([]flow.AccountPublicKey{key.PublicKey(1000)}, address)
   856  	require.NoError(t, err)
   857  
   858  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   859  	trackerStorage := mocktracker.NewMockStorage()
   860  
   861  	prov := provider.NewProvider(
   862  		zerolog.Nop(),
   863  		metrics.NewNoopCollector(),
   864  		execution_data.DefaultSerializer,
   865  		bservice,
   866  		trackerStorage,
   867  	)
   868  
   869  	me := new(modulemock.Local)
   870  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   871  		Return(nil, nil)
   872  
   873  	exe, err := computer.NewBlockComputer(
   874  		vm,
   875  		execCtx,
   876  		metrics.NewNoopCollector(),
   877  		trace.NewNoopTracer(),
   878  		zerolog.Nop(),
   879  		committer.NewNoopViewCommitter(),
   880  		me,
   881  		prov)
   882  	require.NoError(t, err)
   883  
   884  	block := generateBlockWithVisitor(1, 1, fag, func(txBody *flow.TransactionBody) {
   885  		err := testutil.SignTransaction(txBody, txBody.Payer, *key, 0)
   886  		require.NoError(t, err)
   887  	})
   888  
   889  	_, err = exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData())
   890  	assert.NoError(t, err)
   891  
   892  	registerTouches := view.Interactions().RegisterTouches()
   893  
   894  	// make sure check for account status has been registered
   895  	id := flow.RegisterID{
   896  		Owner: string(address.Bytes()),
   897  		Key:   state.AccountStatusKey,
   898  	}
   899  
   900  	require.Contains(t, registerTouches, id)
   901  }
   902  
   903  func Test_ExecutingSystemCollection(t *testing.T) {
   904  
   905  	execCtx := fvm.NewContext(
   906  		fvm.WithChain(flow.Localnet.Chain()),
   907  		fvm.WithBlocks(&environment.NoopBlockFinder{}),
   908  	)
   909  
   910  	vm := fvm.NewVirtualMachine()
   911  
   912  	rag := &RandomAddressGenerator{}
   913  
   914  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
   915  
   916  	committer := new(computermock.ViewCommitter)
   917  	committer.On("CommitView", mock.Anything, mock.Anything).
   918  		Return(nil, nil, nil, nil).
   919  		Times(1) // only system chunk
   920  
   921  	noopCollector := metrics.NewNoopCollector()
   922  
   923  	metrics := new(modulemock.ExecutionMetrics)
   924  	expectedNumberOfEvents := 2
   925  	expectedEventSize := 912
   926  	metrics.On("ExecutionCollectionExecuted",
   927  		mock.Anything,  // duration
   928  		mock.Anything). // stats
   929  		Return(nil).
   930  		Times(1) // system collection
   931  
   932  	metrics.On("ExecutionTransactionExecuted",
   933  		mock.Anything, // duration
   934  		mock.Anything, // computation used
   935  		mock.Anything, // memory used
   936  		mock.Anything, // actual memory used
   937  		expectedNumberOfEvents,
   938  		expectedEventSize,
   939  		false).
   940  		Return(nil).
   941  		Times(1) // system chunk tx
   942  
   943  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   944  	trackerStorage := mocktracker.NewMockStorage()
   945  
   946  	prov := provider.NewProvider(
   947  		zerolog.Nop(),
   948  		noopCollector,
   949  		execution_data.DefaultSerializer,
   950  		bservice,
   951  		trackerStorage,
   952  	)
   953  
   954  	me := new(modulemock.Local)
   955  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   956  		Return(nil, nil)
   957  
   958  	exe, err := computer.NewBlockComputer(
   959  		vm,
   960  		execCtx,
   961  		metrics,
   962  		trace.NewNoopTracer(),
   963  		zerolog.Nop(),
   964  		committer,
   965  		me,
   966  		prov)
   967  	require.NoError(t, err)
   968  
   969  	// create empty block, it will have system collection attached while executing
   970  	block := generateBlock(0, 0, rag)
   971  
   972  	view := delta.NewView(ledger.Get)
   973  
   974  	result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData())
   975  	assert.NoError(t, err)
   976  	assert.Len(t, result.StateSnapshots, 1) // +1 system chunk
   977  	assert.Len(t, result.TransactionResults, 1)
   978  
   979  	assert.Empty(t, result.TransactionResults[0].ErrorMessage)
   980  
   981  	stats := result.CollectionStats(0)
   982  	// ignore computation and memory used
   983  	stats.ComputationUsed = 0
   984  	stats.MemoryUsed = 0
   985  
   986  	assert.Equal(
   987  		t,
   988  		module.ExecutionResultStats{
   989  			EventCounts:                     expectedNumberOfEvents,
   990  			EventSize:                       expectedEventSize,
   991  			NumberOfRegistersTouched:        50,
   992  			NumberOfBytesWrittenToRegisters: 3404,
   993  			NumberOfCollections:             1,
   994  			NumberOfTransactions:            1,
   995  		},
   996  		stats)
   997  
   998  	committer.AssertExpectations(t)
   999  }
  1000  
  1001  func generateBlock(collectionCount, transactionCount int, addressGenerator flow.AddressGenerator) *entity.ExecutableBlock {
  1002  	return generateBlockWithVisitor(collectionCount, transactionCount, addressGenerator, nil)
  1003  }
  1004  
  1005  func generateBlockWithVisitor(collectionCount, transactionCount int, addressGenerator flow.AddressGenerator, visitor func(body *flow.TransactionBody)) *entity.ExecutableBlock {
  1006  	collections := make([]*entity.CompleteCollection, collectionCount)
  1007  	guarantees := make([]*flow.CollectionGuarantee, collectionCount)
  1008  	completeCollections := make(map[flow.Identifier]*entity.CompleteCollection)
  1009  
  1010  	for i := 0; i < collectionCount; i++ {
  1011  		collection := generateCollection(transactionCount, addressGenerator, visitor)
  1012  		collections[i] = collection
  1013  		guarantees[i] = collection.Guarantee
  1014  		completeCollections[collection.Guarantee.ID()] = collection
  1015  	}
  1016  
  1017  	block := flow.Block{
  1018  		Header: &flow.Header{
  1019  			Timestamp: flow.GenesisTime,
  1020  			Height:    42,
  1021  			View:      42,
  1022  		},
  1023  		Payload: &flow.Payload{
  1024  			Guarantees: guarantees,
  1025  		},
  1026  	}
  1027  
  1028  	return &entity.ExecutableBlock{
  1029  		Block:               &block,
  1030  		CompleteCollections: completeCollections,
  1031  		StartState:          unittest.StateCommitmentPointerFixture(),
  1032  	}
  1033  }
  1034  
  1035  func generateCollection(transactionCount int, addressGenerator flow.AddressGenerator, visitor func(body *flow.TransactionBody)) *entity.CompleteCollection {
  1036  	transactions := make([]*flow.TransactionBody, transactionCount)
  1037  
  1038  	for i := 0; i < transactionCount; i++ {
  1039  		nextAddress, err := addressGenerator.NextAddress()
  1040  		if err != nil {
  1041  			panic(fmt.Errorf("cannot generate next address in test: %w", err))
  1042  		}
  1043  		txBody := &flow.TransactionBody{
  1044  			Payer:  nextAddress, // a unique payer for each tx to generate a unique id
  1045  			Script: []byte("transaction { execute {} }"),
  1046  		}
  1047  		if visitor != nil {
  1048  			visitor(txBody)
  1049  		}
  1050  		transactions[i] = txBody
  1051  	}
  1052  
  1053  	collection := flow.Collection{Transactions: transactions}
  1054  
  1055  	guarantee := &flow.CollectionGuarantee{CollectionID: collection.ID()}
  1056  
  1057  	return &entity.CompleteCollection{
  1058  		Guarantee:    guarantee,
  1059  		Transactions: transactions,
  1060  	}
  1061  }
  1062  
  1063  func generateEvents(eventCount int, txIndex uint32) []flow.Event {
  1064  	events := make([]flow.Event, eventCount)
  1065  	for i := 0; i < eventCount; i++ {
  1066  		// creating some dummy event
  1067  		event := flow.Event{Type: "whatever", EventIndex: uint32(i), TransactionIndex: txIndex}
  1068  		events[i] = event
  1069  	}
  1070  	return events
  1071  }