github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/execution_verification_test.go (about)

     1  package computation
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/ipfs/go-datastore"
    10  	dssync "github.com/ipfs/go-datastore/sync"
    11  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    12  	"github.com/onflow/cadence"
    13  	jsoncdc "github.com/onflow/cadence/encoding/json"
    14  	"github.com/rs/zerolog"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/onflow/flow-go/crypto"
    19  	"github.com/koko1123/flow-go-1/engine/execution"
    20  	"github.com/koko1123/flow-go-1/engine/execution/computation/committer"
    21  	"github.com/koko1123/flow-go-1/engine/execution/computation/computer"
    22  	"github.com/koko1123/flow-go-1/engine/execution/state"
    23  	bootstrapexec "github.com/koko1123/flow-go-1/engine/execution/state/bootstrap"
    24  	"github.com/koko1123/flow-go-1/engine/execution/state/delta"
    25  	"github.com/koko1123/flow-go-1/engine/execution/testutil"
    26  	"github.com/koko1123/flow-go-1/engine/execution/utils"
    27  	"github.com/koko1123/flow-go-1/engine/testutil/mocklocal"
    28  	"github.com/koko1123/flow-go-1/engine/verification/fetcher"
    29  	"github.com/koko1123/flow-go-1/fvm"
    30  	"github.com/koko1123/flow-go-1/fvm/blueprints"
    31  	"github.com/koko1123/flow-go-1/fvm/derived"
    32  	"github.com/koko1123/flow-go-1/fvm/environment"
    33  	"github.com/koko1123/flow-go-1/fvm/errors"
    34  	completeLedger "github.com/koko1123/flow-go-1/ledger/complete"
    35  	"github.com/koko1123/flow-go-1/ledger/complete/wal/fixtures"
    36  	"github.com/koko1123/flow-go-1/model/flow"
    37  	"github.com/koko1123/flow-go-1/model/verification"
    38  	"github.com/koko1123/flow-go-1/module/chunks"
    39  	"github.com/koko1123/flow-go-1/module/executiondatasync/execution_data"
    40  	exedataprovider "github.com/koko1123/flow-go-1/module/executiondatasync/provider"
    41  	mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock"
    42  	"github.com/koko1123/flow-go-1/module/metrics"
    43  	requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest"
    44  	"github.com/koko1123/flow-go-1/module/trace"
    45  	"github.com/koko1123/flow-go-1/utils/unittest"
    46  )
    47  
    48  var chain = flow.Emulator.Chain()
    49  
    50  // In the following tests the system transaction is expected to fail, as the epoch related things are not set up properly.
    51  // This is not relevant to the test, as only the non-system transactions are tested.
    52  
    53  func Test_ExecutionMatchesVerification(t *testing.T) {
    54  	t.Run("empty block", func(t *testing.T) {
    55  		executeBlockAndVerify(t,
    56  			[][]*flow.TransactionBody{},
    57  			fvm.DefaultTransactionFees,
    58  			fvm.DefaultMinimumStorageReservation)
    59  	})
    60  
    61  	t.Run("single transaction event", func(t *testing.T) {
    62  
    63  		deployTx := blueprints.DeployContractTransaction(chain.ServiceAddress(), []byte(""+
    64  			`pub contract Foo {
    65  				pub event FooEvent(x: Int, y: Int)
    66  
    67  				pub fun event() { 
    68  					emit FooEvent(x: 2, y: 1)
    69  				}
    70  			}`), "Foo")
    71  
    72  		emitTx := &flow.TransactionBody{
    73  			Script: []byte(fmt.Sprintf(`
    74  			import Foo from 0x%s
    75  			transaction {
    76  				prepare() {}
    77  				execute {
    78  					Foo.event()
    79  				}
    80  			}`, chain.ServiceAddress())),
    81  		}
    82  
    83  		err := testutil.SignTransactionAsServiceAccount(deployTx, 0, chain)
    84  		require.NoError(t, err)
    85  
    86  		err = testutil.SignTransactionAsServiceAccount(emitTx, 1, chain)
    87  		require.NoError(t, err)
    88  
    89  		cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{
    90  			{
    91  				deployTx, emitTx,
    92  			},
    93  		}, fvm.BootstrapProcedureFeeParameters{}, fvm.DefaultMinimumStorageReservation)
    94  
    95  		// ensure event is emitted
    96  		require.Empty(t, cr.TransactionResults[0].ErrorMessage)
    97  		require.Empty(t, cr.TransactionResults[1].ErrorMessage)
    98  		require.Len(t, cr.Events[0], 2)
    99  		require.Equal(t, flow.EventType(fmt.Sprintf("A.%s.Foo.FooEvent", chain.ServiceAddress())), cr.Events[0][1].Type)
   100  	})
   101  
   102  	t.Run("multiple collections events", func(t *testing.T) {
   103  
   104  		deployTx := blueprints.DeployContractTransaction(chain.ServiceAddress(), []byte(""+
   105  			`pub contract Foo {
   106  				pub event FooEvent(x: Int, y: Int)
   107  
   108  				pub fun event() { 
   109  					emit FooEvent(x: 2, y: 1)
   110  				}
   111  			}`), "Foo")
   112  
   113  		emitTx1 := flow.TransactionBody{
   114  			Script: []byte(fmt.Sprintf(`
   115  			import Foo from 0x%s
   116  			transaction {
   117  				prepare() {}
   118  				execute {
   119  					Foo.event()
   120  				}
   121  			}`, chain.ServiceAddress())),
   122  		}
   123  
   124  		// copy txs
   125  		emitTx2 := emitTx1
   126  		emitTx3 := emitTx1
   127  
   128  		err := testutil.SignTransactionAsServiceAccount(deployTx, 0, chain)
   129  		require.NoError(t, err)
   130  
   131  		err = testutil.SignTransactionAsServiceAccount(&emitTx1, 1, chain)
   132  		require.NoError(t, err)
   133  		err = testutil.SignTransactionAsServiceAccount(&emitTx2, 2, chain)
   134  		require.NoError(t, err)
   135  		err = testutil.SignTransactionAsServiceAccount(&emitTx3, 3, chain)
   136  		require.NoError(t, err)
   137  
   138  		cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{
   139  			{
   140  				deployTx, &emitTx1,
   141  			},
   142  			{
   143  				&emitTx2,
   144  			},
   145  			{
   146  				&emitTx3,
   147  			},
   148  		}, fvm.BootstrapProcedureFeeParameters{}, fvm.DefaultMinimumStorageReservation)
   149  
   150  		// ensure event is emitted
   151  		require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   152  		require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   153  		require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   154  		require.Empty(t, cr.TransactionResults[3].ErrorMessage)
   155  		require.Len(t, cr.Events[0], 2)
   156  		require.Equal(t, flow.EventType(fmt.Sprintf("A.%s.Foo.FooEvent", chain.ServiceAddress())), cr.Events[0][1].Type)
   157  	})
   158  
   159  	t.Run("with failed storage limit", func(t *testing.T) {
   160  
   161  		accountPrivKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain)
   162  
   163  		// this should return the address of newly created account
   164  		accountAddress, err := chain.AddressAtIndex(5)
   165  		require.NoError(t, err)
   166  
   167  		err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain)
   168  		require.NoError(t, err)
   169  
   170  		addKeyTx := testutil.CreateAddAnAccountKeyMultipleTimesTransaction(t, &accountPrivKey, 100).AddAuthorizer(accountAddress)
   171  		err = testutil.SignTransaction(addKeyTx, accountAddress, accountPrivKey, 0)
   172  		require.NoError(t, err)
   173  
   174  		minimumStorage, err := cadence.NewUFix64("0.00010807")
   175  		require.NoError(t, err)
   176  
   177  		cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{
   178  			{
   179  				createAccountTx,
   180  			},
   181  			{
   182  				addKeyTx,
   183  			},
   184  		}, fvm.DefaultTransactionFees, minimumStorage)
   185  
   186  		// storage limit error
   187  		assert.Equal(t, cr.TransactionResults[0].ErrorMessage, "")
   188  		// ensure events from the first transaction is emitted
   189  		require.Len(t, cr.Events[0], 10)
   190  		// ensure fee deduction events are emitted even though tx fails
   191  		require.Len(t, cr.Events[1], 3)
   192  		// storage limit error
   193  		assert.Contains(t, cr.TransactionResults[1].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String())
   194  	})
   195  
   196  	t.Run("with failed transaction fee deduction", func(t *testing.T) {
   197  		accountPrivKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain)
   198  		// this should return the address of newly created account
   199  		accountAddress, err := chain.AddressAtIndex(5)
   200  		require.NoError(t, err)
   201  
   202  		err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain)
   203  		require.NoError(t, err)
   204  
   205  		spamTx := &flow.TransactionBody{
   206  			Script: []byte(`
   207  			transaction {
   208  				prepare() {}
   209  				execute {
   210  					var s: Int256 = 1024102410241024
   211  					var i = 0
   212  					var a = Int256(7)
   213  					var b = Int256(5)
   214  					var c = Int256(2)
   215  					while i < 150000 {
   216  						s = s * a
   217  						s = s / b
   218  						s = s / c
   219  						i = i + 1
   220  					}
   221  					log(i)
   222  				}
   223  			}`),
   224  		}
   225  
   226  		spamTx.SetGasLimit(800000)
   227  		err = testutil.SignTransaction(spamTx, accountAddress, accountPrivKey, 0)
   228  		require.NoError(t, err)
   229  
   230  		require.NoError(t, err)
   231  
   232  		cr := executeBlockAndVerifyWithParameters(t, [][]*flow.TransactionBody{
   233  			{
   234  				createAccountTx,
   235  				spamTx,
   236  			},
   237  		},
   238  			[]fvm.Option{
   239  				fvm.WithTransactionFeesEnabled(true),
   240  				fvm.WithAccountStorageLimit(true),
   241  				// make sure we don't run out of memory first.
   242  				fvm.WithMemoryLimit(20_000_000_000),
   243  			}, []fvm.BootstrapProcedureOption{
   244  				fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   245  				fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   246  				fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
   247  				fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   248  				fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   249  			})
   250  
   251  		// no error
   252  		assert.Equal(t, cr.TransactionResults[0].ErrorMessage, "")
   253  
   254  		// ensure events from the first transaction is emitted. Since transactions are in the same block, get all events from Events[0]
   255  		transactionEvents := 0
   256  		for _, event := range cr.Events[0] {
   257  			if event.TransactionID == cr.TransactionResults[0].TransactionID {
   258  				transactionEvents += 1
   259  			}
   260  		}
   261  		require.Equal(t, 10, transactionEvents)
   262  
   263  		assert.Contains(t, cr.TransactionResults[1].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String())
   264  
   265  		// ensure tx fee deduction events are emitted even though tx failed
   266  		transactionEvents = 0
   267  		for _, event := range cr.Events[0] {
   268  			if event.TransactionID == cr.TransactionResults[1].TransactionID {
   269  				transactionEvents += 1
   270  			}
   271  		}
   272  		require.Equal(t, 3, transactionEvents)
   273  	})
   274  
   275  }
   276  
   277  func TestTransactionFeeDeduction(t *testing.T) {
   278  
   279  	type testCase struct {
   280  		name          string
   281  		fundWith      uint64
   282  		tryToTransfer uint64
   283  		checkResult   func(t *testing.T, cr *execution.ComputationResult)
   284  	}
   285  
   286  	txFees := uint64(1_000)
   287  	fundingAmount := uint64(100_000_000)
   288  	transferAmount := uint64(123_456)
   289  
   290  	testCases := []testCase{
   291  		{
   292  			name:          "Transaction fee deduction emits events",
   293  			fundWith:      fundingAmount,
   294  			tryToTransfer: 0,
   295  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   296  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   297  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   298  				require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   299  
   300  				var deposits []flow.Event
   301  				var withdraws []flow.Event
   302  
   303  				for _, e := range cr.Events[2] {
   304  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   305  						deposits = append(deposits, e)
   306  					}
   307  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   308  						withdraws = append(withdraws, e)
   309  					}
   310  				}
   311  
   312  				require.Len(t, deposits, 2)
   313  				require.Len(t, withdraws, 2)
   314  			},
   315  		},
   316  		{
   317  			name:          "If just enough balance, fees are still deducted",
   318  			fundWith:      txFees + transferAmount,
   319  			tryToTransfer: transferAmount,
   320  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   321  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   322  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   323  				require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   324  
   325  				var deposits []flow.Event
   326  				var withdraws []flow.Event
   327  
   328  				for _, e := range cr.Events[2] {
   329  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   330  						deposits = append(deposits, e)
   331  					}
   332  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   333  						withdraws = append(withdraws, e)
   334  					}
   335  				}
   336  
   337  				require.Len(t, deposits, 2)
   338  				require.Len(t, withdraws, 2)
   339  			},
   340  		},
   341  		{
   342  			// this is an edge case that is not applicable to any network.
   343  			// If storage limits were on this would fail due to storage limits
   344  			name:          "If not enough balance, transaction succeeds and fees are deducted to 0",
   345  			fundWith:      txFees,
   346  			tryToTransfer: 1,
   347  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   348  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   349  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   350  				require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   351  
   352  				var deposits []flow.Event
   353  				var withdraws []flow.Event
   354  
   355  				for _, e := range cr.Events[2] {
   356  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   357  						deposits = append(deposits, e)
   358  					}
   359  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   360  						withdraws = append(withdraws, e)
   361  					}
   362  				}
   363  
   364  				require.Len(t, deposits, 2)
   365  				require.Len(t, withdraws, 2)
   366  			},
   367  		},
   368  		{
   369  			name:          "If tx fails, fees are deducted",
   370  			fundWith:      fundingAmount,
   371  			tryToTransfer: 2 * fundingAmount,
   372  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   373  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   374  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   375  				require.Contains(t, cr.TransactionResults[2].ErrorMessage, "Error Code: 1101")
   376  
   377  				var deposits []flow.Event
   378  				var withdraws []flow.Event
   379  
   380  				for _, e := range cr.Events[2] {
   381  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   382  						deposits = append(deposits, e)
   383  					}
   384  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   385  						withdraws = append(withdraws, e)
   386  					}
   387  				}
   388  
   389  				require.Len(t, deposits, 1)
   390  				require.Len(t, withdraws, 1)
   391  			},
   392  		},
   393  	}
   394  
   395  	testCasesWithStorageEnabled := []testCase{
   396  		{
   397  			name:          "Transaction fee deduction emits events",
   398  			fundWith:      fundingAmount,
   399  			tryToTransfer: 0,
   400  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   401  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   402  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   403  				require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   404  
   405  				var deposits []flow.Event
   406  				var withdraws []flow.Event
   407  
   408  				for _, e := range cr.Events[2] {
   409  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   410  						deposits = append(deposits, e)
   411  					}
   412  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   413  						withdraws = append(withdraws, e)
   414  					}
   415  				}
   416  
   417  				require.Len(t, deposits, 2)
   418  				require.Len(t, withdraws, 2)
   419  			},
   420  		},
   421  		{
   422  			name:          "If just enough balance, fees are deducted",
   423  			fundWith:      txFees + transferAmount,
   424  			tryToTransfer: transferAmount,
   425  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   426  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   427  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   428  				require.Empty(t, cr.TransactionResults[2].ErrorMessage)
   429  
   430  				var deposits []flow.Event
   431  				var withdraws []flow.Event
   432  
   433  				for _, e := range cr.Events[2] {
   434  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   435  						deposits = append(deposits, e)
   436  					}
   437  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   438  						withdraws = append(withdraws, e)
   439  					}
   440  				}
   441  
   442  				require.Len(t, deposits, 2)
   443  				require.Len(t, withdraws, 2)
   444  			},
   445  		},
   446  		{
   447  			name:          "If tx fails, fees are still deducted and fee deduction events are emitted",
   448  			fundWith:      fundingAmount,
   449  			tryToTransfer: 2 * fundingAmount,
   450  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   451  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   452  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   453  				require.Contains(t, cr.TransactionResults[2].ErrorMessage, "Error Code: 1101")
   454  
   455  				var deposits []flow.Event
   456  				var withdraws []flow.Event
   457  
   458  				for _, e := range cr.Events[2] {
   459  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   460  						deposits = append(deposits, e)
   461  					}
   462  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   463  						withdraws = append(withdraws, e)
   464  					}
   465  				}
   466  
   467  				require.Len(t, deposits, 1)
   468  				require.Len(t, withdraws, 1)
   469  			},
   470  		},
   471  		{
   472  			name:          "If balance at minimum, transaction fails, fees are deducted and fee deduction events are emitted",
   473  			fundWith:      0,
   474  			tryToTransfer: 0,
   475  			checkResult: func(t *testing.T, cr *execution.ComputationResult) {
   476  				require.Empty(t, cr.TransactionResults[0].ErrorMessage)
   477  				require.Empty(t, cr.TransactionResults[1].ErrorMessage)
   478  				require.Contains(t, cr.TransactionResults[2].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String())
   479  
   480  				var deposits []flow.Event
   481  				var withdraws []flow.Event
   482  
   483  				for _, e := range cr.Events[2] {
   484  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensDeposited", fvm.FlowTokenAddress(chain)) {
   485  						deposits = append(deposits, e)
   486  					}
   487  					if string(e.Type) == fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", fvm.FlowTokenAddress(chain)) {
   488  						withdraws = append(withdraws, e)
   489  					}
   490  				}
   491  
   492  				require.Len(t, deposits, 1)
   493  				require.Len(t, withdraws, 1)
   494  			},
   495  		},
   496  	}
   497  
   498  	transferTokensTx := func(chain flow.Chain) *flow.TransactionBody {
   499  		return flow.NewTransactionBody().
   500  			SetScript([]byte(fmt.Sprintf(`
   501  							// This transaction is a template for a transaction that
   502  							// could be used by anyone to send tokens to another account
   503  							// that has been set up to receive tokens.
   504  							//
   505  							// The withdraw amount and the account from getAccount
   506  							// would be the parameters to the transaction
   507  							
   508  							import FungibleToken from 0x%s
   509  							import FlowToken from 0x%s
   510  							
   511  							transaction(amount: UFix64, to: Address) {
   512  							
   513  								// The Vault resource that holds the tokens that are being transferred
   514  								let sentVault: @FungibleToken.Vault
   515  							
   516  								prepare(signer: AuthAccount) {
   517  							
   518  									// Get a reference to the signer's stored vault
   519  									let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
   520  										?? panic("Could not borrow reference to the owner's Vault!")
   521  							
   522  									// Withdraw tokens from the signer's stored vault
   523  									self.sentVault <- vaultRef.withdraw(amount: amount)
   524  								}
   525  							
   526  								execute {
   527  							
   528  									// Get the recipient's public account object
   529  									let recipient = getAccount(to)
   530  							
   531  									// Get a reference to the recipient's Receiver
   532  									let receiverRef = recipient.getCapability(/public/flowTokenReceiver)
   533  										.borrow<&{FungibleToken.Receiver}>()
   534  										?? panic("Could not borrow receiver reference to the recipient's Vault")
   535  							
   536  									// Deposit the withdrawn tokens in the recipient's receiver
   537  									receiverRef.deposit(from: <-self.sentVault)
   538  								}
   539  							}`, fvm.FungibleTokenAddress(chain), fvm.FlowTokenAddress(chain))),
   540  			)
   541  	}
   542  
   543  	runTx := func(tc testCase,
   544  		opts []fvm.Option,
   545  		bootstrapOpts []fvm.BootstrapProcedureOption) func(t *testing.T) {
   546  		return func(t *testing.T) {
   547  			// ==== Create an account ====
   548  			privateKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain)
   549  
   550  			// this should return the address of newly created account
   551  			address, err := chain.AddressAtIndex(5)
   552  			require.NoError(t, err)
   553  
   554  			err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain)
   555  			require.NoError(t, err)
   556  
   557  			// ==== Transfer tokens to new account ====
   558  			transferTx := transferTokensTx(chain).
   559  				AddAuthorizer(chain.ServiceAddress()).
   560  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.fundWith))).
   561  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address)))
   562  
   563  			transferTx.SetProposalKey(chain.ServiceAddress(), 0, 1)
   564  			transferTx.SetPayer(chain.ServiceAddress())
   565  
   566  			err = testutil.SignEnvelope(
   567  				transferTx,
   568  				chain.ServiceAddress(),
   569  				unittest.ServiceAccountPrivateKey,
   570  			)
   571  			require.NoError(t, err)
   572  
   573  			// ==== Transfer tokens from new account ====
   574  
   575  			transferTx2 := transferTokensTx(chain).
   576  				AddAuthorizer(address).
   577  				AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.tryToTransfer))).
   578  				AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress())))
   579  
   580  			transferTx2.SetProposalKey(address, 0, 0)
   581  			transferTx2.SetPayer(address)
   582  
   583  			err = testutil.SignEnvelope(
   584  				transferTx2,
   585  				address,
   586  				privateKey,
   587  			)
   588  			require.NoError(t, err)
   589  
   590  			cr := executeBlockAndVerifyWithParameters(t, [][]*flow.TransactionBody{
   591  				{
   592  					createAccountTx,
   593  				},
   594  				{
   595  					transferTx,
   596  				},
   597  				{
   598  					transferTx2,
   599  				},
   600  			}, opts, bootstrapOpts)
   601  
   602  			tc.checkResult(t, cr)
   603  		}
   604  	}
   605  
   606  	for i, tc := range testCases {
   607  		t.Run(fmt.Sprintf("Transaction Fees without storage %d: %s", i, tc.name), runTx(tc, []fvm.Option{
   608  			fvm.WithTransactionFeesEnabled(true),
   609  			fvm.WithAccountStorageLimit(false),
   610  		}, []fvm.BootstrapProcedureOption{
   611  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   612  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   613  		}))
   614  	}
   615  
   616  	for i, tc := range testCasesWithStorageEnabled {
   617  		t.Run(fmt.Sprintf("Transaction Fees with storage %d: %s", i, tc.name), runTx(tc, []fvm.Option{
   618  			fvm.WithTransactionFeesEnabled(true),
   619  			fvm.WithAccountStorageLimit(true),
   620  		}, []fvm.BootstrapProcedureOption{
   621  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   622  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   623  			fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
   624  			fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   625  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   626  		}))
   627  	}
   628  }
   629  
   630  func executeBlockAndVerifyWithParameters(t *testing.T,
   631  	txs [][]*flow.TransactionBody,
   632  	opts []fvm.Option,
   633  	bootstrapOpts []fvm.BootstrapProcedureOption) *execution.ComputationResult {
   634  	vm := fvm.NewVirtualMachine()
   635  
   636  	logger := zerolog.Nop()
   637  
   638  	opts = append(opts, fvm.WithChain(chain))
   639  	opts = append(opts, fvm.WithLogger(logger))
   640  	opts = append(opts, fvm.WithBlocks(&environment.NoopBlockFinder{}))
   641  
   642  	fvmContext :=
   643  		fvm.NewContext(
   644  			opts...,
   645  		)
   646  
   647  	collector := metrics.NewNoopCollector()
   648  	tracer := trace.NewNoopTracer()
   649  
   650  	wal := &fixtures.NoopWAL{}
   651  
   652  	ledger, err := completeLedger.NewLedger(wal, 100, collector, logger, completeLedger.DefaultPathFinderVersion)
   653  	require.NoError(t, err)
   654  
   655  	compactor := fixtures.NewNoopCompactor(ledger)
   656  	<-compactor.Ready()
   657  	defer func() {
   658  		<-ledger.Done()
   659  		<-compactor.Done()
   660  	}()
   661  
   662  	bootstrapper := bootstrapexec.NewBootstrapper(logger)
   663  
   664  	initialCommit, err := bootstrapper.BootstrapLedger(
   665  		ledger,
   666  		unittest.ServiceAccountPublicKey,
   667  		chain,
   668  		bootstrapOpts...,
   669  	)
   670  
   671  	require.NoError(t, err)
   672  
   673  	ledgerCommiter := committer.NewLedgerViewCommitter(ledger, tracer)
   674  
   675  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   676  	trackerStorage := mocktracker.NewMockStorage()
   677  
   678  	prov := exedataprovider.NewProvider(
   679  		zerolog.Nop(),
   680  		metrics.NewNoopCollector(),
   681  		execution_data.DefaultSerializer,
   682  		bservice,
   683  		trackerStorage,
   684  	)
   685  
   686  	// generates signing identity including staking key for signing
   687  	myIdentity := unittest.IdentityFixture()
   688  	seed := make([]byte, crypto.KeyGenSeedMinLenBLSBLS12381)
   689  	n, err := rand.Read(seed)
   690  	require.Equal(t, n, crypto.KeyGenSeedMinLenBLSBLS12381)
   691  	require.NoError(t, err)
   692  	sk, err := crypto.GeneratePrivateKey(crypto.BLSBLS12381, seed)
   693  	require.NoError(t, err)
   694  	myIdentity.StakingPubKey = sk.PublicKey()
   695  	me := mocklocal.NewMockLocal(sk, myIdentity.ID(), t)
   696  
   697  	blockComputer, err := computer.NewBlockComputer(
   698  		vm,
   699  		fvmContext,
   700  		collector,
   701  		tracer,
   702  		logger,
   703  		ledgerCommiter,
   704  		me,
   705  		prov)
   706  	require.NoError(t, err)
   707  
   708  	view := delta.NewView(state.LedgerGetRegister(ledger, initialCommit))
   709  
   710  	executableBlock := unittest.ExecutableBlockFromTransactions(chain.ChainID(), txs)
   711  	executableBlock.StartState = &initialCommit
   712  
   713  	computationResult, err := blockComputer.ExecuteBlock(
   714  		context.Background(),
   715  		executableBlock,
   716  		view,
   717  		derived.NewEmptyDerivedBlockData())
   718  	require.NoError(t, err)
   719  
   720  	spockHasher := utils.NewSPOCKHasher()
   721  	for i, snapshot := range computationResult.StateSnapshots {
   722  		valid, err := crypto.SPOCKVerifyAgainstData(
   723  			myIdentity.StakingPubKey,
   724  			computationResult.SpockSignatures[i],
   725  			snapshot.SpockSecret,
   726  			spockHasher)
   727  		require.NoError(t, err)
   728  		require.True(t, valid)
   729  	}
   730  
   731  	prevResultId := unittest.IdentifierFixture()
   732  
   733  	_, chdps, er, err := execution.GenerateExecutionResultAndChunkDataPacks(metrics.NewNoopCollector(), prevResultId, initialCommit, computationResult)
   734  	require.NoError(t, err)
   735  
   736  	verifier := chunks.NewChunkVerifier(vm, fvmContext, logger)
   737  
   738  	vcds := make([]*verification.VerifiableChunkData, er.Chunks.Len())
   739  
   740  	for i, chunk := range er.Chunks {
   741  		isSystemChunk := i == er.Chunks.Len()-1
   742  		offsetForChunk, err := fetcher.TransactionOffsetForChunk(er.Chunks, chunk.Index)
   743  		require.NoError(t, err)
   744  
   745  		vcds[i] = &verification.VerifiableChunkData{
   746  			IsSystemChunk:     isSystemChunk,
   747  			Chunk:             chunk,
   748  			Header:            executableBlock.Block.Header,
   749  			Result:            er,
   750  			ChunkDataPack:     chdps[i],
   751  			EndState:          chunk.EndState,
   752  			TransactionOffset: offsetForChunk,
   753  		}
   754  	}
   755  
   756  	require.Len(t, vcds, len(txs)+1) // +1 for system chunk
   757  
   758  	for _, vcd := range vcds {
   759  		_, fault, err := verifier.Verify(vcd)
   760  		assert.NoError(t, err)
   761  		assert.Nil(t, fault)
   762  	}
   763  
   764  	return computationResult
   765  }
   766  
   767  func executeBlockAndVerify(t *testing.T,
   768  	txs [][]*flow.TransactionBody,
   769  	txFees fvm.BootstrapProcedureFeeParameters,
   770  	minStorageBalance cadence.UFix64) *execution.ComputationResult {
   771  	return executeBlockAndVerifyWithParameters(t,
   772  		txs,
   773  		[]fvm.Option{
   774  			fvm.WithTransactionFeesEnabled(true),
   775  			fvm.WithAccountStorageLimit(true),
   776  		}, []fvm.BootstrapProcedureOption{
   777  			fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   778  			fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   779  			fvm.WithMinimumStorageReservation(minStorageBalance),
   780  			fvm.WithTransactionFee(txFees),
   781  			fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   782  		})
   783  }