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 }