Skip to content

feat(l1,l2): add State Override Set and Block Override Set to RPC simulation methods#6660

Draft
ilitteri wants to merge 1 commit into
mainfrom
feat/state-override-set
Draft

feat(l1,l2): add State Override Set and Block Override Set to RPC simulation methods#6660
ilitteri wants to merge 1 commit into
mainfrom
feat/state-override-set

Conversation

@ilitteri
Copy link
Copy Markdown
Collaborator

Motivation

ethrex rejects geth's State Override Set (3rd RPC param) and Block Override Set (4th RPC param) on eth_call, eth_estimateGas, and eth_createAccessList, and does not expose debug_traceCall at all. Tooling that integrators rely on — Foundry's forge script --rpc-url, Tenderly-style simulators, MEV simulators, wallet "what-if" balance previews, Multicall3 aggregators — assumes these parameters work. Closing the gap is a parity decision: tools that work against a geth RPC must work against an ethrex RPC.

Spec: https://geth.ethereum.org/docs/interacting-with-geth/rpc/objects#state-override-set

Description

Adds geth's override sets and a new debug_traceCall method, on both L1 and L2:

  • State Override Set (3rd param) on eth_call, eth_estimateGas, eth_createAccessList. Per-address fields: balance, nonce, code, state (replace storage), stateDiff (overlay storage), movePrecompileToAddress. state and stateDiff are mutually exclusive — co-presence is rejected at parse time.
  • Block Override Set (4th param) on eth_call, eth_estimateGas. Fields: number, time, gasLimit, coinbase, random (PREVRANDAO), baseFeePerGas, blobBaseFeePerGas (back-computed by inverting fake_exponential), difficulty.
  • debug_traceCall — new RPC method, signature [tx, block, traceConfig?, stateOverride?, blockOverride?], supports callTracer and prestateTracer.
  • L1 + L2 parity — L2 inherits all four methods via the existing _other_eth_method fall-through in crates/l2/networking/rpc/rpc.rs. The L2 sponsor flow's EstimateGasRequest literal is updated with ..Default::default() for the new fields.

Implementation shape:

  • New OverlaidVmDatabase<Inner> in crates/blockchain/vm.rs decorates any VmDatabase with per-address overrides. Six-method intercept; no LEVM changes; no impact on the proof/replay path.
  • New StateOverrideSet and BlockOverrideSet JSON types in crates/networking/rpc/types/ with custom deserialize for geth-strict semantics.
  • simulate_tx_with_overrides in crates/networking/rpc/eth/transaction.rs resolves the historical state, applies block overrides to a synthetic header, optionally wraps the DB, and runs simulate_tx_from_generic.
  • New LEVM::trace_call_from_generic / prestate_call_from_generic reuse the existing tracer machinery on a synthetic transaction.
  • Blockchain::new_evm generalized to accept any VmDatabase + 'static so the wrapper composes through it.

Geth-strict semantics:

Rule Behavior
state + stateDiff together for one address BadParams
code override on a 7702-delegated EOA Override wins
BLOCKHASH(n > real_head) under synthetic future block Zero
eth_createAccessList with 4th param BadParams (geth shape: state override only)
Mixed-case override key Tolerated
Empty override object {} No-op

Test coverage:

  • 97 unit tests passing across ethrex-blockchain, ethrex-rpc, ethrex-vm, ethrex-l2-rpc, of which 21 are new override-related tests (parse, wrapper, semantics).
  • Coverage includes: state/stateDiff exclusivity, mixed-case addresses, malformed hex, BLOCKHASH-past-head, replace vs diff modes, synthetic code hashing, no leakage between calls, parse-level rejection for 5-param eth_call and 4-param eth_createAccessList, blob-base-fee inversion round-trip.

Deferred to a follow-up PR:

  • RPC-server-level integration tests for the four endpoints with overrides (require in-memory genesis with seeded contracts).
  • EVM-execution integration for 7702-delegation interaction, BLOCKHASH-past-head opcode-level test, and precompile-relocation execution.

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync. — N/A, no storage changes.

