github.com/koko1123/flow-go-1@v0.29.6/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  	jsoncdc "github.com/onflow/cadence/encoding/json"
    13  	"github.com/onflow/cadence/runtime"
    14  	"github.com/onflow/cadence/runtime/common"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/onflow/flow-go/crypto"
    19  
    20  	"github.com/koko1123/flow-go-1/engine/execution/testutil"
    21  	exeUtils "github.com/koko1123/flow-go-1/engine/execution/utils"
    22  	"github.com/koko1123/flow-go-1/fvm"
    23  	fvmCrypto "github.com/koko1123/flow-go-1/fvm/crypto"
    24  	"github.com/koko1123/flow-go-1/fvm/derived"
    25  	"github.com/koko1123/flow-go-1/fvm/environment"
    26  	errors "github.com/koko1123/flow-go-1/fvm/errors"
    27  	"github.com/koko1123/flow-go-1/fvm/meter"
    28  	"github.com/koko1123/flow-go-1/fvm/state"
    29  	"github.com/koko1123/flow-go-1/fvm/utils"
    30  	"github.com/koko1123/flow-go-1/model/flow"
    31  	"github.com/koko1123/flow-go-1/utils/unittest"
    32  )
    33  
    34  // from 18.8.2022
    35  var mainnetExecutionEffortWeights = meter.ExecutionEffortWeights{
    36  	common.ComputationKindStatement:          1569,
    37  	common.ComputationKindLoop:               1569,
    38  	common.ComputationKindFunctionInvocation: 1569,
    39  	meter.ComputationKindGetValue:            808,
    40  	meter.ComputationKindCreateAccount:       2837670,
    41  	meter.ComputationKindSetValue:            765,
    42  }
    43  
    44  type vmTest struct {
    45  	bootstrapOptions []fvm.BootstrapProcedureOption
    46  	contextOptions   []fvm.Option
    47  }
    48  
    49  func newVMTest() vmTest {
    50  	return vmTest{}
    51  }
    52  
    53  func (vmt vmTest) withBootstrapProcedureOptions(opts ...fvm.BootstrapProcedureOption) vmTest {
    54  	vmt.bootstrapOptions = append(vmt.bootstrapOptions, opts...)
    55  	return vmt
    56  }
    57  
    58  func (vmt vmTest) withContextOptions(opts ...fvm.Option) vmTest {
    59  	vmt.contextOptions = append(vmt.contextOptions, opts...)
    60  	return vmt
    61  }
    62  
    63  func createChainAndVm(chainID flow.ChainID) (flow.Chain, fvm.VM) {
    64  	return chainID.Chain(), fvm.NewVirtualMachine()
    65  }
    66  
    67  func (vmt vmTest) run(
    68  	f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData),
    69  ) func(t *testing.T) {
    70  	return func(t *testing.T) {
    71  		chain, vm := createChainAndVm(flow.Testnet)
    72  		derivedBlockData := derived.NewEmptyDerivedBlockData()
    73  
    74  		baseOpts := []fvm.Option{
    75  			fvm.WithChain(chain),
    76  			fvm.WithDerivedBlockData(derivedBlockData),
    77  		}
    78  
    79  		opts := append(baseOpts, vmt.contextOptions...)
    80  
    81  		ctx := fvm.NewContext(opts...)
    82  
    83  		view := utils.NewSimpleView()
    84  
    85  		baseBootstrapOpts := []fvm.BootstrapProcedureOption{
    86  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
    87  		}
    88  
    89  		bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...)
    90  
    91  		err := vm.Run(ctx, fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), view)
    92  		require.NoError(t, err)
    93  
    94  		f(t, vm, chain, ctx, view, derivedBlockData)
    95  	}
    96  }
    97  
    98  // bootstrapWith executes the bootstrap procedure and the custom bootstrap function
    99  // and returns a prepared bootstrappedVmTest with all the state needed
   100  func (vmt vmTest) bootstrapWith(
   101  	bootstrap func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) error,
   102  ) (bootstrappedVmTest, error) {
   103  	chain, vm := createChainAndVm(flow.Testnet)
   104  
   105  	baseOpts := []fvm.Option{
   106  		fvm.WithChain(chain),
   107  	}
   108  
   109  	opts := append(baseOpts, vmt.contextOptions...)
   110  
   111  	ctx := fvm.NewContext(opts...)
   112  
   113  	view := utils.NewSimpleView()
   114  
   115  	baseBootstrapOpts := []fvm.BootstrapProcedureOption{
   116  		fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   117  	}
   118  
   119  	derivedBlockData := derived.NewEmptyDerivedBlockData()
   120  
   121  	bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...)
   122  
   123  	err := vm.Run(ctx, fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), view)
   124  	if err != nil {
   125  		return bootstrappedVmTest{}, err
   126  	}
   127  
   128  	err = bootstrap(vm, chain, ctx, view, derivedBlockData)
   129  	if err != nil {
   130  		return bootstrappedVmTest{}, err
   131  	}
   132  
   133  	return bootstrappedVmTest{chain, ctx, view, derivedBlockData}, nil
   134  }
   135  
   136  type bootstrappedVmTest struct {
   137  	chain            flow.Chain
   138  	ctx              fvm.Context
   139  	view             state.View
   140  	derivedBlockData *derived.DerivedBlockData
   141  }
   142  
   143  // run Runs a test from the bootstrapped state, without changing the bootstrapped state
   144  func (vmt bootstrappedVmTest) run(
   145  	f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData),
   146  ) func(t *testing.T) {
   147  	return func(t *testing.T) {
   148  		f(t, fvm.NewVirtualMachine(), vmt.chain, vmt.ctx, vmt.view.NewChild(), vmt.derivedBlockData.NewChildDerivedBlockData())
   149  	}
   150  }
   151  
   152  func TestPrograms(t *testing.T) {
   153  
   154  	t.Run(
   155  		"transaction execution derivedBlockData are committed",
   156  		newVMTest().run(
   157  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
   158  
   159  				txCtx := fvm.NewContextFromParent(ctx)
   160  
   161  				for i := 0; i < 10; i++ {
   162  
   163  					script := []byte(fmt.Sprintf(`
   164  							import FungibleToken from %s
   165  
   166  							transaction {}
   167  						`,
   168  						fvm.FungibleTokenAddress(chain).HexWithPrefix(),
   169  					))
   170  
   171  					serviceAddress := chain.ServiceAddress()
   172  
   173  					txBody := flow.NewTransactionBody().
   174  						SetScript(script).
   175  						SetProposalKey(serviceAddress, 0, uint64(i)).
   176  						SetPayer(serviceAddress)
   177  
   178  					err := testutil.SignEnvelope(
   179  						txBody,
   180  						serviceAddress,
   181  						unittest.ServiceAccountPrivateKey,
   182  					)
   183  					require.NoError(t, err)
   184  
   185  					tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   186  
   187  					err = vm.Run(txCtx, tx, view)
   188  					require.NoError(t, err)
   189  
   190  					require.NoError(t, tx.Err)
   191  				}
   192  			},
   193  		),
   194  	)
   195  
   196  	t.Run("script execution derivedBlockData are not committed",
   197  		newVMTest().withBootstrapProcedureOptions().run(
   198  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
   199  
   200  				scriptCtx := fvm.NewContextFromParent(ctx)
   201  
   202  				script := fvm.Script([]byte(fmt.Sprintf(`
   203  
   204  						import FungibleToken from %s
   205  
   206  						pub fun main() {}
   207  					`,
   208  					fvm.FungibleTokenAddress(chain).HexWithPrefix(),
   209  				)))
   210  
   211  				err := vm.Run(scriptCtx, script, view)
   212  				require.NoError(t, err)
   213  				require.NoError(t, script.Err)
   214  			},
   215  		),
   216  	)
   217  }
   218  
   219  func TestHashing(t *testing.T) {
   220  
   221  	t.Parallel()
   222  
   223  	chain, vm := createChainAndVm(flow.Mainnet)
   224  
   225  	ctx := fvm.NewContext(
   226  		fvm.WithChain(chain),
   227  		fvm.WithCadenceLogging(true),
   228  	)
   229  
   230  	ledger := testutil.RootBootstrappedLedger(vm, ctx)
   231  
   232  	hashScript := func(hashName string) []byte {
   233  		return []byte(fmt.Sprintf(
   234  			`
   235  				import Crypto
   236  
   237  				pub fun main(data: [UInt8]): [UInt8] {
   238  					return Crypto.hash(data, algorithm: HashAlgorithm.%s)
   239  				}
   240  			`, hashName))
   241  	}
   242  	hashWithTagScript := func(hashName string) []byte {
   243  		return []byte(fmt.Sprintf(
   244  			`
   245  				import Crypto
   246  
   247  				pub fun main(data: [UInt8], tag: String): [UInt8] {
   248  					return Crypto.hashWithTag(data, tag: tag, algorithm: HashAlgorithm.%s)
   249  				}
   250  			`, hashName))
   251  	}
   252  
   253  	data := []byte("some random message")
   254  	encodedBytes := make([]cadence.Value, len(data))
   255  	for i := range encodedBytes {
   256  		encodedBytes[i] = cadence.NewUInt8(data[i])
   257  	}
   258  	cadenceData := jsoncdc.MustEncode(cadence.NewArray(encodedBytes))
   259  
   260  	// ===== Test Cases =====
   261  	cases := []struct {
   262  		Algo    runtime.HashAlgorithm
   263  		WithTag bool
   264  		Tag     string
   265  		Check   func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error)
   266  	}{
   267  		{
   268  			Algo:    runtime.HashAlgorithmSHA2_256,
   269  			WithTag: false,
   270  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   271  				require.NoError(t, scriptErr)
   272  				require.NoError(t, executionErr)
   273  				require.Equal(t, "68fb87dfba69b956f4ba98b748a75a604f99b38a4f2740290037957f7e830da8", result)
   274  			},
   275  		},
   276  		{
   277  			Algo:    runtime.HashAlgorithmSHA2_384,
   278  			WithTag: false,
   279  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   280  				require.NoError(t, scriptErr)
   281  				require.NoError(t, executionErr)
   282  				require.Equal(t, "a9b3e62ab9b2a33020e015f245b82e063afd1398211326408bc8fc31c2c15859594b0aee263fbb02f6d8b5065ad49df2", result)
   283  			},
   284  		},
   285  		{
   286  			Algo:    runtime.HashAlgorithmSHA3_256,
   287  			WithTag: false,
   288  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   289  				require.NoError(t, scriptErr)
   290  				require.NoError(t, executionErr)
   291  				require.Equal(t, "38effea5ab9082a2cb0dc9adfafaf88523e8f3ce74bfbeac85ffc719cc2c4677", result)
   292  			},
   293  		},
   294  		{
   295  			Algo:    runtime.HashAlgorithmSHA3_384,
   296  			WithTag: false,
   297  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   298  				require.NoError(t, scriptErr)
   299  				require.NoError(t, executionErr)
   300  				require.Equal(t, "f41e8de9af0c1f46fc56d5a776f1bd500530879a85f3b904821810295927e13a54f3e936dddb84669021052eb12966c3", result)
   301  			},
   302  		},
   303  		{
   304  			Algo:    runtime.HashAlgorithmKECCAK_256,
   305  			WithTag: false,
   306  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   307  				require.NoError(t, scriptErr)
   308  				require.NoError(t, executionErr)
   309  				require.Equal(t, "1d5ced4738dd4e0bb4628dad7a7b59b8e339a75ece97a4ad004773a49ed7b5bc", result)
   310  			},
   311  		},
   312  		{
   313  			Algo:    runtime.HashAlgorithmKECCAK_256,
   314  			WithTag: true,
   315  			Tag:     "some_tag",
   316  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   317  				require.NoError(t, scriptErr)
   318  				require.NoError(t, executionErr)
   319  				require.Equal(t, "8454ec77f76b229a473770c91e3ea6e7e852416d747805215d15d53bdc56ce5f", result)
   320  			},
   321  		},
   322  		{
   323  			Algo:    runtime.HashAlgorithmSHA2_256,
   324  			WithTag: true,
   325  			Tag:     "some_tag",
   326  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   327  				require.NoError(t, scriptErr)
   328  				require.NoError(t, executionErr)
   329  				require.Equal(t, "4e07609b9a856a5e10703d1dba73be34d9ca0f4e780859d66983f41d746ec8b2", result)
   330  			},
   331  		},
   332  		{
   333  			Algo:    runtime.HashAlgorithmSHA2_384,
   334  			WithTag: true,
   335  			Tag:     "some_tag",
   336  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   337  				require.NoError(t, scriptErr)
   338  				require.NoError(t, executionErr)
   339  				require.Equal(t, "f9bd89e15f341a225656944dc8b3c405e66a0f97838ad44c9803164c911e677aea7ad4e24486fba3f803d83ed1ccfce5", result)
   340  			},
   341  		},
   342  		{
   343  			Algo:    runtime.HashAlgorithmSHA3_256,
   344  			WithTag: true,
   345  			Tag:     "some_tag",
   346  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   347  				require.NoError(t, scriptErr)
   348  				require.NoError(t, executionErr)
   349  				require.Equal(t, "f59e2ccc9d7f008a96948a31573670d9976a4a161601ab1cd1d2da019779a0f6", result)
   350  			},
   351  		},
   352  		{
   353  			Algo:    runtime.HashAlgorithmSHA3_384,
   354  			WithTag: true,
   355  			Tag:     "some_tag",
   356  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   357  				require.NoError(t, scriptErr)
   358  				require.NoError(t, executionErr)
   359  				require.Equal(t, "e7875eafdb53327faeace8478d1650c6547d04fb4fb42f34509ad64bde0267bea7e1b3af8fda3ef9d9c9327dd4e97a96", result)
   360  			},
   361  		},
   362  		{
   363  			Algo:    runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   364  			WithTag: false,
   365  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   366  				require.NoError(t, scriptErr)
   367  				require.NoError(t, executionErr)
   368  				require.Equal(t, "44dc46111abacfe2bb4a04cea4805aad03f84e4849f138cc3ed431478472b185548628e96d0c963b21ebaf17132d73fc13031eb82d5f4cbe3b6047ff54d20e8d663904373d73348b97ce18305ebc56114cb7e7394e486684007f78aa59abc5d0a8f6bae6bd186db32528af80857cd12112ce6960be29c96074df9c4aaed5b0e6", result)
   369  			},
   370  		},
   371  		{
   372  			Algo:    runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   373  			WithTag: true,
   374  			Tag:     "some_tag",
   375  			Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) {
   376  				require.NoError(t, scriptErr)
   377  				require.NoError(t, executionErr)
   378  				require.Equal(t, "de7d9aa24274fa12c98cce5c09eea0634108ead2e91828b9a9a450e878088393e3e63eb4b19834f579ce215b00a9915919b67a71dab1112560319e6e1e5e9ad0fb670e8a09d586508c84547cee7ddbe8c9362c996846154865eb271bdc4523dbcdbdae5a77391fb54374f37534c8bb2281589cb2e3d62742596cdad7e4f9f35c", result)
   379  			},
   380  		},
   381  	}
   382  	// ======================
   383  
   384  	for i, c := range cases {
   385  		t.Run(fmt.Sprintf("case %d: %s with tag: %v", i, c.Algo, c.WithTag), func(t *testing.T) {
   386  			code := hashScript(c.Algo.Name())
   387  			if c.WithTag {
   388  				code = hashWithTagScript(c.Algo.Name())
   389  			}
   390  
   391  			script := fvm.Script(code)
   392  
   393  			if c.WithTag {
   394  				script = script.WithArguments(
   395  					cadenceData,
   396  					jsoncdc.MustEncode(cadence.String(c.Tag)),
   397  				)
   398  			} else {
   399  				script = script.WithArguments(
   400  					cadenceData,
   401  				)
   402  			}
   403  
   404  			err := vm.Run(ctx, script, ledger)
   405  
   406  			byteResult := make([]byte, 0)
   407  			if err == nil && script.Err == nil {
   408  				cadenceArray := script.Value.(cadence.Array)
   409  				for _, value := range cadenceArray.Values {
   410  					byteResult = append(byteResult, value.(cadence.UInt8).ToGoValue().(uint8))
   411  				}
   412  			}
   413  
   414  			c.Check(t, hex.EncodeToString(byteResult), script.Err, err)
   415  		})
   416  	}
   417  
   418  	hashAlgos := []runtime.HashAlgorithm{
   419  		runtime.HashAlgorithmSHA2_256,
   420  		runtime.HashAlgorithmSHA3_256,
   421  		runtime.HashAlgorithmSHA2_384,
   422  		runtime.HashAlgorithmSHA3_384,
   423  		runtime.HashAlgorithmKMAC128_BLS_BLS12_381,
   424  		runtime.HashAlgorithmKECCAK_256,
   425  	}
   426  
   427  	for i, algo := range hashAlgos {
   428  		t.Run(fmt.Sprintf("compare hash results without tag %v: %v", i, algo), func(t *testing.T) {
   429  			code := hashWithTagScript(algo.Name())
   430  			script := fvm.Script(code)
   431  			script = script.WithArguments(
   432  				cadenceData,
   433  				jsoncdc.MustEncode(cadence.String("")),
   434  			)
   435  			err := vm.Run(ctx, script, ledger)
   436  			require.NoError(t, err)
   437  			require.NoError(t, script.Err)
   438  
   439  			result1 := make([]byte, 0)
   440  			cadenceArray := script.Value.(cadence.Array)
   441  			for _, value := range cadenceArray.Values {
   442  				result1 = append(result1, value.(cadence.UInt8).ToGoValue().(uint8))
   443  			}
   444  
   445  			code = hashScript(algo.Name())
   446  			script = fvm.Script(code)
   447  			script = script.WithArguments(
   448  				cadenceData,
   449  			)
   450  			err = vm.Run(ctx, script, ledger)
   451  			require.NoError(t, err)
   452  			require.NoError(t, script.Err)
   453  
   454  			result2 := make([]byte, 0)
   455  			cadenceArray = script.Value.(cadence.Array)
   456  			for _, value := range cadenceArray.Values {
   457  				result2 = append(result2, value.(cadence.UInt8).ToGoValue().(uint8))
   458  			}
   459  
   460  			result3, err := fvmCrypto.HashWithTag(fvmCrypto.RuntimeToCryptoHashingAlgorithm(algo), "", data)
   461  			require.NoError(t, err)
   462  
   463  			require.Equal(t, result1, result2)
   464  			require.Equal(t, result1, result3)
   465  		})
   466  	}
   467  }
   468  
   469  func TestWithServiceAccount(t *testing.T) {
   470  
   471  	t.Parallel()
   472  
   473  	chain, vm := createChainAndVm(flow.Mainnet)
   474  
   475  	ctxA := fvm.NewContext(
   476  		fvm.WithChain(chain),
   477  		fvm.WithAuthorizationChecksEnabled(false),
   478  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   479  	)
   480  
   481  	view := utils.NewSimpleView()
   482  
   483  	txBody := flow.NewTransactionBody().
   484  		SetScript([]byte(`transaction { prepare(signer: AuthAccount) { AuthAccount(payer: signer) } }`)).
   485  		AddAuthorizer(chain.ServiceAddress())
   486  
   487  	t.Run("With service account enabled", func(t *testing.T) {
   488  		derivedBlockData := derived.NewEmptyDerivedBlockData()
   489  		ctxB := fvm.NewContextFromParent(
   490  			ctxA,
   491  			fvm.WithDerivedBlockData(derivedBlockData))
   492  
   493  		tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   494  
   495  		err := vm.Run(ctxB, tx, view)
   496  		require.NoError(t, err)
   497  
   498  		// transaction should fail on non-bootstrapped ledger
   499  		require.Error(t, tx.Err)
   500  	})
   501  
   502  	t.Run("With service account disabled", func(t *testing.T) {
   503  		derivedBlockData := derived.NewEmptyDerivedBlockData()
   504  		ctxB := fvm.NewContextFromParent(
   505  			ctxA,
   506  			fvm.WithServiceAccount(false),
   507  			fvm.WithDerivedBlockData(derivedBlockData))
   508  
   509  		tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   510  
   511  		err := vm.Run(ctxB, tx, view)
   512  		require.NoError(t, err)
   513  
   514  		// transaction should succeed on non-bootstrapped ledger
   515  		assert.NoError(t, tx.Err)
   516  	})
   517  }
   518  
   519  func TestEventLimits(t *testing.T) {
   520  	chain, vm := createChainAndVm(flow.Mainnet)
   521  	derivedBlockData := derived.NewEmptyDerivedBlockData()
   522  
   523  	ctx := fvm.NewContext(
   524  		fvm.WithChain(chain),
   525  		fvm.WithAuthorizationChecksEnabled(false),
   526  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   527  		fvm.WithDerivedBlockData(derivedBlockData),
   528  	)
   529  
   530  	ledger := testutil.RootBootstrappedLedger(vm, ctx)
   531  
   532  	testContract := `
   533  	access(all) contract TestContract {
   534  		access(all) event LargeEvent(value: Int256, str: String, list: [UInt256], dic: {String: String})
   535  		access(all) fun EmitEvent() {
   536  			var s: Int256 = 1024102410241024
   537  			var i = 0
   538  
   539  			while i < 20 {
   540  				emit LargeEvent(value: s, str: s.toString(), list:[], dic:{s.toString():s.toString()})
   541  				i = i + 1
   542  			}
   543  		}
   544  	}
   545  	`
   546  
   547  	deployingContractScriptTemplate := `
   548  		transaction {
   549  			prepare(signer: AuthAccount) {
   550  				let code = "%s".decodeHex()
   551  				signer.contracts.add(
   552  					name: "TestContract",
   553  					code: code
   554  				)
   555  		}
   556  	}
   557  	`
   558  
   559  	ctx = fvm.NewContextFromParent(
   560  		ctx,
   561  		fvm.WithEventCollectionSizeLimit(2))
   562  
   563  	txBody := flow.NewTransactionBody().
   564  		SetScript([]byte(fmt.Sprintf(deployingContractScriptTemplate, hex.EncodeToString([]byte(testContract))))).
   565  		SetPayer(chain.ServiceAddress()).
   566  		AddAuthorizer(chain.ServiceAddress())
   567  
   568  	tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   569  	err := vm.Run(ctx, tx, ledger)
   570  	require.NoError(t, err)
   571  
   572  	txBody = flow.NewTransactionBody().
   573  		SetScript([]byte(fmt.Sprintf(`
   574  		import TestContract from 0x%s
   575  			transaction {
   576  			prepare(acct: AuthAccount) {}
   577  			execute {
   578  				TestContract.EmitEvent()
   579  			}
   580  		}`, chain.ServiceAddress()))).
   581  		AddAuthorizer(chain.ServiceAddress())
   582  
   583  	t.Run("With limits", func(t *testing.T) {
   584  		txBody.Payer = unittest.RandomAddressFixture()
   585  		tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   586  		err := vm.Run(ctx, tx, ledger)
   587  		require.NoError(t, err)
   588  
   589  		// transaction should fail due to event size limit
   590  		assert.Error(t, tx.Err)
   591  	})
   592  
   593  	t.Run("With service account as payer", func(t *testing.T) {
   594  		txBody.Payer = chain.ServiceAddress()
   595  		tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   596  		err := vm.Run(ctx, tx, ledger)
   597  		require.NoError(t, err)
   598  
   599  		unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   600  
   601  		// transaction should not fail due to event size limit
   602  		assert.NoError(t, tx.Err)
   603  	})
   604  }
   605  
   606  // TestHappyPathSigning checks that a signing a transaction with `Sign` doesn't produce an error.
   607  // Transaction verification tests are in `TestVerifySignatureFromTransaction`.
   608  func TestHappyPathTransactionSigning(t *testing.T) {
   609  
   610  	newVMTest().run(
   611  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
   612  			// Create an account private key.
   613  			privateKey, err := testutil.GenerateAccountPrivateKey()
   614  			require.NoError(t, err)
   615  
   616  			// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
   617  			accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, []flow.AccountPrivateKey{privateKey}, chain)
   618  			require.NoError(t, err)
   619  
   620  			txBody := flow.NewTransactionBody().
   621  				SetScript([]byte(`transaction(){}`))
   622  
   623  			txBody.SetProposalKey(accounts[0], 0, 0)
   624  			txBody.SetPayer(accounts[0])
   625  
   626  			hasher, err := exeUtils.NewHasher(privateKey.HashAlgo)
   627  			require.NoError(t, err)
   628  
   629  			sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher)
   630  			require.NoError(t, err)
   631  			txBody.AddEnvelopeSignature(accounts[0], 0, sig)
   632  
   633  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   634  
   635  			err = vm.Run(ctx, tx, view)
   636  			require.NoError(t, err)
   637  			require.NoError(t, tx.Err)
   638  		},
   639  	)
   640  }
   641  
   642  func TestTransactionFeeDeduction(t *testing.T) {
   643  	getBalance := func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, address flow.Address) uint64 {
   644  
   645  		code := []byte(fmt.Sprintf(`
   646  					import FungibleToken from 0x%s
   647  					import FlowToken from 0x%s
   648  
   649  					pub fun main(account: Address): UFix64 {
   650  						let acct = getAccount(account)
   651  						let vaultRef = acct.getCapability(/public/flowTokenBalance)
   652  							.borrow<&FlowToken.Vault{FungibleToken.Balance}>()
   653  							?? panic("Could not borrow Balance reference to the Vault")
   654  
   655  						return vaultRef.balance
   656  					}
   657  				`, fvm.FungibleTokenAddress(chain), fvm.FlowTokenAddress(chain)))
   658  		script := fvm.Script(code).WithArguments(
   659  			jsoncdc.MustEncode(cadence.NewAddress(address)),
   660  		)
   661  
   662  		err := vm.Run(ctx, script, view)
   663  		require.NoError(t, err)
   664  		require.NoError(t, script.Err)
   665  		return script.Value.ToGoValue().(uint64)
   666  	}
   667  
   668  	type testCase struct {
   669  		name          string
   670  		fundWith      uint64
   671  		tryToTransfer uint64
   672  		gasLimit      uint64
   673  		checkResult   func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure)
   674  	}
   675  
   676  	txFees := uint64(1_000)              // 0.00001
   677  	fundingAmount := uint64(100_000_000) // 1.0
   678  	transferAmount := uint64(123_456)
   679  	minimumStorageReservation := fvm.DefaultMinimumStorageReservation.ToGoValue().(uint64)
   680  
   681  	testCases := []testCase{
   682  		{
   683  			name:          "Transaction fees are deducted",
   684  			fundWith:      fundingAmount,
   685  			tryToTransfer: 0,
   686  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   687  				require.NoError(t, tx.Err)
   688  				require.Equal(t, txFees, balanceBefore-balanceAfter)
   689  			},
   690  		},
   691  		{
   692  			name:          "Transaction fee deduction emits events",
   693  			fundWith:      fundingAmount,
   694  			tryToTransfer: 0,
   695  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   696  				require.NoError(t, tx.Err)
   697  
   698  				var deposits []flow.Event
   699  				var withdraws []flow.Event
   700  
   701  				chain := flow.Testnet.Chain()
   702  				for _, e := range tx.Events {
   703  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   704  						deposits = append(deposits, e)
   705  					}
   706  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   707  						withdraws = append(withdraws, e)
   708  					}
   709  				}
   710  
   711  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   712  				require.Len(t, deposits, 2)
   713  				require.Len(t, withdraws, 2)
   714  			},
   715  		},
   716  		{
   717  			name:          "Transaction fees are deducted and tx is applied",
   718  			fundWith:      fundingAmount,
   719  			tryToTransfer: transferAmount,
   720  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   721  				require.NoError(t, tx.Err)
   722  				require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter)
   723  			},
   724  		},
   725  		{
   726  			name:          "Transaction fees are deducted and fee deduction is emitted",
   727  			fundWith:      fundingAmount,
   728  			tryToTransfer: transferAmount,
   729  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   730  				require.NoError(t, tx.Err)
   731  				chain := flow.Testnet.Chain()
   732  
   733  				var feeDeduction flow.Event // fee deduction event
   734  				for _, e := range tx.Events {
   735  					if string(e.Type) == fmt.Sprintf("A.%s.FlowFees.FeesDeducted", environment.FlowFeesAddress(chain)) {
   736  						feeDeduction = e
   737  						break
   738  					}
   739  				}
   740  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   741  				require.NotEmpty(t, feeDeduction.Payload)
   742  
   743  				payload, err := jsoncdc.Decode(nil, feeDeduction.Payload)
   744  				require.NoError(t, err)
   745  
   746  				event := payload.(cadence.Event)
   747  
   748  				require.Equal(t, txFees, event.Fields[0].ToGoValue())
   749  				// Inclusion effort should be equivalent to 1.0 UFix64
   750  				require.Equal(t, uint64(100_000_000), event.Fields[1].ToGoValue())
   751  				// Execution effort should be non-0
   752  				require.Greater(t, event.Fields[2].ToGoValue(), uint64(0))
   753  
   754  			},
   755  		},
   756  		{
   757  			name:          "If just enough balance, fees are deducted",
   758  			fundWith:      txFees + transferAmount,
   759  			tryToTransfer: transferAmount,
   760  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   761  				require.NoError(t, tx.Err)
   762  				require.Equal(t, uint64(0), balanceAfter)
   763  			},
   764  		},
   765  		{
   766  			// this is an edge case that is not applicable to any network.
   767  			// If storage limits were on this would fail due to storage limits
   768  			name:          "If not enough balance, transaction succeeds and fees are deducted to 0",
   769  			fundWith:      txFees,
   770  			tryToTransfer: 1,
   771  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   772  				require.NoError(t, tx.Err)
   773  				require.Equal(t, uint64(0), balanceAfter)
   774  			},
   775  		},
   776  		{
   777  			name:          "If tx fails, fees are deducted",
   778  			fundWith:      fundingAmount,
   779  			tryToTransfer: 2 * fundingAmount,
   780  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   781  				require.Error(t, tx.Err)
   782  				require.Equal(t, fundingAmount-txFees, balanceAfter)
   783  			},
   784  		},
   785  		{
   786  			name:          "If tx fails, fee deduction events are emitted",
   787  			fundWith:      fundingAmount,
   788  			tryToTransfer: 2 * fundingAmount,
   789  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   790  				require.Error(t, tx.Err)
   791  
   792  				var deposits []flow.Event
   793  				var withdraws []flow.Event
   794  
   795  				chain := flow.Testnet.Chain()
   796  
   797  				for _, e := range tx.Events {
   798  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   799  						deposits = append(deposits, e)
   800  					}
   801  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   802  						withdraws = append(withdraws, e)
   803  					}
   804  				}
   805  
   806  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   807  				require.Len(t, deposits, 1)
   808  				require.Len(t, withdraws, 1)
   809  			},
   810  		},
   811  		{
   812  			name:          "If tx fails because of gas limit reached, fee deduction events are emitted",
   813  			fundWith:      txFees + transferAmount,
   814  			tryToTransfer: transferAmount,
   815  			gasLimit:      uint64(2),
   816  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   817  				require.ErrorContains(t, tx.Err, "computation exceeds limit (2)")
   818  
   819  				var deposits []flow.Event
   820  				var withdraws []flow.Event
   821  
   822  				chain := flow.Testnet.Chain()
   823  
   824  				for _, e := range tx.Events {
   825  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   826  						deposits = append(deposits, e)
   827  					}
   828  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   829  						withdraws = append(withdraws, e)
   830  					}
   831  				}
   832  
   833  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   834  				require.Len(t, deposits, 1)
   835  				require.Len(t, withdraws, 1)
   836  			},
   837  		},
   838  	}
   839  
   840  	testCasesWithStorageEnabled := []testCase{
   841  		{
   842  			name:          "Transaction fees are deducted",
   843  			fundWith:      fundingAmount,
   844  			tryToTransfer: 0,
   845  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   846  				require.NoError(t, tx.Err)
   847  				require.Equal(t, txFees, balanceBefore-balanceAfter)
   848  			},
   849  		},
   850  		{
   851  			name:          "Transaction fee deduction emits events",
   852  			fundWith:      fundingAmount,
   853  			tryToTransfer: 0,
   854  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   855  				require.NoError(t, tx.Err)
   856  
   857  				var deposits []flow.Event
   858  				var withdraws []flow.Event
   859  
   860  				chain := flow.Testnet.Chain()
   861  
   862  				for _, e := range tx.Events {
   863  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   864  						deposits = append(deposits, e)
   865  					}
   866  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   867  						withdraws = append(withdraws, e)
   868  					}
   869  				}
   870  
   871  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   872  				require.Len(t, deposits, 2)
   873  				require.Len(t, withdraws, 2)
   874  			},
   875  		},
   876  		{
   877  			name:          "Transaction fees are deducted and tx is applied",
   878  			fundWith:      fundingAmount,
   879  			tryToTransfer: transferAmount,
   880  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   881  				require.NoError(t, tx.Err)
   882  				require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter)
   883  			},
   884  		},
   885  		{
   886  			name:          "If just enough balance, fees are deducted",
   887  			fundWith:      txFees + transferAmount,
   888  			tryToTransfer: transferAmount,
   889  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   890  				require.NoError(t, tx.Err)
   891  				require.Equal(t, minimumStorageReservation, balanceAfter)
   892  			},
   893  		},
   894  		{
   895  			name:          "If tx fails, fees are deducted",
   896  			fundWith:      fundingAmount,
   897  			tryToTransfer: 2 * fundingAmount,
   898  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   899  				require.Error(t, tx.Err)
   900  				require.Equal(t, fundingAmount-txFees+minimumStorageReservation, balanceAfter)
   901  			},
   902  		},
   903  		{
   904  			name:          "If tx fails, fee deduction events are emitted",
   905  			fundWith:      fundingAmount,
   906  			tryToTransfer: 2 * fundingAmount,
   907  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   908  				require.Error(t, tx.Err)
   909  
   910  				var deposits []flow.Event
   911  				var withdraws []flow.Event
   912  
   913  				chain := flow.Testnet.Chain()
   914  
   915  				for _, e := range tx.Events {
   916  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   917  						deposits = append(deposits, e)
   918  					}
   919  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   920  						withdraws = append(withdraws, e)
   921  					}
   922  				}
   923  
   924  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   925  				require.Len(t, deposits, 1)
   926  				require.Len(t, withdraws, 1)
   927  			},
   928  		},
   929  		{
   930  			name:          "If balance at minimum, transaction fails, fees are deducted and fee deduction events are emitted",
   931  			fundWith:      0,
   932  			tryToTransfer: 0,
   933  			checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, tx *fvm.TransactionProcedure) {
   934  				require.Error(t, tx.Err)
   935  				require.Equal(t, minimumStorageReservation-txFees, balanceAfter)
   936  
   937  				var deposits []flow.Event
   938  				var withdraws []flow.Event
   939  
   940  				chain := flow.Testnet.Chain()
   941  
   942  				for _, e := range tx.Events {
   943  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   944  						deposits = append(deposits, e)
   945  					}
   946  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   947  						withdraws = append(withdraws, e)
   948  					}
   949  				}
   950  
   951  				unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   952  				require.Len(t, deposits, 1)
   953  				require.Len(t, withdraws, 1)
   954  			},
   955  		},
   956  	}
   957  
   958  	runTx := func(tc testCase) func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
   959  		return func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
   960  			// ==== Create an account ====
   961  			privateKey, txBody := testutil.CreateAccountCreationTransaction(t, chain)
   962  
   963  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
   964  			require.NoError(t, err)
   965  
   966  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
   967  
   968  			err = vm.Run(ctx, tx, view)
   969  			require.NoError(t, err)
   970  
   971  			assert.NoError(t, tx.Err)
   972  
   973  			assert.Len(t, tx.Events, 10)
   974  			unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
   975  
   976  			accountCreatedEvents := filterAccountCreatedEvents(tx.Events)
   977  
   978  			require.Len(t, accountCreatedEvents, 1)
   979  
   980  			// read the address of the account created (e.g. "0x01" and convert it to flow.address)
   981  			data, err := jsoncdc.Decode(nil, accountCreatedEvents[0].Payload)
   982  			require.NoError(t, err)
   983  			address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address))
   984  
   985  			// ==== Transfer tokens to new account ====
   986  			txBody = transferTokensTx(chain).
   987  				AddAuthorizer(chain.ServiceAddress()).
   988  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.fundWith))).
   989  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address)))
   990  
   991  			txBody.SetProposalKey(chain.ServiceAddress(), 0, 1)
   992  			txBody.SetPayer(chain.ServiceAddress())
   993  
   994  			err = testutil.SignEnvelope(
   995  				txBody,
   996  				chain.ServiceAddress(),
   997  				unittest.ServiceAccountPrivateKey,
   998  			)
   999  			require.NoError(t, err)
  1000  
  1001  			tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1002  
  1003  			err = vm.Run(ctx, tx, view)
  1004  			require.NoError(t, err)
  1005  			require.NoError(t, tx.Err)
  1006  
  1007  			balanceBefore := getBalance(vm, chain, ctx, view, address)
  1008  
  1009  			// ==== Transfer tokens from new account ====
  1010  
  1011  			txBody = transferTokensTx(chain).
  1012  				AddAuthorizer(address).
  1013  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.tryToTransfer))).
  1014  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress())))
  1015  
  1016  			txBody.SetProposalKey(address, 0, 0)
  1017  			txBody.SetPayer(address)
  1018  
  1019  			if tc.gasLimit == 0 {
  1020  				txBody.SetGasLimit(fvm.DefaultComputationLimit)
  1021  			} else {
  1022  				txBody.SetGasLimit(tc.gasLimit)
  1023  			}
  1024  
  1025  			err = testutil.SignEnvelope(
  1026  				txBody,
  1027  				address,
  1028  				privateKey,
  1029  			)
  1030  			require.NoError(t, err)
  1031  
  1032  			tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1033  
  1034  			err = vm.Run(ctx, tx, view)
  1035  			require.NoError(t, err)
  1036  
  1037  			balanceAfter := getBalance(vm, chain, ctx, view, address)
  1038  
  1039  			tc.checkResult(
  1040  				t,
  1041  				balanceBefore,
  1042  				balanceAfter,
  1043  				tx,
  1044  			)
  1045  		}
  1046  	}
  1047  
  1048  	for i, tc := range testCases {
  1049  		t.Run(fmt.Sprintf("Transaction Fees %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions(
  1050  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1051  			fvm.WithExecutionMemoryLimit(math.MaxUint64),
  1052  			fvm.WithExecutionEffortWeights(mainnetExecutionEffortWeights),
  1053  			fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
  1054  		).withContextOptions(
  1055  			fvm.WithTransactionFeesEnabled(true),
  1056  		).run(
  1057  			runTx(tc)),
  1058  		)
  1059  	}
  1060  
  1061  	for i, tc := range testCasesWithStorageEnabled {
  1062  		t.Run(fmt.Sprintf("Transaction Fees with storage %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions(
  1063  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1064  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1065  			fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1066  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1067  			fvm.WithExecutionMemoryLimit(math.MaxUint64),
  1068  			fvm.WithExecutionEffortWeights(mainnetExecutionEffortWeights),
  1069  			fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
  1070  		).withContextOptions(
  1071  			fvm.WithTransactionFeesEnabled(true),
  1072  			fvm.WithAccountStorageLimit(true),
  1073  		).run(
  1074  			runTx(tc)),
  1075  		)
  1076  	}
  1077  }
  1078  
  1079  func TestSettingExecutionWeights(t *testing.T) {
  1080  
  1081  	t.Run("transaction should fail with high weights", newVMTest().withBootstrapProcedureOptions(
  1082  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1083  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1084  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1085  		fvm.WithExecutionEffortWeights(
  1086  			meter.ExecutionEffortWeights{
  1087  				common.ComputationKindLoop: 100_000 << meter.MeterExecutionInternalPrecisionBytes,
  1088  			},
  1089  		),
  1090  	).run(
  1091  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1092  
  1093  			txBody := flow.NewTransactionBody().
  1094  				SetScript([]byte(`
  1095  				transaction {
  1096                    prepare(signer: AuthAccount) {
  1097  					var a = 0
  1098  					while a < 100 {
  1099  						a = a + 1
  1100  					}
  1101                    }
  1102                  }
  1103  			`)).
  1104  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1105  				AddAuthorizer(chain.ServiceAddress()).
  1106  				SetPayer(chain.ServiceAddress())
  1107  
  1108  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1109  			require.NoError(t, err)
  1110  
  1111  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1112  			err = vm.Run(ctx, tx, view)
  1113  			require.NoError(t, err)
  1114  
  1115  			assert.True(t, errors.IsComputationLimitExceededError(tx.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, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1138  
  1139  			// Create an account private key.
  1140  			privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1141  			require.NoError(t, err)
  1142  
  1143  			// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1144  			accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1145  			require.NoError(t, err)
  1146  
  1147  			txBody := flow.NewTransactionBody().
  1148  				SetScript([]byte(`
  1149  				transaction {
  1150                    prepare(signer: AuthAccount) {
  1151  					var a = 1
  1152                    }
  1153                  }
  1154  			`)).
  1155  				SetProposalKey(accounts[0], 0, 0).
  1156  				AddAuthorizer(accounts[0]).
  1157  				SetPayer(accounts[0])
  1158  
  1159  			err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0)
  1160  			require.NoError(t, err)
  1161  
  1162  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1163  			err = vm.Run(ctx, tx, view)
  1164  			require.NoError(t, err)
  1165  			require.Greater(t, tx.MemoryEstimate, uint64(highWeight))
  1166  
  1167  			assert.True(t, errors.IsMemoryLimitExceededError(tx.Err))
  1168  		},
  1169  	))
  1170  
  1171  	t.Run("service account transactions should not fail with high memory weights", newVMTest().withBootstrapProcedureOptions(
  1172  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1173  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1174  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1175  		fvm.WithExecutionMemoryWeights(
  1176  			memoryWeights,
  1177  		),
  1178  	).withContextOptions(
  1179  		fvm.WithMemoryLimit(10_000_000_000),
  1180  	).run(
  1181  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1182  
  1183  			txBody := flow.NewTransactionBody().
  1184  				SetScript([]byte(`
  1185  				transaction {
  1186                    prepare(signer: AuthAccount) {
  1187  					var a = 1
  1188                    }
  1189                  }
  1190  			`)).
  1191  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1192  				AddAuthorizer(chain.ServiceAddress()).
  1193  				SetPayer(chain.ServiceAddress())
  1194  
  1195  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1196  			require.NoError(t, err)
  1197  
  1198  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1199  			err = vm.Run(ctx, tx, view)
  1200  			require.NoError(t, err)
  1201  			require.Greater(t, tx.MemoryEstimate, uint64(highWeight))
  1202  
  1203  			require.NoError(t, tx.Err)
  1204  		},
  1205  	))
  1206  
  1207  	memoryWeights = make(map[common.MemoryKind]uint64)
  1208  	for k, v := range meter.DefaultMemoryWeights {
  1209  		memoryWeights[k] = v
  1210  	}
  1211  	memoryWeights[common.MemoryKindBreakStatement] = 1_000_000
  1212  	t.Run("transaction should fail with low memory limit (set in the state)", newVMTest().withBootstrapProcedureOptions(
  1213  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1214  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1215  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1216  		fvm.WithExecutionMemoryLimit(
  1217  			100_000_000,
  1218  		),
  1219  		fvm.WithExecutionMemoryWeights(
  1220  			memoryWeights,
  1221  		),
  1222  	).run(
  1223  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1224  			privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1225  			require.NoError(t, err)
  1226  
  1227  			accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1228  			require.NoError(t, err)
  1229  
  1230  			// This transaction is specially designed to use a lot of breaks
  1231  			// as the weight for breaks is much higher than usual.
  1232  			// putting a `while true {break}` in a loop does not use the same amount of memory.
  1233  			txBody := flow.NewTransactionBody().
  1234  				SetScript([]byte(`
  1235  				transaction {
  1236  					prepare(signer: AuthAccount) {
  1237  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1238  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1239  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1240  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1241  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1242  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1243  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1244  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1245  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1246  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1247  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1248  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1249  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1250  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1251  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1252  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1253  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1254  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1255  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1256  						while true {break};while true {break};while true {break};while true {break};while true {break};
  1257  					}
  1258  				}
  1259  			`))
  1260  
  1261  			err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0)
  1262  			require.NoError(t, err)
  1263  
  1264  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1265  			err = vm.Run(ctx, tx, view)
  1266  			require.NoError(t, err)
  1267  			// There are 100 breaks and each break uses 1_000_000 memory
  1268  			require.Greater(t, tx.MemoryEstimate, uint64(100_000_000))
  1269  
  1270  			assert.True(t, errors.IsMemoryLimitExceededError(tx.Err))
  1271  		},
  1272  	))
  1273  
  1274  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1275  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1276  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1277  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1278  		fvm.WithExecutionEffortWeights(
  1279  			meter.ExecutionEffortWeights{
  1280  				environment.ComputationKindCreateAccount: (fvm.DefaultComputationLimit + 1) << meter.MeterExecutionInternalPrecisionBytes,
  1281  			},
  1282  		),
  1283  	).run(
  1284  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1285  			txBody := flow.NewTransactionBody().
  1286  				SetScript([]byte(`
  1287  				transaction {
  1288                    prepare(signer: AuthAccount) {
  1289  					AuthAccount(payer: signer)
  1290                    }
  1291                  }
  1292  			`)).
  1293  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1294  				AddAuthorizer(chain.ServiceAddress()).
  1295  				SetPayer(chain.ServiceAddress())
  1296  
  1297  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1298  			require.NoError(t, err)
  1299  
  1300  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1301  			err = vm.Run(ctx, tx, view)
  1302  			require.NoError(t, err)
  1303  
  1304  			assert.True(t, errors.IsComputationLimitExceededError(tx.Err))
  1305  		},
  1306  	))
  1307  
  1308  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1309  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1310  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1311  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1312  		fvm.WithExecutionEffortWeights(
  1313  			meter.ExecutionEffortWeights{
  1314  				environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes,
  1315  			},
  1316  		),
  1317  	).run(
  1318  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1319  
  1320  			txBody := flow.NewTransactionBody().
  1321  				SetScript([]byte(`
  1322  				transaction {
  1323                    prepare(signer: AuthAccount) {
  1324  					AuthAccount(payer: signer)
  1325                    }
  1326                  }
  1327  			`)).
  1328  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1329  				AddAuthorizer(chain.ServiceAddress()).
  1330  				SetPayer(chain.ServiceAddress())
  1331  
  1332  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1333  			require.NoError(t, err)
  1334  
  1335  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1336  			err = vm.Run(ctx, tx, view)
  1337  			require.NoError(t, err)
  1338  
  1339  			assert.True(t, errors.IsComputationLimitExceededError(tx.Err))
  1340  		},
  1341  	))
  1342  
  1343  	t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions(
  1344  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1345  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1346  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1347  		fvm.WithExecutionEffortWeights(
  1348  			meter.ExecutionEffortWeights{
  1349  				environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes,
  1350  			},
  1351  		),
  1352  	).run(
  1353  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1354  			txBody := flow.NewTransactionBody().
  1355  				SetScript([]byte(`
  1356  				transaction {
  1357                    prepare(signer: AuthAccount) {
  1358  					AuthAccount(payer: signer)
  1359                    }
  1360                  }
  1361  			`)).
  1362  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1363  				AddAuthorizer(chain.ServiceAddress()).
  1364  				SetPayer(chain.ServiceAddress())
  1365  
  1366  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1367  			require.NoError(t, err)
  1368  
  1369  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1370  			err = vm.Run(ctx, tx, view)
  1371  			require.NoError(t, err)
  1372  
  1373  			assert.True(t, errors.IsComputationLimitExceededError(tx.Err))
  1374  		},
  1375  	))
  1376  
  1377  	t.Run("transaction should not use up more computation that the transaction body itself", newVMTest().withBootstrapProcedureOptions(
  1378  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  1379  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1380  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  1381  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  1382  		fvm.WithExecutionEffortWeights(
  1383  			meter.ExecutionEffortWeights{
  1384  				common.ComputationKindStatement:          0,
  1385  				common.ComputationKindLoop:               1 << meter.MeterExecutionInternalPrecisionBytes,
  1386  				common.ComputationKindFunctionInvocation: 0,
  1387  			},
  1388  		),
  1389  	).withContextOptions(
  1390  		fvm.WithAccountStorageLimit(true),
  1391  		fvm.WithTransactionFeesEnabled(true),
  1392  		fvm.WithMemoryLimit(math.MaxUint64),
  1393  	).run(
  1394  		func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1395  			// Use the maximum amount of computation so that the transaction still passes.
  1396  			loops := uint64(997)
  1397  			maxExecutionEffort := uint64(997)
  1398  			txBody := flow.NewTransactionBody().
  1399  				SetScript([]byte(fmt.Sprintf(`
  1400  				transaction() {prepare(signer: AuthAccount){var i=0;  while i < %d {i = i +1 } } execute{}}
  1401  			`, loops))).
  1402  				SetProposalKey(chain.ServiceAddress(), 0, 0).
  1403  				AddAuthorizer(chain.ServiceAddress()).
  1404  				SetPayer(chain.ServiceAddress()).
  1405  				SetGasLimit(maxExecutionEffort)
  1406  
  1407  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  1408  			require.NoError(t, err)
  1409  
  1410  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1411  			err = vm.Run(ctx, tx, view)
  1412  			require.NoError(t, err)
  1413  			require.NoError(t, tx.Err)
  1414  
  1415  			// expected used is number of loops.
  1416  			assert.Equal(t, loops, tx.ComputationUsed)
  1417  
  1418  			// increasing the number of loops should fail the transaction.
  1419  			loops = loops + 1
  1420  			txBody = flow.NewTransactionBody().
  1421  				SetScript([]byte(fmt.Sprintf(`
  1422  				transaction() {prepare(signer: AuthAccount){var i=0;  while i < %d {i = i +1 } } execute{}}
  1423  			`, loops))).
  1424  				SetProposalKey(chain.ServiceAddress(), 0, 1).
  1425  				AddAuthorizer(chain.ServiceAddress()).
  1426  				SetPayer(chain.ServiceAddress()).
  1427  				SetGasLimit(maxExecutionEffort)
  1428  
  1429  			err = testutil.SignTransactionAsServiceAccount(txBody, 1, chain)
  1430  			require.NoError(t, err)
  1431  
  1432  			tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1433  			err = vm.Run(ctx, tx, view)
  1434  			require.NoError(t, err)
  1435  
  1436  			require.ErrorContains(t, tx.Err, "computation exceeds limit (997)")
  1437  			// computation used should the actual computation used.
  1438  			assert.Equal(t, loops, tx.ComputationUsed)
  1439  
  1440  			for _, event := range tx.Events {
  1441  				// the fee deduction event should only contain the max gas worth of execution effort.
  1442  				if strings.Contains(string(event.Type), "FlowFees.FeesDeducted") {
  1443  					ev, err := jsoncdc.Decode(nil, event.Payload)
  1444  					require.NoError(t, err)
  1445  					assert.Equal(t, maxExecutionEffort, ev.(cadence.Event).Fields[2].ToGoValue().(uint64))
  1446  				}
  1447  			}
  1448  			unittest.EnsureEventsIndexSeq(t, tx.Events, chain.ChainID())
  1449  		},
  1450  	))
  1451  }
  1452  
  1453  func TestStorageUsed(t *testing.T) {
  1454  	t.Parallel()
  1455  
  1456  	chain, vm := createChainAndVm(flow.Testnet)
  1457  
  1458  	ctx := fvm.NewContext(
  1459  		fvm.WithChain(chain),
  1460  		fvm.WithCadenceLogging(true),
  1461  	)
  1462  
  1463  	code := []byte(`
  1464          pub fun main(): UInt64 {
  1465  
  1466              var addresses: [Address]= [
  1467                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1468                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1469                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1470                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1471                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1472                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1473                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1474                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1475                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1476                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731,
  1477                  0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731
  1478              ]
  1479  
  1480              var storageUsed: UInt64 = 0
  1481              for address in addresses {
  1482                  let account = getAccount(address)
  1483                  storageUsed = account.storageUsed
  1484              }
  1485  
  1486              return storageUsed
  1487          }
  1488  	`)
  1489  
  1490  	address, err := hex.DecodeString("2a3c4c2581cef731")
  1491  	require.NoError(t, err)
  1492  
  1493  	simpleView := utils.NewSimpleView()
  1494  	status := environment.NewAccountStatus()
  1495  	status.SetStorageUsed(5)
  1496  	err = simpleView.Set(string(address), state.AccountStatusKey, status.ToBytes())
  1497  	require.NoError(t, err)
  1498  
  1499  	script := fvm.Script(code)
  1500  
  1501  	err = vm.Run(ctx, script, simpleView)
  1502  	require.NoError(t, err)
  1503  
  1504  	assert.Equal(t, cadence.NewUInt64(5), script.Value)
  1505  }
  1506  
  1507  func TestEnforcingComputationLimit(t *testing.T) {
  1508  	t.Parallel()
  1509  
  1510  	chain, vm := createChainAndVm(flow.Testnet)
  1511  	simpleView := utils.NewSimpleView()
  1512  
  1513  	const computationLimit = 5
  1514  
  1515  	type test struct {
  1516  		name           string
  1517  		code           string
  1518  		payerIsServAcc bool
  1519  		ok             bool
  1520  		expCompUsed    uint64
  1521  	}
  1522  
  1523  	tests := []test{
  1524  		{
  1525  			name: "infinite while loop",
  1526  			code: `
  1527  		      while true {}
  1528  		    `,
  1529  			payerIsServAcc: false,
  1530  			ok:             false,
  1531  			expCompUsed:    computationLimit + 1,
  1532  		},
  1533  		{
  1534  			name: "limited while loop",
  1535  			code: `
  1536                var i = 0
  1537                while i < 5 {
  1538                    i = i + 1
  1539                }
  1540              `,
  1541  			payerIsServAcc: false,
  1542  			ok:             false,
  1543  			expCompUsed:    computationLimit + 1,
  1544  		},
  1545  		{
  1546  			name: "too many for-in loop iterations",
  1547  			code: `
  1548                for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {}
  1549              `,
  1550  			payerIsServAcc: false,
  1551  			ok:             false,
  1552  			expCompUsed:    computationLimit + 1,
  1553  		},
  1554  		{
  1555  			name: "too many for-in loop iterations",
  1556  			code: `
  1557                for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {}
  1558              `,
  1559  			payerIsServAcc: true,
  1560  			ok:             true,
  1561  			expCompUsed:    11,
  1562  		},
  1563  		{
  1564  			name: "some for-in loop iterations",
  1565  			code: `
  1566                for i in [1, 2, 3, 4] {}
  1567              `,
  1568  			payerIsServAcc: false,
  1569  			ok:             true,
  1570  			expCompUsed:    5,
  1571  		},
  1572  	}
  1573  
  1574  	for _, test := range tests {
  1575  
  1576  		t.Run(test.name, func(t *testing.T) {
  1577  			derivedBlockData := derived.NewEmptyDerivedBlockData()
  1578  
  1579  			ctx := fvm.NewContext(
  1580  				fvm.WithChain(chain),
  1581  				fvm.WithAuthorizationChecksEnabled(false),
  1582  				fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
  1583  				fvm.WithDerivedBlockData(derivedBlockData),
  1584  			)
  1585  
  1586  			script := []byte(
  1587  				fmt.Sprintf(
  1588  					`
  1589                        transaction {
  1590                            prepare() {
  1591                                %s
  1592                            }
  1593                        }
  1594                      `,
  1595  					test.code,
  1596  				),
  1597  			)
  1598  
  1599  			txBody := flow.NewTransactionBody().
  1600  				SetScript(script).
  1601  				SetGasLimit(computationLimit)
  1602  
  1603  			if test.payerIsServAcc {
  1604  				txBody.SetPayer(chain.ServiceAddress()).
  1605  					SetGasLimit(0)
  1606  			}
  1607  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1608  
  1609  			err := vm.Run(ctx, tx, simpleView)
  1610  			require.NoError(t, err)
  1611  			require.Equal(t, test.expCompUsed, tx.ComputationUsed)
  1612  			if test.ok {
  1613  				require.NoError(t, tx.Err)
  1614  			} else {
  1615  				require.Error(t, tx.Err)
  1616  			}
  1617  
  1618  		})
  1619  	}
  1620  }
  1621  
  1622  func TestStorageCapacity(t *testing.T) {
  1623  	t.Run("Storage capacity updates on FLOW transfer", newVMTest().
  1624  		withContextOptions(
  1625  			fvm.WithAuthorizationChecksEnabled(false),
  1626  			fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
  1627  			fvm.WithCadenceLogging(true),
  1628  		).
  1629  		withBootstrapProcedureOptions(
  1630  			fvm.WithStorageMBPerFLOW(10_0000_0000),
  1631  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  1632  		).
  1633  		run(func(
  1634  			t *testing.T,
  1635  			vm fvm.VM,
  1636  			chain flow.Chain,
  1637  			ctx fvm.Context,
  1638  			view state.View,
  1639  			derivedBlockData *derived.DerivedBlockData,
  1640  		) {
  1641  			service := chain.ServiceAddress()
  1642  			signer := createAccount(t, vm, chain, ctx, view, derivedBlockData)
  1643  			target := createAccount(t, vm, chain, ctx, view, derivedBlockData)
  1644  
  1645  			// Transfer FLOW from service account to test accounts
  1646  
  1647  			transferTxBody := transferTokensTx(chain).
  1648  				AddAuthorizer(service).
  1649  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  1650  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(signer))).
  1651  				SetProposalKey(service, 0, 0).
  1652  				SetPayer(service)
  1653  			tx := fvm.Transaction(transferTxBody, derivedBlockData.NextTxIndexForTestingOnly())
  1654  			err := vm.Run(ctx, tx, view)
  1655  			require.NoError(t, err)
  1656  			require.NoError(t, tx.Err)
  1657  
  1658  			transferTxBody = transferTokensTx(chain).
  1659  				AddAuthorizer(service).
  1660  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  1661  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))).
  1662  				SetProposalKey(service, 0, 0).
  1663  				SetPayer(service)
  1664  			tx = fvm.Transaction(transferTxBody, derivedBlockData.NextTxIndexForTestingOnly())
  1665  			err = vm.Run(ctx, tx, view)
  1666  			require.NoError(t, err)
  1667  			require.NoError(t, tx.Err)
  1668  
  1669  			// Perform test
  1670  
  1671  			txBody := flow.NewTransactionBody().
  1672  				SetScript([]byte(fmt.Sprintf(`
  1673  					import FungibleToken from 0x%s
  1674  					import FlowToken from 0x%s
  1675  		
  1676  					transaction(target: Address) {
  1677  						prepare(signer: AuthAccount) {
  1678  							let receiverRef = getAccount(target)
  1679  								.getCapability(/public/flowTokenReceiver)
  1680  								.borrow<&{FungibleToken.Receiver}>()
  1681  								?? panic("Could not borrow receiver reference to the recipient''s Vault")
  1682  							
  1683  							let vaultRef = signer
  1684  								.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)
  1685  								?? panic("Could not borrow reference to the owner''s Vault!")
  1686  							
  1687  							var cap0: UInt64 = signer.storageCapacity
  1688  							
  1689  							receiverRef.deposit(from: <- vaultRef.withdraw(amount: 0.0000001))
  1690  							
  1691  							var cap1: UInt64 = signer.storageCapacity
  1692  							
  1693  							log(cap0 - cap1)
  1694  						}
  1695  					}`,
  1696  					fvm.FungibleTokenAddress(chain),
  1697  					fvm.FlowTokenAddress(chain),
  1698  				))).
  1699  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))).
  1700  				AddAuthorizer(signer)
  1701  
  1702  			tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1703  
  1704  			err = vm.Run(ctx, tx, view)
  1705  			require.NoError(t, err)
  1706  			require.NoError(t, tx.Err)
  1707  
  1708  			require.Len(t, tx.Logs, 1)
  1709  			assert.Equal(t, tx.Logs[0], "1")
  1710  		}),
  1711  	)
  1712  }
  1713  
  1714  func TestScriptContractMutationsFailure(t *testing.T) {
  1715  	t.Parallel()
  1716  
  1717  	t.Run("contract additions are not committed",
  1718  		newVMTest().run(
  1719  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1720  
  1721  				// Create an account private key.
  1722  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1723  				require.NoError(t, err)
  1724  
  1725  				// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1726  				accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1727  				require.NoError(t, err)
  1728  				account := accounts[0]
  1729  				address := cadence.NewAddress(account)
  1730  
  1731  				scriptCtx := fvm.NewContextFromParent(ctx)
  1732  
  1733  				contract := "pub contract Foo {}"
  1734  
  1735  				script := fvm.Script([]byte(fmt.Sprintf(`
  1736  				pub fun main(account: Address) {
  1737  					let acc = getAuthAccount(account)
  1738  					acc.contracts.add(name: "Foo", code: "%s".decodeHex())
  1739  				}`, hex.EncodeToString([]byte(contract))),
  1740  				)).WithArguments(
  1741  					jsoncdc.MustEncode(address),
  1742  				)
  1743  
  1744  				err = vm.Run(scriptCtx, script, view)
  1745  				require.NoError(t, err)
  1746  				require.Error(t, script.Err)
  1747  				require.True(t, errors.IsCadenceRuntimeError(script.Err))
  1748  				// modifications to contracts are not supported in scripts
  1749  				require.True(t, errors.IsOperationNotSupportedError(script.Err))
  1750  			},
  1751  		),
  1752  	)
  1753  
  1754  	t.Run("contract removals are not committed",
  1755  		newVMTest().run(
  1756  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1757  
  1758  				// Create an account private key.
  1759  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1760  				privateKey := privateKeys[0]
  1761  				require.NoError(t, err)
  1762  
  1763  				// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1764  				accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1765  				require.NoError(t, err)
  1766  				account := accounts[0]
  1767  				address := cadence.NewAddress(account)
  1768  
  1769  				subCtx := fvm.NewContextFromParent(ctx)
  1770  
  1771  				contract := "pub contract Foo {}"
  1772  
  1773  				txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(`
  1774  					transaction {
  1775  						prepare(signer: AuthAccount, service: AuthAccount) {
  1776  							signer.contracts.add(name: "Foo", code: "%s".decodeHex())
  1777  						}
  1778  					}
  1779  				`, hex.EncodeToString([]byte(contract))))).
  1780  					AddAuthorizer(account).
  1781  					AddAuthorizer(chain.ServiceAddress()).
  1782  					SetPayer(chain.ServiceAddress()).
  1783  					SetProposalKey(chain.ServiceAddress(), 0, 0)
  1784  
  1785  				_ = testutil.SignPayload(txBody, account, privateKey)
  1786  				_ = testutil.SignEnvelope(txBody, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
  1787  				tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1788  				err = vm.Run(subCtx, tx, view)
  1789  				require.NoError(t, err)
  1790  				require.NoError(t, tx.Err)
  1791  
  1792  				script := fvm.Script([]byte(`
  1793  				pub fun main(account: Address) {
  1794  					let acc = getAuthAccount(account)
  1795  					let n = acc.contracts.names[0]
  1796  					acc.contracts.remove(name: n)
  1797  				}`,
  1798  				)).WithArguments(
  1799  					jsoncdc.MustEncode(address),
  1800  				)
  1801  
  1802  				err = vm.Run(subCtx, script, view)
  1803  				require.NoError(t, err)
  1804  				require.Error(t, script.Err)
  1805  				require.True(t, errors.IsCadenceRuntimeError(script.Err))
  1806  				// modifications to contracts are not supported in scripts
  1807  				require.True(t, errors.IsOperationNotSupportedError(script.Err))
  1808  			},
  1809  		),
  1810  	)
  1811  
  1812  	t.Run("contract updates are not committed",
  1813  		newVMTest().run(
  1814  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1815  
  1816  				// Create an account private key.
  1817  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1818  				privateKey := privateKeys[0]
  1819  				require.NoError(t, err)
  1820  
  1821  				// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1822  				accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1823  				require.NoError(t, err)
  1824  				account := accounts[0]
  1825  				address := cadence.NewAddress(account)
  1826  
  1827  				subCtx := fvm.NewContextFromParent(ctx)
  1828  
  1829  				contract := "pub contract Foo {}"
  1830  
  1831  				txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(`
  1832  					transaction {
  1833  						prepare(signer: AuthAccount, service: AuthAccount) {
  1834  							signer.contracts.add(name: "Foo", code: "%s".decodeHex())
  1835  						}
  1836  					}
  1837  				`, hex.EncodeToString([]byte(contract))))).
  1838  					AddAuthorizer(account).
  1839  					AddAuthorizer(chain.ServiceAddress()).
  1840  					SetPayer(chain.ServiceAddress()).
  1841  					SetProposalKey(chain.ServiceAddress(), 0, 0)
  1842  
  1843  				_ = testutil.SignPayload(txBody, account, privateKey)
  1844  				_ = testutil.SignEnvelope(txBody, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
  1845  				tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  1846  				err = vm.Run(subCtx, tx, view)
  1847  				require.NoError(t, err)
  1848  				require.NoError(t, tx.Err)
  1849  
  1850  				script := fvm.Script([]byte(fmt.Sprintf(`
  1851  				pub fun main(account: Address) {
  1852  					let acc = getAuthAccount(account)
  1853  					let n = acc.contracts.names[0]
  1854  					acc.contracts.update__experimental(name: n, code: "%s".decodeHex())
  1855  				}`, hex.EncodeToString([]byte(contract))))).WithArguments(
  1856  					jsoncdc.MustEncode(address),
  1857  				)
  1858  
  1859  				err = vm.Run(subCtx, script, view)
  1860  				require.NoError(t, err)
  1861  				require.Error(t, script.Err)
  1862  				require.True(t, errors.IsCadenceRuntimeError(script.Err))
  1863  				// modifications to contracts are not supported in scripts
  1864  				require.True(t, errors.IsOperationNotSupportedError(script.Err))
  1865  			},
  1866  		),
  1867  	)
  1868  }
  1869  
  1870  func TestScriptAccountKeyMutationsFailure(t *testing.T) {
  1871  	t.Parallel()
  1872  
  1873  	t.Run("Account key additions are not committed",
  1874  		newVMTest().run(
  1875  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1876  
  1877  				// Create an account private key.
  1878  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1879  				require.NoError(t, err)
  1880  
  1881  				// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1882  				accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1883  				require.NoError(t, err)
  1884  				account := accounts[0]
  1885  				address := cadence.NewAddress(account)
  1886  
  1887  				scriptCtx := fvm.NewContextFromParent(ctx)
  1888  
  1889  				seed := make([]byte, crypto.KeyGenSeedMinLenECDSAP256)
  1890  				_, _ = rand.Read(seed)
  1891  
  1892  				privateKey, _ := crypto.GeneratePrivateKey(crypto.ECDSAP256, seed)
  1893  
  1894  				script := fvm.Script([]byte(`
  1895  					pub fun main(account: Address, k: [UInt8]) {
  1896  						let acc = getAuthAccount(account)
  1897  						acc.addPublicKey(k)
  1898  					}`,
  1899  				)).WithArguments(
  1900  					jsoncdc.MustEncode(address),
  1901  					jsoncdc.MustEncode(testutil.BytesToCadenceArray(
  1902  						privateKey.PublicKey().Encode(),
  1903  					)),
  1904  				)
  1905  
  1906  				err = vm.Run(scriptCtx, script, view)
  1907  				require.NoError(t, err)
  1908  				require.Error(t, script.Err)
  1909  				require.True(t, errors.IsCadenceRuntimeError(script.Err))
  1910  				// modifications to public keys are not supported in scripts
  1911  				require.True(t, errors.IsOperationNotSupportedError(script.Err))
  1912  			},
  1913  		),
  1914  	)
  1915  
  1916  	t.Run("Account key removals are not committed",
  1917  		newVMTest().run(
  1918  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  1919  
  1920  				// Create an account private key.
  1921  				privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
  1922  				require.NoError(t, err)
  1923  
  1924  				// Bootstrap a ledger, creating accounts with the provided private keys and the root account.
  1925  				accounts, err := testutil.CreateAccounts(vm, view, derivedBlockData, privateKeys, chain)
  1926  				require.NoError(t, err)
  1927  				account := accounts[0]
  1928  				address := cadence.NewAddress(account)
  1929  
  1930  				scriptCtx := fvm.NewContextFromParent(ctx)
  1931  
  1932  				script := fvm.Script([]byte(`
  1933  				pub fun main(account: Address) {
  1934  					let acc = getAuthAccount(account)
  1935  					acc.removePublicKey(0)
  1936  				}`,
  1937  				)).WithArguments(
  1938  					jsoncdc.MustEncode(address),
  1939  				)
  1940  
  1941  				err = vm.Run(scriptCtx, script, view)
  1942  				require.NoError(t, err)
  1943  				require.Error(t, script.Err)
  1944  				require.True(t, errors.IsCadenceRuntimeError(script.Err))
  1945  				// modifications to public keys are not supported in scripts
  1946  				require.True(t, errors.IsOperationNotSupportedError(script.Err))
  1947  			},
  1948  		),
  1949  	)
  1950  }
  1951  
  1952  func TestInteractionLimit(t *testing.T) {
  1953  	type testCase struct {
  1954  		name             string
  1955  		interactionLimit uint64
  1956  		require          func(t *testing.T, tx *fvm.TransactionProcedure)
  1957  	}
  1958  
  1959  	testCases := []testCase{
  1960  		{
  1961  			name:             "high limit succeeds",
  1962  			interactionLimit: math.MaxUint64,
  1963  			require: func(t *testing.T, tx *fvm.TransactionProcedure) {
  1964  				require.NoError(t, tx.Err)
  1965  				require.Len(t, tx.Events, 5)
  1966  			},
  1967  		},
  1968  		{
  1969  			name:             "default limit succeeds",
  1970  			interactionLimit: fvm.DefaultMaxInteractionSize,
  1971  			require: func(t *testing.T, tx *fvm.TransactionProcedure) {
  1972  				require.NoError(t, tx.Err)
  1973  				require.Len(t, tx.Events, 5)
  1974  				unittest.EnsureEventsIndexSeq(t, tx.Events, flow.Testnet.Chain().ChainID())
  1975  			},
  1976  		},
  1977  		{
  1978  			name:             "low limit succeeds",
  1979  			interactionLimit: 170000,
  1980  			require: func(t *testing.T, tx *fvm.TransactionProcedure) {
  1981  				require.NoError(t, tx.Err)
  1982  				require.Len(t, tx.Events, 5)
  1983  				unittest.EnsureEventsIndexSeq(t, tx.Events, flow.Testnet.Chain().ChainID())
  1984  			},
  1985  		},
  1986  		{
  1987  			name:             "even lower low limit fails, and has only 3 events",
  1988  			interactionLimit: 10000,
  1989  			require: func(t *testing.T, tx *fvm.TransactionProcedure) {
  1990  				require.Error(t, tx.Err)
  1991  				require.Len(t, tx.Events, 3)
  1992  				unittest.EnsureEventsIndexSeq(t, tx.Events, flow.Testnet.Chain().ChainID())
  1993  			},
  1994  		},
  1995  	}
  1996  
  1997  	// === setup ===
  1998  	// setup an address with some funds
  1999  	var privateKey flow.AccountPrivateKey
  2000  	var address flow.Address
  2001  	vmt, err := newVMTest().withBootstrapProcedureOptions(
  2002  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
  2003  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
  2004  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
  2005  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
  2006  		fvm.WithExecutionMemoryLimit(math.MaxUint64),
  2007  	).withContextOptions(
  2008  		fvm.WithTransactionFeesEnabled(true),
  2009  		fvm.WithAccountStorageLimit(true),
  2010  	).bootstrapWith(
  2011  		func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) error {
  2012  			// ==== Create an account ====
  2013  			var txBody *flow.TransactionBody
  2014  			privateKey, txBody = testutil.CreateAccountCreationTransaction(t, chain)
  2015  
  2016  			err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
  2017  			if err != nil {
  2018  				return err
  2019  			}
  2020  
  2021  			tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  2022  
  2023  			err = vm.Run(ctx, tx, view)
  2024  			if err != nil {
  2025  				return err
  2026  			}
  2027  			if tx.Err != nil {
  2028  				return tx.Err
  2029  			}
  2030  
  2031  			accountCreatedEvents := filterAccountCreatedEvents(tx.Events)
  2032  
  2033  			// read the address of the account created (e.g. "0x01" and convert it to flow.address)
  2034  			data, err := jsoncdc.Decode(nil, accountCreatedEvents[0].Payload)
  2035  			if err != nil {
  2036  				return err
  2037  			}
  2038  			address = flow.Address(data.(cadence.Event).Fields[0].(cadence.Address))
  2039  
  2040  			// ==== Transfer tokens to new account ====
  2041  			txBody = transferTokensTx(chain).
  2042  				AddAuthorizer(chain.ServiceAddress()).
  2043  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))).
  2044  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address)))
  2045  
  2046  			txBody.SetProposalKey(chain.ServiceAddress(), 0, 1)
  2047  			txBody.SetPayer(chain.ServiceAddress())
  2048  
  2049  			err = testutil.SignEnvelope(
  2050  				txBody,
  2051  				chain.ServiceAddress(),
  2052  				unittest.ServiceAccountPrivateKey,
  2053  			)
  2054  			if err != nil {
  2055  				return err
  2056  			}
  2057  
  2058  			tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  2059  
  2060  			err = vm.Run(ctx, tx, view)
  2061  			if err != nil {
  2062  				return err
  2063  			}
  2064  			if tx.Err != nil {
  2065  				return tx.Err
  2066  			}
  2067  			return nil
  2068  		},
  2069  	)
  2070  	require.NoError(t, err)
  2071  
  2072  	for _, tc := range testCases {
  2073  		t.Run(tc.name, vmt.run(
  2074  			func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, view state.View, derivedBlockData *derived.DerivedBlockData) {
  2075  				// ==== Transfer funds with lowe interaction limit ====
  2076  				txBody := transferTokensTx(chain).
  2077  					AddAuthorizer(address).
  2078  					AddArgument(jsoncdc.MustEncode(cadence.UFix64(1))).
  2079  					AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress())))
  2080  
  2081  				txBody.SetProposalKey(address, 0, 0)
  2082  				txBody.SetPayer(address)
  2083  
  2084  				hasher, err := exeUtils.NewHasher(privateKey.HashAlgo)
  2085  				require.NoError(t, err)
  2086  
  2087  				sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher)
  2088  				require.NoError(t, err)
  2089  				txBody.AddEnvelopeSignature(address, 0, sig)
  2090  
  2091  				tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly())
  2092  
  2093  				// ==== IMPORTANT LINE ====
  2094  				ctx.MaxStateInteractionSize = tc.interactionLimit
  2095  
  2096  				err = vm.Run(ctx, tx, view)
  2097  				require.NoError(t, err)
  2098  				tc.require(t, tx)
  2099  			}),
  2100  		)
  2101  	}
  2102  }