Skip to content

Conversation

@0xOmarA
Copy link
Contributor

@0xOmarA 0xOmarA commented Jul 14, 2025

Notes

⚠️ This PR depends on functionality added in #34 #39 and #42 and therefore all of these PRs should be merged first before this one is merged. I manually merged those branches into this branch in order to continue the development of this.

Summary

This PR supports all of the variables supported by matterlabs (e.g., $COINBASE) which we discussed in #39 which required some of the reworked async runtime APIs in order to make it easier to implement.

Description

Some of the variables present in the matterlabs tests format require the context of the chain. For example, we can encounter a string of $BLOCK_HASH in the input's calldata which we need to resolve into the actual block hash and then use that as an argument to the function being called.

Before this PR, our calldata resolution logic was completely stateless and has no way to access the state of the chain that the arguments are being resolved to run on. With this PR, we changed that so that the resolver function takes in an additional &impl EthereumNode which it can use to get the chain state that it needs to perform the resolution.

This PR extends the EthereumNode trait to allow it to fetch more information from the node's RPC. The set of methods added reflect what is needed by the matterlabs resolver and not a full set of methods that I think that we need. The new trait currently looks like the following:

pub trait EthereumNode {
    /// Execute the [TransactionRequest] and return a [TransactionReceipt].
    fn execute_transaction(&self, transaction: TransactionRequest) -> Result<TransactionReceipt>;

    /// Trace the transaction in the [TransactionReceipt] and return a [GethTrace].
    fn trace_transaction(&self, transaction: TransactionReceipt) -> Result<GethTrace>;

    /// Returns the state diff of the transaction hash in the [TransactionReceipt].
    fn state_diff(&self, transaction: TransactionReceipt) -> Result<DiffMode>;

    /// Returns the next available nonce for the given [Address].
    fn fetch_add_nonce(&self, address: Address) -> Result<u64>;

    /// Returns the ID of the chain that the node is on.
    fn chain_id(&self) -> Result<ChainId>;

    // TODO: This is currently a u128 due to Kitchensink needing more than 64 bits for its gas limit
    // when we implement the changes to the gas we need to adjust this to be a u64.
    /// Returns the gas limit of the specified block.
    fn block_gas_limit(&self, number: BlockNumberOrTag) -> Result<u128>;

    /// Returns the coinbase of the specified block.
    fn block_coinbase(&self, number: BlockNumberOrTag) -> Result<Address>;

    /// Returns the difficulty of the specified block.
    fn block_difficulty(&self, number: BlockNumberOrTag) -> Result<U256>;

    /// Returns the hash of the specified block.
    fn block_hash(&self, number: BlockNumberOrTag) -> Result<BlockHash>;

    /// Returns the timestamp of the specified block,
    fn block_timestamp(&self, number: BlockNumberOrTag) -> Result<BlockTimestamp>;

    /// Returns the number of the last block.
    fn last_block_number(&self) -> Result<BlockNumber>;
}

Concerns

  1. I have a concern that this has made the differential testing harder and has added a lot more variables into the mix.

    For example, say we execute the same input that uses the block number as a variable on kitchensink and on geth, but the two nodes have different block numbers at the moment (could be because of the minor difference between when both nodes were started, or for a myriad of other reasons). In this case, the state diff is going to be different between both kitchensink and geth.

  2. We need a way to pin the state in order to ensure that the resolution happens correctly.

    Say that we have a test case that looks like the following:

    {
        "instance": "Main",
        "method": "main",
        "calldata": ["$BLOCK_NUMBER"],
        "expect": ["$BLOCK_NUMBER"]
    }

    Since the calldata block number and the expect block number get resolved at two different points of time they will be different, even though the format is trying to convey that they should be the same.

    Therefore, we need some kind of mechanism to pin the state in one way or another in order to ensure that the same Input state variables all resolve to the same thing.

This PR does not address the two concerns mentioned above. I imagine that the second one will be easier to solve through some kind of state variable caching that's persisted for the same input, but don't have a design or implementation of that.

0xOmarA added 30 commits July 11, 2025 11:26
This commit adds the `--dev` argument to the `substrate-node` to allow
the chain to keep advancing as time goes own. We have found that if this
option is not added then the chain won't advance forward.
Fix an issue where kitchensink won't advance
@0xOmarA 0xOmarA mentioned this pull request Jul 16, 2025
Copy link
Contributor

@activecoder10 activecoder10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@0xOmarA 0xOmarA added this pull request to the merge queue Jul 18, 2025
Merged via the queue into main with commit 0b97d7d Jul 18, 2025
4 of 5 checks passed
@xermicus xermicus deleted the feature/better-input-parser branch July 18, 2025 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants