Skip to content

feat(l1): implement debug_preimage RPC endpoint#6699

Open
azteca1998 wants to merge 4 commits into
mainfrom
feat/debug-preimage
Open

feat(l1): implement debug_preimage RPC endpoint#6699
azteca1998 wants to merge 4 commits into
mainfrom
feat/debug-preimage

Conversation

@azteca1998
Copy link
Copy Markdown
Contributor

@azteca1998 azteca1998 commented May 21, 2026

Summary

Adds the debug_preimage RPC endpoint for keccak256 preimage lookup.

This is a stub that always returns null. A full implementation is not currently possible because ethrex does not maintain a keccak preimage store:

  • debug_preimage maps a keccak256 hash back to its original input (e.g. storage slot keys, address hashes used in the state trie).
  • Building a preimage store would require recording every keccak256(input) → input mapping during EVM execution, which has significant storage and performance cost.
  • Most Ethereum clients (including geth) don't enable preimage collection by default — it's an opt-in flag (--cache.preimages).

Returning null is geth-compatible behavior meaning "preimage not available for this hash". The endpoint is registered so that tooling that probes for it gets a well-formed response instead of a method not found error.

Part of #6572

Test plan

  • debug_preimage with any hash returns null
  • Missing params returns error
  • e2e integration test (trace_preimage)

Add debug_preimage which looks up the preimage (original data) for a
given keccak hash. Returns null since ethrex does not maintain a
preimage store. The endpoint is registered for API compatibility.

Part of #6572
@github-actions github-actions Bot added the L1 Ethereum client label May 21, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

Known Issues

Tests intentionally excluded from CI. Source of truth for the Known
Issues
section the L1 workflow appends to each ef-tests job summary
and posts as a sticky PR comment.

EF Tests — Stateless coverage narrowed to EIP-8025 optional-proofs

make -C tooling/ef_tests/blockchain test calls test-stateless-zkevm
instead of test-stateless. The zkevm@v0.3.3 fixtures are filled against
bal@v5.6.1, out of sync with current bal spec; the broad target trips ~549
fixtures. Re-broaden once the zkevm bundle is regenerated.

Why and resolution path

PR #6527 broadened
test-stateless to extract the entire for_amsterdam/ tree from the
zkevm bundle and run all of it under --features stateless; combined with
this branch's bal-devnet-7 semantics that scope produces ~549
GasUsedMismatch / ReceiptsRootMismatch /
BlockAccessListHashMismatch failures.

test-stateless-zkevm filters cargo to the eip8025_optional_proofs
suite, which still validates the stateless harness without the bal-version
mismatch.

Re-broaden by switching test: back to test-stateless in
tooling/ef_tests/blockchain/Makefile once the zkevm bundle is regenerated
against the current bal spec.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

Lines of code report

Total lines added: 60
Total lines removed: 0
Total lines changed: 60

Detailed view
+------------------------------------------------+-------+------+
| File                                           | Lines | Diff |
+------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/debug/mod.rs      | 5     | +1   |
+------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/debug/preimage.rs | 57    | +57  |
+------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/rpc.rs            | 1303  | +2   |
+------------------------------------------------+-------+------+

Call debug_preimage and assert it returns null, since ethrex
does not maintain a keccak preimage store.
@azteca1998 azteca1998 linked an issue May 21, 2026 that may be closed by this pull request
@azteca1998 azteca1998 marked this pull request as ready for review May 21, 2026 14:51
@azteca1998 azteca1998 requested a review from a team as a code owner May 21, 2026 14:51
@ethrex-project-sync ethrex-project-sync Bot moved this to In Review in ethrex_l1 May 21, 2026
@github-actions
Copy link
Copy Markdown

🤖 Claude Code Review

Here is my review of PR #6699:


PR Review: feat(l1): implement debug_preimage RPC endpoint

Overview

This PR adds a stub debug_preimage RPC endpoint that always returns null, matching geth's behavior when the preimage store is not enabled. The rationale is clearly documented. The implementation is minimal and correct for a stub.


Code Quality

crates/networking/rpc/debug/preimage.rs

The implementation is clean and follows existing RPC handler patterns well.

  • The _hash field prefix correctly suppresses the unused-field warning for a stub — this is idiomatic Rust.
  • Parameter validation (exactly one H256) is correct and consistent with other handlers.
  • The unit tests cover the three expected parse paths well. One minor gap: there is no test for Some(vec![]) (empty params vector). The code handles it correctly via params.len() != 1, but an explicit test would be thorough.

Test File: test/tests/rpc/debug_trace_tests.rs

This is the most significant concern in the PR.

Dead code — will produce compiler warnings:

