rpc : fix eth_simulateV1 has storage ram batch#20293
Merged
Merged
Conversation
… prior simulated blocks simulationIntraBlockStateReader.HasStorage only queried RangeAsOf(firstMinTxNum), missing storage written to the in-memory RAM batch by prior simulated blocks. This caused false negatives for newly deployed contracts, breaking EIP-7610 CREATE2 collision detection in multi-block simulations. Fix: add HasPrefixInRAM to TemporalMemBatch interface and implement it as a btree-only scan (no disk I/O) for StorageDomain. HasStorage now checks the RAM batch first before falling back to RangeAsOf. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Override called FinalizeTx(rules, NoopWriter) to clear the journal after applying state overrides. For any account that became empty (nonce=0, code=0x, balance=0) after the override, FinalizeTx triggered EIP-161 empty-account removal: DeleteAccount was discarded by NoopWriter but stateObject.deleted was set to true. This caused IntraBlockState.HasStorage to short-circuit to false before reaching the state reader, breaking EIP-7610 CREATE2 collision detection in multi-block eth_simulateV1. Fix: use SoftFinalise instead of FinalizeTx(NoopWriter). SoftFinalise clears the journal (preventing revert from undoing overrides) without triggering EIP-161 account deletion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Override called FinalizeTx(rules, NoopWriter) to flush dirty storage into originStorage (needed for correct SSTORE gas). However, EIP-161 empty-account removal also fired: any account that became empty (nonce=0, code=0x, balance=0) after the override was marked deleted=true in the IBS, even though the deletion was discarded by NoopWriter. This caused IntraBlockState.HasStorage to short-circuit to false before reaching the state reader, breaking EIP-7610 CREATE2 collision detection in multi-block eth_simulateV1 when a prior simulated block had deployed a contract at the overridden address. Fix: copy rules and set IsSpuriousDragon=false before passing to FinalizeTx. This preserves the originStorage commit (gas correctness) while preventing EIP-161 from deleting accounts during stateOverride processing. State overrides are simulation-only mutations and must not trigger consensus rules. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… accounts In multi-block eth_simulateV1 simulations, stateOverrides.Override calls FinalizeTx which marks overridden accounts as dirty in stateObjectsDirty. CommitBlock's MakeWriteSet then iterates all stateObjects and applies EIP-161 to every dirty account — so an override-only account with nonce=0/code=empty is spuriously deleted, which also wipes its storage via DomainDelPrefix. Fix: after Override, snapshot and clear stateObjectsDirty (ExtractAndClearDirty) so CommitBlock only sees transaction-touched accounts. After CommitBlock, CommitOverrideDirtyAccounts writes the override-only accounts with EIP-161 disabled, preserving their state in the simulation. This fixes the stateRoot mismatch in test_28 where a CREATE2-deployed contract's storage survived across blocks as a collision target. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ExtractAndClearDirty: use maps.Clone instead of manual copy loop - CommitOverrideDirtyAccounts: pass false directly to updateAccount instead of going through a noEIP161Rules intermediate copy - HasPrefixInRAM non-StorageDomain path: use strings.HasPrefix to avoid []byte(k) allocation per entry Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AskAlexSharov
approved these changes
Apr 5, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes multi-block eth_simulateV1 state visibility issues so collision detection (EIP-7610 CREATE/CREATE2 rules) correctly observes storage written by prior simulated blocks, including storage that exists only in the in-memory (RAM) batch.
Changes:
- Add a RAM-only prefix check to
TemporalMemBatchand use it insimulationIntraBlockStateReader.HasStoragebefore falling back to canonicalRangeAsOf. - Prevent state override finalization from triggering EIP-161 empty-account removal side effects, and ensure override-only accounts are still written during block commit.
- Add a regression test covering the RAM-batch
HasStoragebehavior; bump RPC workflow test version tov2.5.0.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
rpc/jsonrpc/eth_simulation.go |
Ensures override-only accounts aren’t dropped by CommitBlock (EIP-161) and updates HasStorage to check RAM-batch prefixes first. |
rpc/jsonrpc/eth_simulation_intrablock_test.go |
New regression test verifying HasStorage sees storage written only to the RAM batch across simulated blocks. |
rpc/ethapi/state_overrides.go |
Finalizes overrides without SpuriousDragon/EIP-161 empty-account removal to avoid spurious deleted=true flags in simulation. |
execution/state/intra_block_state.go |
Adds helpers to snapshot/clear dirty-account tracking and to commit override-only dirty accounts with EIP-161 disabled. |
db/state/temporal_mem_batch.go |
Implements HasPrefixInRAM for fast RAM-only prefix existence checks (esp. StorageDomain btree). |
db/kv/kv_interface.go |
Extends TemporalMemBatch interface with HasPrefixInRAM. |
.github/workflows/scripts/run_rpc_tests_remote_ethereum.sh |
Updates RPC test suite version to v2.5.0. |
.github/workflows/scripts/run_rpc_tests_gnosis.sh |
Updates RPC test suite version to v2.5.0. |
.github/workflows/scripts/run_rpc_tests_ethereum.sh |
Updates RPC test suite version to v2.5.0. |
.github/workflows/scripts/run_rpc_tests_ethereum_latest.sh |
Updates RPC test suite version to v2.5.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- HasPrefixInRAM: use ToStringZeroCopy consistently in both branches (StorageDomain btree and domain map) to avoid per-call heap allocations; also eliminate the redundant byte→string→byte round-trip in the btree loop - eth_simulation_intrablock_test: add t.Cleanup(tdb.Close) to prevent resource leak across the test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AskAlexSharov
added a commit
that referenced
this pull request
Apr 13, 2026
Cherry-pick of #20293 from main.
AskAlexSharov
added a commit
that referenced
this pull request
Apr 13, 2026
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.
SimulationIntraBlockStateReader, used by eth_simulateV1, implements HasStorage by querying only RangeAsOf(firstMinTxNum), which reflects the canonical state at the start of the first simulated block. Storage written by prior simulated blocks lives exclusively in the RAM batch (sd.GetMemBatch()) and is invisible to RangeAsOf. When a subsequent simulated block calls HasStorage on a newly deployed contract, it incorrectly returns false.
The concrete impact: EIP-7610 requires CREATE/CREATE2 to revert if the target address already has storage but no code/nonce (collision detection). With the bug, the collision is not detected and CREATE2 succeeds where it should fail.
Fix: added HasPrefixInRAM to the TemporalMemBatch interface, scanning only the in-memory btree for StorageDomain without touching disk. HasStorage now checks the RAM batch first before falling back to RangeAsOf.