Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
2 tasks done
albertov19 opened this issue Feb 9, 2024 · 9 comments
Open
2 tasks done
Labels
A-compatibility Area: compatibility T-bug Type: bug

Comments

@albertov19
Copy link
Contributor

albertov19 commented Feb 9, 2024

Component

Forge

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

  • Foundry
  • Foundryup

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 albertov19 added the T-bug Type: bug label Feb 9, 2024
@gakonst gakonst added this to Foundry Feb 9, 2024
@github-project-automation github-project-automation bot moved this to Todo in Foundry Feb 9, 2024
@albertov19
Copy link
Contributor Author

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
Copy link
Member

mattsse commented Feb 9, 2024

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
Copy link
Member

klkvr commented Feb 9, 2024

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
Copy link
Contributor Author

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
Copy link
Member

klkvr commented Feb 9, 2024

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
Copy link
Contributor Author

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
Copy link
Member

mattsse commented Feb 10, 2024

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

could you point us to the moonbeam precompiles?

@albertov19
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@zerosnacks zerosnacks added the A-compatibility Area: compatibility label Jun 27, 2024
@zerosnacks zerosnacks added this to the v1.0.0 milestone Jul 26, 2024
@grandizzy grandizzy removed this from the v1.0.0 milestone Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-compatibility Area: compatibility T-bug Type: bug
Projects
Status: Todo
Development

No branches or pull requests

5 participants