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 }