… eth_estimateGas, and eth_createAccessList, plus a new debug_traceCall RPC method, matching geth's RPC parity (https://geth.ethereum.org/docs/interacting-with-geth/rpc/objects#state-override-set). Tooling that integrators rely on — Foundry forge script --rpc-url, Tenderly-style simulators, MEV simulators, wallet "what-if" balance previews, Multicall3 aggregators — assumes these parameters work; before this change, ethrex hard-rejected any params beyond two on the three call-family endpoints and did not expose debug_traceCall at all.

State Override Set fields per address: balance, nonce, code, state (replace storage), stateDiff (overlay storage), movePrecompileToAddress. state and stateDiff are mutually exclusive — co-presence is rejected at parse time with a descriptive error. Block Override Set fields: number, time, gasLimit, coinbase, random (PREVRANDAO), baseFeePerGas, blobBaseFeePerGas (back-computed by inverting fake_exponential against the active fork's blob fee update fraction), difficulty.

Implementation: a new OverlaidVmDatabase<Inner> wrapper in crates/blockchain/vm.rs decorates any VmDatabase with per-address overrides via a six-method intercept. State semantics live in ethrex-blockchain (StateOverride, StorageMode, synthetic_code) so the wrapper has no RPC dependencies; JSON-shaped types (StateOverrideSet, BlockOverrideSet, AccountOverride) live in crates/networking/rpc/types/ and convert into the semantic types at the boundary. simulate_tx_with_overrides resolves the historical state, applies block overrides to a synthetic header, optionally wraps the DB, and runs simulate_tx_from_generic. Blockchain::new_evm is generalized to accept any VmDatabase + 'static so the wrapper composes through it without touching LEVM internals or the proof/replay path.

debug_traceCall is a new RPC method with signature [tx, block, traceConfig?, stateOverride?, blockOverride?]. It reuses the existing callTracer and prestateTracer output frames through new LEVM::trace_call_from_generic and LEVM::prestate_call_from_generic functions, which mirror simulate_tx_from_generic but attach the tracer hooks at VM construction. The reexec field on traceConfig is ignored for traceCall per spec — there is no transaction to re-execute against an ancestor.

L1 and L2 RPC share the implementation: L2 inherits all four methods via the existing _other_eth_method fall-through in crates/l2/networking/rpc/rpc.rs. The only L2-side change is updating the sponsor flow's EstimateGasRequest struct literal to use ..Default::default() for the new fields.

Geth-strict semantics: state + stateDiff together returns BadParams; code override on a 7702-delegated EOA executes the override (the wrapper returns the synthetic code_hash from get_account_state before any delegation resolution); BLOCKHASH(n) returns zero when n is past the real chain head under a block override that raises number past the tip; eth_createAccessList rejects a 4th param (geth shape: state override only); address keys are case-insensitive; an empty override object is a no-op.

Test coverage: 21 new override-related unit tests across the four touched crates, including state/stateDiff exclusivity, mixed-case addresses, malformed hex rejection, BLOCKHASH-past-head behavior at the wrapper level, replace vs diff modes with present and absent slots, synthetic code hashing, no-leakage between sequential calls, parse-level rejection for 5-param eth_call and 4-param eth_createAccessList, and blob-base-fee inversion round-trip. Full unit-test suite for the four crates is 97 passing.

Integration tests at the RPC-server and EVM-execution layer (full eth_call/eth_estimateGas/eth_createAccessList/debug_traceCall flows through map_http_requests, 7702-delegation interaction, precompile-relocation execution, L1+L2 dispatch) are deferred to a follow-up PR. They require building a non-trivial harness with an in-memory genesis containing seeded contracts; the behaviors they exercise are already covered at the parser and wrapper level by the unit tests in this PR.
@github-actions
Copy link
Copy Markdown

⚠️ 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.

Hive — bal-devnet-6 Amsterdam consume-engine tests — 32 functions / 54 cases

Same root cause as the blockchain-runner skip list (see EF Tests —
Blockchain
below): snobal-devnet-6 fixtures expect bal-devnet-6 spec
semantics, but our impl runs ahead due to the bal-devnet-7-prep
set_delegation SELFDESTRUCT-style refund subtraction. These fixtures
are routed through hive's eels/consume-engine simulator and produce
the same failures. Excluded via KNOWN_EXCLUDED_TESTS (substring
match on test_<name>[fork_Amsterdam, anchoring to the Amsterdam fork
so legacy Prague/Osaka variants still run).

Affected EELS test functions (32)
  • test_auth_refund_block_gas_accounting
  • test_auth_refund_bypasses_one_fifth_cap
  • test_auth_state_gas_scales_with_cpsb
  • test_auth_with_calldata_and_access_list
  • test_auth_with_multiple_sstores
  • test_authorization_exact_state_gas_boundary
  • test_authorization_to_precompile_address
  • test_authorization_with_sstore
  • test_bal_7702_delegation_clear
  • test_bal_7702_delegation_create
  • test_bal_7702_delegation_update
  • test_bal_7702_double_auth_reset
  • test_bal_7702_double_auth_swap
  • test_bal_7702_null_address_delegation_no_code_change
  • test_bal_all_transaction_types
  • test_bal_selfdestruct_to_7702_delegation
  • test_bal_withdrawal_to_7702_delegation
  • test_duplicate_signer_authorizations
  • test_existing_account_auth_header_gas_used_uses_worst_case
  • test_existing_account_refund
  • test_existing_account_refund_enables_sstore
  • test_existing_auth_with_reverted_execution_preserves_intrinsic
  • test_many_authorizations_state_gas
  • test_mixed_auths_header_gas_used_uses_worst_case
  • test_mixed_new_and_existing_auths
  • test_mixed_valid_and_invalid_auths
  • test_multi_tx_block_auth_refund_and_sstore
  • test_multiple_refund_types_in_one_tx
  • test_simple_gas_accounting
  • test_sstore_state_gas_all_tx_types
  • test_transfer_with_all_tx_types
  • test_varying_calldata_costs

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-6+ semantics (and bal-devnet-7-prep
set_delegation re-application) 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.

EF Tests — Blockchain bal-devnet-6 (Amsterdam fork) — 74 tests

snobal-devnet-6 fixtures expect bal-devnet-6 spec semantics, but our impl
runs ahead due to the bal-devnet-7-prep set_delegation SELFDESTRUCT-style
refund subtraction. Skipped in
tooling/ef_tests/blockchain/tests/all.rs::SKIPPED_BASE, anchored on
[fork_Amsterdam so legacy Prague / Osaka variants still run.

Bucket breakdown (74 total) and resolution path
EIP Bucket Count
EIP-7702 set_code_txs 24
EIP-7702 set_code_txs_2 15
EIP-7702 gas 1
EIP-8037 state_gas_set_code 17
EIP-8037 state_gas_pricing 1
EIP-8037 state_gas_sstore 1
EIP-7928 block_access_lists_eip7702 8
EIP-7928 block_access_lists 1
EIP-7778 gas_accounting 3
EIP-7708 transfer_logs 1
EIP-7976 refunds 1
EIP-1344 chainid (Amsterdam fork-transition fixture) 1
Total 74

Re-enable once we either:

  • (a) bump fixtures to a snobal-devnet-7 release that locks in the new
    accounting; or
  • (b) revert the bal-devnet-7-prep subtraction for bal-devnet-6
    compatibility.
Full test list (74)

EIP-7702 — for_amsterdam/prague/eip7702_set_code_tx/set_code_txs/

  • delegation_clearing
  • delegation_clearing_and_set
  • delegation_clearing_failing_tx
  • delegation_clearing_tx_to
  • eoa_tx_after_set_code
  • ext_code_on_chain_delegating_set_code
  • ext_code_on_self_delegating_set_code
  • ext_code_on_self_set_code
  • ext_code_on_set_code
  • many_delegations
  • nonce_overflow_after_first_authorization
  • nonce_validity
  • reset_code
  • self_code_on_set_code
  • self_sponsored_set_code
  • set_code_multiple_valid_authorization_tuples_same_signer_increasing_nonce
  • set_code_multiple_valid_authorization_tuples_same_signer_increasing_nonce_self_sponsored
  • set_code_to_log
  • set_code_to_non_empty_storage_non_zero_nonce
  • set_code_to_self_destruct
  • set_code_to_self_destructing_account_deployed_in_same_tx
  • set_code_to_sstore
  • set_code_to_sstore_then_sload
  • set_code_to_system_contract

EIP-7702 — for_amsterdam/prague/eip7702_set_code_tx/set_code_txs_2/

  • call_pointer_to_created_from_create_after_oog_call_again
  • call_to_precompile_in_pointer_context
  • contract_storage_to_pointer_with_storage
  • delegation_replacement_call_previous_contract
  • double_auth
  • pointer_measurements
  • pointer_normal
  • pointer_reentry
  • pointer_resets_an_empty_code_account_with_storage
  • pointer_reverts
  • pointer_to_pointer
  • pointer_to_precompile
  • pointer_to_static
  • pointer_to_static_reentry
  • static_to_pointer

EIP-7702 — for_amsterdam/prague/eip7702_set_code_tx/gas/

  • account_warming

EIP-8037 — for_amsterdam/amsterdam/eip8037_state_creation_gas_cost_increase/state_gas_set_code/

  • auth_refund_block_gas_accounting
  • auth_refund_bypasses_one_fifth_cap
  • auth_with_calldata_and_access_list
  • auth_with_multiple_sstores
  • authorization_exact_state_gas_boundary
  • authorization_to_precompile_address
  • authorization_with_sstore
  • duplicate_signer_authorizations
  • existing_account_auth_header_gas_used_uses_worst_case
  • existing_account_refund
  • existing_account_refund_enables_sstore
  • existing_auth_with_reverted_execution_preserves_intrinsic
  • many_authorizations_state_gas
  • mixed_auths_header_gas_used_uses_worst_case
  • mixed_new_and_existing_auths
  • mixed_valid_and_invalid_auths
  • multi_tx_block_auth_refund_and_sstore

EIP-8037 — state_gas_pricing/

  • auth_state_gas_scales_with_cpsb

EIP-8037 — state_gas_sstore/

  • sstore_state_gas_all_tx_types

EIP-7928 — for_amsterdam/amsterdam/eip7928_block_level_access_lists/block_access_lists_eip7702/

  • bal_7702_delegation_clear
  • bal_7702_delegation_create
  • bal_7702_delegation_update
  • bal_7702_double_auth_reset
  • bal_7702_double_auth_swap
  • bal_7702_null_address_delegation_no_code_change
  • bal_selfdestruct_to_7702_delegation
  • bal_withdrawal_to_7702_delegation

EIP-7928 — block_access_lists/

  • bal_all_transaction_types

EIP-7778 — for_amsterdam/amsterdam/eip7778_block_gas_accounting_without_refunds/gas_accounting/

  • multiple_refund_types_in_one_tx
  • simple_gas_accounting
  • varying_calldata_costs

EIP-7708 — for_amsterdam/amsterdam/eip7708_eth_transfer_logs/transfer_logs/

  • transfer_with_all_tx_types

EIP-7976 — for_amsterdam/amsterdam/eip7976_increase_calldata_floor_cost/refunds/

  • gas_refunds_from_data_floor

EIP-1344 — for_amsterdam/istanbul/eip1344_chainid/chainid/

  • chainid (Amsterdam fork-transition fixture)

@github-actions github-actions Bot added L1 Ethereum client L2 Rollup client labels May 14, 2026
@github-actions
Copy link
Copy Markdown

Lines of code report

Total lines added: 1201
Total lines removed: 0
Total lines changed: 1201

Detailed view
+------------------------------------------------------+-------+------+
| File                                                 | Lines | Diff |
+------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/blockchain.rs               | 2523  | +3   |
+------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/vm.rs                       | 568   | +353 |
+------------------------------------------------------+-------+------+
| ethrex/crates/l2/networking/rpc/l2/transaction.rs    | 242   | +1   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/eth/transaction.rs      | 758   | +181 |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/rpc.rs                  | 1302  | +1   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/tracing.rs              | 391   | +135 |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/types/block_override.rs | 159   | +159 |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/types/mod.rs            | 9     | +2   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/types/state_override.rs | 259   | +259 |
+------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/levm/mod.rs                | 2392  | +3   |
+------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/levm/tracing.rs            | 369   | +70  |
+------------------------------------------------------+-------+------+
| ethrex/crates/vm/tracing.rs                          | 101   | +34  |
+------------------------------------------------------+-------+------+

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

Labels

L1 Ethereum client L2 Rollup client

Projects

Status: No status
Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant