github.com/koko1123/flow-go-1@v0.29.6/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/go-datastore"
    13  	dssync "github.com/ipfs/go-datastore/sync"
    14  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    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/koko1123/flow-go-1/engine/execution"
    24  	"github.com/koko1123/flow-go-1/engine/execution/computation/committer"
    25  	"github.com/koko1123/flow-go-1/engine/execution/computation/computer"
    26  	state2 "github.com/koko1123/flow-go-1/engine/execution/state"
    27  	"github.com/koko1123/flow-go-1/engine/execution/state/delta"
    28  	unittest2 "github.com/koko1123/flow-go-1/engine/execution/state/unittest"
    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  	"github.com/koko1123/flow-go-1/fvm/state"
    35  	"github.com/koko1123/flow-go-1/ledger/complete"
    36  	"github.com/koko1123/flow-go-1/ledger/complete/wal/fixtures"
    37  	"github.com/koko1123/flow-go-1/model/flow"
    38  	"github.com/koko1123/flow-go-1/module/executiondatasync/execution_data"
    39  	"github.com/koko1123/flow-go-1/module/executiondatasync/provider"
    40  	mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock"
    41  	"github.com/koko1123/flow-go-1/module/mempool/entity"
    42  	"github.com/koko1123/flow-go-1/module/metrics"
    43  	module "github.com/koko1123/flow-go-1/module/mock"
    44  	requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest"
    45  	"github.com/koko1123/flow-go-1/module/trace"
    46  	"github.com/koko1123/flow-go-1/utils/unittest"
    47  )
    48  
    49  var scriptLogThreshold = 1 * time.Second
    50  
    51  func TestComputeBlockWithStorage(t *testing.T) {
    52  	chain := flow.Mainnet.Chain()
    53  
    54  	vm := fvm.NewVirtualMachine()
    55  	execCtx := fvm.NewContext(fvm.WithChain(chain))
    56  
    57  	privateKeys, err := testutil.GenerateAccountPrivateKeys(2)
    58  	require.NoError(t, err)
    59  
    60  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
    61  	accounts, err := testutil.CreateAccounts(vm, ledger, derived.NewEmptyDerivedBlockData(), privateKeys, chain)
    62  	require.NoError(t, err)
    63  
    64  	tx1 := testutil.DeployCounterContractTransaction(accounts[0], chain)
    65  	tx1.SetProposalKey(chain.ServiceAddress(), 0, 0).
    66  		SetGasLimit(1000).
    67  		SetPayer(chain.ServiceAddress())
    68  
    69  	err = testutil.SignPayload(tx1, accounts[0], privateKeys[0])
    70  	require.NoError(t, err)
    71  
    72  	err = testutil.SignEnvelope(tx1, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
    73  	require.NoError(t, err)
    74  
    75  	tx2 := testutil.CreateCounterTransaction(accounts[0], accounts[1])
    76  	tx2.SetProposalKey(chain.ServiceAddress(), 0, 0).
    77  		SetGasLimit(1000).
    78  		SetPayer(chain.ServiceAddress())
    79  
    80  	err = testutil.SignPayload(tx2, accounts[1], privateKeys[1])
    81  	require.NoError(t, err)
    82  
    83  	err = testutil.SignEnvelope(tx2, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
    84  	require.NoError(t, err)
    85  
    86  	transactions := []*flow.TransactionBody{tx1, tx2}
    87  
    88  	col := flow.Collection{Transactions: transactions}
    89  
    90  	guarantee := flow.CollectionGuarantee{
    91  		CollectionID: col.ID(),
    92  		Signature:    nil,
    93  	}
    94  
    95  	block := flow.Block{
    96  		Header: &flow.Header{
    97  			View: 42,
    98  		},
    99  		Payload: &flow.Payload{
   100  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   101  		},
   102  	}
   103  
   104  	executableBlock := &entity.ExecutableBlock{
   105  		Block: &block,
   106  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   107  			guarantee.ID(): {
   108  				Guarantee:    &guarantee,
   109  				Transactions: transactions,
   110  			},
   111  		},
   112  		StartState: unittest.StateCommitmentPointerFixture(),
   113  	}
   114  
   115  	me := new(module.Local)
   116  	me.On("NodeID").Return(flow.ZeroID)
   117  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   118  		Return(nil, nil)
   119  
   120  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   121  	trackerStorage := mocktracker.NewMockStorage()
   122  
   123  	prov := provider.NewProvider(
   124  		zerolog.Nop(),
   125  		metrics.NewNoopCollector(),
   126  		execution_data.DefaultSerializer,
   127  		bservice,
   128  		trackerStorage,
   129  	)
   130  
   131  	blockComputer, err := computer.NewBlockComputer(
   132  		vm,
   133  		execCtx,
   134  		metrics.NewNoopCollector(),
   135  		trace.NewNoopTracer(),
   136  		zerolog.Nop(),
   137  		committer.NewNoopViewCommitter(),
   138  		me,
   139  		prov)
   140  	require.NoError(t, err)
   141  
   142  	derivedChainData, err := derived.NewDerivedChainData(10)
   143  	require.NoError(t, err)
   144  
   145  	engine := &Manager{
   146  		blockComputer:    blockComputer,
   147  		me:               me,
   148  		derivedChainData: derivedChainData,
   149  		tracer:           trace.NewNoopTracer(),
   150  	}
   151  
   152  	view := delta.NewView(ledger.Get)
   153  	blockView := view.NewChild()
   154  
   155  	returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, blockView)
   156  	require.NoError(t, err)
   157  
   158  	require.NotEmpty(t, blockView.(*delta.View).Delta())
   159  	require.Len(t, returnedComputationResult.StateSnapshots, 1+1) // 1 coll + 1 system chunk
   160  	assert.NotEmpty(t, returnedComputationResult.StateSnapshots[0].Delta)
   161  	stats := returnedComputationResult.BlockStats()
   162  	assert.True(t, stats.ComputationUsed > 0)
   163  	assert.True(t, stats.MemoryUsed > 0)
   164  }
   165  
   166  func TestComputeBlock_Uploader(t *testing.T) {
   167  
   168  	noopCollector := &metrics.NoopCollector{}
   169  
   170  	ledger, err := complete.NewLedger(&fixtures.NoopWAL{}, 10, noopCollector, zerolog.Nop(), complete.DefaultPathFinderVersion)
   171  	require.NoError(t, err)
   172  
   173  	compactor := fixtures.NewNoopCompactor(ledger)
   174  	<-compactor.Ready()
   175  	defer func() {
   176  		<-ledger.Done()
   177  		<-compactor.Done()
   178  	}()
   179  
   180  	me := new(module.Local)
   181  	me.On("NodeID").Return(flow.ZeroID)
   182  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   183  		Return(nil, nil)
   184  
   185  	computationResult := unittest2.ComputationResultFixture([][]flow.Identifier{
   186  		{unittest.IdentifierFixture()},
   187  		{unittest.IdentifierFixture()},
   188  	})
   189  
   190  	blockComputer := &FakeBlockComputer{
   191  		computationResult: computationResult,
   192  	}
   193  
   194  	derivedChainData, err := derived.NewDerivedChainData(10)
   195  	require.NoError(t, err)
   196  
   197  	manager := &Manager{
   198  		blockComputer:    blockComputer,
   199  		me:               me,
   200  		derivedChainData: derivedChainData,
   201  		tracer:           trace.NewNoopTracer(),
   202  	}
   203  
   204  	view := delta.NewView(state2.LedgerGetRegister(ledger, flow.StateCommitment(ledger.InitialState())))
   205  	blockView := view.NewChild()
   206  
   207  	_, err = manager.ComputeBlock(context.Background(), computationResult.ExecutableBlock, blockView)
   208  	require.NoError(t, err)
   209  }
   210  
   211  func TestExecuteScript(t *testing.T) {
   212  
   213  	logger := zerolog.Nop()
   214  
   215  	execCtx := fvm.NewContext(fvm.WithLogger(logger))
   216  
   217  	me := new(module.Local)
   218  	me.On("NodeID").Return(flow.ZeroID)
   219  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   220  		Return(nil, nil)
   221  
   222  	vm := fvm.NewVirtualMachine()
   223  
   224  	ledger := testutil.RootBootstrappedLedger(vm, execCtx, fvm.WithExecutionMemoryLimit(math.MaxUint64))
   225  
   226  	view := delta.NewView(ledger.Get)
   227  
   228  	scriptView := view.NewChild()
   229  
   230  	script := []byte(fmt.Sprintf(
   231  		`
   232  			import FungibleToken from %s
   233  
   234  			pub fun main() {}
   235  		`,
   236  		fvm.FungibleTokenAddress(execCtx.Chain).HexWithPrefix(),
   237  	))
   238  
   239  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   240  	trackerStorage := mocktracker.NewMockStorage()
   241  
   242  	prov := provider.NewProvider(
   243  		zerolog.Nop(),
   244  		metrics.NewNoopCollector(),
   245  		execution_data.DefaultSerializer,
   246  		bservice,
   247  		trackerStorage,
   248  	)
   249  
   250  	engine, err := New(logger,
   251  		metrics.NewNoopCollector(),
   252  		trace.NewNoopTracer(),
   253  		me,
   254  		nil,
   255  		execCtx,
   256  		committer.NewNoopViewCommitter(),
   257  		prov,
   258  		ComputationConfig{
   259  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   260  			ScriptLogThreshold:       scriptLogThreshold,
   261  			ScriptExecutionTimeLimit: DefaultScriptExecutionTimeLimit,
   262  		},
   263  	)
   264  	require.NoError(t, err)
   265  
   266  	header := unittest.BlockHeaderFixture()
   267  	_, err = engine.ExecuteScript(context.Background(), script, nil, header, scriptView)
   268  	require.NoError(t, err)
   269  }
   270  
   271  // Balance script used to swallow errors, which meant that even if the view was empty, a script that did nothing but get
   272  // the balance of an account would succeed and return 0.
   273  func TestExecuteScript_BalanceScriptFailsIfViewIsEmpty(t *testing.T) {
   274  
   275  	logger := zerolog.Nop()
   276  
   277  	execCtx := fvm.NewContext(fvm.WithLogger(logger))
   278  
   279  	me := new(module.Local)
   280  	me.On("NodeID").Return(flow.ZeroID)
   281  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   282  		Return(nil, nil)
   283  
   284  	view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) {
   285  		return nil, fmt.Errorf("error getting register")
   286  	})
   287  
   288  	scriptView := view.NewChild()
   289  
   290  	script := []byte(fmt.Sprintf(
   291  		`
   292  			pub fun main(): UFix64 {
   293  				return getAccount(%s).balance
   294  			}
   295  		`,
   296  		fvm.FungibleTokenAddress(execCtx.Chain).HexWithPrefix(),
   297  	))
   298  
   299  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   300  	trackerStorage := mocktracker.NewMockStorage()
   301  
   302  	prov := provider.NewProvider(
   303  		zerolog.Nop(),
   304  		metrics.NewNoopCollector(),
   305  		execution_data.DefaultSerializer,
   306  		bservice,
   307  		trackerStorage,
   308  	)
   309  
   310  	engine, err := New(logger,
   311  		metrics.NewNoopCollector(),
   312  		trace.NewNoopTracer(),
   313  		me,
   314  		nil,
   315  		execCtx,
   316  		committer.NewNoopViewCommitter(),
   317  		prov,
   318  		ComputationConfig{
   319  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   320  			ScriptLogThreshold:       scriptLogThreshold,
   321  			ScriptExecutionTimeLimit: DefaultScriptExecutionTimeLimit,
   322  		},
   323  	)
   324  	require.NoError(t, err)
   325  
   326  	header := unittest.BlockHeaderFixture()
   327  	_, err = engine.ExecuteScript(context.Background(), script, nil, header, scriptView)
   328  	require.ErrorContains(t, err, "error getting register")
   329  }
   330  
   331  func TestExecuteScripPanicsAreHandled(t *testing.T) {
   332  
   333  	ctx := fvm.NewContext()
   334  
   335  	buffer := &bytes.Buffer{}
   336  	log := zerolog.New(buffer)
   337  
   338  	header := unittest.BlockHeaderFixture()
   339  
   340  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   341  	trackerStorage := mocktracker.NewMockStorage()
   342  
   343  	prov := provider.NewProvider(
   344  		zerolog.Nop(),
   345  		metrics.NewNoopCollector(),
   346  		execution_data.DefaultSerializer,
   347  		bservice,
   348  		trackerStorage,
   349  	)
   350  
   351  	manager, err := New(log,
   352  		metrics.NewNoopCollector(),
   353  		trace.NewNoopTracer(),
   354  		nil,
   355  		nil,
   356  		ctx,
   357  		committer.NewNoopViewCommitter(),
   358  		prov,
   359  		ComputationConfig{
   360  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   361  			ScriptLogThreshold:       scriptLogThreshold,
   362  			ScriptExecutionTimeLimit: DefaultScriptExecutionTimeLimit,
   363  			NewCustomVirtualMachine: func() fvm.VM {
   364  				return &PanickingVM{}
   365  			},
   366  		},
   367  	)
   368  	require.NoError(t, err)
   369  
   370  	_, err = manager.ExecuteScript(context.Background(), []byte("whatever"), nil, header, noopView())
   371  
   372  	require.Error(t, err)
   373  
   374  	require.Contains(t, buffer.String(), "Verunsicherung")
   375  }
   376  
   377  func TestExecuteScript_LongScriptsAreLogged(t *testing.T) {
   378  
   379  	ctx := fvm.NewContext()
   380  
   381  	buffer := &bytes.Buffer{}
   382  	log := zerolog.New(buffer)
   383  
   384  	header := unittest.BlockHeaderFixture()
   385  
   386  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   387  	trackerStorage := mocktracker.NewMockStorage()
   388  
   389  	prov := provider.NewProvider(
   390  		zerolog.Nop(),
   391  		metrics.NewNoopCollector(),
   392  		execution_data.DefaultSerializer,
   393  		bservice,
   394  		trackerStorage,
   395  	)
   396  
   397  	manager, err := New(log,
   398  		metrics.NewNoopCollector(),
   399  		trace.NewNoopTracer(),
   400  		nil,
   401  		nil,
   402  		ctx,
   403  		committer.NewNoopViewCommitter(),
   404  		prov,
   405  		ComputationConfig{
   406  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   407  			ScriptLogThreshold:       1 * time.Millisecond,
   408  			ScriptExecutionTimeLimit: DefaultScriptExecutionTimeLimit,
   409  			NewCustomVirtualMachine: func() fvm.VM {
   410  				return &LongRunningVM{duration: 2 * time.Millisecond}
   411  			},
   412  		},
   413  	)
   414  	require.NoError(t, err)
   415  
   416  	_, err = manager.ExecuteScript(context.Background(), []byte("whatever"), nil, header, noopView())
   417  
   418  	require.NoError(t, err)
   419  
   420  	require.Contains(t, buffer.String(), "exceeded threshold")
   421  }
   422  
   423  func TestExecuteScript_ShortScriptsAreNotLogged(t *testing.T) {
   424  
   425  	ctx := fvm.NewContext()
   426  
   427  	buffer := &bytes.Buffer{}
   428  	log := zerolog.New(buffer)
   429  
   430  	header := unittest.BlockHeaderFixture()
   431  
   432  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   433  	trackerStorage := mocktracker.NewMockStorage()
   434  
   435  	prov := provider.NewProvider(
   436  		zerolog.Nop(),
   437  		metrics.NewNoopCollector(),
   438  		execution_data.DefaultSerializer,
   439  		bservice,
   440  		trackerStorage,
   441  	)
   442  
   443  	manager, err := New(log,
   444  		metrics.NewNoopCollector(),
   445  		trace.NewNoopTracer(),
   446  		nil,
   447  		nil,
   448  		ctx,
   449  		committer.NewNoopViewCommitter(),
   450  		prov,
   451  		ComputationConfig{
   452  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   453  			ScriptLogThreshold:       1 * time.Second,
   454  			ScriptExecutionTimeLimit: DefaultScriptExecutionTimeLimit,
   455  			NewCustomVirtualMachine: func() fvm.VM {
   456  				return &LongRunningVM{duration: 0}
   457  			},
   458  		},
   459  	)
   460  	require.NoError(t, err)
   461  
   462  	_, err = manager.ExecuteScript(context.Background(), []byte("whatever"), nil, header, noopView())
   463  
   464  	require.NoError(t, err)
   465  
   466  	require.NotContains(t, buffer.String(), "exceeded threshold")
   467  }
   468  
   469  type PanickingVM struct{}
   470  
   471  func (p *PanickingVM) Run(f fvm.Context, procedure fvm.Procedure, view state.View) error {
   472  	panic("panic, but expected with sentinel for test: Verunsicherung ")
   473  }
   474  
   475  func (p *PanickingVM) GetAccount(f fvm.Context, address flow.Address, view state.View) (*flow.Account, error) {
   476  	panic("not expected")
   477  }
   478  
   479  type LongRunningVM struct {
   480  	duration time.Duration
   481  }
   482  
   483  func (l *LongRunningVM) Run(f fvm.Context, procedure fvm.Procedure, view state.View) error {
   484  	time.Sleep(l.duration)
   485  	// satisfy value marshaller
   486  	if scriptProcedure, is := procedure.(*fvm.ScriptProcedure); is {
   487  		scriptProcedure.Value = cadence.NewVoid()
   488  	}
   489  
   490  	return nil
   491  }
   492  
   493  func (l *LongRunningVM) GetAccount(f fvm.Context, address flow.Address, view state.View) (*flow.Account, error) {
   494  	panic("not expected")
   495  }
   496  
   497  type FakeBlockComputer struct {
   498  	computationResult *execution.ComputationResult
   499  }
   500  
   501  func (f *FakeBlockComputer) ExecuteBlock(context.Context, *entity.ExecutableBlock, state.View, *derived.DerivedBlockData) (*execution.ComputationResult, error) {
   502  	return f.computationResult, nil
   503  }
   504  
   505  func noopView() *delta.View {
   506  	return delta.NewView(func(_, _ string) (flow.RegisterValue, error) {
   507  		return nil, nil
   508  	})
   509  }
   510  
   511  func TestExecuteScriptTimeout(t *testing.T) {
   512  
   513  	timeout := 1 * time.Millisecond
   514  	manager, err := New(
   515  		zerolog.Nop(),
   516  		metrics.NewNoopCollector(),
   517  		trace.NewNoopTracer(),
   518  		nil,
   519  		nil,
   520  		fvm.NewContext(),
   521  		committer.NewNoopViewCommitter(),
   522  		nil,
   523  		ComputationConfig{
   524  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   525  			ScriptLogThreshold:       DefaultScriptLogThreshold,
   526  			ScriptExecutionTimeLimit: timeout,
   527  		},
   528  	)
   529  
   530  	require.NoError(t, err)
   531  
   532  	script := []byte(`
   533  	pub fun main(): Int {
   534  		var i = 0
   535  		while i < 10000 {
   536  			i = i + 1
   537  		}
   538  		return i
   539  	}
   540  	`)
   541  
   542  	header := unittest.BlockHeaderFixture()
   543  	value, err := manager.ExecuteScript(context.Background(), script, nil, header, noopView())
   544  
   545  	require.Error(t, err)
   546  	require.Nil(t, value)
   547  	require.Contains(t, err.Error(), fvmErrors.ErrCodeScriptExecutionTimedOutError.String())
   548  }
   549  
   550  func TestExecuteScriptCancelled(t *testing.T) {
   551  
   552  	timeout := 30 * time.Second
   553  	manager, err := New(
   554  		zerolog.Nop(),
   555  		metrics.NewNoopCollector(),
   556  		trace.NewNoopTracer(),
   557  		nil,
   558  		nil,
   559  		fvm.NewContext(),
   560  		committer.NewNoopViewCommitter(),
   561  		nil,
   562  		ComputationConfig{
   563  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   564  			ScriptLogThreshold:       DefaultScriptLogThreshold,
   565  			ScriptExecutionTimeLimit: timeout,
   566  		},
   567  	)
   568  
   569  	require.NoError(t, err)
   570  
   571  	script := []byte(`
   572  	pub fun main(): Int {
   573  		var i = 0
   574  		var j = 0 
   575  		while i < 10000000 {
   576  			i = i + 1
   577  			j = i + j
   578  		}
   579  		return i
   580  	}
   581  	`)
   582  
   583  	var value []byte
   584  	var wg sync.WaitGroup
   585  	reqCtx, cancel := context.WithCancel(context.Background())
   586  	wg.Add(1)
   587  	go func() {
   588  		header := unittest.BlockHeaderFixture()
   589  		value, err = manager.ExecuteScript(reqCtx, script, nil, header, noopView())
   590  		wg.Done()
   591  	}()
   592  	cancel()
   593  	wg.Wait()
   594  	require.Nil(t, value)
   595  	require.Contains(t, err.Error(), fvmErrors.ErrCodeScriptExecutionCancelledError.String())
   596  }
   597  
   598  func Test_EventEncodingFailsOnlyTxAndCarriesOn(t *testing.T) {
   599  
   600  	chain := flow.Mainnet.Chain()
   601  	vm := fvm.NewVirtualMachine()
   602  
   603  	eventEncoder := &testingEventEncoder{
   604  		realEncoder: environment.NewCadenceEventEncoder(),
   605  	}
   606  
   607  	execCtx := fvm.NewContext(
   608  		fvm.WithChain(chain),
   609  		fvm.WithEventEncoder(eventEncoder),
   610  	)
   611  
   612  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
   613  	require.NoError(t, err)
   614  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
   615  	accounts, err := testutil.CreateAccounts(vm, ledger, derived.NewEmptyDerivedBlockData(), privateKeys, chain)
   616  	require.NoError(t, err)
   617  
   618  	// setup transactions
   619  	account := accounts[0]
   620  	privKey := privateKeys[0]
   621  	// tx1 deploys contract version 1
   622  	tx1 := testutil.DeployEventContractTransaction(account, chain, 1)
   623  	prepareTx(t, tx1, account, privKey, 0, chain)
   624  
   625  	// tx2 emits event which will fail encoding
   626  	tx2 := testutil.CreateEmitEventTransaction(account, account)
   627  	prepareTx(t, tx2, account, privKey, 1, chain)
   628  
   629  	// tx3 emits event that will work fine
   630  	tx3 := testutil.CreateEmitEventTransaction(account, account)
   631  	prepareTx(t, tx3, account, privKey, 2, chain)
   632  
   633  	transactions := []*flow.TransactionBody{tx1, tx2, tx3}
   634  
   635  	col := flow.Collection{Transactions: transactions}
   636  
   637  	guarantee := flow.CollectionGuarantee{
   638  		CollectionID: col.ID(),
   639  		Signature:    nil,
   640  	}
   641  
   642  	block := flow.Block{
   643  		Header: &flow.Header{
   644  			View: 26,
   645  		},
   646  		Payload: &flow.Payload{
   647  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   648  		},
   649  	}
   650  
   651  	executableBlock := &entity.ExecutableBlock{
   652  		Block: &block,
   653  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   654  			guarantee.ID(): {
   655  				Guarantee:    &guarantee,
   656  				Transactions: transactions,
   657  			},
   658  		},
   659  		StartState: unittest.StateCommitmentPointerFixture(),
   660  	}
   661  
   662  	me := new(module.Local)
   663  	me.On("NodeID").Return(flow.ZeroID)
   664  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   665  		Return(nil, nil)
   666  
   667  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   668  	trackerStorage := mocktracker.NewMockStorage()
   669  
   670  	prov := provider.NewProvider(
   671  		zerolog.Nop(),
   672  		metrics.NewNoopCollector(),
   673  		execution_data.DefaultSerializer,
   674  		bservice,
   675  		trackerStorage,
   676  	)
   677  
   678  	blockComputer, err := computer.NewBlockComputer(
   679  		vm,
   680  		execCtx,
   681  		metrics.NewNoopCollector(),
   682  		trace.NewNoopTracer(),
   683  		zerolog.Nop(),
   684  		committer.NewNoopViewCommitter(),
   685  		me,
   686  		prov,
   687  	)
   688  	require.NoError(t, err)
   689  
   690  	derivedChainData, err := derived.NewDerivedChainData(10)
   691  	require.NoError(t, err)
   692  
   693  	engine := &Manager{
   694  		blockComputer:    blockComputer,
   695  		me:               me,
   696  		derivedChainData: derivedChainData,
   697  		tracer:           trace.NewNoopTracer(),
   698  	}
   699  
   700  	view := delta.NewView(ledger.Get)
   701  	blockView := view.NewChild()
   702  
   703  	eventEncoder.enabled = true
   704  
   705  	returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, blockView)
   706  	require.NoError(t, err)
   707  
   708  	require.Len(t, returnedComputationResult.Events, 2)             // 1 collection + 1 system chunk
   709  	require.Len(t, returnedComputationResult.TransactionResults, 4) // 2 txs + 1 system tx
   710  
   711  	require.Empty(t, returnedComputationResult.TransactionResults[0].ErrorMessage)
   712  	require.Contains(t, returnedComputationResult.TransactionResults[1].ErrorMessage, "I failed encoding")
   713  	require.Empty(t, returnedComputationResult.TransactionResults[2].ErrorMessage)
   714  
   715  	// first event should be contract deployed
   716  	assert.EqualValues(t, "flow.AccountContractAdded", returnedComputationResult.Events[0][0].Type)
   717  
   718  	// second event should come from tx3 (index 2)  as tx2 (index 1) should fail encoding
   719  	hasValidEventValue(t, returnedComputationResult.Events[0][1], 1)
   720  	assert.Equal(t, returnedComputationResult.Events[0][1].TransactionIndex, uint32(2))
   721  }
   722  
   723  type testingEventEncoder struct {
   724  	realEncoder *environment.CadenceEventEncoder
   725  	calls       int
   726  	enabled     bool
   727  }
   728  
   729  func (e *testingEventEncoder) Encode(event cadence.Event) ([]byte, error) {
   730  	defer func() {
   731  		if e.enabled {
   732  			e.calls++
   733  		}
   734  	}()
   735  
   736  	if e.calls == 1 && e.enabled {
   737  		return nil, fmt.Errorf("I failed encoding")
   738  	}
   739  	return e.realEncoder.Encode(event)
   740  }
   741  
   742  func TestScriptStorageMutationsDiscarded(t *testing.T) {
   743  
   744  	timeout := 10 * time.Second
   745  	chain := flow.Mainnet.Chain()
   746  	ctx := fvm.NewContext(fvm.WithChain(chain))
   747  	manager, _ := New(
   748  		zerolog.Nop(),
   749  		metrics.NewExecutionCollector(ctx.Tracer),
   750  		trace.NewNoopTracer(),
   751  		nil,
   752  		nil,
   753  		ctx,
   754  		committer.NewNoopViewCommitter(),
   755  		nil,
   756  		ComputationConfig{
   757  			DerivedDataCacheSize:     derived.DefaultDerivedDataCacheSize,
   758  			ScriptLogThreshold:       DefaultScriptLogThreshold,
   759  			ScriptExecutionTimeLimit: timeout,
   760  		},
   761  	)
   762  	vm := manager.vm
   763  	view := testutil.RootBootstrappedLedger(vm, ctx)
   764  
   765  	derivedBlockData := derived.NewEmptyDerivedBlockData()
   766  	derivedTxnData, err := derivedBlockData.NewDerivedTransactionData(0, 0)
   767  	require.NoError(t, err)
   768  
   769  	txnState := state.NewTransactionState(view, state.DefaultParameters())
   770  
   771  	// Create an account private key.
   772  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
   773  	require.NoError(t, err)
   774  
   775  	// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
   776  	accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
   777  	require.NoError(t, err)
   778  	account := accounts[0]
   779  	address := cadence.NewAddress(account)
   780  	commonAddress, _ := common.HexToAddress(address.Hex())
   781  
   782  	script := []byte(`
   783  	pub fun main(account: Address) {
   784  		let acc = getAuthAccount(account)
   785  		acc.save(3, to: /storage/x)
   786  	}
   787  	`)
   788  
   789  	header := unittest.BlockHeaderFixture()
   790  	scriptView := view.NewChild()
   791  	_, err = manager.ExecuteScript(context.Background(), script, [][]byte{jsoncdc.MustEncode(address)}, header, scriptView)
   792  
   793  	require.NoError(t, err)
   794  
   795  	env := environment.NewScriptEnvironment(
   796  		context.Background(),
   797  		ctx.TracerSpan,
   798  		ctx.EnvironmentParams,
   799  		txnState,
   800  		derivedTxnData)
   801  
   802  	rt := env.BorrowCadenceRuntime()
   803  	defer env.ReturnCadenceRuntime(rt)
   804  
   805  	v, err := rt.ReadStored(
   806  		commonAddress,
   807  		cadence.NewPath("storage", "x"),
   808  	)
   809  
   810  	// the save should not update account storage by writing the delta from the child view back to the parent
   811  	require.NoError(t, err)
   812  	require.Equal(t, nil, v)
   813  }