feat(l1,l2): add State Override Set and Block Override Set to RPC simulation methods#6660
feat(l1,l2): add State Override Set and Block Override Set to RPC simulation methods#6660ilitteri wants to merge 1 commit into
Conversation
… 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.
|
| 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_clearingdelegation_clearing_and_setdelegation_clearing_failing_txdelegation_clearing_tx_toeoa_tx_after_set_codeext_code_on_chain_delegating_set_codeext_code_on_self_delegating_set_codeext_code_on_self_set_codeext_code_on_set_codemany_delegationsnonce_overflow_after_first_authorizationnonce_validityreset_codeself_code_on_set_codeself_sponsored_set_codeset_code_multiple_valid_authorization_tuples_same_signer_increasing_nonceset_code_multiple_valid_authorization_tuples_same_signer_increasing_nonce_self_sponsoredset_code_to_logset_code_to_non_empty_storage_non_zero_nonceset_code_to_self_destructset_code_to_self_destructing_account_deployed_in_same_txset_code_to_sstoreset_code_to_sstore_then_sloadset_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_againcall_to_precompile_in_pointer_contextcontract_storage_to_pointer_with_storagedelegation_replacement_call_previous_contractdouble_authpointer_measurementspointer_normalpointer_reentrypointer_resets_an_empty_code_account_with_storagepointer_revertspointer_to_pointerpointer_to_precompilepointer_to_staticpointer_to_static_reentrystatic_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_accountingauth_refund_bypasses_one_fifth_capauth_with_calldata_and_access_listauth_with_multiple_sstoresauthorization_exact_state_gas_boundaryauthorization_to_precompile_addressauthorization_with_sstoreduplicate_signer_authorizationsexisting_account_auth_header_gas_used_uses_worst_caseexisting_account_refundexisting_account_refund_enables_sstoreexisting_auth_with_reverted_execution_preserves_intrinsicmany_authorizations_state_gasmixed_auths_header_gas_used_uses_worst_casemixed_new_and_existing_authsmixed_valid_and_invalid_authsmulti_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_clearbal_7702_delegation_createbal_7702_delegation_updatebal_7702_double_auth_resetbal_7702_double_auth_swapbal_7702_null_address_delegation_no_code_changebal_selfdestruct_to_7702_delegationbal_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_txsimple_gas_accountingvarying_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)
Lines of code reportTotal lines added: Detailed view |
Motivation
ethrex rejects geth's State Override Set (3rd RPC param) and Block Override Set (4th RPC param) on
eth_call,eth_estimateGas, andeth_createAccessList, and does not exposedebug_traceCallat all. Tooling that integrators rely on — Foundry'sforge 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_traceCallmethod, on both L1 and L2:eth_call,eth_estimateGas,eth_createAccessList. Per-address fields:balance,nonce,code,state(replace storage),stateDiff(overlay storage),movePrecompileToAddress.stateandstateDiffare mutually exclusive — co-presence is rejected at parse time.eth_call,eth_estimateGas. Fields:number,time,gasLimit,coinbase,random(PREVRANDAO),baseFeePerGas,blobBaseFeePerGas(back-computed by invertingfake_exponential),difficulty.debug_traceCall— new RPC method, signature[tx, block, traceConfig?, stateOverride?, blockOverride?], supportscallTracerandprestateTracer._other_eth_methodfall-through incrates/l2/networking/rpc/rpc.rs. The L2 sponsor flow'sEstimateGasRequestliteral is updated with..Default::default()for the new fields.Implementation shape:
OverlaidVmDatabase<Inner>incrates/blockchain/vm.rsdecorates anyVmDatabasewith per-address overrides. Six-method intercept; no LEVM changes; no impact on the proof/replay path.StateOverrideSetandBlockOverrideSetJSON types incrates/networking/rpc/types/with custom deserialize for geth-strict semantics.simulate_tx_with_overridesincrates/networking/rpc/eth/transaction.rsresolves the historical state, applies block overrides to a synthetic header, optionally wraps the DB, and runssimulate_tx_from_generic.LEVM::trace_call_from_generic/prestate_call_from_genericreuse the existing tracer machinery on a synthetic transaction.Blockchain::new_evmgeneralized to accept anyVmDatabase + 'staticso the wrapper composes through it.Geth-strict semantics:
state+stateDifftogether for one addressBadParamscodeoverride on a 7702-delegated EOABLOCKHASH(n > real_head)under synthetic future blocketh_createAccessListwith 4th paramBadParams(geth shape: state override only){}Test coverage:
ethrex-blockchain,ethrex-rpc,ethrex-vm,ethrex-l2-rpc, of which 21 are new override-related tests (parse, wrapper, semantics).BLOCKHASH-past-head, replace vs diff modes, synthetic code hashing, no leakage between calls, parse-level rejection for 5-parameth_calland 4-parameth_createAccessList, blob-base-fee inversion round-trip.Deferred to a follow-up PR:
BLOCKHASH-past-head opcode-level test, and precompile-relocation execution.Checklist
STORE_SCHEMA_VERSION(crates/storage/lib.rs) if the PR includes breaking changes to theStorerequiring a re-sync. — N/A, no storage changes.