Add L1SLOAD (RIP-7728) proof collection and ZK verification for Taiko L2#50
Draft
jmadibekov wants to merge 13 commits intomasterfrom
Draft
Add L1SLOAD (RIP-7728) proof collection and ZK verification for Taiko L2#50jmadibekov wants to merge 13 commits intomasterfrom
jmadibekov wants to merge 13 commits intomasterfrom
Conversation
The L1SloadInspector and related infrastructure (inspect flag, create_block_executor_with_inspector, trace_l1sload_calls stub) are dead code. NMC handles L1SLOAD autonomously via direct L1 RPC calls, so no proposer/driver tracing is needed. Raiko detects L1SLOAD calls via direct transaction scanning in collect_l1_storage_proofs(). Removed: - lib/src/builder/l1sload_inspector.rs (entire file) - inspect field from TaikoWithOptimisticBlockExecutor - create_block_executor_with_inspector method - trace_l1sload_calls stub from preflight/util.rs - Unused parameters from collect_l1_storage_proofs
Update all alethia-reth dependencies to the feat/l1sload-precompile-nmc branch which is based on NMC's main and includes the L1SLOAD precompile with the correct 84-byte API (address + storage key + block number). Also fixes compilation issues from cherry-pick conflicts: - RpcBlockDataProvider::new() signature (1 arg, not 2) - Remove block_numbers assertion (field doesn't exist on master) - Use TaikoBlock type in collect_l1_storage_proofs signature
…hasta mode In Shasta mode, l1_header is set to l1_inclusion_block - 1 which differs from the anchor block referenced by the anchor transaction. The L1SLOAD code incorrectly compared anchor_state_root against l1_header.state_root, causing "Anchor state root mismatch" errors for every batch. Changes: - Remove broken anchor state root validation in preflight (both single-block and batch paths) that compared two different L1 blocks' state roots - Fix execute_transactions() and execute_transaction_batch() to extract the actual anchor block info from the anchor tx instead of using l1_header - Re-export get_anchor_tx_info_by_fork for use in the execution phase Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PR #2: Add L1SLOAD execution lock to serialize concurrent block executions that use L1SLOAD, preventing global cache races. - PR #4: Verify newest ancestor header immediately precedes anchor block in build_verified_state_root_map. - PR #5: Optimize get_and_verify_value to single verification pass instead of double-verifying for existing keys. - PR #6: Distinguish extension vs leaf nodes in get_leaf_value via HP flag check, preventing misparse of extension nodes. - PR #7: Include l1_storage_proofs in single-block GuestInput (was missing due to ..Default::default()). - PR #9: Replace .expect() with proper ? error propagation in L1SLOAD verification paths. - PR #10: Batch storage key proof collection by address to reduce RPC calls from one-per-key to one-per-block-number. - PR #14: Create l1_provider once before spawn loop and clone into each chunk task instead of creating per-chunk.
Update alethia-reth dependency to 136e51a0 which replaces eprintln! with tracing::trace! in the L1SLOAD precompile.
…oofs When the MPT proof terminates at a branch node (non-existent account), get_leaf_value was misinterpreting the branch's child hashes as leaf path+value because it only checked the HP flag without first verifying the node type via element count. A branch node's first child hash starting with 0x3X (75% chance) passes the HP flag >= 2 check, causing garbage value extraction and proof verification failure. Fix: count RLP list elements before HP flag check (17 = branch, 2 = leaf/extension), matching alloy-trie's TrieNode::decode pattern.
Add [jmadibekov] tags to: - preflight: scan detection, proof collection, indirect calls - proving: verify/populate cycle, state root map, MPT verification - cache: populate/clear operations
The log line at preflight/mod.rs:168 referenced `input.l1_storage_proofs` etc., but `input` (GuestInput) isn't constructed until a few lines later. The correct references are the local tuple bindings: `l1_storage_proofs`, `l1_ancestor_headers`, `l1_successor_headers`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is L1SLOAD?
L1SLOAD (RIP-7728) is a precompile at address
0x10001that lets L2 smart contracts read L1 Ethereum storage. An L2 transaction calls the precompile with 84 bytes — an L1 contract address (20 bytes), a storage key (32 bytes), and an L1 block number (32 bytes) — and gets back the 32-byte storage value from that slot at that block.This enables cross-layer composability: L2 contracts can read L1 oracle prices, verify L1 token balances, or check L1 governance state without a separate bridge.
What this PR does
Adds end-to-end L1SLOAD support to Raiko's preflight (witness collection) and proving (ZK verification) phases.
Preflight phase (with network access)
0x10001with 84-byte input and extracts the requested(address, key, block_number)tuples.eth_getProof) for all detected calls, batched by(block_number, address).Proving phase (no network, inside ZK/SGX)
parent_hashlinkage in both directions from the trusted anchor block to build a verifiedblock_number → state_rootmap.alloy_trie::verify_proof.Trust model
The anchor block's state root (committed in the L2 anchor transaction) is the cryptographic trust root. All L1 state is verified against it:
Block range
[anchor - 256, l1origin]Key implementation details
get_anchor_tx_info_by_fork()handles all Taiko fork variants (Shasta, Pacaya, Ontake, default).GuestInputfields use#[serde(default)]so pre-L1SLOAD inputs continue to work.Example: what gets detected
0x10001(direct)0x10001(indirect)0x10001Files changed
lib/src/input.rs,lib/src/anchor.rsL1StorageProofstruct, anchor extractionlib/src/l1_precompiles/l1sload.rscore/src/lib.rsprepare_l1sload_for_execution()orchestrationcore/src/preflight/mod.rs,core/src/preflight/util.rscore/src/provider/rpc.rs,core/src/provider/mod.rslib/src/builder/mod.rsCross references