github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/fvm_test.go (about)

     1  package fvm_test
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"math"
     8  	"strings"
     9  	"testing"
    10  
    11  	stdlib2 "github.com/onflow/cadence/runtime/stdlib"
    12  
    13  	envMock "github.com/onflow/flow-go/fvm/environment/mock"
    14  
    15  	"github.com/onflow/cadence"
    16  	"github.com/onflow/cadence/encoding/ccf"
    17  	jsoncdc "github.com/onflow/cadence/encoding/json"
    18  	"github.com/onflow/cadence/runtime"
    19  	"github.com/onflow/cadence/runtime/common"
    20  	cadenceErrors "github.com/onflow/cadence/runtime/errors"
    21  	"github.com/onflow/cadence/runtime/tests/utils"
    22  	"github.com/onflow/crypto"
    23  	"github.com/stretchr/testify/assert"
    24  	mockery "github.com/stretchr/testify/mock"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/onflow/flow-go/engine/execution/testutil"
    28  	exeUtils "github.com/onflow/flow-go/engine/execution/utils"
    29  	"github.com/onflow/flow-go/fvm"
    30  	fvmCrypto "github.com/onflow/flow-go/fvm/crypto"
    31  	"github.com/onflow/flow-go/fvm/environment"
    32  	"github.com/onflow/flow-go/fvm/errors"
    33  	"github.com/onflow/flow-go/fvm/evm/stdlib"
    34  	"github.com/onflow/flow-go/fvm/evm/types"
    35  	"github.com/onflow/flow-go/fvm/meter"
    36  	reusableRuntime "github.com/onflow/flow-go/fvm/runtime"
    37  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    38  	"github.com/onflow/flow-go/fvm/storage/snapshot/mock"
    39  	"github.com/onflow/flow-go/fvm/storage/testutils"
    40  	"github.com/onflow/flow-go/fvm/systemcontracts"
    41  	"github.com/onflow/flow-go/model/flow"
    42  	"github.com/onflow/flow-go/utils/unittest"
    43  )
    44  
    45  type vmTest struct {
    46  	bootstrapOptions []fvm.BootstrapProcedureOption
    47  	contextOptions   []fvm.Option
    48  }
    49  
    50  func newVMTest() vmTest {
    51  	return vmTest{}
    52  }
    53  
    54  func (vmt vmTest) withBootstrapProcedureOptions(opts ...fvm.BootstrapProcedureOption) vmTest {
    55  	vmt.bootstrapOptions = append(vmt.bootstrapOptions, opts...)
    56  	return vmt
    57  }
    58  
    59  func (vmt vmTest) withContextOptions(opts ...fvm.Option) vmTest {
    60  	vmt.contextOptions = append(vmt.contextOptions, opts...)
    61  	return vmt
    62  }
    63  
    64  func createChainAndVm(chainID flow.ChainID) (flow.Chain, fvm.VM) {
    65  	return chainID.Chain(), fvm.NewVirtualMachine()
    66  }
    67  
    68  func (vmt vmTest) run(
    69  	f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree),
    70  ) func(t *testing.T) {
    71  	return func(t *testing.T) {
    72  		baseOpts := []fvm.Option{
    73  			// default chain is Testnet
    74  			fvm.WithChain(flow.Testnet.Chain()),
    75  			fvm.WithEntropyProvider(testutil.EntropyProviderFixture(nil)),
    76  		}
    77  
    78  		opts := append(baseOpts, vmt.contextOptions...)
    79  		ctx := fvm.NewContext(opts...)
    80  
    81  		chain := ctx.Chain
    82  		vm := fvm.NewVirtualMachine()
    83  
    84  		snapshotTree := snapshot.NewSnapshotTree(nil)
    85  
    86  		baseBootstrapOpts := []fvm.BootstrapProcedureOption{
    87  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
    88  		}
    89  
    90  		bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...)
    91  
    92  		executionSnapshot, _, err := vm.Run(
    93  			ctx,
    94  			fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...),
    95  			snapshotTree)
    96  		require.NoError(t, err)
    97  
    98  		snapshotTree = snapshotTree.Append(executionSnapshot)
    99  
   100  		f(t, vm, chain, ctx, snapshotTree)
   101  	}
   102  }
   103  
   104  // bootstrapWith executes the bootstrap procedure and the custom bootstrap function
   105  // and returns a prepared bootstrappedVmTest with all the state needed
   106  func (vmt vmTest) bootstrapWith(
   107  	bootstrap func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) (snapshot.SnapshotTree, error),
   108  ) (bootstrappedVmTest, error) {
   109  
   110  	baseOpts := []fvm.Option{
   111  		// default chain is Testnet
   112  		fvm.WithChain(flow.Testnet.Chain()),
   113  	}
   114  
   115  	opts := append(baseOpts, vmt.contextOptions...)
   116  	ctx := fvm.NewContext(opts...)
   117  
   118  	chain := ctx.Chain
   119  	vm := fvm.NewVirtualMachine()
   120  
   121  	snapshotTree := snapshot.NewSnapshotTree(nil)
   122  
   123  	baseBootstrapOpts := []fvm.BootstrapProcedureOption{
   124  		fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   125  	}
   126  
   127  	bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...)
   128  
   129  	executionSnapshot, _, err := vm.Run(
   130  		ctx,
   131  		fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...),
   132  		snapshotTree)
   133  	if err != nil {
   134  		return bootstrappedVmTest{}, err
   135  	}
   136  
   137  	snapshotTree = snapshotTree.Append(executionSnapshot)
   138  
   139  	snapshotTree, err = bootstrap(vm, chain, ctx, snapshotTree)
   140  	if err != nil {
   141  		return bootstrappedVmTest{}, err
   142  	}
   143  
   144  	return bootstrappedVmTest{chain, ctx, snapshotTree}, nil
   145  }
   146  
   147  type bootstrappedVmTest struct {
   148  	chain        flow.Chain
   149  	ctx          fvm.Context
   150  	snapshotTree snapshot.SnapshotTree
   151  }
   152  
   153  // run Runs a test from the bootstrapped state, without changing the bootstrapped state
   154  func (vmt bootstrappedVmTest) run(
   155  	f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree),
   156  ) func(t *testing.T) {
   157  	return func(t *testing.T) {
   158  		f(t, fvm.NewVirtualMachine(), vmt.chain, vmt.ctx, vmt.snapshotTree)
   159  	}
   160  }
   161  
   162  func TestHashing(t *testing.T) {
   163  
   164  	t.Parallel()
   165  
   166  	chain, vm := createChainAndVm(flow.Mainnet)
   167  
   168  	ctx := fvm.NewContext(
   169  		fvm.WithChain(chain),
   170  		fvm.WithCadenceLogging(true),
   171  	)
   172  
   173  	snapshotTree := testutil.RootBootstrappedLedger(vm, ctx)
   174  
   175  	hashScript := func(hashName string) []byte {
   176  		return []byte(fmt.Sprintf(
   177  			`
   178  				import Crypto
   179  
   180  				access(all)
   181  				fun main(data: [UInt8]): [UInt8] {
   182  					return Crypto.hash(data, algorithm: HashAlgorithm.%s)
   183  				}
   184  			`, hashName))
   185  	}
   186  	hashWithTagScript := func(hashName string) []byte {
   187  		return []byte(fmt.Sprintf(
   188  			`
   189  				import Crypto
   190  
   191  				access(all) fun main(data: [UInt8], tag: String): [UInt8] {
   192  					return Crypto.hashWithTag(data, tag: tag, algorithm: HashAlgorithm.%s)
   193  				}
   194  			`, hashName))
   195  	}
   196  
   197  	data := []byte("some random message")
   198  	encodedBytes := make([]cadence.Value, len(data))
   199  	for i := range encodedBytes {
   200  		encodedBytes[i] = cadence.NewUInt8(data[i])
   201  	}
   202  	cadenceData := jsoncdc.MustEncode(cadence.NewArray(encodedBytes))
   203  
   204  	// ===== Test Cases =====
   205  	cases := []struct {
   206  		Algo    runtime.HashAlgorithm
   207  		WithTag bool
   208  		Tag     string
   209  		Check   func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error)
   210  	}{
   211  		{
   212  			Algo:    runtime.HashAlgorithmSHA2_256,
   213  			WithTag: false,
   214  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   215  				require.NoError(t, scriptErr)
   216  				require.NoError(t, executionErr)
   217  				require.Equal(t, "68fb87dfba69b956f4ba98b748a75a604f99b38a4f2740290037957f7e830da8", result)
   218  			},
   219  		},
   220  		{
   221  			Algo:    runtime.HashAlgorithmSHA2_384,
   222  			WithTag: false,
   223  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   224  				require.NoError(t, scriptErr)
   225  				require.NoError(t, executionErr)
   226  				require.Equal(t, "a9b3e62ab9b2a33020e015f245b82e063afd1398211326408bc8fc31c2c15859594b0aee263fbb02f6d8b5065ad49df2", result)
   227  			},
   228  		},
   229  		{
   230  			Algo:    runtime.HashAlgorithmSHA3_256,
   231  			WithTag: false,
   232  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   233  				require.NoError(t, scriptErr)
   234  				require.NoError(t, executionErr)
   235  				require.Equal(t, "38effea5ab9082a2cb0dc9adfafaf88523e8f3ce74bfbeac85ffc719cc2c4677", result)
   236  			},
   237  		},
   238  		{
   239  			Algo:    runtime.HashAlgorithmSHA3_384,
   240  			WithTag: false,
   241  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   242  				require.NoError(t, scriptErr)
   243  				require.NoError(t, executionErr)
   244  				require.Equal(t, "f41e8de9af0c1f46fc56d5a776f1bd500530879a85f3b904821810295927e13a54f3e936dddb84669021052eb12966c3", result)
   245  			},
   246  		},
   247  		{
   248  			Algo:    runtime.HashAlgorithmKECCAK_256,
   249  			WithTag: false,
   250  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   251  				require.NoError(t, scriptErr)
   252  				require.NoError(t, executionErr)
   253  				require.Equal(t, "1d5ced4738dd4e0bb4628dad7a7b59b8e339a75ece97a4ad004773a49ed7b5bc", result)
   254  			},
   255  		},
   256  		{
   257  			Algo:    runtime.HashAlgorithmKECCAK_256,
   258  			WithTag: true,
   259  			Tag:     "some_tag",
   260  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   261  				require.NoError(t, scriptErr)
   262  				require.NoError(t, executionErr)
   263  				require.Equal(t, "8454ec77f76b229a473770c91e3ea6e7e852416d747805215d15d53bdc56ce5f", result)
   264  			},
   265  		},
   266  		{
   267  			Algo:    runtime.HashAlgorithmSHA2_256,
   268  			WithTag: true,
   269  			Tag:     "some_tag",
   270  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   271  				require.NoError(t, scriptErr)
   272  				require.NoError(t, executionErr)
   273  				require.Equal(t, "4e07609b9a856a5e10703d1dba73be34d9ca0f4e780859d66983f41d746ec8b2", result)
   274  			},
   275  		},
   276  		{
   277  			Algo:    runtime.HashAlgorithmSHA2_384,
   278  			WithTag: true,
   279  			Tag:     "some_tag",
   280  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   281  				require.NoError(t, scriptErr)
   282  				require.NoError(t, executionErr)
   283  				require.Equal(t, "f9bd89e15f341a225656944dc8b3c405e66a0f97838ad44c9803164c911e677aea7ad4e24486fba3f803d83ed1ccfce5", result)
   284  			},
   285  		},
   286  		{
   287  			Algo:    runtime.HashAlgorithmSHA3_256,
   288  			WithTag: true,
   289  			Tag:     "some_tag",
   290  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   291  				require.NoError(t, scriptErr)
   292  				require.NoError(t, executionErr)
   293  				require.Equal(t, "f59e2ccc9d7f008a96948a31573670d9976a4a161601ab1cd1d2da019779a0f6", result)
   294  			},
   295  		},
   296  		{
   297  			Algo:    runtime.HashAlgorithmSHA3_384,
   298  			WithTag: true,
   299  			Tag:     "some_tag",
   300  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   301  				require.NoError(t, scriptErr)
   302  				require.NoError(t, executionErr)
   303  				require.Equal(t, "e7875eafdb53327faeace8478d1650c6547d04fb4fb42f34509ad64bde0267bea7e1b3af8fda3ef9d9c9327dd4e97a96", result)
   304  			},
   305  		},
   306  		{
   307  			Algo:    runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   308  			WithTag: false,
   309  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   310  				require.NoError(t, scriptErr)
   311  				require.NoError(t, executionErr)
   312  				require.Equal(t, "44dc46111abacfe2bb4a04cea4805aad03f84e4849f138cc3ed431478472b185548628e96d0c963b21ebaf17132d73fc13031eb82d5f4cbe3b6047ff54d20e8d663904373d73348b97ce18305ebc56114cb7e7394e486684007f78aa59abc5d0a8f6bae6bd186db32528af80857cd12112ce6960be29c96074df9c4aaed5b0e6", result)
   313  			},
   314  		},
   315  		{
   316  			Algo:    runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   317  			WithTag: true,
   318  			Tag:     "some_tag",
   319  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   320  				require.NoError(t, scriptErr)
   321  				require.NoError(t, executionErr)
   322  				require.Equal(t, "de7d9aa24274fa12c98cce5c09eea0634108ead2e91828b9a9a450e878088393e3e63eb4b19834f579ce215b00a9915919b67a71dab1112560319e6e1e5e9ad0fb670e8a09d586508c84547cee7ddbe8c9362c996846154865eb271bdc4523dbcdbdae5a77391fb54374f37534c8bb2281589cb2e3d62742596cdad7e4f9f35c", result)
   323  			},
   324  		},
   325  	}
   326  	// ======================
   327  
   328  	for i, c := range cases {
   329  		t.Run(fmt.Sprintf("case %d: %s with tag: %v", i, c.Algo, c.WithTag), func(t *testing.T) {
   330  			code := hashScript(c.Algo.Name())
   331  			if c.WithTag {
   332  				code = hashWithTagScript(c.Algo.Name())
   333  			}
   334  
   335  			script := fvm.Script(code)
   336  
   337  			if c.WithTag {
   338  				script = script.WithArguments(
   339  					cadenceData,
   340  					jsoncdc.MustEncode(cadence.String(c.Tag)),
   341  				)
   342  			} else {
   343  				script = script.WithArguments(
   344  					cadenceData,
   345  				)
   346  			}
   347  
   348  			_, output, err := vm.Run(ctx, script, snapshotTree)
   349  			require.NoError(t, err)
   350  
   351  			byteResult := make([]byte, 0)
   352  			if err == nil && output.Err == nil {
   353  				cadenceArray := output.Value.(cadence.Array)
   354  				for _, value := range cadenceArray.Values {
   355  					byteResult = append(byteResult, uint8(value.(cadence.UInt8)))
   356  				}
   357  			}
   358  
   359  			c.Check(t, hex.EncodeToString(byteResult), output.Err, err)
   360  		})
   361  	}
   362  
   363  	hashAlgos := []runtime.HashAlgorithm{
   364  		runtime.HashAlgorithmSHA2_256,
   365  		runtime.HashAlgorithmSHA3_256,
   366  		runtime.HashAlgorithmSHA2_384,
   367  		runtime.HashAlgorithmSHA3_384,
   368  		runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   369  		runtime.HashAlgorithmKECCAK_256,
   370  	}
   371  
   372  	for i, algo := range hashAlgos {
   373  		t.Run(fmt.Sprintf("compare hash results without tag %v: %v", i, algo), func(t *testing.T) {
   374  			code := hashWithTagScript(algo.Name())
   375  			script := fvm.Script(code)
   376  			script = script.WithArguments(
   377  				cadenceData,
   378  				jsoncdc.MustEncode(cadence.String("")),
   379  			)
   380  			_, output, err := vm.Run(ctx, script, snapshotTree)
   381  			require.NoError(t, err)
   382  			require.NoError(t, output.Err)
   383  
   384  			result1 := make([]byte, 0)
   385  			cadenceArray := output.Value.(cadence.Array)
   386  			for _, value := range cadenceArray.Values {
   387  				result1 = append(result1, uint8(value.(cadence.UInt8)))
   388  			}
   389  
   390  			code = hashScript(algo.Name())
   391  			script = fvm.Script(code)
   392  			script = script.WithArguments(
   393  				cadenceData,
   394  			)
   395  			_, output, err = vm.Run(ctx, script, snapshotTree)
   396  			require.NoError(t, err)
   397  			require.NoError(t, output.Err)
   398  
   399  			result2 := make([]byte, 0)
   400  			cadenceArray = output.Value.(cadence.Array)
   401  			for _, value := range cadenceArray.Values {
   402  				result2 = append(result2, uint8(value.(cadence.UInt8)))
   403  			}
   404  
   405  			result3, err := fvmCrypto.HashWithTag(fvmCrypto.RuntimeToCryptoHashingAlgorithm(algo), "", data)
   406  			require.NoError(t, err)
   407  
   408  			require.Equal(t, result1, result2)
   409  			require.Equal(t, result1, result3)
   410  		})
   411  	}
   412  }
   413  
   414  func TestWithServiceAccount(t *testing.T) {
   415  
   416  	t.Parallel()
   417  
   418  	chain, vm := createChainAndVm(flow.Mainnet)
   419  
   420  	ctxA := fvm.NewContext(
   421  		fvm.WithChain(chain),
   422  		fvm.WithAuthorizationChecksEnabled(false),
   423  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   424  	)
   425  
   426  	snapshotTree := snapshot.NewSnapshotTree(nil)
   427  
   428  	txBody := flow.NewTransactionBody().
   429  		SetScript([]byte(`transaction { prepare(signer: auth(BorrowValue) &Account) { Account(payer: signer) } }`)).
   430  		AddAuthorizer(chain.ServiceAddress())
   431  
   432  	t.Run("With service account enabled", func(t *testing.T) {
   433  		executionSnapshot, output, err := vm.Run(
   434  			ctxA,
   435  			fvm.Transaction(txBody, 0),
   436  			snapshotTree)
   437  		require.NoError(t, err)
   438  
   439  		// transaction should fail on non-bootstrapped ledger
   440  		require.Error(t, output.Err)
   441  
   442  		snapshotTree = snapshotTree.Append(executionSnapshot)
   443  	})
   444  
   445  	t.Run("With service account disabled", func(t *testing.T) {
   446  		ctxB := fvm.NewContextFromParent(
   447  			ctxA,
   448  			fvm.WithServiceAccount(false))
   449  
   450  		_, output, err := vm.Run(
   451  			ctxB,
   452  			fvm.Transaction(txBody, 0),
   453  			snapshotTree)
   454  		require.NoError(t, err)
   455  
   456  		// transaction should succeed on non-bootstrapped ledger
   457  		require.NoError(t, output.Err)
   458  	})
   459  }
   460  
   461  func TestEventLimits(t *testing.T) {
   462  	chain, vm := createChainAndVm(flow.Mainnet)
   463  
   464  	ctx := fvm.NewContext(
   465  		fvm.WithChain(chain),
   466  		fvm.WithAuthorizationChecksEnabled(false),
   467  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   468  	)
   469  
   470  	snapshotTree := testutil.RootBootstrappedLedger(vm, ctx)
   471  
   472  	testContract := `
   473  	access(all) contract TestContract {
   474  		access(all) event LargeEvent(value: Int256, str: String, list: [UInt256], dic: {String: String})
   475  		access(all) fun EmitEvent() {
   476  			var s: Int256 = 1024102410241024
   477  			var i = 0
   478  
   479  			while i < 20 {
   480  				emit LargeEvent(value: s, str: s.toString(), list:[], dic:{s.toString():s.toString()})
   481  				i = i + 1
   482  			}
   483  		}
   484  	}
   485  	`
   486  
   487  	deployingContractScriptTemplate := `
   488  		transaction {
   489  			prepare(signer: auth(AddContract) &Account) {
   490  				let code = "%s".decodeHex()
   491  				signer.contracts.add(
   492  					name: "TestContract",
   493  					code: code
   494  				)
   495  		}
   496  	}
   497  	`
   498  
   499  	ctx = fvm.NewContextFromParent(
   500  		ctx,
   501  		fvm.WithEventCollectionSizeLimit(2))
   502  
   503  	txBody := flow.NewTransactionBody().
   504  		SetScript([]byte(fmt.Sprintf(deployingContractScriptTemplate, hex.EncodeToString([]byte(testContract))))).
   505  		SetPayer(chain.ServiceAddress()).
   506  		AddAuthorizer(chain.ServiceAddress())
   507  
   508  	executionSnapshot, output, err := vm.Run(
   509  		ctx,
   510  		fvm.Transaction(txBody, 0),
   511  		snapshotTree)
   512  	require.NoError(t, err)
   513  	require.NoError(t, output.Err)
   514  
   515  	snapshotTree = snapshotTree.Append(executionSnapshot)
   516  
   517  	txBody = flow.NewTransactionBody().
   518  		SetScript([]byte(fmt.Sprintf(`
   519  		import TestContract from 0x%s
   520  			transaction {
   521  			prepare(acct: &Account) {}
   522  			execute {
   523  				TestContract.EmitEvent()
   524  			}
   525  		}`, chain.ServiceAddress()))).
   526  		AddAuthorizer(chain.ServiceAddress())
   527  
   528  	t.Run("With limits", func(t *testing.T) {
   529  		txBody.Payer = unittest.RandomAddressFixture()
   530  
   531  		executionSnapshot, output, err := vm.Run(
   532  			ctx,
   533  			fvm.Transaction(txBody, 0),
   534  			snapshotTree)
   535  		require.NoError(t, err)
   536  
   537  		// transaction should fail due to event size limit
   538  		require.Error(t, output.Err)
   539  
   540  		snapshotTree = snapshotTree.Append(executionSnapshot)
   541  	})
   542  
   543  	t.Run("With service account as payer", func(t *testing.T) {
   544  		txBody.Payer = chain.ServiceAddress()
   545  
   546  		_, output, err := vm.Run(
   547  			ctx,
   548  			fvm.Transaction(txBody, 0),
   549  			snapshotTree)
   550  		require.NoError(t, err)
   551  
   552  		unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   553  
   554  		// transaction should not fail due to event size limit
   555  		require.NoError(t, output.Err)
   556  	})
   557  }
   558  
   559  // TestHappyPathSigning checks that a signing a transaction with `Sign` doesn't produce an error.
   560  // Transaction verification tests are in `TestVerifySignatureFromTransaction`.
   561  func TestHappyPathTransactionSigning(t *testing.T) {
   562  
   563  	newVMTest().run(
   564  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
   565  			// Create an account private key.
   566  			privateKey, err := testutil.GenerateAccountPrivateKey()
   567  			require.NoError(t, err)
   568  
   569  			// Bootstrap a ledger, creating accounts with the provided private
   570  			// keys and the root account.
   571  			snapshotTree, accounts, err := testutil.CreateAccounts(
   572  				vm,
   573  				snapshotTree,
   574  				[]flow.AccountPrivateKey{privateKey},
   575  				chain)
   576  			require.NoError(t, err)
   577  
   578  			txBody := flow.NewTransactionBody().
   579  				SetScript([]byte(`transaction(){}`))
   580  
   581  			txBody.SetProposalKey(accounts[0], 0, 0)
   582  			txBody.SetPayer(accounts[0])
   583  
   584  			hasher, err := exeUtils.NewHasher(privateKey.HashAlgo)
   585  			require.NoError(t, err)
   586  
   587  			sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher)
   588  			require.NoError(t, err)
   589  			txBody.AddEnvelopeSignature(accounts[0], 0, sig)
   590  
   591  			_, output, err := vm.Run(
   592  				ctx,
   593  				fvm.Transaction(txBody, 0),
   594  				snapshotTree)
   595  			require.NoError(t, err)
   596  			require.NoError(t, output.Err)
   597  		},
   598  	)
   599  }
   600  
   601  func TestTransactionFeeDeduction(t *testing.T) {
   602  	getBalance := func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree, address flow.Address) uint64 {
   603  		sc := systemcontracts.SystemContractsForChain(chain.ChainID())
   604  		code := []byte(fmt.Sprintf(
   605  			`
   606  					import FungibleToken from 0x%s
   607  					import FlowToken from 0x%s
   608  
   609  					access(all) fun main(account: Address): UFix64 {
   610  						let acct = getAccount(account)
   611  						let vaultRef = acct.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance)
   612  							?? panic("Could not borrow Balance reference to the Vault")
   613  
   614  						return vaultRef.balance
   615  					}
   616  				`,
   617  			sc.FungibleToken.Address.Hex(),
   618  			sc.FlowToken.Address.Hex(),
   619  		))
   620  		script := fvm.Script(code).WithArguments(
   621  			jsoncdc.MustEncode(cadence.NewAddress(address)),
   622  		)
   623  
   624  		_, output, err := vm.Run(ctx, script, snapshotTree)
   625  		require.NoError(t, err)
   626  		require.NoError(t, output.Err)
   627  		return uint64(output.Value.(cadence.UFix64))
   628  	}
   629  
   630  	type testCase struct {
   631  		name          string
   632  		fundWith      uint64
   633  		tryToTransfer uint64
   634  		gasLimit      uint64
   635  		checkResult   func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput)
   636  	}
   637  
   638  	txFees := uint64(1_000)              // 0.00001
   639  	fundingAmount := uint64(100_000_000) // 1.0
   640  	transferAmount := uint64(123_456)
   641  	minimumStorageReservation := uint64(fvm.DefaultMinimumStorageReservation)
   642  
   643  	chain := flow.Testnet.Chain()
   644  	sc := systemcontracts.SystemContractsForChain(chain.ChainID())
   645  	depositedEvent := fmt.Sprintf("A.%s.FlowToken.TokensDeposited", sc.FlowToken.Address)
   646  	withdrawnEvent := fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", sc.FlowToken.Address)
   647  	feesDeductedEvent := fmt.Sprintf("A.%s.FlowFees.FeesDeducted", sc.FlowFees.Address)
   648  
   649  	testCases := []testCase{
   650  		{
   651  			name:          "Transaction fees are deducted",
   652  			fundWith:      fundingAmount,
   653  			tryToTransfer: 0,
   654  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   655  				require.NoError(t, output.Err)
   656  				require.Equal(t, txFees, balanceBefore-balanceAfter)
   657  			},
   658  		},
   659  		{
   660  			name:          "Transaction fee deduction emits events",
   661  			fundWith:      fundingAmount,
   662  			tryToTransfer: 0,
   663  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   664  				require.NoError(t, output.Err)
   665  
   666  				var deposits []flow.Event
   667  				var withdraws []flow.Event
   668  
   669  				chain := flow.Testnet.Chain()
   670  				for _, e := range output.Events {
   671  					if string(e.Type) == depositedEvent {
   672  						deposits = append(deposits, e)
   673  					}
   674  					if string(e.Type) == withdrawnEvent {
   675  						withdraws = append(withdraws, e)
   676  					}
   677  				}
   678  
   679  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   680  				require.Len(t, deposits, 2)
   681  				require.Len(t, withdraws, 2)
   682  			},
   683  		},
   684  		{
   685  			name:          "Transaction fees are deducted and tx is applied",
   686  			fundWith:      fundingAmount,
   687  			tryToTransfer: transferAmount,
   688  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   689  				require.NoError(t, output.Err)
   690  				require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter)
   691  			},
   692  		},
   693  		{
   694  			name:          "Transaction fees are deducted and fee deduction is emitted",
   695  			fundWith:      fundingAmount,
   696  			tryToTransfer: transferAmount,
   697  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   698  				require.NoError(t, output.Err)
   699  				chain := flow.Testnet.Chain()
   700  
   701  				var feeDeduction flow.Event // fee deduction event
   702  				for _, e := range output.Events {
   703  					if string(e.Type) == feesDeductedEvent {
   704  						feeDeduction = e
   705  						break
   706  					}
   707  				}
   708  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   709  				require.NotEmpty(t, feeDeduction.Payload)
   710  
   711  				payload, err := ccf.Decode(nil, feeDeduction.Payload)
   712  				require.NoError(t, err)
   713  
   714  				event := payload.(cadence.Event)
   715  
   716  				fields := cadence.FieldsMappedByName(event)
   717  
   718  				actualTXFees := fields["amount"]
   719  				actualExecutionEffort := fields["executionEffort"]
   720  				actualInclusionEffort := fields["inclusionEffort"]
   721  
   722  				require.Equal(t,
   723  					txFees,
   724  					uint64(actualTXFees.(cadence.UFix64)),
   725  				)
   726  				// Inclusion effort should be equivalent to 1.0 UFix64
   727  				require.Equal(t,
   728  					uint64(100_000_000),
   729  					uint64(actualInclusionEffort.(cadence.UFix64)),
   730  				)
   731  				// Execution effort should be non-0
   732  				require.Greater(t, actualExecutionEffort, uint64(0))
   733  
   734  			},
   735  		},
   736  		{
   737  			name:          "If just enough balance, fees are deducted",
   738  			fundWith:      txFees + transferAmount,
   739  			tryToTransfer: transferAmount,
   740  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   741  				require.NoError(t, output.Err)
   742  				require.Equal(t, uint64(0), balanceAfter)
   743  			},
   744  		},
   745  		{
   746  			// this is an edge case that is not applicable to any network.
   747  			// If storage limits were on this would fail due to storage limits
   748  			name:          "If not enough balance, transaction succeeds and fees are deducted to 0",
   749  			fundWith:      txFees,
   750  			tryToTransfer: 1,
   751  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   752  				require.NoError(t, output.Err)
   753  				require.Equal(t, uint64(0), balanceAfter)
   754  			},
   755  		},
   756  		{
   757  			name:          "If tx fails, fees are deducted",
   758  			fundWith:      fundingAmount,
   759  			tryToTransfer: 2 * fundingAmount,
   760  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   761  				require.Error(t, output.Err)
   762  				require.Equal(t, fundingAmount-txFees, balanceAfter)
   763  			},
   764  		},
   765  		{
   766  			name:          "If tx fails, fee deduction events are emitted",
   767  			fundWith:      fundingAmount,
   768  			tryToTransfer: 2 * fundingAmount,
   769  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   770  				require.Error(t, output.Err)
   771  
   772  				var deposits []flow.Event
   773  				var withdraws []flow.Event
   774  
   775  				chain := flow.Testnet.Chain()
   776  
   777  				for _, e := range output.Events {
   778  					if string(e.Type) == depositedEvent {
   779  						deposits = append(deposits, e)
   780  					}
   781  					if string(e.Type) == withdrawnEvent {
   782  						withdraws = append(withdraws, e)
   783  					}
   784  				}
   785  
   786  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   787  				require.Len(t, deposits, 1)
   788  				require.Len(t, withdraws, 1)
   789  			},
   790  		},
   791  		{
   792  			name:          "If tx fails because of gas limit reached, fee deduction events are emitted",
   793  			fundWith:      txFees + transferAmount,
   794  			tryToTransfer: transferAmount,
   795  			gasLimit:      uint64(2),
   796  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   797  				require.ErrorContains(t, output.Err, "computation exceeds limit (2)")
   798  
   799  				var deposits []flow.Event
   800  				var withdraws []flow.Event
   801  
   802  				chain := flow.Testnet.Chain()
   803  
   804  				for _, e := range output.Events {
   805  					if string(e.Type) == depositedEvent {
   806  						deposits = append(deposits, e)
   807  					}
   808  					if string(e.Type) == withdrawnEvent {
   809  						withdraws = append(withdraws, e)
   810  					}
   811  				}
   812  
   813  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   814  				require.Len(t, deposits, 1)
   815  				require.Len(t, withdraws, 1)
   816  			},
   817  		},
   818  	}
   819  
   820  	testCasesWithStorageEnabled := []testCase{
   821  		{
   822  			name:          "Transaction fees are deducted",
   823  			fundWith:      fundingAmount,
   824  			tryToTransfer: 0,
   825  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   826  				require.NoError(t, output.Err)
   827  				require.Equal(t, txFees, balanceBefore-balanceAfter)
   828  			},
   829  		},
   830  		{
   831  			name:          "Transaction fee deduction emits events",
   832  			fundWith:      fundingAmount,
   833  			tryToTransfer: 0,
   834  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   835  				require.NoError(t, output.Err)
   836  
   837  				var deposits []flow.Event
   838  				var withdraws []flow.Event
   839  
   840  				chain := flow.Testnet.Chain()
   841  
   842  				for _, e := range output.Events {
   843  					if string(e.Type) == depositedEvent {
   844  						deposits = append(deposits, e)
   845  					}
   846  					if string(e.Type) == withdrawnEvent {
   847  						withdraws = append(withdraws, e)
   848  					}
   849  				}
   850  
   851  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   852  				require.Len(t, deposits, 2)
   853  				require.Len(t, withdraws, 2)
   854  			},
   855  		},
   856  		{
   857  			name:          "Transaction fees are deducted and tx is applied",
   858  			fundWith:      fundingAmount,
   859  			tryToTransfer: transferAmount,
   860  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   861  				require.NoError(t, output.Err)
   862  				require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter)
   863  			},
   864  		},
   865  		{
   866  			name:          "If just enough balance, fees are deducted",
   867  			fundWith:      txFees + transferAmount,
   868  			tryToTransfer: transferAmount,
   869  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   870  				require.NoError(t, output.Err)
   871  				require.Equal(t, minimumStorageReservation, balanceAfter)
   872  			},
   873  		},
   874  		{
   875  			name:          "If tx fails, fees are deducted",
   876  			fundWith:      fundingAmount,
   877  			tryToTransfer: 2 * fundingAmount,
   878  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   879  				require.Error(t, output.Err)
   880  				require.Equal(t, fundingAmount-txFees+minimumStorageReservation, balanceAfter)
   881  			},
   882  		},
   883  		{
   884  			name:          "If tx fails, fee deduction events are emitted",
   885  			fundWith:      fundingAmount,
   886  			tryToTransfer: 2 * fundingAmount,
   887  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   888  				require.Error(t, output.Err)
   889  
   890  				var deposits []flow.Event
   891  				var withdraws []flow.Event
   892  
   893  				chain := flow.Testnet.Chain()
   894  
   895  				for _, e := range output.Events {
   896  					if string(e.Type) == depositedEvent {
   897  						deposits = append(deposits, e)
   898  					}
   899  					if string(e.Type) == withdrawnEvent {
   900  						withdraws = append(withdraws, e)
   901  					}
   902  				}
   903  
   904  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   905  				require.Len(t, deposits, 1)
   906  				require.Len(t, withdraws, 1)
   907  			},
   908  		},
   909  		{
   910  			name:          "If balance at minimum, transaction fails, fees are deducted and fee deduction events are emitted",
   911  			fundWith:      0,
   912  			tryToTransfer: 0,
   913  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) {
   914  				require.Error(t, output.Err)
   915  				require.Equal(t, minimumStorageReservation-txFees, balanceAfter)
   916  
   917  				var deposits []flow.Event
   918  				var withdraws []flow.Event
   919  
   920  				chain := flow.Testnet.Chain()
   921  
   922  				for _, e := range output.Events {
   923  					if string(e.Type) == depositedEvent {
   924  						deposits = append(deposits, e)
   925  					}
   926  					if string(e.Type) == withdrawnEvent {
   927  						withdraws = append(withdraws, e)
   928  					}
   929  				}
   930  
   931  				unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   932  				require.Len(t, deposits, 1)
   933  				require.Len(t, withdraws, 1)
   934  			},
   935  		},
   936  	}
   937  
   938  	runTx := func(tc testCase) func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
   939  		return func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
   940  			// ==== Create an account ====
   941  			privateKey, txBody := testutil.CreateAccountCreationTransaction(t, chain)
   942  
   943  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
   944  			require.NoError(t, err)
   945  
   946  			executionSnapshot, output, err := vm.Run(
   947  				ctx,
   948  				fvm.Transaction(txBody, 0),
   949  				snapshotTree)
   950  			require.NoError(t, err)
   951  			require.NoError(t, output.Err)
   952  
   953  			snapshotTree = snapshotTree.Append(executionSnapshot)
   954  
   955  			require.Len(t, output.Events, 16)
   956  			unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
   957  
   958  			accountCreatedEvents := filterAccountCreatedEvents(output.Events)
   959  
   960  			require.Len(t, accountCreatedEvents, 1)
   961  
   962  			// read the address of the account created (e.g. "0x01" and convert it to flow.address)
   963  			data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload)
   964  			require.NoError(t, err)
   965  
   966  			address := flow.ConvertAddress(
   967  				cadence.SearchFieldByName(
   968  					data.(cadence.Event),
   969  					stdlib2.AccountEventAddressParameter.Identifier,
   970  				).(cadence.Address),
   971  			)
   972  
   973  			// ==== Transfer tokens to new account ====
   974  			txBody = transferTokensTx(chain).
   975  				AddAuthorizer(chain.ServiceAddress()).
   976  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.fundWith))).
   977  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address)))
   978  
   979  			txBody.SetProposalKey(chain.ServiceAddress(), 0, 1)
   980  			txBody.SetPayer(chain.ServiceAddress())
   981  
   982  			err = testutil.SignEnvelope(
   983  				txBody,
   984  				chain.ServiceAddress(),
   985  				unittest.ServiceAccountPrivateKey,
   986  			)
   987  			require.NoError(t, err)
   988  
   989  			executionSnapshot, output, err = vm.Run(
   990  				ctx,
   991  				fvm.Transaction(txBody, 0),
   992  				snapshotTree)
   993  			require.NoError(t, err)
   994  			require.NoError(t, output.Err)
   995  
   996  			snapshotTree = snapshotTree.Append(executionSnapshot)
   997  
   998  			balanceBefore := getBalance(vm, chain, ctx, snapshotTree, address)
   999  
  1000  			// ==== Transfer tokens from new account ====
  1001  
  1002  			txBody = transferTokensTx(chain).
  1003  				AddAuthorizer(address).
  1004  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.tryToTransfer))).
  1005  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress())))
  1006  
  1007  			txBody.SetProposalKey(address, 0, 0)
  1008  			txBody.SetPayer(address)
  1009  
  1010  			if tc.gasLimit == 0 {
  1011  				txBody.SetComputeLimit(fvm.DefaultComputationLimit)
  1012  			} else {
  1013  				txBody.SetComputeLimit(tc.gasLimit)
  1014  			}
  1015  
  1016  			err = testutil.SignEnvelope(
  1017  				txBody,
  1018  				address,
  1019  				privateKey,
  1020  			)
  1021  			require.NoError(t, err)
  1022  
  1023  			executionSnapshot, output, err = vm.Run(
  1024  				ctx,
  1025  				fvm.Transaction(txBody, 0),
  1026  				snapshotTree)
  1027  			require.NoError(t, err)
  1028  
  1029  			snapshotTree = snapshotTree.Append(executionSnapshot)
  1030  
  1031  			balanceAfter := getBalance(vm, chain, ctx, snapshotTree, address)
  1032  
  1033  			tc.checkResult(
  1034  				t,
  1035  				balanceBefore,
  1036  				balanceAfter,
  1037  				output,
  1038  			)
  1039  		}
  1040  	}
  1041  
  1042  	for i, tc := range testCases {
  1043  		t.Run(fmt.Sprintf("Transaction Fees %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions(
  1044  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1045  			fvm.WithExecutionMemoryLimit(math.MaxUint64),
  1046  			fvm.WithExecutionEffortWeights(environment.MainnetExecutionEffortWeights),
  1047  			fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
  1048  		).withContextOptions(
  1049  			fvm.WithTransactionFeesEnabled(true),
  1050  			fvm.WithChain(chain),
  1051  		).run(
  1052  			runTx(tc)),
  1053  		)
  1054  	}
  1055  
  1056  	for i, tc := range testCasesWithStorageEnabled {
  1057  		t.Run(fmt.Sprintf("Transaction Fees with storage %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions(
  1058  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1059  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1060  			fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1061  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1062  			fvm.WithExecutionMemoryLimit(math.MaxUint64),
  1063  			fvm.WithExecutionEffortWeights(environment.MainnetExecutionEffortWeights),
  1064  			fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
  1065  		).withContextOptions(
  1066  			fvm.WithTransactionFeesEnabled(true),
  1067  			fvm.WithAccountStorageLimit(true),
  1068  			fvm.WithChain(chain),
  1069  		).run(
  1070  			runTx(tc)),
  1071  		)
  1072  	}
  1073  }
  1074  
  1075  func TestSettingExecutionWeights(t *testing.T) {
  1076  
  1077  	t.Run("transaction should fail with high weights", newVMTest().withBootstrapProcedureOptions(
  1078  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1079  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1080  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1081  		fvm.WithExecutionEffortWeights(
  1082  			meter.ExecutionEffortWeights{
  1083  				common.ComputationKindLoop: 100_000 << meter.MeterExecutionInternalPrecisionBytes,
  1084  			},
  1085  		),
  1086  	).run(
  1087  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1088  
  1089  			txBody := flow.NewTransactionBody().
  1090  				SetScript([]byte(`
  1091  				transaction {
  1092                    prepare(signer: &Account) {
  1093  					var a = 0
  1094  					while a < 100 {
  1095  						a = a + 1
  1096  					}
  1097                    }
  1098                  }
  1099  			`)).
  1100  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1101  				AddAuthorizer(chain.ServiceAddress()).
  1102  				SetPayer(chain.ServiceAddress())
  1103  
  1104  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1105  			require.NoError(t, err)
  1106  
  1107  			_, output, err := vm.Run(
  1108  				ctx,
  1109  				fvm.Transaction(txBody, 0),
  1110  				snapshotTree)
  1111  			require.NoError(t, err)
  1112  
  1113  			require.True(t, errors.IsComputationLimitExceededError(output.Err))
  1114  		},
  1115  	))
  1116  
  1117  	memoryWeights := make(map[common.MemoryKind]uint64)
  1118  	for k, v := range meter.DefaultMemoryWeights {
  1119  		memoryWeights[k] = v
  1120  	}
  1121  
  1122  	const highWeight = 20_000_000_000
  1123  	memoryWeights[common.MemoryKindIntegerExpression] = highWeight
  1124  
  1125  	t.Run("normal transactions should fail with high memory weights", newVMTest().withBootstrapProcedureOptions(
  1126  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1127  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1128  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1129  		fvm.WithExecutionMemoryWeights(
  1130  			memoryWeights,
  1131  		),
  1132  	).withContextOptions(
  1133  		fvm.WithMemoryLimit(10_000_000_000),
  1134  	).run(
  1135  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1136  			// Create an account private key.
  1137  			privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1138  			require.NoError(t, err)
  1139  
  1140  			// Bootstrap a ledger, creating accounts with the provided private
  1141  			// keys and the root account.
  1142  			snapshotTree, accounts, err := testutil.CreateAccounts(
  1143  				vm,
  1144  				snapshotTree,
  1145  				privateKeys,
  1146  				chain)
  1147  			require.NoError(t, err)
  1148  
  1149  			txBody := flow.NewTransactionBody().
  1150  				SetScript([]byte(`
  1151  				transaction {
  1152                    prepare(signer: &Account) {
  1153  					var a = 1
  1154                    }
  1155                  }
  1156  			`)).
  1157  				SetProposalKey(accounts[0], 0, 0).
  1158  				AddAuthorizer(accounts[0]).
  1159  				SetPayer(accounts[0])
  1160  
  1161  			err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0)
  1162  			require.NoError(t, err)
  1163  
  1164  			_, output, err := vm.Run(
  1165  				ctx,
  1166  				fvm.Transaction(txBody, 0),
  1167  				snapshotTree)
  1168  			require.NoError(t, err)
  1169  			require.Greater(t, output.MemoryEstimate, uint64(highWeight))
  1170  
  1171  			require.True(t, errors.IsMemoryLimitExceededError(output.Err))
  1172  		},
  1173  	))
  1174  
  1175  	t.Run("service account transactions should not fail with high memory weights", newVMTest().withBootstrapProcedureOptions(
  1176  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1177  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1178  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1179  		fvm.WithExecutionMemoryWeights(
  1180  			memoryWeights,
  1181  		),
  1182  	).withContextOptions(
  1183  		fvm.WithMemoryLimit(10_000_000_000),
  1184  	).run(
  1185  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1186  
  1187  			txBody := flow.NewTransactionBody().
  1188  				SetScript([]byte(`
  1189  				transaction {
  1190                    prepare(signer: &Account) {
  1191  					var a = 1
  1192                    }
  1193                  }
  1194  			`)).
  1195  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1196  				AddAuthorizer(chain.ServiceAddress()).
  1197  				SetPayer(chain.ServiceAddress())
  1198  
  1199  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1200  			require.NoError(t, err)
  1201  
  1202  			_, output, err := vm.Run(
  1203  				ctx,
  1204  				fvm.Transaction(txBody, 0),
  1205  				snapshotTree)
  1206  			require.NoError(t, err)
  1207  			require.Greater(t, output.MemoryEstimate, uint64(highWeight))
  1208  
  1209  			require.NoError(t, output.Err)
  1210  		},
  1211  	))
  1212  
  1213  	memoryWeights = make(map[common.MemoryKind]uint64)
  1214  	for k, v := range meter.DefaultMemoryWeights {
  1215  		memoryWeights[k] = v
  1216  	}
  1217  	memoryWeights[common.MemoryKindBreakStatement] = 1_000_000
  1218  	t.Run("transaction should fail with low memory limit (set in the state)", newVMTest().withBootstrapProcedureOptions(
  1219  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1220  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1221  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1222  		fvm.WithExecutionMemoryLimit(
  1223  			100_000_000,
  1224  		),
  1225  		fvm.WithExecutionMemoryWeights(
  1226  			memoryWeights,
  1227  		),
  1228  	).run(
  1229  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1230  			privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1231  			require.NoError(t, err)
  1232  
  1233  			snapshotTree, accounts, err := testutil.CreateAccounts(
  1234  				vm,
  1235  				snapshotTree,
  1236  				privateKeys,
  1237  				chain)
  1238  			require.NoError(t, err)
  1239  
  1240  			// This transaction is specially designed to use a lot of breaks
  1241  			// as the weight for breaks is much higher than usual.
  1242  			// putting a `while true {break}` in a loop does not use the same amount of memory.
  1243  			txBody := flow.NewTransactionBody().
  1244  				SetScript([]byte(`
  1245  				transaction {
  1246  					prepare(signer: &Account) {
  1247  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1248  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1249  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1250  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1251  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1252  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1253  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1254  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1255  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1256  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1257  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1258  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1259  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1260  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1261  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1262  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1263  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1264  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1265  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1266  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1267  					}
  1268  				}
  1269  			`))
  1270  
  1271  			err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0)
  1272  			require.NoError(t, err)
  1273  
  1274  			_, output, err := vm.Run(
  1275  				ctx,
  1276  				fvm.Transaction(txBody, 0),
  1277  				snapshotTree)
  1278  			require.NoError(t, err)
  1279  			// There are 100 breaks and each break uses 1_000_000 memory
  1280  			require.Greater(t, output.MemoryEstimate, uint64(100_000_000))
  1281  
  1282  			require.True(t, errors.IsMemoryLimitExceededError(output.Err))
  1283  		},
  1284  	))
  1285  
  1286  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1287  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1288  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1289  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1290  		fvm.WithExecutionEffortWeights(
  1291  			meter.ExecutionEffortWeights{
  1292  				environment.ComputationKindCreateAccount: (fvm.DefaultComputationLimit + 1) << meter.MeterExecutionInternalPrecisionBytes,
  1293  			},
  1294  		),
  1295  	).run(
  1296  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1297  			txBody := flow.NewTransactionBody().
  1298  				SetScript([]byte(`
  1299  				transaction {
  1300                    prepare(signer: auth(BorrowValue) &Account) {
  1301  					Account(payer: signer)
  1302                    }
  1303                  }
  1304  			`)).
  1305  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1306  				AddAuthorizer(chain.ServiceAddress()).
  1307  				SetPayer(chain.ServiceAddress())
  1308  
  1309  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1310  			require.NoError(t, err)
  1311  
  1312  			_, output, err := vm.Run(
  1313  				ctx,
  1314  				fvm.Transaction(txBody, 0),
  1315  				snapshotTree)
  1316  			require.NoError(t, err)
  1317  
  1318  			require.True(t, errors.IsComputationLimitExceededError(output.Err))
  1319  		},
  1320  	))
  1321  
  1322  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1323  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1324  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1325  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1326  		fvm.WithExecutionEffortWeights(
  1327  			meter.ExecutionEffortWeights{
  1328  				environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes,
  1329  			},
  1330  		),
  1331  	).run(
  1332  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1333  
  1334  			txBody := flow.NewTransactionBody().
  1335  				SetScript([]byte(`
  1336  				transaction {
  1337                    prepare(signer: auth(BorrowValue) &Account) {
  1338  					Account(payer: signer)
  1339                    }
  1340                  }
  1341  			`)).
  1342  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1343  				AddAuthorizer(chain.ServiceAddress()).
  1344  				SetPayer(chain.ServiceAddress())
  1345  
  1346  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1347  			require.NoError(t, err)
  1348  
  1349  			_, output, err := vm.Run(
  1350  				ctx,
  1351  				fvm.Transaction(txBody, 0),
  1352  				snapshotTree)
  1353  			require.NoError(t, err)
  1354  
  1355  			require.True(t, errors.IsComputationLimitExceededError(output.Err))
  1356  		},
  1357  	))
  1358  
  1359  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1360  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1361  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1362  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1363  		fvm.WithExecutionEffortWeights(
  1364  			meter.ExecutionEffortWeights{
  1365  				environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes,
  1366  			},
  1367  		),
  1368  	).run(
  1369  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1370  			txBody := flow.NewTransactionBody().
  1371  				SetScript([]byte(`
  1372  				transaction {
  1373                    prepare(signer: auth(BorrowValue) &Account) {
  1374  					Account(payer: signer)
  1375                    }
  1376                  }
  1377  			`)).
  1378  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1379  				AddAuthorizer(chain.ServiceAddress()).
  1380  				SetPayer(chain.ServiceAddress())
  1381  
  1382  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1383  			require.NoError(t, err)
  1384  
  1385  			_, output, err := vm.Run(
  1386  				ctx,
  1387  				fvm.Transaction(txBody, 0),
  1388  				snapshotTree)
  1389  			require.NoError(t, err)
  1390  
  1391  			require.True(t, errors.IsComputationLimitExceededError(output.Err))
  1392  		},
  1393  	))
  1394  
  1395  	t.Run("transaction should not use up more computation that the transaction body itself", newVMTest().withBootstrapProcedureOptions(
  1396  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1397  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1398  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1399  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1400  		fvm.WithExecutionEffortWeights(
  1401  			meter.ExecutionEffortWeights{
  1402  				common.ComputationKindStatement:          0,
  1403  				common.ComputationKindLoop:               1 << meter.MeterExecutionInternalPrecisionBytes,
  1404  				common.ComputationKindFunctionInvocation: 0,
  1405  			},
  1406  		),
  1407  	).withContextOptions(
  1408  		fvm.WithAccountStorageLimit(true),
  1409  		fvm.WithTransactionFeesEnabled(true),
  1410  		fvm.WithMemoryLimit(math.MaxUint64),
  1411  	).run(
  1412  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1413  			// Use the maximum amount of computation so that the transaction still passes.
  1414  			loops := uint64(996)
  1415  			executionEffortNeededToCheckStorage := uint64(1)
  1416  			maxExecutionEffort := uint64(997)
  1417  			txBody := flow.NewTransactionBody().
  1418  				SetScript([]byte(fmt.Sprintf(`
  1419  				transaction() {prepare(signer: &Account){var i=0;  while i < %d {i = i +1 } } execute{}}
  1420  			`, loops))).
  1421  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1422  				AddAuthorizer(chain.ServiceAddress()).
  1423  				SetPayer(chain.ServiceAddress()).
  1424  				SetComputeLimit(maxExecutionEffort)
  1425  
  1426  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1427  			require.NoError(t, err)
  1428  
  1429  			executionSnapshot, output, err := vm.Run(
  1430  				ctx,
  1431  				fvm.Transaction(txBody, 0),
  1432  				snapshotTree)
  1433  			require.NoError(t, err)
  1434  			require.NoError(t, output.Err)
  1435  
  1436  			snapshotTree = snapshotTree.Append(executionSnapshot)
  1437  
  1438  			// expected computation used is number of loops + 1 (from the storage limit check).
  1439  			require.Equal(t, loops+executionEffortNeededToCheckStorage, output.ComputationUsed)
  1440  
  1441  			// increasing the number of loops should fail the transaction.
  1442  			loops = loops + 1
  1443  			txBody = flow.NewTransactionBody().
  1444  				SetScript([]byte(fmt.Sprintf(`
  1445  				transaction() {prepare(signer: &Account){var i=0;  while i < %d {i = i +1 } } execute{}}
  1446  			`, loops))).
  1447  				SetProposalKey(chain.ServiceAddress(), 0, 1).
  1448  				AddAuthorizer(chain.ServiceAddress()).
  1449  				SetPayer(chain.ServiceAddress()).
  1450  				SetComputeLimit(maxExecutionEffort)
  1451  
  1452  			err = testutil.SignTransactionAsServiceAccount(txBody, 1, chain)
  1453  			require.NoError(t, err)
  1454  
  1455  			_, output, err = vm.Run(
  1456  				ctx,
  1457  				fvm.Transaction(txBody, 0),
  1458  				snapshotTree)
  1459  			require.NoError(t, err)
  1460  
  1461  			require.ErrorContains(t, output.Err, "computation exceeds limit (997)")
  1462  			// expected computation used is still number of loops + 1 (from the storage limit check).
  1463  			require.Equal(t, loops+executionEffortNeededToCheckStorage, output.ComputationUsed)
  1464  
  1465  			for _, event := range output.Events {
  1466  				// the fee deduction event should only contain the max gas worth of execution effort.
  1467  				if strings.Contains(string(event.Type), "FlowFees.FeesDeducted") {
  1468  					v, err := ccf.Decode(nil, event.Payload)
  1469  					require.NoError(t, err)
  1470  
  1471  					ev := v.(cadence.Event)
  1472  
  1473  					actualExecutionEffort := cadence.SearchFieldByName(ev, "executionEffort")
  1474  
  1475  					require.Equal(
  1476  						t,
  1477  						maxExecutionEffort,
  1478  						uint64(actualExecutionEffort.(cadence.UFix64)),
  1479  					)
  1480  				}
  1481  			}
  1482  			unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID())
  1483  		},
  1484  	))
  1485  
  1486  	t.Run("transaction with more accounts touched uses more computation", newVMTest().withBootstrapProcedureOptions(
  1487  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1488  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1489  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1490  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1491  		fvm.WithExecutionEffortWeights(
  1492  			meter.ExecutionEffortWeights{
  1493  				common.ComputationKindStatement: 0,
  1494  				// only count loops
  1495  				// the storage check has a loop
  1496  				common.ComputationKindLoop:               1 << meter.MeterExecutionInternalPrecisionBytes,
  1497  				common.ComputationKindFunctionInvocation: 0,
  1498  			},
  1499  		),
  1500  	).withContextOptions(
  1501  		fvm.WithAccountStorageLimit(true),
  1502  		fvm.WithTransactionFeesEnabled(true),
  1503  		fvm.WithMemoryLimit(math.MaxUint64),
  1504  	).run(
  1505  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1506  			// Create an account private key.
  1507  			privateKeys, err := testutil.GenerateAccountPrivateKeys(5)
  1508  			require.NoError(t, err)
  1509  
  1510  			// Bootstrap a ledger, creating accounts with the provided
  1511  			// private keys and the root account.
  1512  			snapshotTree, accounts, err := testutil.CreateAccounts(
  1513  				vm,
  1514  				snapshotTree,
  1515  				privateKeys,
  1516  				chain)
  1517  			require.NoError(t, err)
  1518  
  1519  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  1520  
  1521  			// create a transaction without loops so only the looping in the storage check is counted.
  1522  			txBody := flow.NewTransactionBody().
  1523  				SetScript([]byte(fmt.Sprintf(`
  1524  					import FungibleToken from 0x%s
  1525  					import FlowToken from 0x%s
  1526  	
  1527  					transaction() {
  1528  						let sentVault: @{FungibleToken.Vault}
  1529  	
  1530  						prepare(signer: auth(BorrowValue) &Account) {
  1531  							let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
  1532  								?? panic("Could not borrow reference to the owner's Vault!")
  1533  	
  1534  							self.sentVault <- vaultRef.withdraw(amount: 5.0)
  1535  						}
  1536  	
  1537  						execute {
  1538  							let recipient1 = getAccount(%s)
  1539  							let recipient2 = getAccount(%s)
  1540  							let recipient3 = getAccount(%s)
  1541  							let recipient4 = getAccount(%s)
  1542  							let recipient5 = getAccount(%s)
  1543  
  1544  							let receiverRef1 = recipient1.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1545  								?? panic("Could not borrow receiver reference to the recipient's Vault")
  1546  							let receiverRef2 = recipient2.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1547  								?? panic("Could not borrow receiver reference to the recipient's Vault")
  1548  							let receiverRef3 = recipient3.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1549  								?? panic("Could not borrow receiver reference to the recipient's Vault")
  1550  							let receiverRef4 = recipient4.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1551  								?? panic("Could not borrow receiver reference to the recipient's Vault")
  1552  							let receiverRef5 = recipient5.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1553  								?? panic("Could not borrow receiver reference to the recipient's Vault")
  1554  	
  1555  							receiverRef1.deposit(from: <-self.sentVault.withdraw(amount: 1.0))
  1556  							receiverRef2.deposit(from: <-self.sentVault.withdraw(amount: 1.0))
  1557  							receiverRef3.deposit(from: <-self.sentVault.withdraw(amount: 1.0))
  1558  							receiverRef4.deposit(from: <-self.sentVault.withdraw(amount: 1.0))
  1559  							receiverRef5.deposit(from: <-self.sentVault.withdraw(amount: 1.0))
  1560  
  1561  							destroy self.sentVault
  1562  						}
  1563  					}`,
  1564  					sc.FungibleToken.Address,
  1565  					sc.FlowToken.Address,
  1566  					accounts[0].HexWithPrefix(),
  1567  					accounts[1].HexWithPrefix(),
  1568  					accounts[2].HexWithPrefix(),
  1569  					accounts[3].HexWithPrefix(),
  1570  					accounts[4].HexWithPrefix(),
  1571  				))).
  1572  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1573  				AddAuthorizer(chain.ServiceAddress()).
  1574  				SetPayer(chain.ServiceAddress())
  1575  
  1576  			err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1577  			require.NoError(t, err)
  1578  
  1579  			_, output, err := vm.Run(
  1580  				ctx,
  1581  				fvm.Transaction(txBody, 0),
  1582  				snapshotTree)
  1583  			require.NoError(t, err)
  1584  			require.NoError(t, output.Err)
  1585  
  1586  			// The storage check should loop once for each of the five accounts created +
  1587  			// once for the service account
  1588  			require.Equal(t, uint64(5+1), output.ComputationUsed)
  1589  		},
  1590  	))
  1591  }
  1592  
  1593  func TestStorageUsed(t *testing.T) {
  1594  	t.Parallel()
  1595  
  1596  	chain, vm := createChainAndVm(flow.Testnet)
  1597  
  1598  	ctx := fvm.NewContext(
  1599  		fvm.WithChain(chain),
  1600  		fvm.WithCadenceLogging(true),
  1601  	)
  1602  
  1603  	code := []byte(`
  1604          access(all) fun main(): UInt64 {
  1605  
  1606              var addresses: [Address]= [
  1607                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1608                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1609                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1610                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1611                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1612                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1613                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1614                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1615                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1616                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1617                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731
  1618              ]
  1619  
  1620              var storageUsed: UInt64 = 0
  1621              for address in addresses {
  1622                  let account = getAccount(address)
  1623                  storageUsed = account.storage.used
  1624              }
  1625  
  1626              return storageUsed
  1627          }
  1628  	`)
  1629  
  1630  	address, err := hex.DecodeString("2a3c4c2581cef731")
  1631  	require.NoError(t, err)
  1632  
  1633  	accountStatusId := flow.AccountStatusRegisterID(
  1634  		flow.BytesToAddress(address))
  1635  
  1636  	status := environment.NewAccountStatus()
  1637  	status.SetStorageUsed(5)
  1638  
  1639  	_, output, err := vm.Run(
  1640  		ctx,
  1641  		fvm.Script(code),
  1642  		snapshot.MapStorageSnapshot{
  1643  			accountStatusId: status.ToBytes(),
  1644  		})
  1645  	require.NoError(t, err)
  1646  
  1647  	require.Equal(t, cadence.NewUInt64(5), output.Value)
  1648  }
  1649  
  1650  func TestEnforcingComputationLimit(t *testing.T) {
  1651  	t.Parallel()
  1652  
  1653  	chain, vm := createChainAndVm(flow.Testnet)
  1654  
  1655  	const computationLimit = 5
  1656  
  1657  	type test struct {
  1658  		name           string
  1659  		code           string
  1660  		payerIsServAcc bool
  1661  		ok             bool
  1662  		expCompUsed    uint64
  1663  	}
  1664  
  1665  	tests := []test{
  1666  		{
  1667  			name: "infinite while loop",
  1668  			code: `
  1669  		      while true {}
  1670  		    `,
  1671  			payerIsServAcc: false,
  1672  			ok:             false,
  1673  			expCompUsed:    computationLimit + 1,
  1674  		},
  1675  		{
  1676  			name: "limited while loop",
  1677  			code: `
  1678                var i = 0
  1679                while i < 5 {
  1680                    i = i + 1
  1681                }
  1682              `,
  1683  			payerIsServAcc: false,
  1684  			ok:             false,
  1685  			expCompUsed:    computationLimit + 1,
  1686  		},
  1687  		{
  1688  			name: "too many for-in loop iterations",
  1689  			code: `
  1690                for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {}
  1691              `,
  1692  			payerIsServAcc: false,
  1693  			ok:             false,
  1694  			expCompUsed:    computationLimit + 1,
  1695  		},
  1696  		{
  1697  			name: "too many for-in loop iterations",
  1698  			code: `
  1699                for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {}
  1700              `,
  1701  			payerIsServAcc: true,
  1702  			ok:             true,
  1703  			expCompUsed:    11,
  1704  		},
  1705  		{
  1706  			name: "some for-in loop iterations",
  1707  			code: `
  1708                for i in [1, 2, 3, 4] {}
  1709              `,
  1710  			payerIsServAcc: false,
  1711  			ok:             true,
  1712  			expCompUsed:    5,
  1713  		},
  1714  	}
  1715  
  1716  	for _, test := range tests {
  1717  
  1718  		t.Run(test.name, func(t *testing.T) {
  1719  			ctx := fvm.NewContext(
  1720  				fvm.WithChain(chain),
  1721  				fvm.WithAuthorizationChecksEnabled(false),
  1722  				fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
  1723  			)
  1724  
  1725  			script := []byte(
  1726  				fmt.Sprintf(
  1727  					`
  1728                        transaction {
  1729                            prepare() {
  1730                                %s
  1731                            }
  1732                        }
  1733                      `,
  1734  					test.code,
  1735  				),
  1736  			)
  1737  
  1738  			txBody := flow.NewTransactionBody().
  1739  				SetScript(script).
  1740  				SetComputeLimit(computationLimit)
  1741  
  1742  			if test.payerIsServAcc {
  1743  				txBody.SetPayer(chain.ServiceAddress()).
  1744  					SetComputeLimit(0)
  1745  			}
  1746  			tx := fvm.Transaction(txBody, 0)
  1747  
  1748  			_, output, err := vm.Run(ctx, tx, nil)
  1749  			require.NoError(t, err)
  1750  			require.Equal(t, test.expCompUsed, output.ComputationUsed)
  1751  			if test.ok {
  1752  				require.NoError(t, output.Err)
  1753  			} else {
  1754  				require.Error(t, output.Err)
  1755  			}
  1756  
  1757  		})
  1758  	}
  1759  }
  1760  
  1761  func TestStorageCapacity(t *testing.T) {
  1762  	t.Run("Storage capacity updates on FLOW transfer", newVMTest().
  1763  		withContextOptions(
  1764  			fvm.WithAuthorizationChecksEnabled(false),
  1765  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
  1766  			fvm.WithCadenceLogging(true),
  1767  		).
  1768  		withBootstrapProcedureOptions(
  1769  			fvm.WithStorageMBPerFLOW(10_0000_0000),
  1770  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1771  		).
  1772  		run(func(
  1773  			t *testing.T,
  1774  			vm fvm.VM,
  1775  			chain flow.Chain,
  1776  			ctx fvm.Context,
  1777  			snapshotTree snapshot.SnapshotTree,
  1778  		) {
  1779  			service := chain.ServiceAddress()
  1780  			snapshotTree, signer := createAccount(
  1781  				t,
  1782  				vm,
  1783  				chain,
  1784  				ctx,
  1785  				snapshotTree)
  1786  			snapshotTree, target := createAccount(
  1787  				t,
  1788  				vm,
  1789  				chain,
  1790  				ctx,
  1791  				snapshotTree)
  1792  
  1793  			// Transfer FLOW from service account to test accounts
  1794  
  1795  			transferTxBody := transferTokensTx(chain).
  1796  				AddAuthorizer(service).
  1797  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  1798  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(signer))).
  1799  				SetProposalKey(service, 0, 0).
  1800  				SetPayer(service)
  1801  
  1802  			executionSnapshot, output, err := vm.Run(
  1803  				ctx,
  1804  				fvm.Transaction(transferTxBody, 0),
  1805  				snapshotTree)
  1806  			require.NoError(t, err)
  1807  			require.NoError(t, output.Err)
  1808  
  1809  			snapshotTree = snapshotTree.Append(executionSnapshot)
  1810  
  1811  			transferTxBody = transferTokensTx(chain).
  1812  				AddAuthorizer(service).
  1813  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  1814  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))).
  1815  				SetProposalKey(service, 0, 0).
  1816  				SetPayer(service)
  1817  
  1818  			executionSnapshot, output, err = vm.Run(
  1819  				ctx,
  1820  				fvm.Transaction(transferTxBody, 0),
  1821  				snapshotTree)
  1822  			require.NoError(t, err)
  1823  			require.NoError(t, output.Err)
  1824  
  1825  			snapshotTree = snapshotTree.Append(executionSnapshot)
  1826  
  1827  			// Perform test
  1828  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  1829  
  1830  			txBody := flow.NewTransactionBody().
  1831  				SetScript([]byte(fmt.Sprintf(
  1832  					`
  1833  					import FungibleToken from 0x%s
  1834  					import FlowToken from 0x%s
  1835  
  1836  					transaction(target: Address) {
  1837  						prepare(signer: auth(BorrowValue) &Account) {
  1838  							let receiverRef = getAccount(target)
  1839  								.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
  1840  								?? panic("Could not borrow receiver reference to the recipient''s Vault")
  1841  
  1842  							let vaultRef = signer.storage
  1843  								.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
  1844  								?? panic("Could not borrow reference to the owner''s Vault!")
  1845  
  1846  							var cap0: UInt64 = signer.storage.capacity
  1847  
  1848  							receiverRef.deposit(from: <- vaultRef.withdraw(amount: 0.0000001))
  1849  
  1850  							var cap1: UInt64 = signer.storage.capacity
  1851  
  1852  							log(cap0 - cap1)
  1853  						}
  1854  					}`,
  1855  					sc.FungibleToken.Address.Hex(),
  1856  					sc.FlowToken.Address.Hex(),
  1857  				))).
  1858  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))).
  1859  				AddAuthorizer(signer)
  1860  
  1861  			_, output, err = vm.Run(
  1862  				ctx,
  1863  				fvm.Transaction(txBody, 0),
  1864  				snapshotTree)
  1865  			require.NoError(t, err)
  1866  			require.NoError(t, output.Err)
  1867  
  1868  			require.Len(t, output.Logs, 1)
  1869  			require.Equal(t, output.Logs[0], "1")
  1870  		}),
  1871  	)
  1872  }
  1873  
  1874  func TestScriptContractMutationsFailure(t *testing.T) {
  1875  	t.Parallel()
  1876  
  1877  	t.Run("contract additions are not committed",
  1878  		newVMTest().run(
  1879  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1880  				// Create an account private key.
  1881  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1882  				require.NoError(t, err)
  1883  
  1884  				// Bootstrap a ledger, creating accounts with the provided
  1885  				// private keys and the root account.
  1886  				snapshotTree, accounts, err := testutil.CreateAccounts(
  1887  					vm,
  1888  					snapshotTree,
  1889  					privateKeys,
  1890  					chain)
  1891  				require.NoError(t, err)
  1892  				account := accounts[0]
  1893  				address := cadence.NewAddress(account)
  1894  
  1895  				scriptCtx := fvm.NewContextFromParent(ctx)
  1896  
  1897  				contract := "access(all) contract Foo {}"
  1898  
  1899  				script := fvm.Script([]byte(fmt.Sprintf(`
  1900  				access(all) fun main(account: Address) {
  1901  					let acc = getAuthAccount<auth(AddContract) &Account>(account)
  1902  					acc.contracts.add(name: "Foo", code: "%s".decodeHex())
  1903  				}`, hex.EncodeToString([]byte(contract))),
  1904  				)).WithArguments(
  1905  					jsoncdc.MustEncode(address),
  1906  				)
  1907  
  1908  				_, output, err := vm.Run(scriptCtx, script, snapshotTree)
  1909  				require.NoError(t, err)
  1910  				require.Error(t, output.Err)
  1911  				require.True(t, errors.IsCadenceRuntimeError(output.Err))
  1912  				// modifications to contracts are not supported in scripts
  1913  				require.True(t, errors.IsOperationNotSupportedError(output.Err))
  1914  			},
  1915  		),
  1916  	)
  1917  
  1918  	t.Run("contract removals are not committed",
  1919  		newVMTest().run(
  1920  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1921  				// Create an account private key.
  1922  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1923  				privateKey := privateKeys[0]
  1924  				require.NoError(t, err)
  1925  
  1926  				// Bootstrap a ledger, creating accounts with the provided
  1927  				// private keys and the root account.
  1928  				snapshotTree, accounts, err := testutil.CreateAccounts(
  1929  					vm,
  1930  					snapshotTree,
  1931  					privateKeys,
  1932  					chain)
  1933  				require.NoError(t, err)
  1934  				account := accounts[0]
  1935  				address := cadence.NewAddress(account)
  1936  
  1937  				subCtx := fvm.NewContextFromParent(ctx)
  1938  
  1939  				contract := "access(all) contract Foo {}"
  1940  
  1941  				txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(`
  1942  					transaction {
  1943  						prepare(signer: auth(AddContract) &Account, service: &Account) {
  1944  							signer.contracts.add(name: "Foo", code: "%s".decodeHex())
  1945  						}
  1946  					}
  1947  				`, hex.EncodeToString([]byte(contract))))).
  1948  					AddAuthorizer(account).
  1949  					AddAuthorizer(chain.ServiceAddress()).
  1950  					SetPayer(chain.ServiceAddress()).
  1951  					SetProposalKey(chain.ServiceAddress(), 0, 0)
  1952  
  1953  				_ = testutil.SignPayload(txBody, account, privateKey)
  1954  				_ = testutil.SignEnvelope(
  1955  					txBody,
  1956  					chain.ServiceAddress(),
  1957  					unittest.ServiceAccountPrivateKey)
  1958  
  1959  				executionSnapshot, output, err := vm.Run(
  1960  					subCtx,
  1961  					fvm.Transaction(txBody, 0),
  1962  					snapshotTree)
  1963  				require.NoError(t, err)
  1964  				require.NoError(t, output.Err)
  1965  
  1966  				snapshotTree = snapshotTree.Append(executionSnapshot)
  1967  
  1968  				script := fvm.Script([]byte(`
  1969  				access(all) fun main(account: Address) {
  1970  					let acc = getAuthAccount<auth(RemoveContract) &Account>(account)
  1971  					let n = acc.contracts.names[0]
  1972  					acc.contracts.remove(name: n)
  1973  				}`,
  1974  				)).WithArguments(
  1975  					jsoncdc.MustEncode(address),
  1976  				)
  1977  
  1978  				_, output, err = vm.Run(subCtx, script, snapshotTree)
  1979  				require.NoError(t, err)
  1980  				require.Error(t, output.Err)
  1981  				require.True(t, errors.IsCadenceRuntimeError(output.Err))
  1982  				// modifications to contracts are not supported in scripts
  1983  				require.True(t, errors.IsOperationNotSupportedError(output.Err))
  1984  			},
  1985  		),
  1986  	)
  1987  
  1988  	t.Run("contract updates are not committed",
  1989  		newVMTest().run(
  1990  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  1991  				// Create an account private key.
  1992  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1993  				privateKey := privateKeys[0]
  1994  				require.NoError(t, err)
  1995  
  1996  				// Bootstrap a ledger, creating accounts with the provided
  1997  				// private keys and the root account.
  1998  				snapshotTree, accounts, err := testutil.CreateAccounts(
  1999  					vm,
  2000  					snapshotTree,
  2001  					privateKeys,
  2002  					chain)
  2003  				require.NoError(t, err)
  2004  				account := accounts[0]
  2005  				address := cadence.NewAddress(account)
  2006  
  2007  				subCtx := fvm.NewContextFromParent(ctx)
  2008  
  2009  				contract := "access(all) contract Foo {}"
  2010  
  2011  				txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(`
  2012  					transaction {
  2013  						prepare(signer: auth(AddContract) &Account, service: &Account) {
  2014  							signer.contracts.add(name: "Foo", code: "%s".decodeHex())
  2015  						}
  2016  					}
  2017  				`, hex.EncodeToString([]byte(contract))))).
  2018  					AddAuthorizer(account).
  2019  					AddAuthorizer(chain.ServiceAddress()).
  2020  					SetPayer(chain.ServiceAddress()).
  2021  					SetProposalKey(chain.ServiceAddress(), 0, 0)
  2022  
  2023  				_ = testutil.SignPayload(txBody, account, privateKey)
  2024  				_ = testutil.SignEnvelope(
  2025  					txBody,
  2026  					chain.ServiceAddress(),
  2027  					unittest.ServiceAccountPrivateKey)
  2028  
  2029  				executionSnapshot, output, err := vm.Run(
  2030  					subCtx,
  2031  					fvm.Transaction(txBody, 0),
  2032  					snapshotTree)
  2033  				require.NoError(t, err)
  2034  				require.NoError(t, output.Err)
  2035  
  2036  				snapshotTree = snapshotTree.Append(executionSnapshot)
  2037  
  2038  				script := fvm.Script([]byte(fmt.Sprintf(`
  2039  				access(all) fun main(account: Address) {
  2040  					let acc = getAuthAccount<auth(UpdateContract) &Account>(account)
  2041  					let n = acc.contracts.names[0]
  2042  					acc.contracts.update(name: n, code: "%s".decodeHex())
  2043  				}`, hex.EncodeToString([]byte(contract))))).WithArguments(
  2044  					jsoncdc.MustEncode(address),
  2045  				)
  2046  
  2047  				_, output, err = vm.Run(subCtx, script, snapshotTree)
  2048  				require.NoError(t, err)
  2049  				require.Error(t, output.Err)
  2050  				require.True(t, errors.IsCadenceRuntimeError(output.Err))
  2051  				// modifications to contracts are not supported in scripts
  2052  				require.True(t, errors.IsOperationNotSupportedError(output.Err))
  2053  			},
  2054  		),
  2055  	)
  2056  }
  2057  
  2058  func TestScriptAccountKeyMutationsFailure(t *testing.T) {
  2059  	t.Parallel()
  2060  
  2061  	t.Run("Account key additions are not committed",
  2062  		newVMTest().run(
  2063  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  2064  				// Create an account private key.
  2065  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  2066  				require.NoError(t, err)
  2067  
  2068  				// Bootstrap a ledger, creating accounts with the provided
  2069  				// private keys and the root account.
  2070  				snapshotTree, accounts, err := testutil.CreateAccounts(
  2071  					vm,
  2072  					snapshotTree,
  2073  					privateKeys,
  2074  					chain)
  2075  				require.NoError(t, err)
  2076  				account := accounts[0]
  2077  				address := cadence.NewAddress(account)
  2078  
  2079  				scriptCtx := fvm.NewContextFromParent(ctx)
  2080  
  2081  				seed := make([]byte, crypto.KeyGenSeedMinLen)
  2082  				_, _ = rand.Read(seed)
  2083  
  2084  				privateKey, _ := crypto.GeneratePrivateKey(crypto.ECDSAP256, seed)
  2085  
  2086  				script := fvm.Script([]byte(`
  2087  					access(all) fun main(account: Address, k: [UInt8]) {
  2088  						let acc = getAuthAccount<auth(AddKey) &Account>(account)
  2089  						acc.keys.add(
  2090  							publicKey: PublicKey(
  2091                                  publicKey: k,
  2092                                  signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
  2093                              ),
  2094                              hashAlgorithm: HashAlgorithm.SHA3_256,
  2095                              weight: 100.0
  2096  						)
  2097  					}`,
  2098  				)).WithArguments(
  2099  					jsoncdc.MustEncode(address),
  2100  					jsoncdc.MustEncode(testutil.BytesToCadenceArray(
  2101  						privateKey.PublicKey().Encode(),
  2102  					)),
  2103  				)
  2104  
  2105  				_, output, err := vm.Run(scriptCtx, script, snapshotTree)
  2106  				require.NoError(t, err)
  2107  				require.Error(t, output.Err)
  2108  				require.True(t, errors.IsCadenceRuntimeError(output.Err))
  2109  				// modifications to public keys are not supported in scripts
  2110  				require.True(t, errors.IsOperationNotSupportedError(output.Err))
  2111  			},
  2112  		),
  2113  	)
  2114  
  2115  	t.Run("Account key removals are not committed",
  2116  		newVMTest().run(
  2117  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  2118  				// Create an account private key.
  2119  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  2120  				require.NoError(t, err)
  2121  
  2122  				// Bootstrap a ledger, creating accounts with the provided
  2123  				// private keys and the root account.
  2124  				snapshotTree, accounts, err := testutil.CreateAccounts(
  2125  					vm,
  2126  					snapshotTree,
  2127  					privateKeys,
  2128  					chain)
  2129  				require.NoError(t, err)
  2130  				account := accounts[0]
  2131  				address := cadence.NewAddress(account)
  2132  
  2133  				scriptCtx := fvm.NewContextFromParent(ctx)
  2134  
  2135  				script := fvm.Script([]byte(`
  2136  				access(all) fun main(account: Address) {
  2137  					let acc = getAuthAccount<auth(RevokeKey) &Account>(account)
  2138  					acc.keys.revoke(keyIndex: 0)
  2139  				}`,
  2140  				)).WithArguments(
  2141  					jsoncdc.MustEncode(address),
  2142  				)
  2143  
  2144  				_, output, err := vm.Run(scriptCtx, script, snapshotTree)
  2145  				require.NoError(t, err)
  2146  				require.Error(t, output.Err)
  2147  				require.True(t, errors.IsCadenceRuntimeError(output.Err))
  2148  				// modifications to public keys are not supported in scripts
  2149  				require.True(t, errors.IsOperationNotSupportedError(output.Err))
  2150  			},
  2151  		),
  2152  	)
  2153  }
  2154  
  2155  func TestScriptExecutionLimit(t *testing.T) {
  2156  
  2157  	t.Parallel()
  2158  
  2159  	script := fvm.Script([]byte(`
  2160  		access(all) fun main() {
  2161  			var s: Int256 = 1024102410241024
  2162  			var i: Int256 = 0
  2163  			var a: Int256 = 7
  2164  			var b: Int256 = 5
  2165  			var c: Int256 = 2
  2166  
  2167  			while i < 150000 {
  2168  				s = s * a
  2169  				s = s / b
  2170  				s = s / c
  2171  				i = i + 1
  2172  			}
  2173  		}
  2174  	`))
  2175  
  2176  	bootstrapProcedureOptions := []fvm.BootstrapProcedureOption{
  2177  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  2178  		fvm.WithExecutionMemoryLimit(math.MaxUint32),
  2179  		fvm.WithExecutionEffortWeights(map[common.ComputationKind]uint64{
  2180  			common.ComputationKindStatement:          1569,
  2181  			common.ComputationKindLoop:               1569,
  2182  			common.ComputationKindFunctionInvocation: 1569,
  2183  			environment.ComputationKindGetValue:      808,
  2184  			environment.ComputationKindCreateAccount: 2837670,
  2185  			environment.ComputationKindSetValue:      765,
  2186  		}),
  2187  		fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
  2188  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  2189  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  2190  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  2191  	}
  2192  
  2193  	t.Run("Exceeding computation limit",
  2194  		newVMTest().withBootstrapProcedureOptions(
  2195  			bootstrapProcedureOptions...,
  2196  		).withContextOptions(
  2197  			fvm.WithTransactionFeesEnabled(true),
  2198  			fvm.WithAccountStorageLimit(true),
  2199  			fvm.WithComputationLimit(10000),
  2200  		).run(
  2201  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  2202  				scriptCtx := fvm.NewContextFromParent(ctx)
  2203  
  2204  				_, output, err := vm.Run(scriptCtx, script, snapshotTree)
  2205  				require.NoError(t, err)
  2206  				require.Error(t, output.Err)
  2207  				require.True(t, errors.IsComputationLimitExceededError(output.Err))
  2208  				require.ErrorContains(t, output.Err, "computation exceeds limit (10000)")
  2209  				require.GreaterOrEqual(t, output.ComputationUsed, uint64(10000))
  2210  				require.GreaterOrEqual(t, output.MemoryEstimate, uint64(548020260))
  2211  			},
  2212  		),
  2213  	)
  2214  
  2215  	t.Run("Sufficient computation limit",
  2216  		newVMTest().withBootstrapProcedureOptions(
  2217  			bootstrapProcedureOptions...,
  2218  		).withContextOptions(
  2219  			fvm.WithTransactionFeesEnabled(true),
  2220  			fvm.WithAccountStorageLimit(true),
  2221  			fvm.WithComputationLimit(20000),
  2222  		).run(
  2223  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  2224  				scriptCtx := fvm.NewContextFromParent(ctx)
  2225  
  2226  				_, output, err := vm.Run(scriptCtx, script, snapshotTree)
  2227  				require.NoError(t, err)
  2228  				require.NoError(t, output.Err)
  2229  				require.GreaterOrEqual(t, output.ComputationUsed, uint64(17955))
  2230  				require.GreaterOrEqual(t, output.MemoryEstimate, uint64(984017413))
  2231  			},
  2232  		),
  2233  	)
  2234  }
  2235  
  2236  func TestInteractionLimit(t *testing.T) {
  2237  	type testCase struct {
  2238  		name             string
  2239  		interactionLimit uint64
  2240  		require          func(t *testing.T, output fvm.ProcedureOutput)
  2241  	}
  2242  
  2243  	testCases := []testCase{
  2244  		{
  2245  			name:             "high limit succeeds",
  2246  			interactionLimit: math.MaxUint64,
  2247  			require: func(t *testing.T, output fvm.ProcedureOutput) {
  2248  				require.NoError(t, output.Err)
  2249  				require.Len(t, output.Events, 9)
  2250  			},
  2251  		},
  2252  		{
  2253  			name:             "default limit succeeds",
  2254  			interactionLimit: fvm.DefaultMaxInteractionSize,
  2255  			require: func(t *testing.T, output fvm.ProcedureOutput) {
  2256  				require.NoError(t, output.Err)
  2257  				require.Len(t, output.Events, 9)
  2258  				unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID())
  2259  			},
  2260  		},
  2261  		{
  2262  			name:             "low limit succeeds",
  2263  			interactionLimit: 170000,
  2264  			require: func(t *testing.T, output fvm.ProcedureOutput) {
  2265  				require.NoError(t, output.Err)
  2266  				require.Len(t, output.Events, 9)
  2267  				unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID())
  2268  			},
  2269  		},
  2270  		{
  2271  			name:             "even lower low limit fails, and has only 5 events",
  2272  			interactionLimit: 5000,
  2273  			require: func(t *testing.T, output fvm.ProcedureOutput) {
  2274  				require.Error(t, output.Err)
  2275  				require.Len(t, output.Events, 5)
  2276  				unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID())
  2277  			},
  2278  		},
  2279  	}
  2280  
  2281  	// === setup ===
  2282  	// setup an address with some funds
  2283  	var privateKey flow.AccountPrivateKey
  2284  	var address flow.Address
  2285  	vmt, err := newVMTest().withBootstrapProcedureOptions(
  2286  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  2287  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  2288  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  2289  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  2290  		fvm.WithExecutionMemoryLimit(math.MaxUint64),
  2291  	).withContextOptions(
  2292  		fvm.WithTransactionFeesEnabled(true),
  2293  		fvm.WithAccountStorageLimit(true),
  2294  	).bootstrapWith(
  2295  		func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) (snapshot.SnapshotTree, error) {
  2296  			// ==== Create an account ====
  2297  			var txBody *flow.TransactionBody
  2298  			privateKey, txBody = testutil.CreateAccountCreationTransaction(t, chain)
  2299  
  2300  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2301  			if err != nil {
  2302  				return snapshotTree, err
  2303  			}
  2304  
  2305  			executionSnapshot, output, err := vm.Run(
  2306  				ctx,
  2307  				fvm.Transaction(txBody, 0),
  2308  				snapshotTree)
  2309  			if err != nil {
  2310  				return snapshotTree, err
  2311  			}
  2312  
  2313  			snapshotTree = snapshotTree.Append(executionSnapshot)
  2314  
  2315  			if output.Err != nil {
  2316  				return snapshotTree, output.Err
  2317  			}
  2318  
  2319  			accountCreatedEvents := filterAccountCreatedEvents(output.Events)
  2320  
  2321  			// read the address of the account created (e.g. "0x01" and convert it to flow.address)
  2322  			data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload)
  2323  			if err != nil {
  2324  				return snapshotTree, err
  2325  			}
  2326  
  2327  			address = flow.ConvertAddress(
  2328  				cadence.SearchFieldByName(
  2329  					data.(cadence.Event),
  2330  					stdlib2.AccountEventAddressParameter.Identifier,
  2331  				).(cadence.Address),
  2332  			)
  2333  
  2334  			// ==== Transfer tokens to new account ====
  2335  			txBody = transferTokensTx(chain).
  2336  				AddAuthorizer(chain.ServiceAddress()).
  2337  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  2338  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address)))
  2339  
  2340  			txBody.SetProposalKey(chain.ServiceAddress(), 0, 1)
  2341  			txBody.SetPayer(chain.ServiceAddress())
  2342  
  2343  			err = testutil.SignEnvelope(
  2344  				txBody,
  2345  				chain.ServiceAddress(),
  2346  				unittest.ServiceAccountPrivateKey,
  2347  			)
  2348  			if err != nil {
  2349  				return snapshotTree, err
  2350  			}
  2351  
  2352  			executionSnapshot, output, err = vm.Run(
  2353  				ctx,
  2354  				fvm.Transaction(txBody, 0),
  2355  				snapshotTree)
  2356  			if err != nil {
  2357  				return snapshotTree, err
  2358  			}
  2359  
  2360  			return snapshotTree.Append(executionSnapshot), output.Err
  2361  		},
  2362  	)
  2363  	require.NoError(t, err)
  2364  
  2365  	for _, tc := range testCases {
  2366  		t.Run(tc.name, vmt.run(
  2367  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) {
  2368  				// ==== Transfer funds with lowe interaction limit ====
  2369  				txBody := transferTokensTx(chain).
  2370  					AddAuthorizer(address).
  2371  					AddArgument(jsoncdc.MustEncode(cadence.UFix64(1))).
  2372  					AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress())))
  2373  
  2374  				txBody.SetProposalKey(address, 0, 0)
  2375  				txBody.SetPayer(address)
  2376  
  2377  				hasher, err := exeUtils.NewHasher(privateKey.HashAlgo)
  2378  				require.NoError(t, err)
  2379  
  2380  				sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher)
  2381  				require.NoError(t, err)
  2382  				txBody.AddEnvelopeSignature(address, 0, sig)
  2383  
  2384  				// ==== IMPORTANT LINE ====
  2385  				ctx.MaxStateInteractionSize = tc.interactionLimit
  2386  
  2387  				_, output, err := vm.Run(
  2388  					ctx,
  2389  					fvm.Transaction(txBody, 0),
  2390  					snapshotTree)
  2391  				require.NoError(t, err)
  2392  				tc.require(t, output)
  2393  			}),
  2394  		)
  2395  	}
  2396  }
  2397  
  2398  func TestAttachments(t *testing.T) {
  2399  
  2400  	newVMTest().
  2401  		withBootstrapProcedureOptions().
  2402  		withContextOptions(
  2403  			fvm.WithReusableCadenceRuntimePool(
  2404  				reusableRuntime.NewReusableCadenceRuntimePool(
  2405  					1,
  2406  					runtime.Config{
  2407  						AttachmentsEnabled: true,
  2408  					},
  2409  				),
  2410  			),
  2411  		).
  2412  		run(
  2413  			func(
  2414  				t *testing.T,
  2415  				vm fvm.VM,
  2416  				chain flow.Chain,
  2417  				ctx fvm.Context,
  2418  				snapshotTree snapshot.SnapshotTree,
  2419  			) {
  2420  				script := fvm.Script([]byte(`
  2421  
  2422  						access(all) resource R {}
  2423  
  2424  						access(all) attachment A for R {}
  2425  
  2426  						access(all) fun main() {
  2427  							let r <- create R()
  2428  							r[A]
  2429  							destroy r
  2430  						}
  2431  					`))
  2432  
  2433  				_, output, err := vm.Run(ctx, script, snapshotTree)
  2434  				require.NoError(t, err)
  2435  				require.NoError(t, output.Err)
  2436  
  2437  			},
  2438  		)(t)
  2439  
  2440  }
  2441  
  2442  func TestCapabilityControllers(t *testing.T) {
  2443  	test := func(t *testing.T) {
  2444  		newVMTest().
  2445  			withBootstrapProcedureOptions().
  2446  			withContextOptions(
  2447  				fvm.WithReusableCadenceRuntimePool(
  2448  					reusableRuntime.NewReusableCadenceRuntimePool(
  2449  						1,
  2450  						runtime.Config{},
  2451  					),
  2452  				),
  2453  			).
  2454  			run(func(
  2455  				t *testing.T,
  2456  				vm fvm.VM,
  2457  				chain flow.Chain,
  2458  				ctx fvm.Context,
  2459  				snapshotTree snapshot.SnapshotTree,
  2460  			) {
  2461  				txBody := flow.NewTransactionBody().
  2462  					SetScript([]byte(`
  2463  						transaction {
  2464  						  prepare(signer: auth(Capabilities) &Account) {
  2465  							let cap = signer.capabilities.storage.issue<&Int>(/storage/foo)
  2466  							assert(cap.id == 6)
  2467  
  2468  							let cap2 = signer.capabilities.storage.issue<&String>(/storage/bar)
  2469  							assert(cap2.id == 7)
  2470  						  }
  2471  						}
  2472  					`)).
  2473  					SetProposalKey(chain.ServiceAddress(), 0, 0).
  2474  					AddAuthorizer(chain.ServiceAddress()).
  2475  					SetPayer(chain.ServiceAddress())
  2476  
  2477  				err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2478  				require.NoError(t, err)
  2479  
  2480  				_, output, err := vm.Run(
  2481  					ctx,
  2482  					fvm.Transaction(txBody, 0),
  2483  					snapshotTree)
  2484  				require.NoError(t, err)
  2485  				require.NoError(t, output.Err)
  2486  			},
  2487  			)(t)
  2488  	}
  2489  
  2490  	test(t)
  2491  
  2492  }
  2493  
  2494  func TestStorageIterationWithBrokenValues(t *testing.T) {
  2495  
  2496  	t.Parallel()
  2497  
  2498  	newVMTest().
  2499  		withBootstrapProcedureOptions().
  2500  		withContextOptions(
  2501  			fvm.WithReusableCadenceRuntimePool(
  2502  				reusableRuntime.NewReusableCadenceRuntimePool(
  2503  					1,
  2504  					runtime.Config{},
  2505  				),
  2506  			),
  2507  			fvm.WithContractDeploymentRestricted(false),
  2508  		).
  2509  		run(
  2510  			func(
  2511  				t *testing.T,
  2512  				vm fvm.VM,
  2513  				chain flow.Chain,
  2514  				ctx fvm.Context,
  2515  				snapshotTree snapshot.SnapshotTree,
  2516  			) {
  2517  				// Create a private key
  2518  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  2519  				require.NoError(t, err)
  2520  
  2521  				// Bootstrap a ledger, creating an account with the provided private key and the root account.
  2522  				snapshotTree, accounts, err := testutil.CreateAccounts(
  2523  					vm,
  2524  					snapshotTree,
  2525  					privateKeys,
  2526  					chain,
  2527  				)
  2528  				require.NoError(t, err)
  2529  
  2530  				contractA := `
  2531  				    access(all) contract A {
  2532  						access(all) struct interface Foo{}
  2533  					}
  2534  				`
  2535  
  2536  				updatedContractA := `
  2537  				    access(all) contract A {
  2538  						access(all) struct interface Foo{
  2539  							access(all) fun hello()
  2540  						}
  2541  					}
  2542  				`
  2543  
  2544  				contractB := fmt.Sprintf(`
  2545  				    import A from %s
  2546  
  2547  				    access(all) contract B {
  2548  						access(all) struct Bar : A.Foo {}
  2549  
  2550  						access(all) struct interface Foo2{}
  2551  					}`,
  2552  					accounts[0].HexWithPrefix(),
  2553  				)
  2554  
  2555  				contractC := fmt.Sprintf(`
  2556  				    import B from %s
  2557  				    import A from %s
  2558  
  2559  				    access(all) contract C {
  2560  						access(all) struct Bar : A.Foo, B.Foo2 {}
  2561  
  2562  						access(all) struct interface Foo3{}
  2563  					}`,
  2564  					accounts[0].HexWithPrefix(),
  2565  					accounts[0].HexWithPrefix(),
  2566  				)
  2567  
  2568  				contractD := fmt.Sprintf(`
  2569  				    import C from %s
  2570  				    import B from %s
  2571  				    import A from %s
  2572  
  2573  				    access(all) contract D {
  2574  						access(all) struct Bar : A.Foo, B.Foo2, C.Foo3 {}
  2575  					}`,
  2576  					accounts[0].HexWithPrefix(),
  2577  					accounts[0].HexWithPrefix(),
  2578  					accounts[0].HexWithPrefix(),
  2579  				)
  2580  
  2581  				var sequenceNumber uint64 = 0
  2582  
  2583  				runTransaction := func(code []byte) {
  2584  					txBody := flow.NewTransactionBody().
  2585  						SetScript(code).
  2586  						SetPayer(chain.ServiceAddress()).
  2587  						SetProposalKey(chain.ServiceAddress(), 0, sequenceNumber).
  2588  						AddAuthorizer(accounts[0])
  2589  
  2590  					_ = testutil.SignPayload(txBody, accounts[0], privateKeys[0])
  2591  					_ = testutil.SignEnvelope(txBody, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
  2592  
  2593  					executionSnapshot, output, err := vm.Run(
  2594  						ctx,
  2595  						fvm.Transaction(txBody, 0),
  2596  						snapshotTree,
  2597  					)
  2598  					require.NoError(t, err)
  2599  					require.NoError(t, output.Err)
  2600  
  2601  					snapshotTree = snapshotTree.Append(executionSnapshot)
  2602  
  2603  					// increment sequence number
  2604  					sequenceNumber++
  2605  				}
  2606  
  2607  				// Deploy `A`
  2608  				runTransaction(utils.DeploymentTransaction(
  2609  					"A",
  2610  					[]byte(contractA),
  2611  				))
  2612  
  2613  				// Deploy `B`
  2614  				runTransaction(utils.DeploymentTransaction(
  2615  					"B",
  2616  					[]byte(contractB),
  2617  				))
  2618  
  2619  				// Deploy `C`
  2620  				runTransaction(utils.DeploymentTransaction(
  2621  					"C",
  2622  					[]byte(contractC),
  2623  				))
  2624  
  2625  				// Deploy `D`
  2626  				runTransaction(utils.DeploymentTransaction(
  2627  					"D",
  2628  					[]byte(contractD),
  2629  				))
  2630  
  2631  				// Store values
  2632  				runTransaction([]byte(fmt.Sprintf(
  2633  					`
  2634  					import D from %s
  2635  					import C from %s
  2636  					import B from %s
  2637  
  2638  					transaction {
  2639  						prepare(signer: auth(Capabilities, Storage) &Account) {
  2640  							signer.storage.save("Hello, World!", to: /storage/a)
  2641  							signer.storage.save(["one", "two", "three"], to: /storage/b)
  2642  							signer.storage.save(D.Bar(), to: /storage/c)
  2643  							signer.storage.save(C.Bar(), to: /storage/d)
  2644  							signer.storage.save(B.Bar(), to: /storage/e)
  2645  
  2646  							let aCap = signer.capabilities.storage.issue<&String>(/storage/a)
  2647  							signer.capabilities.publish(aCap, at: /public/a)
  2648  
  2649  							let bCap = signer.capabilities.storage.issue<&[String]>(/storage/b)
  2650  							signer.capabilities.publish(bCap, at: /public/b)
  2651  
  2652  							let cCap = signer.capabilities.storage.issue<&D.Bar>(/storage/c)
  2653  							signer.capabilities.publish(cCap, at: /public/c)
  2654  
  2655  							let dCap = signer.capabilities.storage.issue<&C.Bar>(/storage/d)
  2656  							signer.capabilities.publish(dCap, at: /public/d)
  2657  
  2658  							let eCap = signer.capabilities.storage.issue<&B.Bar>(/storage/e)
  2659  							signer.capabilities.publish(eCap, at: /public/e)
  2660  						}
  2661  					}`,
  2662  					accounts[0].HexWithPrefix(),
  2663  					accounts[0].HexWithPrefix(),
  2664  					accounts[0].HexWithPrefix(),
  2665  				)))
  2666  
  2667  				// Update `A`, such that `B`, `C` and `D` are now broken.
  2668  				runTransaction(utils.UpdateTransaction(
  2669  					"A",
  2670  					[]byte(updatedContractA),
  2671  				))
  2672  
  2673  				// Iterate stored values
  2674  				runTransaction([]byte(
  2675  					`
  2676  					transaction {
  2677  						prepare(account: auth(Storage) &Account) {
  2678  							var total = 0
  2679  							account.storage.forEachPublic(fun (path: PublicPath, type: Type): Bool {
  2680  								let cap = account.capabilities.get<&AnyStruct>(path)
  2681  								if cap.check() {
  2682  									total = total + 1
  2683  								}
  2684                                  return true
  2685  							})
  2686  							assert(total == 2, message:"found ".concat(total.toString()))
  2687  
  2688  							total = 0
  2689  							account.storage.forEachStored(fun (path: StoragePath, type: Type): Bool {
  2690  								if account.storage.check<AnyStruct>(from: path) {
  2691  								    account.storage.copy<AnyStruct>(from: path)
  2692  								    total = total + 1
  2693  								}
  2694                                  return true
  2695  							})
  2696  
  2697  							assert(total == 2, message:"found ".concat(total.toString()))
  2698  						}
  2699  					}`,
  2700  				))
  2701  			},
  2702  		)(t)
  2703  }
  2704  
  2705  func TestEntropyCallOnlyOkIfAllowed(t *testing.T) {
  2706  	source := testutil.EntropyProviderFixture(nil)
  2707  
  2708  	test := func(t *testing.T, allowed bool) {
  2709  		newVMTest().
  2710  			withBootstrapProcedureOptions().
  2711  			withContextOptions(
  2712  				fvm.WithRandomSourceHistoryCallAllowed(allowed),
  2713  				fvm.WithEntropyProvider(source),
  2714  			).
  2715  			run(func(
  2716  				t *testing.T,
  2717  				vm fvm.VM,
  2718  				chain flow.Chain,
  2719  				ctx fvm.Context,
  2720  				snapshotTree snapshot.SnapshotTree,
  2721  			) {
  2722  				txBody := flow.NewTransactionBody().
  2723  					SetScript([]byte(`
  2724  						transaction {
  2725  						  prepare() {
  2726  							randomSourceHistory()
  2727  						  }
  2728  						}
  2729  					`)).
  2730  					SetProposalKey(chain.ServiceAddress(), 0, 0).
  2731  					SetPayer(chain.ServiceAddress())
  2732  
  2733  				err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2734  				require.NoError(t, err)
  2735  
  2736  				_, output, err := vm.Run(
  2737  					ctx,
  2738  					fvm.Transaction(txBody, 0),
  2739  					snapshotTree)
  2740  				require.NoError(t, err)
  2741  
  2742  				if allowed {
  2743  					require.NoError(t, output.Err)
  2744  				} else {
  2745  					require.Error(t, output.Err)
  2746  					require.True(t, errors.HasErrorCode(output.Err, errors.ErrCodeOperationNotSupportedError))
  2747  				}
  2748  			},
  2749  			)(t)
  2750  	}
  2751  
  2752  	t.Run("enabled", func(t *testing.T) {
  2753  		test(t, true)
  2754  	})
  2755  
  2756  	t.Run("disabled", func(t *testing.T) {
  2757  		test(t, false)
  2758  	})
  2759  }
  2760  
  2761  func TestEntropyCallExpectsNoParameters(t *testing.T) {
  2762  	source := testutil.EntropyProviderFixture(nil)
  2763  	newVMTest().
  2764  		withBootstrapProcedureOptions().
  2765  		withContextOptions(
  2766  			fvm.WithRandomSourceHistoryCallAllowed(true),
  2767  			fvm.WithEntropyProvider(source),
  2768  		).
  2769  		run(func(
  2770  			t *testing.T,
  2771  			vm fvm.VM,
  2772  			chain flow.Chain,
  2773  			ctx fvm.Context,
  2774  			snapshotTree snapshot.SnapshotTree,
  2775  		) {
  2776  			txBody := flow.NewTransactionBody().
  2777  				SetScript([]byte(`
  2778  						transaction {
  2779  						  prepare() {
  2780  							randomSourceHistory("foo")
  2781  						  }
  2782  						}
  2783  					`)).
  2784  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  2785  				SetPayer(chain.ServiceAddress())
  2786  
  2787  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2788  			require.NoError(t, err)
  2789  
  2790  			_, output, err := vm.Run(
  2791  				ctx,
  2792  				fvm.Transaction(txBody, 0),
  2793  				snapshotTree)
  2794  			require.NoError(t, err)
  2795  
  2796  			require.ErrorContains(t, output.Err, "too many arguments")
  2797  		},
  2798  		)(t)
  2799  }
  2800  
  2801  func TestTransientNetworkCoreContractAddresses(t *testing.T) {
  2802  
  2803  	// This test ensures that the transient networks have the correct core contract addresses.
  2804  	newVMTest().
  2805  		run(
  2806  			func(
  2807  				t *testing.T,
  2808  				vm fvm.VM,
  2809  				chain flow.Chain,
  2810  				ctx fvm.Context,
  2811  				snapshotTree snapshot.SnapshotTree,
  2812  			) {
  2813  				sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  2814  
  2815  				for _, contract := range sc.All() {
  2816  					txnState := testutils.NewSimpleTransaction(snapshotTree)
  2817  					accounts := environment.NewAccounts(txnState)
  2818  
  2819  					yes, err := accounts.ContractExists(contract.Name, contract.Address)
  2820  					require.NoError(t, err)
  2821  					require.True(t, yes, "contract %s does not exist", contract.Name)
  2822  				}
  2823  			})
  2824  }
  2825  
  2826  func TestEVM(t *testing.T) {
  2827  	blocks := new(envMock.Blocks)
  2828  	block1 := unittest.BlockFixture()
  2829  	blocks.On("ByHeightFrom",
  2830  		block1.Header.Height,
  2831  		block1.Header,
  2832  	).Return(block1.Header, nil)
  2833  
  2834  	ctxOpts := []fvm.Option{
  2835  		// default is testnet, but testnet has a special EVM storage contract location
  2836  		// so we have to use emulator here so that the EVM storage contract is deployed
  2837  		// to the 5th address
  2838  		fvm.WithChain(flow.Emulator.Chain()),
  2839  		fvm.WithEVMEnabled(true),
  2840  		fvm.WithBlocks(blocks),
  2841  		fvm.WithBlockHeader(block1.Header),
  2842  		fvm.WithCadenceLogging(true),
  2843  	}
  2844  
  2845  	t.Run("successful transaction", newVMTest().
  2846  		withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
  2847  		withContextOptions(ctxOpts...).
  2848  		run(func(
  2849  			t *testing.T,
  2850  			vm fvm.VM,
  2851  			chain flow.Chain,
  2852  			ctx fvm.Context,
  2853  			snapshotTree snapshot.SnapshotTree,
  2854  		) {
  2855  			// generate test address
  2856  			genArr := make([]cadence.Value, 20)
  2857  			for i := range genArr {
  2858  				genArr[i] = cadence.UInt8(i)
  2859  			}
  2860  			addrBytes := cadence.NewArray(genArr).WithType(stdlib.EVMAddressBytesCadenceType)
  2861  			encodedArg, err := jsoncdc.Encode(addrBytes)
  2862  			require.NoError(t, err)
  2863  
  2864  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  2865  
  2866  			txBody := flow.NewTransactionBody().
  2867  				SetScript([]byte(fmt.Sprintf(`
  2868  						import EVM from %s
  2869  
  2870  						transaction(bytes: [UInt8; 20]) {
  2871  							execute {
  2872  								let addr = EVM.EVMAddress(bytes: bytes)
  2873  								log(addr)
  2874  							}
  2875  						}
  2876  					`, sc.EVMContract.Address.HexWithPrefix()))).
  2877  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  2878  				SetPayer(chain.ServiceAddress()).
  2879  				AddArgument(encodedArg)
  2880  
  2881  			err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2882  			require.NoError(t, err)
  2883  
  2884  			_, output, err := vm.Run(
  2885  				ctx,
  2886  				fvm.Transaction(txBody, 0),
  2887  				snapshotTree)
  2888  
  2889  			require.NoError(t, err)
  2890  			require.NoError(t, output.Err)
  2891  			require.Len(t, output.Logs, 1)
  2892  			require.Equal(t, output.Logs[0], fmt.Sprintf(
  2893  				"A.%s.EVM.EVMAddress(bytes: %s)",
  2894  				sc.EVMContract.Address,
  2895  				addrBytes.String(),
  2896  			))
  2897  		}),
  2898  	)
  2899  
  2900  	// this test makes sure the execution error is correctly handled and returned as a correct type
  2901  	t.Run("execution reverted", newVMTest().
  2902  		withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
  2903  		withContextOptions(ctxOpts...).
  2904  		run(func(
  2905  			t *testing.T,
  2906  			vm fvm.VM,
  2907  			chain flow.Chain,
  2908  			ctx fvm.Context,
  2909  			snapshotTree snapshot.SnapshotTree,
  2910  		) {
  2911  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  2912  			script := fvm.Script([]byte(fmt.Sprintf(`
  2913  				import EVM from %s
  2914  				
  2915  				access(all) fun main() {
  2916  					let bal = EVM.Balance(attoflow: 1000000000000000000)
  2917  					let acc <- EVM.createCadenceOwnedAccount()
  2918  
  2919  					// withdraw insufficient balance
  2920  					destroy acc.withdraw(balance: bal)
  2921  					destroy acc
  2922  				}
  2923  			`, sc.EVMContract.Address.HexWithPrefix())))
  2924  
  2925  			_, output, err := vm.Run(ctx, script, snapshotTree)
  2926  
  2927  			require.NoError(t, err)
  2928  			require.Error(t, output.Err)
  2929  			require.True(t, errors.IsEVMError(output.Err))
  2930  
  2931  			// make sure error is not treated as internal error by Cadence
  2932  			var internal cadenceErrors.InternalError
  2933  			require.False(t, errors.As(output.Err, &internal))
  2934  		}),
  2935  	)
  2936  
  2937  	// this test makes sure the EVM error is correctly returned as an error and has a correct type
  2938  	// we have implemented a snapshot wrapper to return an error from the EVM
  2939  	t.Run("internal evm error handling", newVMTest().
  2940  		withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
  2941  		withContextOptions(ctxOpts...).
  2942  		run(func(
  2943  			t *testing.T,
  2944  			vm fvm.VM,
  2945  			chain flow.Chain,
  2946  			ctx fvm.Context,
  2947  			snapshotTree snapshot.SnapshotTree,
  2948  		) {
  2949  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  2950  
  2951  			tests := []struct {
  2952  				err        error
  2953  				errChecker func(error) bool
  2954  			}{{
  2955  				types.ErrNotImplemented,
  2956  				types.IsAFatalError,
  2957  			}, {
  2958  				types.NewStateError(fmt.Errorf("test state error")),
  2959  				types.IsAStateError,
  2960  			}}
  2961  
  2962  			for _, e := range tests {
  2963  				// this mock will return an error we provide with the test once it starts to access address allocator registers
  2964  				// that is done to make sure the error is coming out of EVM execution
  2965  				errStorage := &mock.StorageSnapshot{}
  2966  				errStorage.
  2967  					On("Get", mockery.AnythingOfType("flow.RegisterID")).
  2968  					Return(func(id flow.RegisterID) (flow.RegisterValue, error) {
  2969  						if id.Key == "LatestBlock" {
  2970  							return nil, e.err
  2971  						}
  2972  						return snapshotTree.Get(id)
  2973  					})
  2974  
  2975  				script := fvm.Script([]byte(fmt.Sprintf(`
  2976  					import EVM from %s
  2977  					
  2978  					access(all)
  2979                      fun main() {
  2980  						destroy <- EVM.createCadenceOwnedAccount()
  2981  					}
  2982  				`, sc.EVMContract.Address.HexWithPrefix())))
  2983  
  2984  				_, output, err := vm.Run(ctx, script, errStorage)
  2985  
  2986  				require.NoError(t, output.Err)
  2987  				require.Error(t, err)
  2988  				// make sure error it's the right type of error
  2989  				require.True(t, e.errChecker(err), "error is not of the right type")
  2990  			}
  2991  		}),
  2992  	)
  2993  
  2994  	t.Run("deploy contract code", newVMTest().
  2995  		withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
  2996  		withContextOptions(ctxOpts...).
  2997  		run(func(
  2998  			t *testing.T,
  2999  			vm fvm.VM,
  3000  			chain flow.Chain,
  3001  			ctx fvm.Context,
  3002  			snapshotTree snapshot.SnapshotTree,
  3003  		) {
  3004  			sc := systemcontracts.SystemContractsForChain(chain.ChainID())
  3005  
  3006  			txBody := flow.NewTransactionBody().
  3007  				SetScript([]byte(fmt.Sprintf(`
  3008  					import FungibleToken from %s
  3009  					import FlowToken from %s						
  3010  					import EVM from %s
  3011  
  3012  					transaction() {
  3013  						prepare(acc: auth(Storage) &Account) {
  3014  							let vaultRef = acc.storage
  3015                                  .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
  3016  							    ?? panic("Could not borrow reference to the owner's Vault!")
  3017  
  3018  							let acc <- EVM.createCadenceOwnedAccount()
  3019  							let amount <- vaultRef.withdraw(amount: 0.0000001) as! @FlowToken.Vault
  3020  							acc.deposit(from: <- amount)
  3021  							destroy acc
  3022  						}
  3023  					}`,
  3024  					sc.FungibleToken.Address.HexWithPrefix(),
  3025  					sc.FlowToken.Address.HexWithPrefix(),
  3026  					sc.FlowServiceAccount.Address.HexWithPrefix(), // TODO this should be sc.EVM.Address not found there???
  3027  				))).
  3028  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  3029  				AddAuthorizer(chain.ServiceAddress()).
  3030  				SetPayer(chain.ServiceAddress())
  3031  
  3032  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  3033  			require.NoError(t, err)
  3034  
  3035  			ctx = fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true))
  3036  			_, output, err := vm.Run(
  3037  				ctx,
  3038  				fvm.Transaction(txBody, 0),
  3039  				snapshotTree)
  3040  
  3041  			require.NoError(t, err)
  3042  			require.NoError(t, output.Err)
  3043  			require.Len(t, output.Events, 7)
  3044  
  3045  			txExe, blockExe := output.Events[4], output.Events[5]
  3046  			txExecutedID := common.NewAddressLocation(
  3047  				nil,
  3048  				common.Address(sc.EVMContract.Address),
  3049  				string(types.EventTypeTransactionExecuted),
  3050  			).ID()
  3051  			blockExecutedID := common.NewAddressLocation(
  3052  				nil,
  3053  				common.Address(sc.EVMContract.Address),
  3054  				string(types.EventTypeBlockExecuted),
  3055  			).ID()
  3056  			assert.Equal(t, txExecutedID, string(txExe.Type))
  3057  			assert.Equal(t, blockExecutedID, string(blockExe.Type))
  3058  
  3059  			// convert events to type ids
  3060  			eventTypeIDs := make([]common.TypeID, 0, len(output.Events))
  3061  
  3062  			for _, event := range output.Events {
  3063  				eventTypeIDs = append(eventTypeIDs, common.TypeID(event.Type))
  3064  			}
  3065  
  3066  			assert.ElementsMatch(
  3067  				t,
  3068  				[]common.TypeID{
  3069  					common.TypeID(txExecutedID),
  3070  					common.TypeID(blockExecutedID),
  3071  					"A.f8d6e0586b0a20c7.EVM.CadenceOwnedAccountCreated",
  3072  					"A.ee82856bf20e2aa6.FungibleToken.Withdrawn",
  3073  					common.TypeID(txExecutedID),
  3074  					common.TypeID(blockExecutedID),
  3075  					"A.f8d6e0586b0a20c7.EVM.FLOWTokensDeposited",
  3076  				},
  3077  				eventTypeIDs,
  3078  			)
  3079  		}),
  3080  	)
  3081  }