Skip to content

Add rollup_getVirtualZkEVMStateMerkleProofV0#135

Closed
garyschulte wants to merge 20 commits intoConsensys:mainfrom
garyschulte:feature/virtual-zkevm-state-proof
Closed

Add rollup_getVirtualZkEVMStateMerkleProofV0#135
garyschulte wants to merge 20 commits intoConsensys:mainfrom
garyschulte:feature/virtual-zkevm-state-proof

Conversation

@garyschulte
Copy link
Collaborator

@garyschulte garyschulte commented Jan 23, 2026

PR Description

Adds an rpc endpoint to encapsulate the eth_simulateV1 call for a single transaction, and returns a proof of a 'virtual' block on top of the specified parent, akin to rollup_getZkEVMStateMerkleProofV0.

Fixed Issue(s)

relates to Consensys/besu-shomei-plugin#70

Documentation

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.

Changelog

  • I thought about adding a changelog entry, and added one if I deemed necessary.

Note

Medium Risk
Adds a new RPC path that drives worldstate simulation and trace generation on cached snapshots, plus updates trielog decoding for a new Besu format; correctness and cache/overlay edge cases could affect proof outputs or runtime behavior.

Overview
Adds a new JSON-RPC method rollup_getVirtualZkEVMStateMerkleProofV0 that simulates a single transaction on top of a specified parent block (via Besu eth_simulateV1), then returns the parent/end zk state root hashes plus the generated zk state merkle proof.

To support non-persistent simulation, introduces LayeredWorldStateStorage (in-memory overlay with delete tracking) and ZkWorldStateArchive.generateVirtualTrace(...) which applies a decoded trielog against a cached parent snapshot using an ephemeral trace store. Also updates trielog decoding for forward compatibility with Besu metadata, improves JSON-RPC client error handling/modeling, bumps besuVersion, and adjusts sync queue waiting logic to poll with timeouts/backoff for cleaner shutdown behavior.

Written by Cursor Bugbot for commit 8ff6eac. This will update automatically on new commits. Configure here.

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
@garyschulte garyschulte marked this pull request as draft January 23, 2026 16:26
… not head

Signed-off-by: garyschulte <garyschulte@gmail.com>
…_getVirtualZkEVMStateMerkleProofV0

Signed-off-by: garyschulte <garyschulte@gmail.com>
@garyschulte garyschulte marked this pull request as ready for review January 23, 2026 22:42
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…might potentially be a snapshot

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
@garyschulte garyschulte force-pushed the feature/virtual-zkevm-state-proof branch from b680ab4 to 6a04fea Compare January 29, 2026 07:09
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

@garyschulte garyschulte requested a review from matkt January 29, 2026 07:53
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…onor interrupted exception

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
@garyschulte
Copy link
Collaborator Author

when testing, I found these resources to be useful:
test genesis, toml, and 33 test blocks

and this curl to simulate a block 34 on top of the chain of 33:

curl --location 'http://localhost:8888' \
--header 'Content-Type: application/json' \
--data '{
    "jsonrpc": "2.0",
    "method": "rollup_getVirtualZkEVMStateMerkleProofV0",
    "params": [
        {
            "blockNumber": 34,
            "transaction": "0xf868808405f5e10082520894353535353535353535353535353535353535353585e8d4a510008025a0263f64b115ad522b540585a37302fc710dd5d48702f93420abd0e03c4fa12e4da0711c61d2cfd3fc6efd6cd9a8a6dbd6a873e12b74801980614ac7e7a3d2117d7e"
        }
    ],
    "id": 1
}
'

final TrieStorage.Range parentRange = parentStorage.getNearestKeys(hkey);

// Check for exact match (center node) in overlay first, then parent (if not deleted)
Optional<Map.Entry<Bytes, FlattenedLeaf>> centerNode = overlayRange.getCenterNode();
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need also to use getValidParentNode in this case ?

because it seems we are not checking if the key returned by the overlay is deleted

final TrieStorage.Range overlayRange = overlayStorage.getNearestKeys(hkey);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We shouldn't need getValidParentNode in that case because we only exact match for center nodes. Line 120 we filter parent range center node by deleted keys.


private final WorldStateStorage parentStorage;
private final InMemoryWorldStateStorage overlayStorage;
private final Set<Bytes> deletedKeys = ConcurrentHashMap.newKeySet();
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not managing the delete in the InMemoryWorldStateStorage ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was reluctant to change InMemoryWorldStateStorage - if you want to change this to use the Besu approach that sounds reasonable. I think the complexity is the same either way since we still have to defer to the parent for getNearest - that is where the complexity is. The unit tests assert correct behavior (on toy state), so we should have good coverage for a refactor

* This is useful for virtual/simulated blocks where we want to apply changes temporarily
* without modifying the cached parent state or persist the state permanently.
*/
public class LayeredWorldStateStorage implements WorldStateStorage {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not doing something similar to the LayeredKeyValueStorage besu implementation. this way to have InMemoryWorldStateStorage as a field seems to add a lot of complexity in the code

in Besu LayeredKeyValueStorage extend directly a InMemoryWorldStateStorage version that track deletion and we only have the parent

@matkt matkt closed this Feb 23, 2026
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.

2 participants