github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/computation/manager_test.go (about)

     1  package computation
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"math"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/ipfs/boxo/blockstore"
    13  	"github.com/ipfs/go-datastore"
    14  	dssync "github.com/ipfs/go-datastore/sync"
    15  	"github.com/onflow/cadence"
    16  	jsoncdc "github.com/onflow/cadence/encoding/json"
    17  	"github.com/onflow/cadence/runtime/common"
    18  	"github.com/rs/zerolog"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/mock"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/onflow/flow-go/engine/execution"
    24  	"github.com/onflow/flow-go/engine/execution/computation/committer"
    25  	"github.com/onflow/flow-go/engine/execution/computation/computer"
    26  	"github.com/onflow/flow-go/engine/execution/computation/query"
    27  	state2 "github.com/onflow/flow-go/engine/execution/state"
    28  	unittest2 "github.com/onflow/flow-go/engine/execution/state/unittest"
    29  	"github.com/onflow/flow-go/engine/execution/testutil"
    30  	"github.com/onflow/flow-go/fvm"
    31  	"github.com/onflow/flow-go/fvm/environment"
    32  	fvmErrors "github.com/onflow/flow-go/fvm/errors"
    33  	"github.com/onflow/flow-go/fvm/storage"
    34  	"github.com/onflow/flow-go/fvm/storage/derived"
    35  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    36  	"github.com/onflow/flow-go/fvm/systemcontracts"
    37  	"github.com/onflow/flow-go/ledger/complete"
    38  	"github.com/onflow/flow-go/ledger/complete/wal/fixtures"
    39  	"github.com/onflow/flow-go/model/flow"
    40  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    41  	"github.com/onflow/flow-go/module/executiondatasync/provider"
    42  	mocktracker "github.com/onflow/flow-go/module/executiondatasync/tracker/mock"
    43  	"github.com/onflow/flow-go/module/mempool/entity"
    44  	"github.com/onflow/flow-go/module/metrics"
    45  	module "github.com/onflow/flow-go/module/mock"
    46  	requesterunit "github.com/onflow/flow-go/module/state_synchronization/requester/unittest"
    47  	"github.com/onflow/flow-go/module/trace"
    48  	"github.com/onflow/flow-go/utils/unittest"
    49  )
    50  
    51  var scriptLogThreshold = 1 * time.Second
    52  
    53  func TestComputeBlockWithStorage(t *testing.T) {
    54  	chain := flow.Mainnet.Chain()
    55  
    56  	vm := fvm.NewVirtualMachine()
    57  	execCtx := fvm.NewContext(fvm.WithChain(chain))
    58  
    59  	privateKeys, err := testutil.GenerateAccountPrivateKeys(2)
    60  	require.NoError(t, err)
    61  
    62  	snapshotTree, accounts, err := testutil.CreateAccounts(
    63  		vm,
    64  		testutil.RootBootstrappedLedger(vm, execCtx),
    65  		privateKeys,
    66  		chain)
    67  	require.NoError(t, err)
    68  
    69  	tx1 := testutil.DeployCounterContractTransaction(accounts[0], chain)
    70  	tx1.SetProposalKey(chain.ServiceAddress(), 0, 0).
    71  		SetComputeLimit(1000).
    72  		SetPayer(chain.ServiceAddress())
    73  
    74  	err = testutil.SignPayload(tx1, accounts[0], privateKeys[0])
    75  	require.NoError(t, err)
    76  
    77  	err = testutil.SignEnvelope(tx1, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
    78  	require.NoError(t, err)
    79  
    80  	tx2 := testutil.CreateCounterTransaction(accounts[0], accounts[1])
    81  	tx2.SetProposalKey(chain.ServiceAddress(), 0, 0).
    82  		SetComputeLimit(1000).
    83  		SetPayer(chain.ServiceAddress())
    84  
    85  	err = testutil.SignPayload(tx2, accounts[1], privateKeys[1])
    86  	require.NoError(t, err)
    87  
    88  	err = testutil.SignEnvelope(tx2, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
    89  	require.NoError(t, err)
    90  
    91  	transactions := []*flow.TransactionBody{tx1, tx2}
    92  
    93  	col := flow.Collection{Transactions: transactions}
    94  
    95  	guarantee := flow.CollectionGuarantee{
    96  		CollectionID: col.ID(),
    97  		Signature:    nil,
    98  	}
    99  
   100  	block := flow.Block{
   101  		Header: &flow.Header{
   102  			View: 42,
   103  		},
   104  		Payload: &flow.Payload{
   105  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   106  		},
   107  	}
   108  
   109  	executableBlock := &entity.ExecutableBlock{
   110  		Block: &block,
   111  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   112  			guarantee.ID(): {
   113  				Guarantee:    &guarantee,
   114  				Transactions: transactions,
   115  			},
   116  		},
   117  		StartState: unittest.StateCommitmentPointerFixture(),
   118  	}
   119  
   120  	me := new(module.Local)
   121  	me.On("NodeID").Return(unittest.IdentifierFixture())
   122  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   123  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   124  		Return(nil, nil)
   125  
   126  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   127  	trackerStorage := mocktracker.NewMockStorage()
   128  
   129  	prov := provider.NewProvider(
   130  		zerolog.Nop(),
   131  		metrics.NewNoopCollector(),
   132  		execution_data.DefaultSerializer,
   133  		bservice,
   134  		trackerStorage,
   135  	)
   136  
   137  	blockComputer, err := computer.NewBlockComputer(
   138  		vm,
   139  		execCtx,
   140  		metrics.NewNoopCollector(),
   141  		trace.NewNoopTracer(),
   142  		zerolog.Nop(),
   143  		committer.NewNoopViewCommitter(),
   144  		me,
   145  		prov,
   146  		nil,
   147  		testutil.ProtocolStateWithSourceFixture(nil),
   148  		testMaxConcurrency)
   149  	require.NoError(t, err)
   150  
   151  	derivedChainData, err := derived.NewDerivedChainData(10)
   152  	require.NoError(t, err)
   153  
   154  	engine := &Manager{
   155  		blockComputer:    blockComputer,
   156  		derivedChainData: derivedChainData,
   157  	}
   158  
   159  	returnedComputationResult, err := engine.ComputeBlock(
   160  		context.Background(),
   161  		unittest.IdentifierFixture(),
   162  		executableBlock,
   163  		snapshotTree)
   164  	require.NoError(t, err)
   165  
   166  	hasUpdates := false
   167  	for _, snapshot := range returnedComputationResult.AllExecutionSnapshots() {
   168  		if len(snapshot.WriteSet) > 0 {
   169  			hasUpdates = true
   170  			break
   171  		}
   172  	}
   173  	require.True(t, hasUpdates)
   174  	require.Equal(t, returnedComputationResult.BlockExecutionResult.Size(), 1+1) // 1 coll + 1 system chunk
   175  	assert.NotEmpty(t, returnedComputationResult.AllExecutionSnapshots()[0].UpdatedRegisters())
   176  }
   177  
   178  func TestComputeBlock_Uploader(t *testing.T) {
   179  
   180  	noopCollector := &metrics.NoopCollector{}
   181  
   182  	ledger, err := complete.NewLedger(&fixtures.NoopWAL{}, 10, noopCollector, zerolog.Nop(), complete.DefaultPathFinderVersion)
   183  	require.NoError(t, err)
   184  
   185  	compactor := fixtures.NewNoopCompactor(ledger)
   186  	<-compactor.Ready()
   187  	defer func() {
   188  		<-ledger.Done()
   189  		<-compactor.Done()
   190  	}()
   191  
   192  	me := new(module.Local)
   193  	me.On("NodeID").Return(unittest.IdentifierFixture())
   194  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   195  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   196  		Return(nil, nil)
   197  
   198  	computationResult := unittest2.ComputationResultFixture(
   199  		t,
   200  		unittest.IdentifierFixture(),
   201  		[][]flow.Identifier{
   202  			{unittest.IdentifierFixture()},
   203  			{unittest.IdentifierFixture()},
   204  		})
   205  
   206  	blockComputer := &FakeBlockComputer{
   207  		computationResult: computationResult,
   208  	}
   209  
   210  	derivedChainData, err := derived.NewDerivedChainData(10)
   211  	require.NoError(t, err)
   212  
   213  	manager := &Manager{
   214  		blockComputer:    blockComputer,
   215  		derivedChainData: derivedChainData,
   216  	}
   217  
   218  	_, err = manager.ComputeBlock(
   219  		context.Background(),
   220  		unittest.IdentifierFixture(),
   221  		computationResult.ExecutableBlock,
   222  		state2.NewLedgerStorageSnapshot(
   223  			ledger,
   224  			flow.StateCommitment(ledger.InitialState())))
   225  	require.NoError(t, err)
   226  }
   227  
   228  func TestExecuteScript(t *testing.T) {
   229  
   230  	logger := zerolog.Nop()
   231  
   232  	execCtx := fvm.NewContext(fvm.WithLogger(logger))
   233  
   234  	me := new(module.Local)
   235  	me.On("NodeID").Return(unittest.IdentifierFixture())
   236  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   237  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   238  		Return(nil, nil)
   239  
   240  	vm := fvm.NewVirtualMachine()
   241  
   242  	ledger := testutil.RootBootstrappedLedger(vm, execCtx, fvm.WithExecutionMemoryLimit(math.MaxUint64))
   243  
   244  	sc := systemcontracts.SystemContractsForChain(execCtx.Chain.ChainID())
   245  
   246  	script := []byte(fmt.Sprintf(
   247  		`
   248  			import FungibleToken from %s
   249  
   250  			access(all) fun main() {}
   251  		`,
   252  		sc.FungibleToken.Address.HexWithPrefix(),
   253  	))
   254  
   255  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   256  	trackerStorage := mocktracker.NewMockStorage()
   257  
   258  	prov := provider.NewProvider(
   259  		zerolog.Nop(),
   260  		metrics.NewNoopCollector(),
   261  		execution_data.DefaultSerializer,
   262  		bservice,
   263  		trackerStorage,
   264  	)
   265  
   266  	engine, err := New(logger,
   267  		metrics.NewNoopCollector(),
   268  		trace.NewNoopTracer(),
   269  		me,
   270  		testutil.ProtocolStateWithSourceFixture(nil),
   271  		execCtx,
   272  		committer.NewNoopViewCommitter(),
   273  		prov,
   274  		ComputationConfig{
   275  			QueryConfig:          query.NewDefaultConfig(),
   276  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   277  			MaxConcurrency:       1,
   278  		},
   279  	)
   280  	require.NoError(t, err)
   281  
   282  	header := unittest.BlockHeaderFixture()
   283  	_, _, err = engine.ExecuteScript(
   284  		context.Background(),
   285  		script,
   286  		nil,
   287  		header,
   288  		ledger)
   289  	require.NoError(t, err)
   290  }
   291  
   292  // Balance script used to swallow errors, which meant that even if the view was empty, a script that did nothing but get
   293  // the balance of an account would succeed and return 0.
   294  func TestExecuteScript_BalanceScriptFailsIfViewIsEmpty(t *testing.T) {
   295  
   296  	logger := zerolog.Nop()
   297  
   298  	execCtx := fvm.NewContext(fvm.WithLogger(logger))
   299  
   300  	me := new(module.Local)
   301  	me.On("NodeID").Return(unittest.IdentifierFixture())
   302  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   303  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   304  		Return(nil, nil)
   305  
   306  	snapshot := snapshot.NewReadFuncStorageSnapshot(
   307  		func(id flow.RegisterID) (flow.RegisterValue, error) {
   308  			return nil, fmt.Errorf("error getting register")
   309  		})
   310  
   311  	sc := systemcontracts.SystemContractsForChain(execCtx.Chain.ChainID())
   312  
   313  	script := []byte(fmt.Sprintf(
   314  		`
   315  			access(all) fun main(): UFix64 {
   316  				return getAccount(%s).balance
   317  			}
   318  		`,
   319  		sc.FungibleToken.Address.HexWithPrefix(),
   320  	))
   321  
   322  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   323  	trackerStorage := mocktracker.NewMockStorage()
   324  
   325  	prov := provider.NewProvider(
   326  		zerolog.Nop(),
   327  		metrics.NewNoopCollector(),
   328  		execution_data.DefaultSerializer,
   329  		bservice,
   330  		trackerStorage,
   331  	)
   332  
   333  	engine, err := New(logger,
   334  		metrics.NewNoopCollector(),
   335  		trace.NewNoopTracer(),
   336  		me,
   337  		testutil.ProtocolStateWithSourceFixture(nil),
   338  		execCtx,
   339  		committer.NewNoopViewCommitter(),
   340  		prov,
   341  		ComputationConfig{
   342  			QueryConfig:          query.NewDefaultConfig(),
   343  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   344  			MaxConcurrency:       1,
   345  		},
   346  	)
   347  	require.NoError(t, err)
   348  
   349  	header := unittest.BlockHeaderFixture()
   350  	_, _, err = engine.ExecuteScript(
   351  		context.Background(),
   352  		script,
   353  		nil,
   354  		header,
   355  		snapshot)
   356  	require.ErrorContains(t, err, "error getting register")
   357  }
   358  
   359  func TestExecuteScripPanicsAreHandled(t *testing.T) {
   360  
   361  	ctx := fvm.NewContext()
   362  
   363  	buffer := &bytes.Buffer{}
   364  	log := zerolog.New(buffer)
   365  
   366  	header := unittest.BlockHeaderFixture()
   367  
   368  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   369  	trackerStorage := mocktracker.NewMockStorage()
   370  
   371  	prov := provider.NewProvider(
   372  		zerolog.Nop(),
   373  		metrics.NewNoopCollector(),
   374  		execution_data.DefaultSerializer,
   375  		bservice,
   376  		trackerStorage,
   377  	)
   378  
   379  	manager, err := New(log,
   380  		metrics.NewNoopCollector(),
   381  		trace.NewNoopTracer(),
   382  		nil,
   383  		testutil.ProtocolStateWithSourceFixture(nil),
   384  		ctx,
   385  		committer.NewNoopViewCommitter(),
   386  		prov,
   387  		ComputationConfig{
   388  			QueryConfig:          query.NewDefaultConfig(),
   389  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   390  			MaxConcurrency:       1,
   391  			NewCustomVirtualMachine: func() fvm.VM {
   392  				return &PanickingVM{}
   393  			},
   394  		},
   395  	)
   396  	require.NoError(t, err)
   397  
   398  	_, _, err = manager.ExecuteScript(
   399  		context.Background(),
   400  		[]byte("whatever"),
   401  		nil,
   402  		header,
   403  		nil)
   404  
   405  	require.Error(t, err)
   406  	require.Contains(t, buffer.String(), "Verunsicherung")
   407  }
   408  
   409  func TestExecuteScript_LongScriptsAreLogged(t *testing.T) {
   410  
   411  	ctx := fvm.NewContext()
   412  
   413  	buffer := &bytes.Buffer{}
   414  	log := zerolog.New(buffer)
   415  
   416  	header := unittest.BlockHeaderFixture()
   417  
   418  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   419  	trackerStorage := mocktracker.NewMockStorage()
   420  
   421  	prov := provider.NewProvider(
   422  		zerolog.Nop(),
   423  		metrics.NewNoopCollector(),
   424  		execution_data.DefaultSerializer,
   425  		bservice,
   426  		trackerStorage,
   427  	)
   428  
   429  	manager, err := New(log,
   430  		metrics.NewNoopCollector(),
   431  		trace.NewNoopTracer(),
   432  		nil,
   433  		testutil.ProtocolStateWithSourceFixture(nil),
   434  		ctx,
   435  		committer.NewNoopViewCommitter(),
   436  		prov,
   437  		ComputationConfig{
   438  			QueryConfig: query.QueryConfig{
   439  				LogTimeThreshold:   1 * time.Millisecond,
   440  				ExecutionTimeLimit: query.DefaultExecutionTimeLimit,
   441  			},
   442  			DerivedDataCacheSize: 10,
   443  			MaxConcurrency:       1,
   444  			NewCustomVirtualMachine: func() fvm.VM {
   445  				return &LongRunningVM{duration: 2 * time.Millisecond}
   446  			},
   447  		},
   448  	)
   449  	require.NoError(t, err)
   450  
   451  	_, _, err = manager.ExecuteScript(
   452  		context.Background(),
   453  		[]byte("whatever"),
   454  		nil,
   455  		header,
   456  		nil)
   457  
   458  	require.NoError(t, err)
   459  	require.Contains(t, buffer.String(), "exceeded threshold")
   460  }
   461  
   462  func TestExecuteScript_ShortScriptsAreNotLogged(t *testing.T) {
   463  
   464  	ctx := fvm.NewContext()
   465  
   466  	buffer := &bytes.Buffer{}
   467  	log := zerolog.New(buffer)
   468  
   469  	header := unittest.BlockHeaderFixture()
   470  
   471  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   472  	trackerStorage := mocktracker.NewMockStorage()
   473  
   474  	prov := provider.NewProvider(
   475  		zerolog.Nop(),
   476  		metrics.NewNoopCollector(),
   477  		execution_data.DefaultSerializer,
   478  		bservice,
   479  		trackerStorage,
   480  	)
   481  
   482  	manager, err := New(log,
   483  		metrics.NewNoopCollector(),
   484  		trace.NewNoopTracer(),
   485  		nil,
   486  		testutil.ProtocolStateWithSourceFixture(nil),
   487  		ctx,
   488  		committer.NewNoopViewCommitter(),
   489  		prov,
   490  		ComputationConfig{
   491  			QueryConfig: query.QueryConfig{
   492  				LogTimeThreshold:   1 * time.Second,
   493  				ExecutionTimeLimit: query.DefaultExecutionTimeLimit,
   494  			},
   495  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   496  			MaxConcurrency:       1,
   497  			NewCustomVirtualMachine: func() fvm.VM {
   498  				return &LongRunningVM{duration: 0}
   499  			},
   500  		},
   501  	)
   502  	require.NoError(t, err)
   503  
   504  	_, _, err = manager.ExecuteScript(
   505  		context.Background(),
   506  		[]byte("whatever"),
   507  		nil,
   508  		header,
   509  		nil)
   510  
   511  	require.NoError(t, err)
   512  	require.NotContains(t, buffer.String(), "exceeded threshold")
   513  }
   514  
   515  type PanickingExecutor struct{}
   516  
   517  func (PanickingExecutor) Cleanup() {}
   518  
   519  func (PanickingExecutor) Preprocess() error {
   520  	return nil
   521  }
   522  
   523  func (PanickingExecutor) Execute() error {
   524  	panic("panic, but expected with sentinel for test: Verunsicherung ")
   525  }
   526  
   527  func (PanickingExecutor) Output() fvm.ProcedureOutput {
   528  	return fvm.ProcedureOutput{}
   529  }
   530  
   531  type PanickingVM struct{}
   532  
   533  func (p *PanickingVM) NewExecutor(
   534  	f fvm.Context,
   535  	procedure fvm.Procedure,
   536  	txn storage.TransactionPreparer,
   537  ) fvm.ProcedureExecutor {
   538  	return PanickingExecutor{}
   539  }
   540  
   541  func (p *PanickingVM) Run(
   542  	f fvm.Context,
   543  	procedure fvm.Procedure,
   544  	storageSnapshot snapshot.StorageSnapshot,
   545  ) (
   546  	*snapshot.ExecutionSnapshot,
   547  	fvm.ProcedureOutput,
   548  	error,
   549  ) {
   550  	panic("panic, but expected with sentinel for test: Verunsicherung ")
   551  }
   552  
   553  func (p *PanickingVM) GetAccount(
   554  	ctx fvm.Context,
   555  	address flow.Address,
   556  	storageSnapshot snapshot.StorageSnapshot,
   557  ) (
   558  	*flow.Account,
   559  	error,
   560  ) {
   561  	panic("not expected")
   562  }
   563  
   564  type LongRunningExecutor struct {
   565  	duration time.Duration
   566  }
   567  
   568  func (LongRunningExecutor) Cleanup() {}
   569  
   570  func (LongRunningExecutor) Preprocess() error {
   571  	return nil
   572  }
   573  
   574  func (l LongRunningExecutor) Execute() error {
   575  	time.Sleep(l.duration)
   576  	return nil
   577  }
   578  
   579  func (LongRunningExecutor) Output() fvm.ProcedureOutput {
   580  	return fvm.ProcedureOutput{
   581  		Value: cadence.NewVoid(),
   582  	}
   583  }
   584  
   585  type LongRunningVM struct {
   586  	duration time.Duration
   587  }
   588  
   589  func (l *LongRunningVM) NewExecutor(
   590  	f fvm.Context,
   591  	procedure fvm.Procedure,
   592  	txn storage.TransactionPreparer,
   593  ) fvm.ProcedureExecutor {
   594  	return LongRunningExecutor{
   595  		duration: l.duration,
   596  	}
   597  }
   598  
   599  func (l *LongRunningVM) Run(
   600  	f fvm.Context,
   601  	procedure fvm.Procedure,
   602  	storageSnapshot snapshot.StorageSnapshot,
   603  ) (
   604  	*snapshot.ExecutionSnapshot,
   605  	fvm.ProcedureOutput,
   606  	error,
   607  ) {
   608  	time.Sleep(l.duration)
   609  
   610  	snapshot := &snapshot.ExecutionSnapshot{}
   611  	output := fvm.ProcedureOutput{
   612  		Value: cadence.NewVoid(),
   613  	}
   614  	return snapshot, output, nil
   615  }
   616  
   617  func (l *LongRunningVM) GetAccount(
   618  	ctx fvm.Context,
   619  	address flow.Address,
   620  	storageSnapshot snapshot.StorageSnapshot,
   621  ) (
   622  	*flow.Account,
   623  	error,
   624  ) {
   625  	panic("not expected")
   626  }
   627  
   628  type FakeBlockComputer struct {
   629  	computationResult *execution.ComputationResult
   630  }
   631  
   632  func (f *FakeBlockComputer) ExecuteBlock(
   633  	context.Context,
   634  	flow.Identifier,
   635  	*entity.ExecutableBlock,
   636  	snapshot.StorageSnapshot,
   637  	*derived.DerivedBlockData,
   638  ) (
   639  	*execution.ComputationResult,
   640  	error,
   641  ) {
   642  	return f.computationResult, nil
   643  }
   644  
   645  func TestExecuteScriptTimeout(t *testing.T) {
   646  
   647  	timeout := 1 * time.Millisecond
   648  	manager, err := New(
   649  		zerolog.Nop(),
   650  		metrics.NewNoopCollector(),
   651  		trace.NewNoopTracer(),
   652  		nil,
   653  		testutil.ProtocolStateWithSourceFixture(nil),
   654  		fvm.NewContext(),
   655  		committer.NewNoopViewCommitter(),
   656  		nil,
   657  		ComputationConfig{
   658  			QueryConfig: query.QueryConfig{
   659  				LogTimeThreshold:   query.DefaultLogTimeThreshold,
   660  				ExecutionTimeLimit: timeout,
   661  			},
   662  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   663  			MaxConcurrency:       1,
   664  		},
   665  	)
   666  
   667  	require.NoError(t, err)
   668  
   669  	script := []byte(`
   670  	access(all) fun main(): Int {
   671  		var i = 0
   672  		while i < 10000 {
   673  			i = i + 1
   674  		}
   675  		return i
   676  	}
   677  	`)
   678  
   679  	header := unittest.BlockHeaderFixture()
   680  	value, _, err := manager.ExecuteScript(
   681  		context.Background(),
   682  		script,
   683  		nil,
   684  		header,
   685  		nil)
   686  
   687  	require.Error(t, err)
   688  	require.Nil(t, value)
   689  	require.Contains(t, err.Error(), fvmErrors.ErrCodeScriptExecutionTimedOutError.String())
   690  }
   691  
   692  func TestExecuteScriptCancelled(t *testing.T) {
   693  
   694  	timeout := 30 * time.Second
   695  	manager, err := New(
   696  		zerolog.Nop(),
   697  		metrics.NewNoopCollector(),
   698  		trace.NewNoopTracer(),
   699  		nil,
   700  		testutil.ProtocolStateWithSourceFixture(nil),
   701  		fvm.NewContext(),
   702  		committer.NewNoopViewCommitter(),
   703  		nil,
   704  		ComputationConfig{
   705  			QueryConfig: query.QueryConfig{
   706  				LogTimeThreshold:   query.DefaultLogTimeThreshold,
   707  				ExecutionTimeLimit: timeout,
   708  			},
   709  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   710  			MaxConcurrency:       1,
   711  		},
   712  	)
   713  
   714  	require.NoError(t, err)
   715  
   716  	script := []byte(`
   717  	access(all) fun main(): Int {
   718  		var i = 0
   719  		var j = 0 
   720  		while i < 10000000 {
   721  			i = i + 1
   722  			j = i + j
   723  		}
   724  		return i
   725  	}
   726  	`)
   727  
   728  	var value []byte
   729  	var wg sync.WaitGroup
   730  	reqCtx, cancel := context.WithCancel(context.Background())
   731  	wg.Add(1)
   732  	go func() {
   733  		header := unittest.BlockHeaderFixture()
   734  		value, _, err = manager.ExecuteScript(
   735  			reqCtx,
   736  			script,
   737  			nil,
   738  			header,
   739  			nil)
   740  		wg.Done()
   741  	}()
   742  	cancel()
   743  	wg.Wait()
   744  	require.Nil(t, value)
   745  	require.Contains(t, err.Error(), fvmErrors.ErrCodeScriptExecutionCancelledError.String())
   746  }
   747  
   748  func Test_EventEncodingFailsOnlyTxAndCarriesOn(t *testing.T) {
   749  
   750  	chain := flow.Mainnet.Chain()
   751  	vm := fvm.NewVirtualMachine()
   752  
   753  	eventEncoder := &testingEventEncoder{
   754  		realEncoder: environment.NewCadenceEventEncoder(),
   755  	}
   756  
   757  	execCtx := fvm.NewContext(
   758  		fvm.WithChain(chain),
   759  		fvm.WithEventEncoder(eventEncoder),
   760  	)
   761  
   762  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
   763  	require.NoError(t, err)
   764  	snapshotTree, accounts, err := testutil.CreateAccounts(
   765  		vm,
   766  		testutil.RootBootstrappedLedger(vm, execCtx),
   767  		privateKeys,
   768  		chain)
   769  	require.NoError(t, err)
   770  
   771  	// setup transactions
   772  	account := accounts[0]
   773  	privKey := privateKeys[0]
   774  	// tx1 deploys contract version 1
   775  	tx1 := testutil.DeployEventContractTransaction(account, chain, 1)
   776  	prepareTx(t, tx1, account, privKey, 0, chain)
   777  
   778  	// tx2 emits event which will fail encoding
   779  	tx2 := testutil.CreateEmitEventTransaction(account, account)
   780  	prepareTx(t, tx2, account, privKey, 1, chain)
   781  
   782  	// tx3 emits event that will work fine
   783  	tx3 := testutil.CreateEmitEventTransaction(account, account)
   784  	prepareTx(t, tx3, account, privKey, 2, chain)
   785  
   786  	transactions := []*flow.TransactionBody{tx1, tx2, tx3}
   787  
   788  	col := flow.Collection{Transactions: transactions}
   789  
   790  	guarantee := flow.CollectionGuarantee{
   791  		CollectionID: col.ID(),
   792  		Signature:    nil,
   793  	}
   794  
   795  	block := flow.Block{
   796  		Header: &flow.Header{
   797  			View: 26,
   798  		},
   799  		Payload: &flow.Payload{
   800  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   801  		},
   802  	}
   803  
   804  	executableBlock := &entity.ExecutableBlock{
   805  		Block: &block,
   806  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   807  			guarantee.ID(): {
   808  				Guarantee:    &guarantee,
   809  				Transactions: transactions,
   810  			},
   811  		},
   812  		StartState: unittest.StateCommitmentPointerFixture(),
   813  	}
   814  
   815  	me := new(module.Local)
   816  	me.On("NodeID").Return(unittest.IdentifierFixture())
   817  	me.On("Sign", mock.Anything, mock.Anything).Return(nil, nil)
   818  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   819  		Return(nil, nil)
   820  
   821  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   822  	trackerStorage := mocktracker.NewMockStorage()
   823  
   824  	prov := provider.NewProvider(
   825  		zerolog.Nop(),
   826  		metrics.NewNoopCollector(),
   827  		execution_data.DefaultSerializer,
   828  		bservice,
   829  		trackerStorage,
   830  	)
   831  
   832  	blockComputer, err := computer.NewBlockComputer(
   833  		vm,
   834  		execCtx,
   835  		metrics.NewNoopCollector(),
   836  		trace.NewNoopTracer(),
   837  		zerolog.Nop(),
   838  		committer.NewNoopViewCommitter(),
   839  		me,
   840  		prov,
   841  		nil,
   842  		testutil.ProtocolStateWithSourceFixture(nil),
   843  		testMaxConcurrency)
   844  	require.NoError(t, err)
   845  
   846  	derivedChainData, err := derived.NewDerivedChainData(10)
   847  	require.NoError(t, err)
   848  
   849  	engine := &Manager{
   850  		blockComputer:    blockComputer,
   851  		derivedChainData: derivedChainData,
   852  	}
   853  
   854  	eventEncoder.enabled = true
   855  
   856  	returnedComputationResult, err := engine.ComputeBlock(
   857  		context.Background(),
   858  		unittest.IdentifierFixture(),
   859  		executableBlock,
   860  		snapshotTree)
   861  	require.NoError(t, err)
   862  
   863  	txResults := returnedComputationResult.AllTransactionResults()
   864  	require.Len(t, txResults, 4) // 2 txs + 1 system tx
   865  
   866  	require.Empty(t, txResults[0].ErrorMessage)
   867  	require.Contains(t, txResults[1].ErrorMessage, "I failed encoding")
   868  	require.Empty(t, txResults[2].ErrorMessage)
   869  
   870  	colRes := returnedComputationResult.CollectionExecutionResultAt(0)
   871  	events := colRes.Events()
   872  	require.Len(t, events, 2) // 1 collection + 1 system chunk
   873  
   874  	// first event should be contract deployed
   875  	assert.EqualValues(t, "flow.AccountContractAdded", events[0].Type)
   876  
   877  	// second event should come from tx3 (index 2)  as tx2 (index 1) should fail encoding
   878  	hasValidEventValue(t, events[1], 1)
   879  	assert.Equal(t, events[1].TransactionIndex, uint32(2))
   880  }
   881  
   882  type testingEventEncoder struct {
   883  	realEncoder *environment.CadenceEventEncoder
   884  	calls       int
   885  	enabled     bool
   886  }
   887  
   888  func (e *testingEventEncoder) Encode(event cadence.Event) ([]byte, error) {
   889  	defer func() {
   890  		if e.enabled {
   891  			e.calls++
   892  		}
   893  	}()
   894  
   895  	if e.calls == 1 && e.enabled {
   896  		return nil, fmt.Errorf("I failed encoding")
   897  	}
   898  	return e.realEncoder.Encode(event)
   899  }
   900  
   901  func TestScriptStorageMutationsDiscarded(t *testing.T) {
   902  
   903  	timeout := 10 * time.Second
   904  	chain := flow.Mainnet.Chain()
   905  	ctx := fvm.NewContext(fvm.WithChain(chain))
   906  	manager, _ := New(
   907  		zerolog.Nop(),
   908  		metrics.NewExecutionCollector(ctx.Tracer),
   909  		trace.NewNoopTracer(),
   910  		nil,
   911  		testutil.ProtocolStateWithSourceFixture(nil),
   912  		ctx,
   913  		committer.NewNoopViewCommitter(),
   914  		nil,
   915  		ComputationConfig{
   916  			QueryConfig: query.QueryConfig{
   917  				LogTimeThreshold:   query.DefaultLogTimeThreshold,
   918  				ExecutionTimeLimit: timeout,
   919  			},
   920  			DerivedDataCacheSize: derived.DefaultDerivedDataCacheSize,
   921  			MaxConcurrency:       1,
   922  		},
   923  	)
   924  	vm := manager.vm
   925  
   926  	// Create an account private key.
   927  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
   928  	require.NoError(t, err)
   929  
   930  	// Bootstrap a ledger, creating accounts with the provided private keys
   931  	// and the root account.
   932  	snapshotTree, accounts, err := testutil.CreateAccounts(
   933  		vm,
   934  		testutil.RootBootstrappedLedger(vm, ctx),
   935  		privateKeys,
   936  		chain)
   937  	require.NoError(t, err)
   938  	account := accounts[0]
   939  	address := cadence.NewAddress(account)
   940  	commonAddress, _ := common.HexToAddress(address.Hex())
   941  
   942  	script := []byte(`
   943  	access(all) fun main(account: Address) {
   944  		let acc = getAuthAccount<auth(SaveValue) &Account>(account)
   945  		acc.storage.save(3, to: /storage/x)
   946  	}
   947  	`)
   948  
   949  	header := unittest.BlockHeaderFixture()
   950  	_, compUsed, err := manager.ExecuteScript(
   951  		context.Background(),
   952  		script,
   953  		[][]byte{jsoncdc.MustEncode(address)},
   954  		header,
   955  		snapshotTree)
   956  
   957  	require.NoError(t, err)
   958  	require.Greater(t, compUsed, uint64(0))
   959  
   960  	env := environment.NewScriptEnvironmentFromStorageSnapshot(
   961  		ctx.EnvironmentParams,
   962  		snapshotTree)
   963  
   964  	rt := env.BorrowCadenceRuntime()
   965  	defer env.ReturnCadenceRuntime(rt)
   966  
   967  	path, err := cadence.NewPath(common.PathDomainStorage, "x")
   968  	require.NoError(t, err)
   969  
   970  	v, err := rt.ReadStored(commonAddress, path)
   971  	// the save should not update account storage by writing the updates
   972  	// back to the snapshotTree
   973  	require.NoError(t, err)
   974  	require.Equal(t, nil, v)
   975  }