fix(l1): eth_call/eth_estimateGas tolerate a missing header slot_number#6961
fix(l1): eth_call/eth_estimateGas tolerate a missing header slot_number#6961ilitteri wants to merge 2 commits into
Conversation
…ulation env_from_generic errored with 'slot_number must be present in Amsterdam+ blocks' whenever the header carried None, while the block-execution path defaults the same value to zero. Headers legitimately lack the slot on Amsterdam+ chains whose consensus client drives a pre-V4 engine API (the slot only arrives in PayloadAttributesV4), and such blocks import and execute fine with SLOTNUM reading 0 — so every eth_call and eth_estimateGas on those chains failed while execution succeeded, breaking wallet flows that estimate gas before sending. Simulation must never be stricter than execution: default to zero exactly like the execution path.
Regression tests for env_from_generic on an Amsterdam+ chain whose header carries no slot_number (as ethrex --dev mode and any pre-V4-engine CL produce): simulation must succeed like execution does, and must still work when a slot is present. The tolerance test fails without the accompanying fix with 'slot_number must be present in Amsterdam+ blocks'.
🤖 Kimi Code ReviewThe PR correctly fixes a divergence between block execution and simulation (eth_call/eth_estimateGas) environments. The change is safe and aligns with Ethereum consensus behavior.
The logic simplification is correct. By removing the strict
Well-structured regression tests. Minor notes:
Module inclusion is correct. Overall: The fix is precise—simulation must never be stricter than execution. The tests verify both the regression case (missing slot) and existing behavior (present slot). No security issues identified. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code Review
No blocking correctness, security, or gas-accounting issues stood out in the code change itself. The relaxed handling in I could not run the targeted cargo test in this environment because the preconfigured Rust/Cargo homes are read-only and dependency resolution requires a writable cargo directory. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewReview:
|
Greptile SummaryThis PR fixes
Confidence Score: 5/5Safe to merge; the change removes a spurious error that made simulation stricter than execution without any functional benefit. The deleted code path was unreachable on properly-operated Amsterdam+ networks (canonical headers always have a slot via PayloadAttributesV4), and on pre-V4 or dev-mode chains it was erroneously blocking valid simulations. The replacement logic (unwrap_or(U256::zero())) matches what setup_env and the block-execution constructor already do. Two targeted regression tests cover both the None and Some cases. Only test/tests/levm/mod.rs has a trivial ordering nit; no file requires substantive attention.
|
| Filename | Overview |
|---|---|
| crates/vm/backends/levm/mod.rs | Removes the Amsterdam+ slot_number error guard from env_from_generic, defaulting to zero like the two other env constructors already do; the change is minimal and correct. |
| test/tests/levm/simulation_env_tests.rs | Adds two regression tests for the fixed behaviour (Amsterdam header with and without a slot number); test setup is sound. |
| test/tests/levm/mod.rs | Registers the new test module; simulation_env_tests is inserted out of alphabetical order relative to the surrounding module list. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["eth_call / eth_estimateGas / eth_createAccessList"] --> B["env_from_generic()"]
B --> C{VMType is L2?}
C -->|Yes| D["slot_number = 0"]
C -->|No| E["Read header.slot_number"]
E --> F{slot_number present?}
F -->|Yes| G["slot_number = header value"]
F -->|No - was Error, now 0| H["slot_number = 0"]
D --> I["Build Environment and run VM"]
G --> I
H --> I
style H fill:#90EE90
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["eth_call / eth_estimateGas / eth_createAccessList"] --> B["env_from_generic()"]
B --> C{VMType is L2?}
C -->|Yes| D["slot_number = 0"]
C -->|No| E["Read header.slot_number"]
E --> F{slot_number present?}
F -->|Yes| G["slot_number = header value"]
F -->|No - was Error, now 0| H["slot_number = 0"]
D --> I["Build Environment and run VM"]
G --> I
H --> I
style H fill:#90EE90
Comments Outside Diff (1)
-
test/tests/levm/mod.rs, line 15-27 (link)The new module declaration is placed between
eip8246_testsandl2_fee_token_ratio_tests, but alphabeticallysimulation_env_tests(si…) belongs afterrequests_eip8282_extraction_tests(r…) and beforestack_tests(st…), which is the ordering the rest of the list follows.Prompt To Fix With AI
This is a comment left during a code review. Path: test/tests/levm/mod.rs Line: 15-27 Comment: The new module declaration is placed between `eip8246_tests` and `l2_fee_token_ratio_tests`, but alphabetically `simulation_env_tests` (`si…`) belongs after `requests_eip8282_extraction_tests` (`r…`) and before `stack_tests` (`st…`), which is the ordering the rest of the list follows. 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!
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
test/tests/levm/mod.rs:15-27
The new module declaration is placed between `eip8246_tests` and `l2_fee_token_ratio_tests`, but alphabetically `simulation_env_tests` (`si…`) belongs after `requests_eip8282_extraction_tests` (`r…`) and before `stack_tests` (`st…`), which is the ordering the rest of the list follows.
```suggestion
mod eip8246_tests;
mod l2_fee_token_ratio_tests;
mod l2_fee_token_tests;
mod l2_gas_reservation_tests;
mod l2_hook_tests;
mod l2_privileged_tx_tests;
mod memory_tests;
mod opcode_tracer_tests;
mod precompile_tests;
mod prestate_tracer_tests;
mod requests_eip8282_extraction_tests;
mod simulation_env_tests;
mod stack_tests;
```
Reviews (1): Last reviewed commit: "test(l1): eth_call/eth_estimateGas toler..." | Re-trigger Greptile
Lines of code reportTotal lines added: Detailed view |
There was a problem hiding this comment.
Pull request overview
Aligns the LEVM simulation RPC environment builder (eth_call, eth_estimateGas, eth_createAccessList) with the execution path by defaulting a missing slot_number to 0 on Amsterdam+ forks, preventing simulation from failing on chains/headers that legitimately omit the field.
Changes:
- Remove Amsterdam+
slot_numberpresence validation fromenv_from_generic, defaulting missing values to0(L2 remains forced to0). - Add regression tests covering simulation on Amsterdam headers with
slot_number: NoneandSome(_). - Register the new test module in the LEVM test suite.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| crates/vm/backends/levm/mod.rs | Makes simulation env construction tolerate missing slot_number by defaulting to 0, matching execution behavior. |
| test/tests/levm/simulation_env_tests.rs | Adds regression tests ensuring simulation succeeds with missing/present slot_number under Amsterdam fork rules. |
| test/tests/levm/mod.rs | Includes the new simulation environment test module. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| #[test] | ||
| fn simulation_uses_the_slot_number_when_present() { |
| let (mut db, header) = amsterdam_db_and_header(Some(1234)); | ||
| let result = | ||
| LEVM::simulate_tx_from_generic(&plain_transfer(), &header, &mut db, VMType::L1, &NativeCrypto); | ||
| assert!(result.is_ok(), "got {result:?}"); |
There was a problem hiding this comment.
Building on Copilot's callout above: this test's assertion (result.is_ok()) is identical to the first test's, so it would pass regardless of whether the slot value flows through at all — nothing here actually distinguishes Some(1234) from None. Two shapes worth considering: (a) execute a small SLOTNUM-reading contract and assert the returned value equals 1234, which makes the test name honest; or (b) rename to something like simulation_accepts_a_present_slot_number so the name matches what's asserted. Non-blocking.
Motivation
eth_call,eth_estimateGas, andeth_createAccessListfail withslot_number must be present in Amsterdam+ blockson any chain whose blockheaders carry no
slot_numberand run Amsterdam-or-later rules — eventhough the very same blocks import and execute fine.
Scope, to be precise (the current live fork is Fusaka/Osaka, which is
pre-Amsterdam, so this does not affect mainnet or the default local
genesis today):
fork >= Amsterdam. Pre-Amsterdam chains — mainnet atFusaka, the default
fixtures/genesis/l1.json(osakaTime: 0, noamsterdamTime), stock--dev— take theelsebranch, which alreadydefaults the slot to 0. Unaffected.
engine call, so the header gets no slot: e.g.
--dev/engine_forkchoiceUpdatedV3-driven production against anAmsterdam-activated genesis such as
fixtures/genesis/l1-bal.json(
amsterdamTime: 0), and Amsterdam+ testnets driven by a consensus clientthat hasn't adopted
forkchoiceUpdatedV4. On these, wallet flows break(gas estimation before send) while transactions still execute.
Description
env_from_generic(the environment builder behind the simulation RPCs) wasthe only environment constructor that treated a missing
slot_numberas anerror on Amsterdam+. The block-execution environment and
setup_envbothdefault it to 0, so the chain accepts, executes, and re-validates such blocks
with
SLOTNUMreading 0 — only simulation refused. Simulation should never bestricter than execution: its job is to predict what execution does.
This aligns
env_from_genericwith the other two constructors: a missingslot_numberdefaults to 0 (L2 keeps its explicit 0). Behavior belowAmsterdam is unchanged (that path already defaulted to 0). On a properly
operated Amsterdam+ network the change is also invisible — the slot is a
required field of
PayloadAttributesV4on the build side andengine_newPayloadV5rejects payloads without it on the import side, socanonical Amsterdam+ headers always carry
Some(slot); the removed error pathwas only reachable in the header-has-no-slot case above.
Adds two regression tests (Amsterdam genesis): simulation succeeds on a header
without a slot number, and with one.
Checklist
STORE_SCHEMA_VERSION(crates/storage/lib.rs) if the PR includes breaking changes to theStorerequiring a re-sync. — N/A, no store changes.