Skip to content

Demo of Integrating the MPT from RSP with Reth's ExecutionWitness #17416

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

koxu1996
Copy link

@koxu1996 koxu1996 commented Jul 15, 2025

Let me provide some context to avoid confusion.

Ethereum Block Proving

For proving Ethereum blocks, most zkVM teams are using SuccintLabs's RSP. There is also Risc0 Zeth, but it is not widely adopted.

Both RSP and Zeth are built upon Reth crates, with additional optimizations to make them zkVM-friendly.

However, they both suffer from a long input preparation times - several minutes - due to thousands calls to RPC:

  • eth_getProof
  • eth_getStorageAt

Manually building the state and storage tries is just too slow.

Execution Witness

The Reth team introduced a dedicated endpoint: debug_getExecutionWitness. It provides all relevant state and storage data in a single RPC request. Fetching the witness is fast for the recent blocks:

  • Latest block: 0.69s
  • 10 blocks ago: 0.87s
  • 20 blocks ago: 1.66s
  • 100 blocks ago: 2.63s
  • 500 blocks ago: 9.59s
  • 1000 blocks ago: 20.00s (default window limit is 1024 blocks)
  • 2000 blocks ago: 46.87s

That is not all! Reth also provides reth-stateless crate, which is the state-of-the-art for stateless execution and block validation. Naturally, it uses ExecutionWitness, allowing input preparation time to take only seconds.

One might now wonder: why not just replace RSP with reth-stateless?

Well, there is a trade-off. For block 22830000 and SP1 zkVM, we got:

  • RSP:
    • Input preparation time: few minutes
    • Program cycles: ~225M
    • Proving time: ~150 seconds.
  • reth-stateless:
    • Input preparation time: <1 second
    • Program cycles: ~500M cycles
    • Proving time: ~300 seconds

Input preparation time got reduced almost to zero, but we doubled the proving time.

This difference is understandable - Reth was not designed for zkVM efficiency. Its trie implementation is one of the bottlenecks.

Witness in Action

I currently work at ZkCloud, where I have build a prototype primarily based on reth-stateless logic, incorporating MPT/state implementation from RSP.

By constructing RSP's trie using ExecutionWitness, we drastically reduced total time to proof:

total-time-to-proof

At this point I got many questions like: "How did you build this low-latency data fetcher? Can you open source the integration?"

The truth is, there is no custom data fetcher. Fetching the ExecutionWitness is simply a call to Reth node.

So for everyone interested: this PR open-sources the integration of RSP's state with ExecutionWitness format.

⚠️ Please note: this is a rough prototype - use at your own risk.

Updating RSP / RPC

I imagine most people just want to plug updated RSP code and run it. While I am not using the original RSP, you should be able to adapt your code with minimal effort. Essentially, SP1ZkvmTrie replaces RSP’s EthereumState.

Also, you will need to run your own Reth node that exposes the execution witness endpoint. Public RPC providers will not work here.

Upstreaming Changes to Reth

By using RSP-style trie, instead of default SparseStateTrie:

reth_stateless::stateless_validation_with_trie::<
-  reth_stateless::trie::StatelessSparseTrie, _, _
+  reth_trie_sp1_zkvm::SP1ZkvmTrie, _, _
>(current_block, witness, chainspec_arc, evm_config).unwrap();

It helped to reduce ~60M cycles when executing block 22830000.

However, I do not plan to polish this PR for merging, since I am now experimenting with replacing RSP's MPT with Zeth's version (see risc0-ethereum-trie), or even writing custom implementation.


@kevaundray Currently, reth-stateless validation takes ExecutionWitness and builds the tries from it , which is not optimal for zkVM use. It would be great to have an API that builds a serializable NotValidatedTrie, which can then be deserialized and validated separately using reth-stateless.

@gakonst
Copy link
Member

gakonst commented Jul 15, 2025

This is very much in scope and we care for Reth e2e to be sota for proving whether it is fetching input data or trie stuff. Thank you. Cc @shekhirin @Rjected @mediocregopher for the rest of our trie work.

@koxu1996
Copy link
Author

koxu1996 commented Jul 16, 2025

This is very much in scope and we care for Reth e2e to be sota for proving whether it is fetching input data or trie stuff.

@gakonst Oh, that is good! In general, it would be great to split reth-stateless into the host logic and guest logic; put as much as possible in the host one. This allows nice optimizations, like avoiding senders recovery inside zkVM. This is how Zeth works: senders are recovered on the host, and guest only validates them against tx signatures.

@gakonst
Copy link
Member

gakonst commented Jul 16, 2025

Makes sense. Go for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

Successfully merging this pull request may close these issues.

2 participants