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