TestEnv (line 119) and setup_single_transfer_block (line 127) are defined but never called by any test. The only test in the file is preimage, which does not use either of them. In a Rust codebase this will emit dead_code warnings unless suppressed with #[allow(dead_code)]. If CI treats warnings as errors (-D warnings), this may fail the build.

The entire block-building and transaction infrastructure (build_block, create_transfer_tx, build_and_execute_block) is also unreferenced by any test in the file right now, though it is referenced by the dead setup_single_transfer_block — so it transitively compiles in but serves no purpose in this PR.

Recommendation: Either remove the unused scaffolding from this PR and add it when the trace tests that need it are actually written, or add #[allow(dead_code)] with a comment explaining it is placeholder for upcoming tests. The former is cleaner.

File naming: debug_trace_tests.rs does not describe what is actually tested here (debug_preimage). If this file is intended to grow into a trace test suite, the name is forward-looking but misleading right now.

Minor style: There is a double blank line after setup_single_transfer_block before the test (line 149–150). One blank line is the Rust convention.


Security / Correctness

No concerns. The stub is read-only, has no side effects, and null is the documented geth-compatible response for a missing preimage. The endpoint registration in rpc.rs is correct.


Summary

Area Finding
Core implementation Correct, idiomatic, well-tested
Dead code in test file TestEnv + setup_single_transfer_block unused — likely triggers dead_code warnings
Test coverage Good for the stub; minor gap on empty-params case
File naming debug_trace_tests.rs is misleading for what this file currently tests

The main actionable item is the dead code in the test file. Everything else is minor or cosmetic.


Automated review by Claude (Anthropic) · sonnet · custom prompt

@github-actions
Copy link
Copy Markdown

🤖 Kimi Code Review

Overall Assessment: The PR correctly implements a stub debug_preimage RPC endpoint that returns null (as ethrex does not maintain a preimage store). The implementation follows existing patterns and includes appropriate tests.

Minor Issues:

  1. Documentation (preimage.rs, lines 6-8): Add doc comments to the public struct explaining the endpoint behavior:

    /// debug_preimage RPC stub.
    /// Returns null as ethrex does not maintain a Keccak-256 preimage store.
    pub struct PreimageRequest {
        _hash: H256,
    }
  2. Test file dead code (debug_trace_tests.rs, lines 1-100): The integration test file imports and defines many helpers (build_block, create_transfer_tx, setup_single_transfer_block, etc.) that are unused by the current preimage test. While likely intended for future tests, this generates compiler warnings. Consider either:

    • Using #[allow(dead_code)] on unused helpers temporarily
    • Removing unused imports/functions until needed
    • Adding the planned tests in this PR to utilize the helpers
  3. L2 dependency in general RPC tests (debug_trace_tests.rs, line 12): The test imports ethrex_l2_rpc::signer for L1 transaction signing. While functional, this creates an undesirable coupling between L2 and core RPC test code. Consider using a generic signer or moving L2-dependent tests to the L2 crate.

Security/Consensus Notes:

  • No vulnerabilities: The endpoint performs no state access, I/O, or computation beyond parameter parsing. Returning null is the correct behavior for nodes without preimage storage and aligns with Ethereum client behavior.

Code Quality:

  • Item 1: The underscore prefix on _hash correctly suppresses unused field warnings while allowing test access.
  • Item 2: Parameter validation (exactly 1 param) in parse() is correct.
  • Item 3: The serde_json::from_value error propagation properly handles malformed hashes.

Suggestion: If future plans include actual preimage storage, consider adding a TODO comment in the handle method indicating where the lookup would occur.


Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 21, 2026

Greptile Summary

This PR registers debug_preimage as a new JSON-RPC endpoint that deliberately returns null for every hash, matching geth's behaviour when preimage collection is disabled. The PR is well-scoped and clearly documents why a full implementation is deferred.

  • crates/networking/rpc/debug/preimage.rs — new PreimageRequest handler that validates the single H256 parameter and unconditionally returns null, with inline unit tests covering the parse paths and the null response.
  • crates/networking/rpc/rpc.rs — routes debug_preimage to the new handler, following the existing dispatcher pattern.
  • test/tests/rpc/debug_trace_tests.rs — integration test confirming the null response; the file also contains a sizeable block of block-building scaffolding that is not called by any test in this PR and will produce dead-code warnings.

Confidence Score: 4/5

Safe to merge — the RPC change is a well-contained stub with no logic risk; the only concern is dead scaffolding code in the test file.

The handler itself is trivially correct (parse, discard, return null), the endpoint registration follows the established pattern, and the unit tests cover the expected parse failures. The test file ships a noticeable amount of unused helper infrastructure that will generate compiler warnings and could mislead future contributors about what is actually tested.

