foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.13k stars 1.69k forks source link

Test Fails after `getCode` Call to Moonbeam Precompiles against `--fork-url` Moonbeam Fork #7061

Open albertov19 opened 7 months ago

albertov19 commented 7 months ago

Component

Forge

Have you ensured that all of these are up to date?

What version of Foundry are you on?

forge 0.2.0 (b174c3a 2024-02-09T00:16:22.953958126Z)

What command(s) is the bug in?

forge test

Operating System

Linux

Describe the bug

We are executing a simple test against a Moonbeam precompile running it against a Moonbeam specific fork that supports their precompiles. We check precompile support by doing a simple eth_call to the precompile fetching the balanceOf a specific address. The JSON-RPC works, meaning that the fork supports Precompiles.

The test is fairly simple, it loads the precompile address through an ERC_20 token interface and calls the balanceOf method. We added a console log for Hello! just to understand where it failed, if in the loading the token interface, or executing the balanceOf.

pragma solidity ^0.8.0;

import "@forge-std/Test.sol";
import "@forge-std/console.sol";

import {IERC20} from "@openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract BalanceIntegrationTest is Test {
    function testBalance() public {
        IERC20 token = IERC20(0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080);
        console.log("Hello!");
        uint256 balance = token.balanceOf(0xD22Da948c0aB3A27f5570b604f3ADef5F68211C3);
        console.log(balance);
    }
}

After running the test, it failed in the balanceOf operation. Test logs:

No files changed, compilation skipped

Running 1 test for test/BalanceIntegrationTest.t.sol:BalanceIntegrationTest
[FAIL. Reason: EvmError: Revert] testBalance() (gas: 6176)
Logs:
  Hello!

Traces:
  [6176] BalanceIntegrationTest::testBalance()
    ├─ [0] console::log("Hello!") [staticcall]
    │   └─ ← ()
    ├─ [6] 0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080::balanceOf(0xD22Da948c0aB3A27f5570b604f3ADef5F68211C3) [staticcall]
    │   └─ ← EvmError: Revert
    └─ ← EvmError: Revert

In the Moonbeam Fork, we enabled the JSON-RPC trace, meaning we had visibility to all the JSON-RPC requests and responses from the fork itself.

The first weird thing we noticed is that there are a lot of weird eth_getTransactionCount, eth_getBalance and eth_getCode calls to smart contracts, some of them were not even in the tests! Another weird thing is that these calls referred to values in block 0x59. Moonbeam fork actually builds on top of the latest block of the chain, which is already past 5.4M blocks. For example, this is the JSON-RPC trace for the precompile address before the test fails and there are no more JSON-RPC calls:

2024-02-09 10:46:09.396 TRACE tokio-runtime-worker rpc_metrics: [http] on_call name=eth_getTransactionCount params=Params(Some("[\"0xffffffff1fcacbd218edc0eba20fc2308c778080\",\"0x59\"]")) kind=method call    
result="{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":19}" 

2024-02-09 10:46:09.401 TRACE tokio-runtime-worker rpc_metrics: [http] on_call name=eth_getBalance params=Params(Some("[\"0xffffffff1fcacbd218edc0eba20fc2308c778080\",\"0x59\"]")) kind=method call    
result="{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":18}"  

2024-02-09 10:46:09.405 TRACE tokio-runtime-worker rpc_metrics: [http] on_call name=eth_getCode params=Params(Some("[\"0xffffffff1fcacbd218edc0eba20fc2308c778080\",\"0x59\"]")) kind=method call    
result="{\"jsonrpc\":\"2.0\",\"result\":\"0x60006000fd\",\"id\":20}" 

Note that the bytecode for the precompile is just REVERT, but it is set as such so that when doing a address size check it returns something different to zero and the execution understands that the target address is a contract and not an EOA.

We never see the eth_call to fetch the balance of the address on the precompile. Our theory is that Foundry is doing some checks against the bytecode returned by eth_getCode.

Any ideas?

Thanks in advance

albertov19 commented 7 months ago

