github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/emulator/emulator_test.go (about) 1 package emulator_test 2 3 import ( 4 "fmt" 5 "math" 6 "math/big" 7 "testing" 8 9 gethCommon "github.com/onflow/go-ethereum/common" 10 gethTypes "github.com/onflow/go-ethereum/core/types" 11 gethVM "github.com/onflow/go-ethereum/core/vm" 12 gethParams "github.com/onflow/go-ethereum/params" 13 "github.com/stretchr/testify/require" 14 15 "github.com/onflow/flow-go/fvm/evm/emulator" 16 "github.com/onflow/flow-go/fvm/evm/testutils" 17 "github.com/onflow/flow-go/fvm/evm/types" 18 "github.com/onflow/flow-go/model/flow" 19 ) 20 21 var blockNumber = big.NewInt(10) 22 var defaultCtx = types.NewDefaultBlockContext(blockNumber.Uint64()) 23 24 func RunWithNewEmulator(t testing.TB, backend *testutils.TestBackend, rootAddr flow.Address, f func(*emulator.Emulator)) { 25 env := emulator.NewEmulator(backend, rootAddr) 26 f(env) 27 } 28 29 func RunWithNewBlockView(t testing.TB, em *emulator.Emulator, f func(blk types.BlockView)) { 30 blk, err := em.NewBlockView(defaultCtx) 31 require.NoError(t, err) 32 f(blk) 33 } 34 35 func RunWithNewReadOnlyBlockView(t testing.TB, em *emulator.Emulator, f func(blk types.ReadOnlyBlockView)) { 36 blk, err := em.NewReadOnlyBlockView(defaultCtx) 37 require.NoError(t, err) 38 f(blk) 39 } 40 41 func TestNativeTokenBridging(t *testing.T) { 42 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 43 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { 44 originalBalance := big.NewInt(10000) 45 testAccount := types.NewAddressFromString("test") 46 bridgeAccount := types.NewAddressFromString("bridge") 47 nonce := uint64(0) 48 49 t.Run("mint tokens to the first account", func(t *testing.T) { 50 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 51 RunWithNewBlockView(t, env, func(blk types.BlockView) { 52 call := types.NewDepositCall(bridgeAccount, testAccount, originalBalance, nonce) 53 res, err := blk.DirectCall(call) 54 require.NoError(t, err) 55 require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed) 56 expectedHash, err := call.Hash() 57 require.NoError(t, err) 58 require.Equal(t, expectedHash, res.TxHash) 59 nonce += 1 60 }) 61 }) 62 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 63 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 64 retBalance, err := blk.BalanceOf(testAccount) 65 require.NoError(t, err) 66 require.Equal(t, originalBalance, retBalance) 67 // check balance of bridgeAccount to be zero 68 69 retBalance, err = blk.BalanceOf(bridgeAccount) 70 require.NoError(t, err) 71 require.Equal(t, big.NewInt(0), retBalance) 72 }) 73 }) 74 }) 75 t.Run("tokens withdraw", func(t *testing.T) { 76 amount := big.NewInt(1000) 77 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 78 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 79 retBalance, err := blk.BalanceOf(testAccount) 80 require.NoError(t, err) 81 require.Equal(t, originalBalance, retBalance) 82 }) 83 }) 84 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 85 RunWithNewBlockView(t, env, func(blk types.BlockView) { 86 call := types.NewWithdrawCall(bridgeAccount, testAccount, amount, nonce) 87 res, err := blk.DirectCall(call) 88 require.NoError(t, err) 89 require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed) 90 expectedHash, err := call.Hash() 91 require.NoError(t, err) 92 require.Equal(t, expectedHash, res.TxHash) 93 nonce += 1 94 }) 95 }) 96 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 97 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 98 retBalance, err := blk.BalanceOf(testAccount) 99 require.NoError(t, err) 100 require.Equal(t, amount.Sub(originalBalance, amount), retBalance) 101 // check balance of bridgeAccount to be zero 102 103 retBalance, err = blk.BalanceOf(bridgeAccount) 104 require.NoError(t, err) 105 require.Equal(t, big.NewInt(0), retBalance) 106 }) 107 }) 108 }) 109 }) 110 }) 111 } 112 113 func TestContractInteraction(t *testing.T) { 114 t.Parallel() 115 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 116 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { 117 118 testContract := testutils.GetStorageTestContract(t) 119 120 testAccount := types.NewAddressFromString("test") 121 bridgeAccount := types.NewAddressFromString("bridge") 122 nonce := uint64(0) 123 124 amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether)) 125 amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) 126 127 // fund test account 128 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 129 RunWithNewBlockView(t, env, func(blk types.BlockView) { 130 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, nonce)) 131 require.NoError(t, err) 132 nonce += 1 133 }) 134 }) 135 136 var contractAddr types.Address 137 138 t.Run("deploy contract", func(t *testing.T) { 139 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 140 RunWithNewBlockView(t, env, func(blk types.BlockView) { 141 call := types.NewDeployCall( 142 testAccount, 143 testContract.ByteCode, 144 math.MaxUint64, 145 amountToBeTransfered, 146 nonce) 147 res, err := blk.DirectCall(call) 148 require.NoError(t, err) 149 require.NotNil(t, res.DeployedContractAddress) 150 contractAddr = *res.DeployedContractAddress 151 expectedHash, err := call.Hash() 152 require.NoError(t, err) 153 require.Equal(t, expectedHash, res.TxHash) 154 nonce += 1 155 }) 156 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 157 require.NotNil(t, contractAddr) 158 retCode, err := blk.CodeOf(contractAddr) 159 require.NoError(t, err) 160 require.NotEmpty(t, retCode) 161 162 retBalance, err := blk.BalanceOf(contractAddr) 163 require.NoError(t, err) 164 require.Equal(t, amountToBeTransfered, retBalance) 165 166 retBalance, err = blk.BalanceOf(testAccount) 167 require.NoError(t, err) 168 require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance) 169 }) 170 }) 171 }) 172 173 t.Run("call contract", func(t *testing.T) { 174 num := big.NewInt(10) 175 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 176 RunWithNewBlockView(t, env, func(blk types.BlockView) { 177 res, err := blk.DirectCall( 178 types.NewContractCall( 179 testAccount, 180 contractAddr, 181 testContract.MakeCallData(t, "store", num), 182 1_000_000, 183 big.NewInt(0), // this should be zero because the contract doesn't have receiver 184 nonce, 185 ), 186 ) 187 require.NoError(t, err) 188 require.GreaterOrEqual(t, res.GasConsumed, uint64(40_000)) 189 nonce += 1 190 }) 191 }) 192 193 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 194 RunWithNewBlockView(t, env, func(blk types.BlockView) { 195 res, err := blk.DirectCall( 196 types.NewContractCall( 197 testAccount, 198 contractAddr, 199 testContract.MakeCallData(t, "retrieve"), 200 1_000_000, 201 big.NewInt(0), // this should be zero because the contract doesn't have receiver 202 nonce, 203 ), 204 ) 205 require.NoError(t, err) 206 nonce += 1 207 208 ret := new(big.Int).SetBytes(res.ReturnedValue) 209 require.Equal(t, num, ret) 210 require.GreaterOrEqual(t, res.GasConsumed, uint64(23_000)) 211 }) 212 }) 213 214 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 215 RunWithNewBlockView(t, env, func(blk types.BlockView) { 216 res, err := blk.DirectCall( 217 types.NewContractCall( 218 testAccount, 219 contractAddr, 220 testContract.MakeCallData(t, "blockNumber"), 221 1_000_000, 222 big.NewInt(0), // this should be zero because the contract doesn't have receiver 223 nonce, 224 ), 225 ) 226 require.NoError(t, err) 227 nonce += 1 228 229 ret := new(big.Int).SetBytes(res.ReturnedValue) 230 require.Equal(t, blockNumber, ret) 231 }) 232 }) 233 234 RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) { 235 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 236 blk, err := em.NewBlockView(ctx) 237 require.NoError(t, err) 238 res, err := blk.DirectCall( 239 types.NewContractCall( 240 testAccount, 241 contractAddr, 242 testContract.MakeCallData(t, "chainID"), 243 1_000_000, 244 big.NewInt(0), // this should be zero because the contract doesn't have receiver 245 nonce, 246 ), 247 ) 248 require.NoError(t, err) 249 nonce += 1 250 251 ret := new(big.Int).SetBytes(res.ReturnedValue) 252 require.Equal(t, types.FlowEVMPreviewNetChainID, ret) 253 }) 254 }) 255 256 t.Run("test sending transactions (happy case)", func(t *testing.T) { 257 account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) 258 fAddr := account.Address() 259 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 260 RunWithNewBlockView(t, env, func(blk types.BlockView) { 261 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce())) 262 require.NoError(t, err) 263 }) 264 }) 265 266 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 267 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 268 ctx.GasFeeCollector = types.NewAddressFromString("coinbase") 269 coinbaseOrgBalance := gethCommon.Big1 270 // small amount of money to create account 271 RunWithNewBlockView(t, env, func(blk types.BlockView) { 272 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 0)) 273 require.NoError(t, err) 274 }) 275 276 blk, err := env.NewBlockView(ctx) 277 require.NoError(t, err) 278 tx := account.PrepareAndSignTx( 279 t, 280 testAccount.ToCommon(), // to 281 nil, // data 282 big.NewInt(1000), // amount 283 gethParams.TxGas, // gas limit 284 gethCommon.Big1, // gas fee 285 286 ) 287 res, err := blk.RunTransaction(tx) 288 require.NoError(t, err) 289 require.NoError(t, res.VMError) 290 require.NoError(t, res.ValidationError) 291 require.Greater(t, res.GasConsumed, uint64(0)) 292 293 // check the balance of coinbase 294 RunWithNewReadOnlyBlockView(t, env, func(blk2 types.ReadOnlyBlockView) { 295 bal, err := blk2.BalanceOf(ctx.GasFeeCollector) 296 require.NoError(t, err) 297 expected := gethParams.TxGas*gethCommon.Big1.Uint64() + gethCommon.Big1.Uint64() 298 require.Equal(t, expected, bal.Uint64()) 299 300 nonce, err := blk2.NonceOf(fAddr) 301 require.NoError(t, err) 302 require.Equal(t, 1, int(nonce)) 303 }) 304 }) 305 }) 306 307 t.Run("test batch running transactions", func(t *testing.T) { 308 account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) 309 account.SetNonce(account.Nonce() + 1) 310 fAddr := account.Address() 311 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 312 RunWithNewBlockView(t, env, func(blk types.BlockView) { 313 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce())) 314 require.NoError(t, err) 315 }) 316 }) 317 318 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 319 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 320 ctx.GasFeeCollector = types.NewAddressFromString("coinbase-collector") 321 coinbaseOrgBalance := gethCommon.Big1 322 // small amount of money to create account 323 RunWithNewBlockView(t, env, func(blk types.BlockView) { 324 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 0)) 325 require.NoError(t, err) 326 }) 327 328 blk, err := env.NewBlockView(ctx) 329 require.NoError(t, err) 330 331 const batchSize = 3 332 txs := make([]*gethTypes.Transaction, batchSize) 333 for i := range txs { 334 txs[i] = account.PrepareAndSignTx( 335 t, 336 testAccount.ToCommon(), // to 337 nil, // data 338 big.NewInt(1000), // amount 339 gethParams.TxGas, // gas limit 340 gethCommon.Big1, // gas fee 341 342 ) 343 } 344 345 results, err := blk.BatchRunTransactions(txs) 346 require.NoError(t, err) 347 for _, res := range results { 348 require.NoError(t, res.VMError) 349 require.NoError(t, res.ValidationError) 350 require.Greater(t, res.GasConsumed, uint64(0)) 351 } 352 353 // check the balance of coinbase 354 RunWithNewReadOnlyBlockView(t, env, func(blk2 types.ReadOnlyBlockView) { 355 bal, err := blk2.BalanceOf(ctx.GasFeeCollector) 356 require.NoError(t, err) 357 expected := gethParams.TxGas*batchSize + gethCommon.Big1.Uint64() 358 require.Equal(t, expected, bal.Uint64()) 359 360 nonce, err := blk2.NonceOf(fAddr) 361 require.NoError(t, err) 362 require.Equal(t, batchSize+1, int(nonce)) 363 }) 364 }) 365 }) 366 367 t.Run("test runing transactions with dynamic fees (happy case)", func(t *testing.T) { 368 account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) 369 fAddr := account.Address() 370 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 371 RunWithNewBlockView(t, env, func(blk types.BlockView) { 372 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce())) 373 require.NoError(t, err) 374 }) 375 }) 376 account.SetNonce(account.Nonce() + 4) 377 378 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 379 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 380 ctx.GasFeeCollector = types.NewAddressFromString("coinbase") 381 coinbaseOrgBalance := gethCommon.Big1 382 // small amount of money to create account 383 RunWithNewBlockView(t, env, func(blk types.BlockView) { 384 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, ctx.GasFeeCollector, coinbaseOrgBalance, 1)) 385 require.NoError(t, err) 386 }) 387 388 blk, err := env.NewBlockView(ctx) 389 require.NoError(t, err) 390 tx := account.SignTx( 391 t, 392 gethTypes.NewTx(&gethTypes.DynamicFeeTx{ 393 ChainID: types.FlowEVMPreviewNetChainID, 394 Nonce: account.Nonce(), 395 GasTipCap: big.NewInt(2), 396 GasFeeCap: big.NewInt(3), 397 Gas: gethParams.TxGas, 398 To: &gethCommon.Address{}, 399 Value: big.NewInt(1), 400 }), 401 ) 402 account.SetNonce(account.Nonce() + 1) 403 404 res, err := blk.RunTransaction(tx) 405 require.NoError(t, err) 406 require.NoError(t, res.VMError) 407 require.NoError(t, res.ValidationError) 408 require.Greater(t, res.GasConsumed, uint64(0)) 409 }) 410 }) 411 412 t.Run("test sending transactions (invalid nonce)", func(t *testing.T) { 413 account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) 414 fAddr := account.Address() 415 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 416 RunWithNewBlockView(t, env, func(blk types.BlockView) { 417 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, fAddr, amount, account.Nonce())) 418 require.NoError(t, err) 419 }) 420 }) 421 422 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 423 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 424 blk, err := env.NewBlockView(ctx) 425 require.NoError(t, err) 426 tx := account.SignTx(t, 427 gethTypes.NewTransaction( 428 100, // nonce 429 testAccount.ToCommon(), // to 430 big.NewInt(1000), // amount 431 gethParams.TxGas, // gas limit 432 gethCommon.Big1, // gas fee 433 nil, // data 434 ), 435 ) 436 res, err := blk.RunTransaction(tx) 437 require.NoError(t, err) 438 require.Error(t, res.ValidationError) 439 }) 440 }) 441 442 t.Run("test sending transactions (bad signature)", func(t *testing.T) { 443 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 444 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 445 blk, err := env.NewBlockView(ctx) 446 require.NoError(t, err) 447 tx := gethTypes.NewTx(&gethTypes.LegacyTx{ 448 Nonce: 0, 449 GasPrice: gethCommon.Big1, 450 Gas: gethParams.TxGas, // gas limit 451 To: nil, // to 452 Value: big.NewInt(1000), // amount 453 Data: nil, // data 454 V: big.NewInt(1), 455 R: big.NewInt(2), 456 S: big.NewInt(3), 457 }) 458 res, err := blk.RunTransaction(tx) 459 require.NoError(t, err) 460 require.Error(t, res.ValidationError) 461 }) 462 }) 463 }) 464 }) 465 } 466 467 func TestDeployAtFunctionality(t *testing.T) { 468 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 469 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { 470 testContract := testutils.GetStorageTestContract(t) 471 testAccount := types.NewAddressFromString("test") 472 bridgeAccount := types.NewAddressFromString("bridge") 473 474 amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether)) 475 amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) 476 477 // fund test account 478 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 479 RunWithNewBlockView(t, env, func(blk types.BlockView) { 480 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0)) 481 require.NoError(t, err) 482 }) 483 }) 484 485 t.Run("deploy contract at target address", func(t *testing.T) { 486 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 487 target := types.Address{1, 2, 3} 488 RunWithNewBlockView(t, env, func(blk types.BlockView) { 489 res, err := blk.DirectCall( 490 types.NewDeployCallWithTargetAddress( 491 testAccount, 492 target, 493 testContract.ByteCode, 494 math.MaxUint64, 495 amountToBeTransfered, 496 0, 497 ), 498 ) 499 require.NoError(t, err) 500 require.NotNil(t, res.DeployedContractAddress) 501 require.Equal(t, target, *res.DeployedContractAddress) 502 }) 503 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 504 require.NotNil(t, target) 505 retCode, err := blk.CodeOf(target) 506 require.NoError(t, err) 507 require.NotEmpty(t, retCode) 508 509 retBalance, err := blk.BalanceOf(target) 510 require.NoError(t, err) 511 require.Equal(t, amountToBeTransfered, retBalance) 512 513 retBalance, err = blk.BalanceOf(testAccount) 514 require.NoError(t, err) 515 require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance) 516 }) 517 // test deployment to an address that is already exist 518 RunWithNewBlockView(t, env, func(blk types.BlockView) { 519 res, err := blk.DirectCall( 520 types.NewDeployCallWithTargetAddress( 521 testAccount, 522 target, 523 testContract.ByteCode, 524 math.MaxUint64, 525 amountToBeTransfered, 526 0), 527 ) 528 require.NoError(t, err) 529 require.Equal(t, gethVM.ErrContractAddressCollision, res.VMError) 530 }) 531 // test deployment with not enough gas 532 RunWithNewBlockView(t, env, func(blk types.BlockView) { 533 res, err := blk.DirectCall( 534 types.NewDeployCallWithTargetAddress( 535 testAccount, 536 types.Address{3, 4, 5}, 537 testContract.ByteCode, 538 100, 539 new(big.Int), 540 0), 541 ) 542 require.NoError(t, err) 543 require.Equal(t, fmt.Errorf("out of gas"), res.VMError) 544 }) 545 }) 546 }) 547 }) 548 }) 549 } 550 551 // Self destruct test deploys a contract with a selfdestruct function 552 // this function is called and we make sure the balance the contract had 553 // is returned to the address provided, and the contract data stays according to the 554 // EIP 6780 https://eips.ethereum.org/EIPS/eip-6780 in case where the selfdestruct 555 // is not caleld in the same transaction as deployment. 556 func TestSelfdestruct(t *testing.T) { 557 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 558 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { 559 testutils.RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *testutils.EOATestAccount) { 560 561 testContract := testutils.GetStorageTestContract(t) 562 testAddress := types.NewAddressFromString("testaddr") 563 bridgeAccount := types.NewAddressFromString("bridge") 564 565 startBalance := big.NewInt(0).Mul(big.NewInt(1000), big.NewInt(gethParams.Ether)) 566 deployBalance := big.NewInt(0).Mul(big.NewInt(10), big.NewInt(gethParams.Ether)) 567 var contractAddr types.Address 568 569 // setup the test with funded account and deploying a selfdestruct contract. 570 RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { 571 RunWithNewBlockView(t, env, func(blk types.BlockView) { 572 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAddress, startBalance, 0)) 573 require.NoError(t, err) 574 }) 575 576 RunWithNewBlockView(t, env, func(blk types.BlockView) { 577 res, err := blk.DirectCall( 578 types.NewDeployCall( 579 testAddress, 580 testContract.ByteCode, 581 math.MaxUint64, 582 deployBalance, 583 0), 584 ) 585 require.NoError(t, err) 586 require.NotNil(t, res.DeployedContractAddress) 587 contractAddr = *res.DeployedContractAddress 588 }) 589 590 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 591 bal, err := blk.BalanceOf(testAddress) 592 require.NoError(t, err) 593 require.Equal(t, big.NewInt(0).Sub(startBalance, deployBalance), bal) 594 595 bal, err = blk.BalanceOf(contractAddr) 596 require.NoError(t, err) 597 require.Equal(t, deployBalance, bal) 598 }) 599 600 // call the destroy method which executes selfdestruct call. 601 RunWithNewBlockView(t, env, func(blk types.BlockView) { 602 res, err := blk.DirectCall(&types.DirectCall{ 603 Type: types.DirectCallTxType, 604 From: testAddress, 605 To: contractAddr, 606 Data: testContract.MakeCallData(t, "destroy"), 607 Value: big.NewInt(0), 608 GasLimit: 100_000, 609 }) 610 require.NoError(t, err) 611 require.False(t, res.Failed()) 612 }) 613 614 // after calling selfdestruct the balance should be returned to the caller and 615 // equal initial funded balance of the caller. 616 RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { 617 bal, err := blk.BalanceOf(testAddress) 618 require.NoError(t, err) 619 require.Equal(t, startBalance, bal) 620 621 bal, err = blk.BalanceOf(contractAddr) 622 require.NoError(t, err) 623 require.Equal(t, big.NewInt(0), bal) 624 625 nonce, err := blk.NonceOf(contractAddr) 626 require.NoError(t, err) 627 require.Equal(t, uint64(1), nonce) 628 629 code, err := blk.CodeOf(contractAddr) 630 require.NoError(t, err) 631 require.True(t, len(code) > 0) 632 }) 633 }) 634 }) 635 }) 636 }) 637 } 638 639 func TestTransfers(t *testing.T) { 640 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 641 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { 642 643 testAccount1 := types.NewAddressFromString("test1") 644 testAccount2 := types.NewAddressFromString("test2") 645 bridgeAccount := types.NewAddressFromString("bridge") 646 647 amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether)) 648 amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) 649 650 RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) { 651 RunWithNewBlockView(t, em, func(blk types.BlockView) { 652 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount1, amount, 0)) 653 require.NoError(t, err) 654 }) 655 }) 656 657 RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) { 658 RunWithNewBlockView(t, em, func(blk types.BlockView) { 659 _, err := blk.DirectCall(types.NewTransferCall(testAccount1, testAccount2, amountToBeTransfered, 0)) 660 require.NoError(t, err) 661 }) 662 }) 663 664 RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) { 665 RunWithNewReadOnlyBlockView(t, em, func(blk types.ReadOnlyBlockView) { 666 bal, err := blk.BalanceOf(testAccount2) 667 require.NoError(t, err) 668 require.Equal(t, amountToBeTransfered.Uint64(), bal.Uint64()) 669 670 bal, err = blk.BalanceOf(testAccount1) 671 require.NoError(t, err) 672 require.Equal(t, new(big.Int).Sub(amount, amountToBeTransfered).Uint64(), bal.Uint64()) 673 }) 674 }) 675 }) 676 }) 677 } 678 679 func TestStorageNoSideEffect(t *testing.T) { 680 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 681 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) { 682 var err error 683 em := emulator.NewEmulator(backend, flowEVMRoot) 684 testAccount := types.NewAddressFromString("test") 685 bridgeAccount := types.NewAddressFromString("bridge") 686 687 amount := big.NewInt(10) 688 RunWithNewBlockView(t, em, func(blk types.BlockView) { 689 _, err = blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0)) 690 require.NoError(t, err) 691 }) 692 693 orgSize := backend.TotalStorageSize() 694 RunWithNewBlockView(t, em, func(blk types.BlockView) { 695 _, err = blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0)) 696 require.NoError(t, err) 697 }) 698 require.Equal(t, orgSize, backend.TotalStorageSize()) 699 }) 700 }) 701 } 702 703 func TestCallingExtraPrecompiles(t *testing.T) { 704 testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { 705 testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) { 706 RunWithNewEmulator(t, backend, flowEVMRoot, func(em *emulator.Emulator) { 707 708 testAccount := types.NewAddressFromString("test") 709 bridgeAccount := types.NewAddressFromString("bridge") 710 amount := big.NewInt(10_000_000) 711 RunWithNewBlockView(t, em, func(blk types.BlockView) { 712 _, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAccount, amount, 0)) 713 require.NoError(t, err) 714 }) 715 716 input := []byte{1, 2} 717 output := []byte{3, 4} 718 addr := testutils.RandomAddress(t) 719 pc := &MockedPrecompile{ 720 AddressFunc: func() types.Address { 721 return addr 722 }, 723 RequiredGasFunc: func(input []byte) uint64 { 724 return uint64(10) 725 }, 726 RunFunc: func(inp []byte) ([]byte, error) { 727 require.Equal(t, input, inp) 728 return output, nil 729 }, 730 } 731 732 ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) 733 ctx.ExtraPrecompiles = []types.Precompile{pc} 734 735 blk, err := em.NewBlockView(ctx) 736 require.NoError(t, err) 737 738 res, err := blk.DirectCall( 739 types.NewContractCall( 740 testAccount, 741 types.NewAddress(addr.ToCommon()), 742 input, 743 1_000_000, 744 big.NewInt(0), // this should be zero because the contract doesn't have receiver 745 0, 746 ), 747 ) 748 require.NoError(t, err) 749 require.Equal(t, output, res.ReturnedValue) 750 }) 751 }) 752 }) 753 } 754 755 type MockedPrecompile struct { 756 AddressFunc func() types.Address 757 RequiredGasFunc func(input []byte) uint64 758 RunFunc func(input []byte) ([]byte, error) 759 } 760 761 func (mp *MockedPrecompile) Address() types.Address { 762 if mp.AddressFunc == nil { 763 panic("Address not set for the mocked precompile") 764 } 765 return mp.AddressFunc() 766 } 767 768 func (mp *MockedPrecompile) RequiredGas(input []byte) uint64 { 769 if mp.RequiredGasFunc == nil { 770 panic("RequiredGas not set for the mocked precompile") 771 } 772 return mp.RequiredGasFunc(input) 773 } 774 775 func (mp *MockedPrecompile) Run(input []byte) ([]byte, error) { 776 if mp.RunFunc == nil { 777 panic("Run not set for the mocked precompile") 778 } 779 return mp.RunFunc(input) 780 }