test/tests/rpc/debug_trace_tests.rs — contains unused scaffolding functions and a name that does not match its content.

Important Files Changed

Filename Overview
crates/networking/rpc/debug/preimage.rs New stub RPC handler for debug_preimage; always returns null with proper parameter validation, well-documented rationale, and inline unit tests.
crates/networking/rpc/debug/mod.rs Adds pub mod preimage declaration — one-line, low-risk change.
crates/networking/rpc/rpc.rs Registers debug_preimage in the debug request dispatcher — straightforward, follows existing pattern.
test/tests/rpc/debug_trace_tests.rs Contains a large amount of scaffolding (block-building helpers, TestEnv, transfer-tx utilities, unused constants) that is not exercised by the single preimage test; will produce dead-code warnings and the file name does not match its content.
test/tests/rpc/mod.rs Registers the new debug_trace_tests module — trivial one-line change.

Sequence Diagram

sequenceDiagram
    participant Client
    participant RPC as map_debug_requests (rpc.rs)
    participant Handler as PreimageRequest (preimage.rs)

    Client->>RPC: POST debug_preimage [hash]
    RPC->>Handler: parse(params)
    alt params missing or wrong count
        Handler-->>RPC: Err(RpcErr::BadParams)
        RPC-->>Client: JSON-RPC error
    else valid H256 hash
        Handler-->>RPC: "Ok(PreimageRequest { _hash })"
        RPC->>Handler: handle(context)
        Handler-->>RPC: Ok(Value::Null)
        RPC-->>Client: "{ result: null }"
    end
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
test/tests/rpc/debug_trace_tests.rs:65-160
**Unused scaffolding will produce dead-code warnings**

`build_block`, `create_transfer_tx`, `build_and_execute_block`, `setup_single_transfer_block`, `TestEnv`, and the constants `TEST_MAX_FEE_PER_GAS` / `TEST_GAS_LIMIT` are all defined here but referenced only from each other — none are called by the single actual test (`preimage`). Rust will emit `dead_code` warnings for every one of them. If this infrastructure is intended for future trace tests, it would be cleaner to land it alongside the tests that exercise it, or at least annotate it with `#[allow(dead_code)]` with a comment explaining the intent.

### Issue 2 of 2
test/tests/rpc/debug_trace_tests.rs:1
**File name doesn't reflect its content**

The file is named `debug_trace_tests.rs` but the only test in it (`preimage`) covers `debug_preimage`, not any tracing endpoint. Consider renaming it to `debug_preimage_tests.rs` (or a broader `debug_tests.rs` if trace tests will be added here later) to keep the module name self-documenting.

Reviews (1): Last reviewed commit: "test(rpc): add e2e integration test for ..." | Re-trigger Greptile

Comment on lines +65 to +160
async fn build_block(store: &Store, blockchain: &Blockchain, parent_header: &BlockHeader) -> Block {
let args = BuildPayloadArgs {
parent: parent_header.hash(),
timestamp: parent_header.timestamp + 12,
fee_recipient: H160::zero(),
random: H256::zero(),
withdrawals: Some(Vec::new()),
beacon_root: Some(H256::zero()),
slot_number: None,
version: 1,
elasticity_multiplier: ELASTICITY_MULTIPLIER,
gas_ceil: DEFAULT_BUILDER_GAS_CEIL,
};
let block = create_payload(&args, store, Bytes::new()).unwrap();
let result = blockchain.build_payload(block).unwrap();
result.payload
}

async fn create_transfer_tx(
chain_id: u64,
nonce: u64,
to: Address,
value: U256,
signer: &Signer,
) -> Transaction {
let mut tx = Transaction::EIP1559Transaction(EIP1559Transaction {
chain_id,
nonce,
max_priority_fee_per_gas: 0,
max_fee_per_gas: TEST_MAX_FEE_PER_GAS,
gas_limit: TEST_GAS_LIMIT,
to: TxKind::Call(to),
value,
data: Bytes::new(),
..Default::default()
});
tx.sign_inplace(signer).await.unwrap();
tx
}

async fn build_and_execute_block(
store: &Store,
blockchain: &Blockchain,
parent_header: &BlockHeader,
transactions: Vec<Transaction>,
) -> Block {
for tx in &transactions {
blockchain
.add_transaction_to_pool(tx.clone())
.await
.expect("tx should enter pool");
}
let block = build_block(store, blockchain, parent_header).await;
assert_eq!(block.body.transactions.len(), transactions.len());
blockchain.add_block(block.clone()).expect("block should be valid");
store
.forkchoice_update(vec![], block.header.number, block.hash(), None, None)
.await
.unwrap();
block
}