We ran the same test but against a regular ERC-20 and it seems that our precompile does not support eth_getStorageAt queries. Why is this not done via a simple eth_call?

Thanks in advance

mattsse commented 7 months ago

thanks for flagging, this sounds weird indeed.

I'd appreciate if you could put this simple example into a minimal repro, so I don't have set it up manually what are you're cli args?

klkvr commented 7 months ago

We never see the eth_call to fetch the balance of the address on the precompile. Our theory is that Foundry is doing some checks against the bytecode returned by eth_getCode.

The way fork testing works is by fetching bytecode of the contract via eth_getCode and executing it instead of making eth_calls. This is needed because eth_calls does not let you mutate and track changes of the EVM state.

This limitation is explained in Moonbeam docs https://docs.moonbeam.network/builders/build/eth-api/dev-env/foundry/#forking-with-cast-anvil

albertov19 commented 7 months ago

We never see the eth_call to fetch the balance of the address on the precompile. Our theory is that Foundry is doing some checks against the bytecode returned by eth_getCode.

The way fork testing works is by fetching bytecode of the contract via eth_getCode and executing it instead of making eth_calls. This is needed because eth_calls does not let you mutate and track changes of the EVM state.

This limitation is explained in Moonbeam docs https://docs.moonbeam.network/builders/build/eth-api/dev-env/foundry/#forking-with-cast-anvil

Hey @klkvr thanks for your reply.

The issue is not that, we are well aware. This test was run against a Moonbeam fork that does support precompiles (note the successful JSON RPC eth_call for example).

We realized that the issue is that Moonbeam clients don't support rth_getStoragrAt for precompiles. We need to add support for this at a client level, but we did not realize that these kind of calls were executed like this and not via eth_call

Is there a way to run tests without eth_getStorageAt for contract static calls?

Thanks in advance

klkvr commented 7 months ago

Yeah, eth_getStorageAt queries don't work as precompiles do not store any data in the actual smart contract storage.

There is currently no way to run tests without eth_getStorage as we need those queries to be able to process storage reads happening during execution.

If we'd use eth_calls, it would only be able to response with data relevant for current mainnet state. That way, if your test mutates state of precompile token (via transfer), we wouldn't be able to know what the next result of balanceOf will be. Thus, you wouldn't be able to test any behavior dependent on precompiles state

albertov19 commented 7 months ago

Yeah, eth_getStorageAt queries don't work as precompiles do not store any data in the actual smart contract storage.

There is currently no way to run tests without eth_getStorage as we need those queries to be able to process storage reads happening during execution.

If we'd use eth_calls, it would only be able to response with data relevant for current mainnet state. That way, if your test mutates state of precompile token (via transfer), we wouldn't be able to know what the next result of balanceOf will be. Thus, you wouldn't be able to test any behavior dependent on precompiles state

Understood.

What happens is not that precompiles do not store any data in the actual smart contract data (which you are correct and they don't), is just that Moonbeam's Ethereum compatibility layer does not have a map for eth_getStorageAt for precompiles so it looks in the Ethereum emulation layer we run etc.

So we'll have to support this so that devs can run tests against these type of Moonbeam forks.

Is there a way to have Foundry use a custom fork methodology for Moonbeam that is not Anvil? This will allow devs to fork and use precompiles (after we fix the issue of getStorageAt)

Thanks for all your replies!

mattsse commented 7 months ago

custom precompile handlers would be possible once we've updated the evm dependency

could you point us to the moonbeam precompiles?

albertov19 commented 7 months ago

custom precompile handlers would be possible once we've updated the evm dependency

could you point us to the moonbeam precompiles?

Happy to help!

We have the following: https://docs.moonbeam.network/builders/pallets-precompiles/precompiles/

And also a lot of Substrate based assets like DOT is served to our EVM as a precompile as well. Here are the list of such precompiles (that are erc-20)

https://docs.moonbeam.network/builders/interoperability/xcm/xc20/overview/#current-xc20-assets

albertov19 commented 7 months ago

Hey @mattsse @klkvr, we are happy to help out however we can to make Moonbeam testing more smooth.

Let us know what we can do on our end.

Regards