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

     1  package handler_test
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"math"
     7  	"math/big"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/onflow/cadence"
    12  	jsoncdc "github.com/onflow/cadence/encoding/json"
    13  	"github.com/onflow/cadence/runtime/common"
    14  	gethCommon "github.com/onflow/go-ethereum/common"
    15  	gethCore "github.com/onflow/go-ethereum/core"
    16  	gethTypes "github.com/onflow/go-ethereum/core/types"
    17  	gethVM "github.com/onflow/go-ethereum/core/vm"
    18  	gethParams "github.com/onflow/go-ethereum/params"
    19  	"github.com/onflow/go-ethereum/rlp"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/onflow/flow-go/fvm/errors"
    24  	"github.com/onflow/flow-go/fvm/evm/emulator"
    25  	"github.com/onflow/flow-go/fvm/evm/handler"
    26  	"github.com/onflow/flow-go/fvm/evm/handler/coa"
    27  	"github.com/onflow/flow-go/fvm/evm/precompiles"
    28  	"github.com/onflow/flow-go/fvm/evm/testutils"
    29  	"github.com/onflow/flow-go/fvm/evm/types"
    30  	"github.com/onflow/flow-go/fvm/systemcontracts"
    31  	"github.com/onflow/flow-go/model/flow"
    32  )
    33  
    34  // TODO add test for fatal errors
    35  
    36  var flowTokenAddress = common.MustBytesToAddress(systemcontracts.SystemContractsForChain(flow.Emulator).FlowToken.Address.Bytes())
    37  var randomBeaconAddress = systemcontracts.SystemContractsForChain(flow.Emulator).RandomBeaconHistory.Address
    38  
    39  func TestHandler_TransactionRunOrPanic(t *testing.T) {
    40  	t.Parallel()
    41  
    42  	t.Run("test RunOrPanic run (happy case)", func(t *testing.T) {
    43  		t.Parallel()
    44  
    45  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
    46  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
    47  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
    48  
    49  					bs := handler.NewBlockStore(backend, rootAddr)
    50  
    51  					aa := handler.NewAddressAllocator()
    52  
    53  					result := &types.Result{
    54  						ReturnedValue: testutils.RandomData(t),
    55  						GasConsumed:   testutils.RandomGas(1000),
    56  						Logs: []*gethTypes.Log{
    57  							testutils.GetRandomLogFixture(t),
    58  							testutils.GetRandomLogFixture(t),
    59  						},
    60  					}
    61  
    62  					em := &testutils.TestEmulator{
    63  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
    64  							return result, nil
    65  						},
    66  					}
    67  					handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
    68  
    69  					coinbase := types.NewAddress(gethCommon.Address{})
    70  
    71  					tx := eoa.PrepareSignAndEncodeTx(
    72  						t,
    73  						gethCommon.Address{},
    74  						nil,
    75  						nil,
    76  						100_000,
    77  						big.NewInt(1),
    78  					)
    79  
    80  					// calculate tx id to match it
    81  					var evmTx gethTypes.Transaction
    82  					err := evmTx.UnmarshalBinary(tx)
    83  					require.NoError(t, err)
    84  					result.TxHash = evmTx.Hash()
    85  
    86  					// successfully run (no-panic)
    87  					handler.RunOrPanic(tx, coinbase)
    88  
    89  					// check gas usage
    90  					// TODO: uncomment and investigate me
    91  					// computationUsed, err := backend.ComputationUsed()
    92  					// require.NoError(t, err)
    93  					// require.Equal(t, result.GasConsumed, computationUsed)
    94  
    95  					// check events (1 extra for block event)
    96  					events := backend.Events()
    97  
    98  					require.Len(t, events, 2)
    99  
   100  					event := events[0]
   101  					assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   102  					ev, err := jsoncdc.Decode(nil, event.Payload)
   103  					require.NoError(t, err)
   104  					cadenceEvent, ok := ev.(cadence.Event)
   105  					require.True(t, ok)
   106  
   107  					// TODO: add an event decoder in types.event
   108  					cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs")
   109  
   110  					encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", ""))
   111  					require.NoError(t, err)
   112  
   113  					var logs []*gethTypes.Log
   114  					err = rlp.DecodeBytes(encodedLogs, &logs)
   115  					require.NoError(t, err)
   116  
   117  					for i, l := range result.Logs {
   118  						assert.Equal(t, l, logs[i])
   119  					}
   120  
   121  					// check block event
   122  					event = events[1]
   123  
   124  					assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   125  					ev, err = jsoncdc.Decode(nil, event.Payload)
   126  					require.NoError(t, err)
   127  
   128  					// make sure block transaction list references the above transaction id
   129  					cadenceEvent, ok = ev.(cadence.Event)
   130  					require.True(t, ok)
   131  					blockEvent, err := types.DecodeBlockEventPayload(cadenceEvent)
   132  					require.NoError(t, err)
   133  
   134  					eventTxID := blockEvent.TransactionHashes[0] // only one hash in block
   135  					// make sure the transaction id included in the block transaction list is the same as tx sumbmitted
   136  					assert.Equal(
   137  						t,
   138  						evmTx.Hash().String(),
   139  						string(eventTxID),
   140  					)
   141  				})
   142  			})
   143  		})
   144  	})
   145  
   146  	t.Run("test RunOrPanic (unhappy non-fatal cases)", func(t *testing.T) {
   147  		t.Parallel()
   148  
   149  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   150  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   151  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   152  					bs := handler.NewBlockStore(backend, rootAddr)
   153  					aa := handler.NewAddressAllocator()
   154  					em := &testutils.TestEmulator{
   155  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   156  							return &types.Result{
   157  								ValidationError: fmt.Errorf("some sort of validation error"),
   158  							}, nil
   159  						},
   160  					}
   161  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   162  
   163  					coinbase := types.NewAddress(gethCommon.Address{})
   164  
   165  					// test RLP decoding (non fatal)
   166  					assertPanic(t, isNotFatal, func() {
   167  						// invalid RLP encoding
   168  						invalidTx := "badencoding"
   169  						handler.RunOrPanic([]byte(invalidTx), coinbase)
   170  					})
   171  
   172  					// test gas limit (non fatal)
   173  					assertPanic(t, isNotFatal, func() {
   174  						gasLimit := uint64(testutils.TestComputationLimit + 1)
   175  						tx := eoa.PrepareSignAndEncodeTx(
   176  							t,
   177  							gethCommon.Address{},
   178  							nil,
   179  							nil,
   180  							gasLimit,
   181  							big.NewInt(1),
   182  						)
   183  
   184  						handler.RunOrPanic([]byte(tx), coinbase)
   185  					})
   186  
   187  					// tx validation error
   188  					assertPanic(t, isNotFatal, func() {
   189  						// tx execution failure
   190  						tx := eoa.PrepareSignAndEncodeTx(
   191  							t,
   192  							gethCommon.Address{},
   193  							nil,
   194  							nil,
   195  							100_000,
   196  							big.NewInt(1),
   197  						)
   198  
   199  						handler.RunOrPanic([]byte(tx), coinbase)
   200  					})
   201  				})
   202  			})
   203  		})
   204  
   205  		t.Run("test RunOrPanic (fatal cases)", func(t *testing.T) {
   206  			t.Parallel()
   207  
   208  			testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   209  				testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   210  					testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   211  						bs := handler.NewBlockStore(backend, rootAddr)
   212  						aa := handler.NewAddressAllocator()
   213  						em := &testutils.TestEmulator{
   214  							RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   215  								return &types.Result{}, types.NewFatalError(fmt.Errorf("Fatal error"))
   216  							},
   217  						}
   218  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   219  						assertPanic(t, errors.IsFailure, func() {
   220  							tx := eoa.PrepareSignAndEncodeTx(
   221  								t,
   222  								gethCommon.Address{},
   223  								nil,
   224  								nil,
   225  								100_000,
   226  								big.NewInt(1),
   227  							)
   228  							handler.RunOrPanic([]byte(tx), types.NewAddress(gethCommon.Address{}))
   229  						})
   230  					})
   231  				})
   232  			})
   233  		})
   234  	})
   235  
   236  	t.Run("test RunOrPanic (with integrated emulator)", func(t *testing.T) {
   237  		t.Parallel()
   238  
   239  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   240  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   241  				handler := SetupHandler(t, backend, rootAddr)
   242  
   243  				eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   244  
   245  				// deposit 1 Flow to the foa account
   246  				addr := handler.DeployCOA(1)
   247  				orgBalance := types.NewBalanceFromUFix64(types.OneFlowInUFix64)
   248  				vault := types.NewFlowTokenVault(orgBalance)
   249  				foa := handler.AccountByAddress(addr, true)
   250  				foa.Deposit(vault)
   251  
   252  				// transfer 0.1 flow to the non-foa address
   253  				deduction := types.NewBalance(big.NewInt(1e17))
   254  				foa.Call(eoa.Address(), nil, 400000, deduction)
   255  				expected, err := types.SubBalance(orgBalance, deduction)
   256  				require.NoError(t, err)
   257  				require.Equal(t, expected, foa.Balance())
   258  
   259  				// transfer 0.01 flow back to the foa through
   260  				addition := types.NewBalance(big.NewInt(1e16))
   261  
   262  				tx := eoa.PrepareSignAndEncodeTx(
   263  					t,
   264  					foa.Address().ToCommon(),
   265  					nil,
   266  					addition,
   267  					gethParams.TxGas*10,
   268  					big.NewInt(1e8), // high gas fee to test coinbase collection,
   269  				)
   270  
   271  				// setup coinbase
   272  				foa2 := handler.DeployCOA(2)
   273  				account2 := handler.AccountByAddress(foa2, true)
   274  				require.Equal(t, types.NewBalanceFromUFix64(0), account2.Balance())
   275  
   276  				// no panic means success here
   277  				handler.RunOrPanic(tx, account2.Address())
   278  				expected, err = types.SubBalance(orgBalance, deduction)
   279  				require.NoError(t, err)
   280  				expected, err = types.AddBalance(expected, addition)
   281  				require.NoError(t, err)
   282  				require.Equal(t, expected, foa.Balance())
   283  
   284  				require.NotEqual(t, types.NewBalanceFromUFix64(0), account2.Balance())
   285  			})
   286  		})
   287  	})
   288  }
   289  
   290  func TestHandler_OpsWithoutEmulator(t *testing.T) {
   291  	t.Parallel()
   292  
   293  	t.Run("test last executed block call", func(t *testing.T) {
   294  		t.Parallel()
   295  
   296  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   297  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   298  				handler := SetupHandler(t, backend, rootAddr)
   299  
   300  				// test call last executed block without initialization
   301  				b := handler.LastExecutedBlock()
   302  				require.Equal(t, types.GenesisBlock, b)
   303  
   304  				// do some changes
   305  				address := testutils.RandomAddress(t)
   306  				account := handler.AccountByAddress(address, true)
   307  				bal := types.OneFlowBalance
   308  				account.Deposit(types.NewFlowTokenVault(bal))
   309  
   310  				// check if block height has been incremented
   311  				b = handler.LastExecutedBlock()
   312  				require.Equal(t, uint64(1), b.Height)
   313  			})
   314  		})
   315  	})
   316  
   317  	t.Run("test address allocation", func(t *testing.T) {
   318  		t.Parallel()
   319  
   320  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   321  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   322  				h := SetupHandler(t, backend, rootAddr)
   323  
   324  				coa := h.DeployCOA(12)
   325  				require.NotNil(t, coa)
   326  
   327  				expectedAddress := handler.MakeCOAAddress(12)
   328  				require.Equal(t, expectedAddress, coa)
   329  			})
   330  		})
   331  	})
   332  }
   333  
   334  func TestHandler_COA(t *testing.T) {
   335  	t.Parallel()
   336  	t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) {
   337  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   338  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   339  				handler := SetupHandler(t, backend, rootAddr)
   340  
   341  				foa := handler.AccountByAddress(handler.DeployCOA(1), true)
   342  				require.NotNil(t, foa)
   343  
   344  				zeroBalance := types.NewBalance(big.NewInt(0))
   345  				require.Equal(t, zeroBalance, foa.Balance())
   346  
   347  				balance := types.OneFlowBalance
   348  				vault := types.NewFlowTokenVault(balance)
   349  
   350  				foa.Deposit(vault)
   351  				require.Equal(t, balance, foa.Balance())
   352  
   353  				v := foa.Withdraw(balance)
   354  				require.Equal(t, balance, v.Balance())
   355  
   356  				require.Equal(t, zeroBalance, foa.Balance())
   357  
   358  				events := backend.Events()
   359  				require.Len(t, events, 6)
   360  
   361  				// first two transactions are for COA setup
   362  
   363  				// transaction event
   364  				event := events[2]
   365  				assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   366  
   367  				// block event
   368  				event = events[3]
   369  				assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   370  
   371  				// transaction event
   372  				event = events[4]
   373  				assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   374  				_, err := jsoncdc.Decode(nil, event.Payload)
   375  				require.NoError(t, err)
   376  				// TODO: decode encoded tx and check for the amount and value
   377  				// assert.Equal(t, foa.Address(), ret.Address)
   378  				// assert.Equal(t, balance, ret.Amount)
   379  
   380  				// block event
   381  				event = events[5]
   382  				assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   383  
   384  				// check gas usage
   385  				computationUsed, err := backend.ComputationUsed()
   386  				require.NoError(t, err)
   387  				require.Greater(t, computationUsed, types.DefaultDirectCallBaseGasUsage*3)
   388  
   389  				// Withdraw with invalid balance
   390  				assertPanic(t, types.IsWithdrawBalanceRoundingError, func() {
   391  					// deposit some money
   392  					foa.Deposit(vault)
   393  					// then withdraw invalid balance
   394  					foa.Withdraw(types.NewBalance(big.NewInt(1)))
   395  				})
   396  			})
   397  		})
   398  	})
   399  
   400  	t.Run("test coa deployment", func(t *testing.T) {
   401  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   402  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   403  				h := SetupHandler(t, backend, rootAddr)
   404  
   405  				coa1 := h.DeployCOA(1)
   406  				acc := h.AccountByAddress(coa1, true)
   407  				require.NotEmpty(t, acc.Code())
   408  
   409  				// make a second account with some money
   410  				coa2 := h.DeployCOA(2)
   411  				acc2 := h.AccountByAddress(coa2, true)
   412  				acc2.Deposit(types.NewFlowTokenVault(types.MakeABalanceInFlow(100)))
   413  
   414  				// transfer money to COA
   415  				acc2.Transfer(
   416  					coa1,
   417  					types.MakeABalanceInFlow(1),
   418  				)
   419  
   420  				// make a call to the contract
   421  				ret := acc2.Call(
   422  					coa1,
   423  					testutils.MakeCallData(t,
   424  						coa.ContractABIJSON,
   425  						"onERC721Received",
   426  						gethCommon.Address{1},
   427  						gethCommon.Address{1},
   428  						big.NewInt(0),
   429  						[]byte{'A'},
   430  					),
   431  					types.GasLimit(3_000_000),
   432  					types.EmptyBalance)
   433  
   434  				// 0x150b7a02
   435  				expected := types.Data([]byte{
   436  					21, 11, 122, 2, 0, 0, 0, 0,
   437  					0, 0, 0, 0, 0, 0, 0, 0,
   438  					0, 0, 0, 0, 0, 0, 0, 0,
   439  					0, 0, 0, 0, 0, 0, 0, 0,
   440  				})
   441  				require.Equal(t, types.StatusSuccessful, ret.Status)
   442  				require.Equal(t, expected, ret.ReturnedValue)
   443  			})
   444  		})
   445  	})
   446  
   447  	t.Run("test withdraw (unhappy case)", func(t *testing.T) {
   448  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   449  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   450  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   451  					bs := handler.NewBlockStore(backend, rootAddr)
   452  					aa := handler.NewAddressAllocator()
   453  
   454  					// Withdraw calls are only possible within FOA accounts
   455  					assertPanic(t, types.IsAUnAuthroizedMethodCallError, func() {
   456  						em := &testutils.TestEmulator{
   457  							NonceOfFunc: func(address types.Address) (uint64, error) {
   458  								return 0, nil
   459  							},
   460  						}
   461  
   462  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   463  
   464  						account := handler.AccountByAddress(testutils.RandomAddress(t), false)
   465  						account.Withdraw(types.NewBalanceFromUFix64(1))
   466  					})
   467  
   468  					// test insufficient total supply error
   469  					assertPanic(t, types.IsAInsufficientTotalSupplyError, func() {
   470  						em := &testutils.TestEmulator{
   471  							NonceOfFunc: func(address types.Address) (uint64, error) {
   472  								return 0, nil
   473  							},
   474  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   475  								return &types.Result{}, nil
   476  							},
   477  						}
   478  
   479  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   480  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   481  
   482  						account.Withdraw(types.NewBalanceFromUFix64(1))
   483  					})
   484  
   485  					// test non fatal error of emulator
   486  					assertPanic(t, isNotFatal, func() {
   487  						em := &testutils.TestEmulator{
   488  							NonceOfFunc: func(address types.Address) (uint64, error) {
   489  								return 0, nil
   490  							},
   491  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   492  								return &types.Result{}, fmt.Errorf("some sort of error")
   493  							},
   494  						}
   495  
   496  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   497  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   498  
   499  						account.Withdraw(types.NewBalanceFromUFix64(0))
   500  					})
   501  
   502  					// test fatal error of emulator
   503  					assertPanic(t, types.IsAFatalError, func() {
   504  						em := &testutils.TestEmulator{
   505  							NonceOfFunc: func(address types.Address) (uint64, error) {
   506  								return 0, nil
   507  							},
   508  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   509  								return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
   510  							},
   511  						}
   512  
   513  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   514  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   515  
   516  						account.Withdraw(types.NewBalanceFromUFix64(0))
   517  					})
   518  				})
   519  			})
   520  		})
   521  	})
   522  
   523  	t.Run("test deposit (unhappy case)", func(t *testing.T) {
   524  		t.Parallel()
   525  
   526  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   527  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   528  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   529  					bs := handler.NewBlockStore(backend, rootAddr)
   530  					aa := handler.NewAddressAllocator()
   531  
   532  					// test non fatal error of emulator
   533  					assertPanic(t, isNotFatal, func() {
   534  						em := &testutils.TestEmulator{
   535  							NonceOfFunc: func(address types.Address) (uint64, error) {
   536  								return 0, nil
   537  							},
   538  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   539  								return &types.Result{}, fmt.Errorf("some sort of error")
   540  							},
   541  						}
   542  
   543  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   544  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   545  
   546  						account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(1)))
   547  					})
   548  
   549  					// test fatal error of emulator
   550  					assertPanic(t, types.IsAFatalError, func() {
   551  						em := &testutils.TestEmulator{
   552  							NonceOfFunc: func(address types.Address) (uint64, error) {
   553  								return 0, nil
   554  							},
   555  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   556  								return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
   557  							},
   558  						}
   559  
   560  						handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   561  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   562  
   563  						account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(1)))
   564  					})
   565  				})
   566  			})
   567  		})
   568  	})
   569  
   570  	t.Run("test deploy/call (with integrated emulator)", func(t *testing.T) {
   571  		t.Parallel()
   572  
   573  		// TODO update this test with events, gas metering, etc
   574  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   575  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   576  				handler := SetupHandler(t, backend, rootAddr)
   577  
   578  				foa := handler.AccountByAddress(handler.DeployCOA(1), true)
   579  				require.NotNil(t, foa)
   580  
   581  				// deposit 10000 flow
   582  				bal := types.MakeABalanceInFlow(10000)
   583  				vault := types.NewFlowTokenVault(bal)
   584  				foa.Deposit(vault)
   585  				require.Equal(t, bal, foa.Balance())
   586  
   587  				testContract := testutils.GetStorageTestContract(t)
   588  				result := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.NewBalanceFromUFix64(0))
   589  				require.NotNil(t, result.DeployedContractAddress)
   590  				addr := *result.DeployedContractAddress
   591  				// skip first few bytes as they are deploy codes
   592  				assert.Equal(t, testContract.ByteCode[17:], []byte(result.ReturnedValue))
   593  				require.NotNil(t, addr)
   594  
   595  				num := big.NewInt(22)
   596  				_ = foa.Call(
   597  					addr,
   598  					testContract.MakeCallData(t, "store", num),
   599  					math.MaxUint64,
   600  					types.NewBalanceFromUFix64(0))
   601  
   602  				res := foa.Call(
   603  					addr,
   604  					testContract.MakeCallData(t, "retrieve"),
   605  					math.MaxUint64,
   606  					types.NewBalanceFromUFix64(0))
   607  
   608  				require.Equal(t, num, res.ReturnedValue.AsBigInt())
   609  			})
   610  		})
   611  	})
   612  
   613  	t.Run("test call to cadence arch", func(t *testing.T) {
   614  		t.Parallel()
   615  
   616  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   617  			blockHeight := uint64(123)
   618  			backend.GetCurrentBlockHeightFunc = func() (uint64, error) {
   619  				return blockHeight, nil
   620  			}
   621  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   622  				h := SetupHandler(t, backend, rootAddr)
   623  
   624  				foa := h.AccountByAddress(h.DeployCOA(1), true)
   625  				require.NotNil(t, foa)
   626  
   627  				vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(10000))
   628  				foa.Deposit(vault)
   629  
   630  				arch := handler.MakePrecompileAddress(1)
   631  
   632  				ret := foa.Call(arch, precompiles.FlowBlockHeightFuncSig[:], math.MaxUint64, types.NewBalanceFromUFix64(0))
   633  				require.Equal(t, big.NewInt(int64(blockHeight)), new(big.Int).SetBytes(ret.ReturnedValue))
   634  			})
   635  		})
   636  	})
   637  
   638  	t.Run("test block.random call (with integrated emulator)", func(t *testing.T) {
   639  		t.Parallel()
   640  
   641  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   642  			random := testutils.RandomCommonHash(t)
   643  			backend.ReadRandomFunc = func(buffer []byte) error {
   644  				copy(buffer, random.Bytes())
   645  				return nil
   646  			}
   647  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   648  				handler := SetupHandler(t, backend, rootAddr)
   649  
   650  				foa := handler.AccountByAddress(handler.DeployCOA(1), true)
   651  				require.NotNil(t, foa)
   652  
   653  				vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(100))
   654  				foa.Deposit(vault)
   655  
   656  				testContract := testutils.GetStorageTestContract(t)
   657  				result := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.EmptyBalance)
   658  				require.NotNil(t, result.DeployedContractAddress)
   659  				addr := *result.DeployedContractAddress
   660  				require.Equal(t, types.StatusSuccessful, result.Status)
   661  				require.Equal(t, types.ErrCodeNoError, result.ErrorCode)
   662  
   663  				ret := foa.Call(
   664  					addr,
   665  					testContract.MakeCallData(t, "random"),
   666  					math.MaxUint64,
   667  					types.EmptyBalance)
   668  
   669  				require.Equal(t, random.Bytes(), []byte(ret.ReturnedValue))
   670  			})
   671  		})
   672  	})
   673  
   674  	// TODO add test with test emulator for unhappy cases (emulator)
   675  }
   676  
   677  func TestHandler_TransactionRun(t *testing.T) {
   678  	t.Parallel()
   679  
   680  	t.Run("test - transaction run (success)", func(t *testing.T) {
   681  		t.Parallel()
   682  
   683  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   684  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   685  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   686  
   687  					bs := handler.NewBlockStore(backend, rootAddr)
   688  					aa := handler.NewAddressAllocator()
   689  
   690  					result := &types.Result{
   691  						ReturnedValue: testutils.RandomData(t),
   692  						GasConsumed:   testutils.RandomGas(1000),
   693  						Logs: []*gethTypes.Log{
   694  							testutils.GetRandomLogFixture(t),
   695  							testutils.GetRandomLogFixture(t),
   696  						},
   697  					}
   698  
   699  					em := &testutils.TestEmulator{
   700  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   701  							return result, nil
   702  						},
   703  					}
   704  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   705  					tx := eoa.PrepareSignAndEncodeTx(
   706  						t,
   707  						gethCommon.Address{},
   708  						nil,
   709  						nil,
   710  						100_000,
   711  						big.NewInt(1),
   712  					)
   713  
   714  					rs := handler.Run(tx, types.NewAddress(gethCommon.Address{}))
   715  					require.Equal(t, types.StatusSuccessful, rs.Status)
   716  					require.Equal(t, result.GasConsumed, rs.GasConsumed)
   717  					require.Equal(t, types.ErrCodeNoError, rs.ErrorCode)
   718  
   719  				})
   720  			})
   721  		})
   722  	})
   723  
   724  	t.Run("test - transaction run (failed)", func(t *testing.T) {
   725  		t.Parallel()
   726  
   727  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   728  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   729  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   730  
   731  					bs := handler.NewBlockStore(backend, rootAddr)
   732  					aa := handler.NewAddressAllocator()
   733  
   734  					result := &types.Result{
   735  						VMError:       gethVM.ErrOutOfGas,
   736  						ReturnedValue: testutils.RandomData(t),
   737  						GasConsumed:   testutils.RandomGas(1000),
   738  						Logs: []*gethTypes.Log{
   739  							testutils.GetRandomLogFixture(t),
   740  							testutils.GetRandomLogFixture(t),
   741  						},
   742  					}
   743  
   744  					em := &testutils.TestEmulator{
   745  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   746  							return result, nil
   747  						},
   748  					}
   749  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   750  
   751  					tx := eoa.PrepareSignAndEncodeTx(
   752  						t,
   753  						gethCommon.Address{},
   754  						nil,
   755  						nil,
   756  						100_000,
   757  						big.NewInt(1),
   758  					)
   759  
   760  					rs := handler.Run(tx, types.NewAddress(gethCommon.Address{}))
   761  					require.Equal(t, types.StatusFailed, rs.Status)
   762  					require.Equal(t, result.GasConsumed, rs.GasConsumed)
   763  					require.Equal(t, types.ExecutionErrCodeOutOfGas, rs.ErrorCode)
   764  
   765  				})
   766  			})
   767  		})
   768  	})
   769  
   770  	t.Run("test - transaction run (unhappy cases)", func(t *testing.T) {
   771  		t.Parallel()
   772  
   773  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   774  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   775  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   776  					bs := handler.NewBlockStore(backend, rootAddr)
   777  					aa := handler.NewAddressAllocator()
   778  					evmErr := fmt.Errorf("%w: next nonce %v, tx nonce %v", gethCore.ErrNonceTooLow, 1, 0)
   779  					em := &testutils.TestEmulator{
   780  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   781  							return &types.Result{ValidationError: evmErr}, nil
   782  						},
   783  					}
   784  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em)
   785  
   786  					coinbase := types.NewAddress(gethCommon.Address{})
   787  
   788  					gasLimit := uint64(testutils.TestComputationLimit + 1)
   789  					tx := eoa.PrepareSignAndEncodeTx(
   790  						t,
   791  						gethCommon.Address{},
   792  						nil,
   793  						nil,
   794  						gasLimit,
   795  						big.NewInt(1),
   796  					)
   797  
   798  					assertPanic(t, isNotFatal, func() {
   799  						rs := handler.Run([]byte(tx), coinbase)
   800  						require.Equal(t, types.StatusInvalid, rs.Status)
   801  					})
   802  
   803  					tx = eoa.PrepareSignAndEncodeTx(
   804  						t,
   805  						gethCommon.Address{},
   806  						nil,
   807  						nil,
   808  						100,
   809  						big.NewInt(1),
   810  					)
   811  
   812  					rs := handler.Run([]byte(tx), coinbase)
   813  					require.Equal(t, types.StatusInvalid, rs.Status)
   814  					require.Equal(t, types.ValidationErrCodeNonceTooLow, rs.ErrorCode)
   815  				})
   816  			})
   817  		})
   818  	})
   819  
   820  	t.Run("test - transaction batch run (success)", func(t *testing.T) {
   821  		t.Parallel()
   822  
   823  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   824  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   825  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   826  					bs := handler.NewBlockStore(backend, rootAddr)
   827  					aa := handler.NewAddressAllocator()
   828  
   829  					gasConsumed := testutils.RandomGas(1000)
   830  					addr := testutils.RandomAddress(t)
   831  					result := func(tx *gethTypes.Transaction) *types.Result {
   832  						return &types.Result{
   833  							DeployedContractAddress: &addr,
   834  							ReturnedValue:           testutils.RandomData(t),
   835  							GasConsumed:             gasConsumed,
   836  							TxHash:                  tx.Hash(),
   837  							Logs: []*gethTypes.Log{
   838  								testutils.GetRandomLogFixture(t),
   839  								testutils.GetRandomLogFixture(t),
   840  							},
   841  						}
   842  					}
   843  
   844  					var runResults []*types.Result
   845  					em := &testutils.TestEmulator{
   846  						BatchRunTransactionFunc: func(txs []*gethTypes.Transaction) ([]*types.Result, error) {
   847  							runResults = make([]*types.Result, len(txs))
   848  							for i, tx := range txs {
   849  								runResults[i] = result(tx)
   850  							}
   851  							return runResults, nil
   852  						},
   853  					}
   854  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em)
   855  
   856  					coinbase := types.NewAddress(gethCommon.Address{})
   857  					gasLimit := uint64(100_000)
   858  
   859  					// run multiple successful transactions
   860  					const batchSize = 3
   861  					txs := make([][]byte, batchSize)
   862  					for i := range txs {
   863  						txs[i] = eoa.PrepareSignAndEncodeTx(
   864  							t,
   865  							gethCommon.Address{},
   866  							nil,
   867  							nil,
   868  							gasLimit,
   869  							big.NewInt(1),
   870  						)
   871  					}
   872  
   873  					results := handler.BatchRun(txs, coinbase)
   874  					for _, rs := range results {
   875  						require.Equal(t, types.StatusSuccessful, rs.Status)
   876  						require.Equal(t, gasConsumed, rs.GasConsumed)
   877  						require.Equal(t, types.ErrCodeNoError, rs.ErrorCode)
   878  					}
   879  
   880  					events := backend.Events()
   881  					require.Len(t, events, batchSize+1) // +1 block event
   882  
   883  					for i, event := range events {
   884  						if i == batchSize {
   885  							continue // don't check last block event
   886  						}
   887  						assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   888  						ev, err := jsoncdc.Decode(nil, event.Payload)
   889  						require.NoError(t, err)
   890  						cadenceEvent, ok := ev.(cadence.Event)
   891  						require.True(t, ok)
   892  
   893  						// TODO: add an event decoder in types.event
   894  						cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs")
   895  						encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", ""))
   896  						require.NoError(t, err)
   897  
   898  						var logs []*gethTypes.Log
   899  						err = rlp.DecodeBytes(encodedLogs, &logs)
   900  						require.NoError(t, err)
   901  
   902  						for k, l := range runResults[i].Logs {
   903  							assert.Equal(t, l, logs[k])
   904  						}
   905  					}
   906  
   907  					// run single transaction passed in as batch
   908  					txs = make([][]byte, 1)
   909  					for i := range txs {
   910  						txs[i] = eoa.PrepareSignAndEncodeTx(
   911  							t,
   912  							gethCommon.Address{},
   913  							nil,
   914  							nil,
   915  							gasLimit,
   916  							big.NewInt(1),
   917  						)
   918  					}
   919  
   920  					results = handler.BatchRun(txs, coinbase)
   921  					for _, rs := range results {
   922  						require.Equal(t, types.StatusSuccessful, rs.Status)
   923  					}
   924  				})
   925  			})
   926  		})
   927  	})
   928  
   929  	t.Run("test - transaction batch run (unhappy case)", func(t *testing.T) {
   930  		t.Parallel()
   931  
   932  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   933  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   934  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   935  					bs := handler.NewBlockStore(backend, rootAddr)
   936  					aa := handler.NewAddressAllocator()
   937  
   938  					gasConsumed := testutils.RandomGas(1000)
   939  					addr := testutils.RandomAddress(t)
   940  					result := func() *types.Result {
   941  						return &types.Result{
   942  							DeployedContractAddress: &addr,
   943  							ReturnedValue:           testutils.RandomData(t),
   944  							GasConsumed:             gasConsumed,
   945  							Logs: []*gethTypes.Log{
   946  								testutils.GetRandomLogFixture(t),
   947  								testutils.GetRandomLogFixture(t),
   948  							},
   949  						}
   950  					}
   951  
   952  					em := &testutils.TestEmulator{
   953  						BatchRunTransactionFunc: func(txs []*gethTypes.Transaction) ([]*types.Result, error) {
   954  							res := make([]*types.Result, len(txs))
   955  							for i := range res {
   956  								res[i] = result()
   957  							}
   958  							return res, nil
   959  						},
   960  					}
   961  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em)
   962  					coinbase := types.NewAddress(gethCommon.Address{})
   963  
   964  					// batch run empty transactions
   965  					txs := make([][]byte, 1)
   966  					assertPanic(t, isNotFatal, func() {
   967  						handler.BatchRun(txs, coinbase)
   968  					})
   969  
   970  				})
   971  			})
   972  		})
   973  	})
   974  
   975  	t.Run("test dry run successful", func(t *testing.T) {
   976  		t.Parallel()
   977  
   978  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   979  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   980  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   981  
   982  					bs := handler.NewBlockStore(backend, rootAddr)
   983  					aa := handler.NewAddressAllocator()
   984  
   985  					nonce := uint64(1)
   986  					to := gethCommon.Address{1, 2}
   987  					amount := big.NewInt(13)
   988  					gasLimit := uint64(1337)
   989  					gasPrice := big.NewInt(2000)
   990  					data := []byte{1, 5}
   991  					from := types.Address{3, 4}
   992  
   993  					tx := gethTypes.NewTransaction(
   994  						nonce,
   995  						to,
   996  						amount,
   997  						gasLimit,
   998  						gasPrice,
   999  						data,
  1000  					)
  1001  					rlpTx, err := tx.MarshalBinary()
  1002  					require.NoError(t, err)
  1003  
  1004  					addr := testutils.RandomAddress(t)
  1005  					result := &types.Result{
  1006  						DeployedContractAddress: &addr,
  1007  						ReturnedValue:           testutils.RandomData(t),
  1008  						GasConsumed:             testutils.RandomGas(1000),
  1009  						Logs: []*gethTypes.Log{
  1010  							testutils.GetRandomLogFixture(t),
  1011  							testutils.GetRandomLogFixture(t),
  1012  						},
  1013  					}
  1014  
  1015  					called := false
  1016  					em := &testutils.TestEmulator{
  1017  						DryRunTransactionFunc: func(tx *gethTypes.Transaction, address gethCommon.Address) (*types.Result, error) {
  1018  							assert.Equal(t, nonce, tx.Nonce())
  1019  							assert.Equal(t, &to, tx.To())
  1020  							assert.Equal(t, gasLimit, tx.Gas())
  1021  							assert.Equal(t, gasPrice, tx.GasPrice())
  1022  							assert.Equal(t, data, tx.Data())
  1023  							assert.Equal(t, from.ToCommon(), address)
  1024  							called = true
  1025  							return result, nil
  1026  						},
  1027  					}
  1028  
  1029  					handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em)
  1030  
  1031  					rs := handler.DryRun(rlpTx, from)
  1032  					require.Equal(t, types.StatusSuccessful, rs.Status)
  1033  					require.Equal(t, result.GasConsumed, rs.GasConsumed)
  1034  					require.Equal(t, types.ErrCodeNoError, rs.ErrorCode)
  1035  					require.True(t, called)
  1036  				})
  1037  			})
  1038  		})
  1039  	})
  1040  }
  1041  
  1042  // returns true if error passes the checks
  1043  type checkError func(error) bool
  1044  
  1045  var isNotFatal = func(err error) bool {
  1046  	return !errors.IsFailure(err)
  1047  }
  1048  
  1049  func assertPanic(t *testing.T, check checkError, f func()) {
  1050  	defer func() {
  1051  		r := recover()
  1052  		if r == nil {
  1053  			t.Fatal("The code did not panic")
  1054  		}
  1055  		err, ok := r.(error)
  1056  		if !ok {
  1057  			t.Fatal("panic is not with an error type")
  1058  		}
  1059  		require.True(t, check(err))
  1060  	}()
  1061  	f()
  1062  }
  1063  
  1064  func SetupHandler(t testing.TB, backend types.Backend, rootAddr flow.Address) *handler.ContractHandler {
  1065  	bs := handler.NewBlockStore(backend, rootAddr)
  1066  	aa := handler.NewAddressAllocator()
  1067  	emulator := emulator.NewEmulator(backend, rootAddr)
  1068  
  1069  	handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, emulator)
  1070  	return handler
  1071  }