async fn rpc_call(store: &Store, method: &str, params: Vec<Value>) -> Value {
let body = json!({
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": 1
});
let request: RpcRequest = serde_json::from_value(body).expect("valid RPC request");
let context = default_context_with_storage(store.clone()).await;
map_http_requests(&request, context)
.await
.expect("RPC call should succeed")
}

struct TestEnv {
store: Store,
block: Block,
tx_hash: H256,
}

async fn setup_single_transfer_block() -> TestEnv {
let sk = test_secret_key();
let sender = sender_from_key(&sk);
let signer: Signer = LocalSigner::new(sk).into();
let (store, chain_id) = setup_store(sender).await;
let blockchain = Blockchain::default_with_store(store.clone());
let genesis_header = store.get_block_header(0).unwrap().unwrap();
let recipient = Address::from_low_u64_be(0xAA);
let value = U256::from(1_000_000_000_000_000_000u64);
let tx = create_transfer_tx(chain_id, 0, recipient, value, &signer).await;
let tx_hash = tx.hash();
let block = build_and_execute_block(&store, &blockchain, &genesis_header, vec![tx]).await;
TestEnv { store, block, tx_hash }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Unused scaffolding will produce dead-code warnings

build_block, create_transfer_tx, build_and_execute_block, setup_single_transfer_block, TestEnv, and the constants TEST_MAX_FEE_PER_GAS / TEST_GAS_LIMIT are all defined here but referenced only from each other — none are called by the single actual test (preimage). Rust will emit dead_code warnings for every one of them. If this infrastructure is intended for future trace tests, it would be cleaner to land it alongside the tests that exercise it, or at least annotate it with #[allow(dead_code)] with a comment explaining the intent.

Prompt To Fix With AI
This is a comment left during a code review.
Path: test/tests/rpc/debug_trace_tests.rs
Line: 65-160

Comment:
**Unused scaffolding will produce dead-code warnings**

`build_block`, `create_transfer_tx`, `build_and_execute_block`, `setup_single_transfer_block`, `TestEnv`, and the constants `TEST_MAX_FEE_PER_GAS` / `TEST_GAS_LIMIT` are all defined here but referenced only from each other — none are called by the single actual test (`preimage`). Rust will emit `dead_code` warnings for every one of them. If this infrastructure is intended for future trace tests, it would be cleaner to land it alongside the tests that exercise it, or at least annotate it with `#[allow(dead_code)]` with a comment explaining the intent.

How can I resolve this? If you propose a fix, please make it concise.

@@ -0,0 +1,178 @@
use std::{fs::File, io::BufReader, path::PathBuf};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 File name doesn't reflect its content

The file is named debug_trace_tests.rs but the only test in it (preimage) covers debug_preimage, not any tracing endpoint. Consider renaming it to debug_preimage_tests.rs (or a broader debug_tests.rs if trace tests will be added here later) to keep the module name self-documenting.

Prompt To Fix With AI
This is a comment left during a code review.
Path: test/tests/rpc/debug_trace_tests.rs
Line: 1

Comment:
**File name doesn't reflect its content**

The file is named `debug_trace_tests.rs` but the only test in it (`preimage`) covers `debug_preimage`, not any tracing endpoint. Consider renaming it to `debug_preimage_tests.rs` (or a broader `debug_tests.rs` if trace tests will be added here later) to keep the module name self-documenting.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@github-actions
Copy link
Copy Markdown

🤖 Codex Code Review

  1. debug_preimage is exposed as a supported RPC, but the implementation ignores the requested hash and unconditionally returns null (preimage.rs, rpc.rs). That is risky API behavior: callers cannot distinguish “this hash has no known preimage” from “ethrex never stores preimages at all.” Geth documents debug_preimage as returning “the preimage for a sha3 hash, if known,” and separately notes that preimages may be unavailable depending on node configuration. (geth.ethereum.org) If ethrex has no backing preimage store, I would prefer MethodNotFound or another explicit unsupported-feature error instead of advertising a permanently non-functional method.

  2. The new integration test mostly asserts the stubbed behavior and brings in a large amount of unrelated unused scaffolding (debug_trace_tests.rs, debug_trace_tests.rs). As written, it does not protect the important edges of this API choice: malformed hashes, extra params at the HTTP routing layer, or the intended “unsupported vs not-found” semantics. I’d trim this to a focused preimage test module and add at least one negative-path RPC test.

No EVM/gas-accounting/consensus-critical issues stood out in this diff beyond the API-semantics concern above.

I couldn’t run the test suite in this environment: cargo attempted to write under /home/runner/.cargo and fetch dependencies, which is blocked by the sandbox.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

Implement debug_preimage

1 participant