github.com/onflow/flow-go@v0.33.17/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  
    13  	jsoncdc "github.com/onflow/cadence/encoding/json"
    14  
    15  	gethCommon "github.com/ethereum/go-ethereum/common"
    16  	gethTypes "github.com/ethereum/go-ethereum/core/types"
    17  	gethParams "github.com/ethereum/go-ethereum/params"
    18  	"github.com/ethereum/go-ethereum/rlp"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"github.com/onflow/cadence/runtime/common"
    23  
    24  	"github.com/onflow/flow-go/fvm/errors"
    25  	"github.com/onflow/flow-go/fvm/evm/emulator"
    26  	"github.com/onflow/flow-go/fvm/evm/handler"
    27  	"github.com/onflow/flow-go/fvm/evm/testutils"
    28  	"github.com/onflow/flow-go/fvm/evm/types"
    29  	"github.com/onflow/flow-go/fvm/systemcontracts"
    30  	"github.com/onflow/flow-go/model/flow"
    31  )
    32  
    33  // TODO add test for fatal errors
    34  
    35  var flowTokenAddress = common.MustBytesToAddress(systemcontracts.SystemContractsForChain(flow.Emulator).FlowToken.Address.Bytes())
    36  
    37  func TestHandler_TransactionRun(t *testing.T) {
    38  	t.Parallel()
    39  
    40  	t.Run("test - transaction run (happy case)", func(t *testing.T) {
    41  		t.Parallel()
    42  
    43  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
    44  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
    45  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
    46  
    47  					bs, err := handler.NewBlockStore(backend, rootAddr)
    48  					require.NoError(t, err)
    49  
    50  					aa, err := handler.NewAddressAllocator(backend, rootAddr)
    51  					require.NoError(t, err)
    52  
    53  					result := &types.Result{
    54  						DeployedContractAddress: types.Address(testutils.RandomAddress(t)),
    55  						ReturnedValue:           testutils.RandomData(t),
    56  						GasConsumed:             testutils.RandomGas(1000),
    57  						Logs: []*gethTypes.Log{
    58  							testutils.GetRandomLogFixture(t),
    59  							testutils.GetRandomLogFixture(t),
    60  						},
    61  					}
    62  
    63  					em := &testutils.TestEmulator{
    64  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
    65  							return result, nil
    66  						},
    67  					}
    68  					handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
    69  
    70  					coinbase := types.NewAddress(gethCommon.Address{})
    71  
    72  					tx := eoa.PrepareSignAndEncodeTx(
    73  						t,
    74  						gethCommon.Address{},
    75  						nil,
    76  						nil,
    77  						100_000,
    78  						big.NewInt(1),
    79  					)
    80  
    81  					// successfully run (no-panic)
    82  					handler.Run(tx, coinbase)
    83  
    84  					// check gas usage
    85  					// TODO: uncomment and investigate me
    86  					// computationUsed, err := backend.ComputationUsed()
    87  					// require.NoError(t, err)
    88  					// require.Equal(t, result.GasConsumed, computationUsed)
    89  
    90  					// check events (1 extra for block event)
    91  					events := backend.Events()
    92  
    93  					require.Len(t, events, 2)
    94  
    95  					event := events[0]
    96  					assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
    97  					ev, err := jsoncdc.Decode(nil, event.Payload)
    98  					require.NoError(t, err)
    99  					cadenceEvent, ok := ev.(cadence.Event)
   100  					require.True(t, ok)
   101  					for j, f := range cadenceEvent.GetFields() {
   102  						// todo add an event decoder in types.event
   103  						if f.Identifier == "logs" {
   104  							cadenceLogs := cadenceEvent.GetFieldValues()[j]
   105  							encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", ""))
   106  							require.NoError(t, err)
   107  
   108  							var logs []*gethTypes.Log
   109  							err = rlp.DecodeBytes(encodedLogs, &logs)
   110  							require.NoError(t, err)
   111  
   112  							for i, l := range result.Logs {
   113  								assert.Equal(t, l, logs[i])
   114  							}
   115  						}
   116  					}
   117  
   118  					// check block event
   119  					event = events[1]
   120  					assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   121  					_, err = jsoncdc.Decode(nil, event.Payload)
   122  					require.NoError(t, err)
   123  				})
   124  			})
   125  		})
   126  	})
   127  
   128  	t.Run("test - transaction run (unhappy cases)", func(t *testing.T) {
   129  		t.Parallel()
   130  
   131  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   132  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   133  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   134  
   135  					bs, err := handler.NewBlockStore(backend, rootAddr)
   136  					require.NoError(t, err)
   137  
   138  					aa, err := handler.NewAddressAllocator(backend, rootAddr)
   139  					require.NoError(t, err)
   140  
   141  					em := &testutils.TestEmulator{
   142  						RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) {
   143  							return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
   144  						},
   145  					}
   146  					handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   147  
   148  					coinbase := types.NewAddress(gethCommon.Address{})
   149  
   150  					// test RLP decoding (non fatal)
   151  					assertPanic(t, isNotFatal, func() {
   152  						// invalid RLP encoding
   153  						invalidTx := "badencoding"
   154  						handler.Run([]byte(invalidTx), coinbase)
   155  					})
   156  
   157  					// test gas limit (non fatal)
   158  					assertPanic(t, isNotFatal, func() {
   159  						gasLimit := uint64(testutils.TestComputationLimit + 1)
   160  						tx := eoa.PrepareSignAndEncodeTx(
   161  							t,
   162  							gethCommon.Address{},
   163  							nil,
   164  							nil,
   165  							gasLimit,
   166  							big.NewInt(1),
   167  						)
   168  
   169  						handler.Run([]byte(tx), coinbase)
   170  					})
   171  
   172  					// tx execution failure
   173  					tx := eoa.PrepareSignAndEncodeTx(
   174  						t,
   175  						gethCommon.Address{},
   176  						nil,
   177  						nil,
   178  						100_000,
   179  						big.NewInt(1),
   180  					)
   181  
   182  					assertPanic(t, isNotFatal, func() {
   183  						handler.Run([]byte(tx), coinbase)
   184  					})
   185  				})
   186  			})
   187  		})
   188  	})
   189  
   190  	t.Run("test running transaction (with integrated emulator)", func(t *testing.T) {
   191  		t.Parallel()
   192  
   193  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   194  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   195  				handler := SetupHandler(t, backend, rootAddr)
   196  
   197  				eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
   198  
   199  				// deposit 1 Flow to the foa account
   200  				addr := handler.AllocateAddress()
   201  				orgBalance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
   202  				require.NoError(t, err)
   203  				vault := types.NewFlowTokenVault(orgBalance)
   204  				foa := handler.AccountByAddress(addr, true)
   205  				foa.Deposit(vault)
   206  
   207  				// transfer 0.1 flow to the non-foa address
   208  				deduction, err := types.NewBalanceFromAttoFlow(big.NewInt(1e17))
   209  				require.NoError(t, err)
   210  				foa.Call(eoa.Address(), nil, 400000, deduction)
   211  				require.Equal(t, orgBalance.Sub(deduction), foa.Balance())
   212  
   213  				// transfer 0.01 flow back to the foa through
   214  				addition, err := types.NewBalanceFromAttoFlow(big.NewInt(1e16))
   215  				require.NoError(t, err)
   216  
   217  				tx := eoa.PrepareSignAndEncodeTx(
   218  					t,
   219  					foa.Address().ToCommon(),
   220  					nil,
   221  					addition.ToAttoFlow(),
   222  					gethParams.TxGas*10,
   223  					big.NewInt(1e8), // high gas fee to test coinbase collection,
   224  				)
   225  
   226  				// setup coinbase
   227  				foa2 := handler.AllocateAddress()
   228  				account2 := handler.AccountByAddress(foa2, true)
   229  				require.Equal(t, types.Balance(0), account2.Balance())
   230  
   231  				// no panic means success here
   232  				handler.Run(tx, account2.Address())
   233  				require.Equal(t, orgBalance.Sub(deduction).Add(addition), foa.Balance())
   234  
   235  				// fees has been collected to the coinbase
   236  				require.NotEqual(t, types.Balance(0), account2.Balance())
   237  
   238  			})
   239  		})
   240  	})
   241  }
   242  
   243  func TestHandler_OpsWithoutEmulator(t *testing.T) {
   244  	t.Parallel()
   245  
   246  	t.Run("test last executed block call", func(t *testing.T) {
   247  		t.Parallel()
   248  
   249  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   250  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   251  				handler := SetupHandler(t, backend, rootAddr)
   252  
   253  				// test call last executed block without initialization
   254  				b := handler.LastExecutedBlock()
   255  				require.Equal(t, types.GenesisBlock, b)
   256  
   257  				// do some changes
   258  				address := testutils.RandomAddress(t)
   259  				account := handler.AccountByAddress(address, true)
   260  				bal, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
   261  				require.NoError(t, err)
   262  				account.Deposit(types.NewFlowTokenVault(bal))
   263  
   264  				// check if block height has been incremented
   265  				b = handler.LastExecutedBlock()
   266  				require.Equal(t, uint64(1), b.Height)
   267  			})
   268  		})
   269  	})
   270  
   271  	t.Run("test address allocation", func(t *testing.T) {
   272  		t.Parallel()
   273  
   274  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   275  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   276  				blockchain, err := handler.NewBlockStore(backend, rootAddr)
   277  				require.NoError(t, err)
   278  
   279  				aa, err := handler.NewAddressAllocator(backend, rootAddr)
   280  				require.NoError(t, err)
   281  
   282  				handler := handler.NewContractHandler(flowTokenAddress, blockchain, aa, backend, nil)
   283  
   284  				foa := handler.AllocateAddress()
   285  				require.NotNil(t, foa)
   286  
   287  				expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x00000000000000000001"))
   288  				require.Equal(t, expectedAddress, foa)
   289  			})
   290  		})
   291  	})
   292  }
   293  
   294  func TestHandler_BridgedAccount(t *testing.T) {
   295  
   296  	t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) {
   297  		t.Parallel()
   298  
   299  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   300  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   301  				handler := SetupHandler(t, backend, rootAddr)
   302  
   303  				foa := handler.AccountByAddress(handler.AllocateAddress(), true)
   304  				require.NotNil(t, foa)
   305  
   306  				zeroBalance, err := types.NewBalanceFromAttoFlow(big.NewInt(0))
   307  				require.NoError(t, err)
   308  				require.Equal(t, zeroBalance, foa.Balance())
   309  
   310  				balance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
   311  				require.NoError(t, err)
   312  				vault := types.NewFlowTokenVault(balance)
   313  
   314  				foa.Deposit(vault)
   315  				require.NoError(t, err)
   316  				require.Equal(t, balance, foa.Balance())
   317  
   318  				v := foa.Withdraw(balance)
   319  				require.NoError(t, err)
   320  				require.Equal(t, balance, v.Balance())
   321  
   322  				require.NoError(t, err)
   323  				require.Equal(t, zeroBalance, foa.Balance())
   324  
   325  				events := backend.Events()
   326  				require.Len(t, events, 4)
   327  
   328  				// transaction event
   329  				event := events[0]
   330  				assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   331  
   332  				// block event
   333  				event = events[1]
   334  				assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   335  
   336  				// transaction event
   337  				event = events[2]
   338  				assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
   339  				_, err = jsoncdc.Decode(nil, event.Payload)
   340  				require.NoError(t, err)
   341  				// TODO: decode encoded tx and check for the amount and value
   342  				// assert.Equal(t, foa.Address(), ret.Address)
   343  				// assert.Equal(t, balance, ret.Amount)
   344  
   345  				// block event
   346  				event = events[3]
   347  				assert.Equal(t, event.Type, types.EventTypeBlockExecuted)
   348  
   349  				// check gas usage
   350  				computationUsed, err := backend.ComputationUsed()
   351  				require.NoError(t, err)
   352  				require.Equal(t, types.DefaultDirectCallBaseGasUsage*2, computationUsed)
   353  			})
   354  		})
   355  	})
   356  
   357  	t.Run("test withdraw (unhappy case)", func(t *testing.T) {
   358  		t.Parallel()
   359  
   360  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   361  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   362  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   363  					bs, err := handler.NewBlockStore(backend, rootAddr)
   364  					require.NoError(t, err)
   365  
   366  					aa, err := handler.NewAddressAllocator(backend, rootAddr)
   367  					require.NoError(t, err)
   368  
   369  					// Withdraw calls are only possible within FOA accounts
   370  					assertPanic(t, types.IsAUnAuthroizedMethodCallError, func() {
   371  						em := &testutils.TestEmulator{}
   372  
   373  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   374  
   375  						account := handler.AccountByAddress(testutils.RandomAddress(t), false)
   376  						account.Withdraw(types.Balance(1))
   377  					})
   378  
   379  					// test insufficient total supply
   380  					assertPanic(t, types.IsAInsufficientTotalSupplyError, func() {
   381  						em := &testutils.TestEmulator{
   382  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   383  								return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
   384  							},
   385  						}
   386  
   387  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   388  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   389  
   390  						account.Withdraw(types.Balance(1))
   391  					})
   392  
   393  					// test non fatal error of emulator
   394  					assertPanic(t, types.IsEVMExecutionError, func() {
   395  						em := &testutils.TestEmulator{
   396  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   397  								return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
   398  							},
   399  						}
   400  
   401  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   402  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   403  
   404  						account.Withdraw(types.Balance(0))
   405  					})
   406  
   407  					// test fatal error of emulator
   408  					assertPanic(t, types.IsAFatalError, func() {
   409  						em := &testutils.TestEmulator{
   410  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   411  								return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
   412  							},
   413  						}
   414  
   415  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   416  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   417  
   418  						account.Withdraw(types.Balance(0))
   419  					})
   420  				})
   421  			})
   422  		})
   423  	})
   424  
   425  	t.Run("test deposit (unhappy case)", func(t *testing.T) {
   426  		t.Parallel()
   427  
   428  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   429  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   430  				testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
   431  					bs, err := handler.NewBlockStore(backend, rootAddr)
   432  					require.NoError(t, err)
   433  
   434  					aa, err := handler.NewAddressAllocator(backend, rootAddr)
   435  					require.NoError(t, err)
   436  
   437  					// test non fatal error of emulator
   438  					assertPanic(t, types.IsEVMExecutionError, func() {
   439  						em := &testutils.TestEmulator{
   440  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   441  								return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
   442  							},
   443  						}
   444  
   445  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   446  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   447  
   448  						account.Deposit(types.NewFlowTokenVault(1))
   449  					})
   450  
   451  					// test fatal error of emulator
   452  					assertPanic(t, types.IsAFatalError, func() {
   453  						em := &testutils.TestEmulator{
   454  							DirectCallFunc: func(call *types.DirectCall) (*types.Result, error) {
   455  								return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
   456  							},
   457  						}
   458  
   459  						handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
   460  						account := handler.AccountByAddress(testutils.RandomAddress(t), true)
   461  
   462  						account.Deposit(types.NewFlowTokenVault(1))
   463  					})
   464  				})
   465  			})
   466  		})
   467  	})
   468  
   469  	t.Run("test deploy/call (with integrated emulator)", func(t *testing.T) {
   470  		t.Parallel()
   471  
   472  		// TODO update this test with events, gas metering, etc
   473  		testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
   474  			testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
   475  				handler := SetupHandler(t, backend, rootAddr)
   476  
   477  				foa := handler.AccountByAddress(handler.AllocateAddress(), true)
   478  				require.NotNil(t, foa)
   479  
   480  				// deposit 10000 flow
   481  				orgBalance, err := types.NewBalanceFromAttoFlow(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(10000)))
   482  				require.NoError(t, err)
   483  				vault := types.NewFlowTokenVault(orgBalance)
   484  				foa.Deposit(vault)
   485  
   486  				testContract := testutils.GetStorageTestContract(t)
   487  				addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.Balance(0))
   488  				require.NotNil(t, addr)
   489  
   490  				num := big.NewInt(22)
   491  
   492  				_ = foa.Call(
   493  					addr,
   494  					testContract.MakeCallData(t, "store", num),
   495  					math.MaxUint64,
   496  					types.Balance(0))
   497  
   498  				ret := foa.Call(
   499  					addr,
   500  					testContract.MakeCallData(t, "retrieve"),
   501  					math.MaxUint64,
   502  					types.Balance(0))
   503  
   504  				require.Equal(t, num, new(big.Int).SetBytes(ret))
   505  			})
   506  		})
   507  	})
   508  
   509  	// TODO add test with test emulator for unhappy cases (emulator)
   510  }
   511  
   512  // returns true if error passes the checks
   513  type checkError func(error) bool
   514  
   515  var isNotFatal = func(err error) bool {
   516  	return !errors.IsFailure(err)
   517  }
   518  
   519  func assertPanic(t *testing.T, check checkError, f func()) {
   520  	defer func() {
   521  		r := recover()
   522  		if r == nil {
   523  			t.Fatal("The code did not panic")
   524  		}
   525  		err, ok := r.(error)
   526  		if !ok {
   527  			t.Fatal("panic is not with an error type")
   528  		}
   529  		require.True(t, check(err))
   530  	}()
   531  	f()
   532  }
   533  
   534  func SetupHandler(t testing.TB, backend types.Backend, rootAddr flow.Address) *handler.ContractHandler {
   535  	bs, err := handler.NewBlockStore(backend, rootAddr)
   536  	require.NoError(t, err)
   537  
   538  	aa, err := handler.NewAddressAllocator(backend, rootAddr)
   539  	require.NoError(t, err)
   540  
   541  	emulator := emulator.NewEmulator(backend, rootAddr)
   542  
   543  	handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, emulator)
   544  	return handler
   545  }