Skip to content

eth: specify eth_simulateV1 block identity after Amsterdam - #881

Open
MysticRyuujin wants to merge 4 commits into
ethereum:mainfrom
MysticRyuujin:simulate-amsterdam-identity
Open

MysticRyuujin wants to merge 4 commits into
ethereum:mainfrom
MysticRyuujin:simulate-amsterdam-identity

Conversation

@MysticRyuujin

Copy link
Copy Markdown
Contributor

eth_simulateV1 does not say what a simulated block looks like after Amsterdam, so every client answers differently (#868). This PR specifies it.

Header fields: a simulated block carries every header field that a real block at that fork carries. After Amsterdam that is blockAccessListHash and slotNumber. slotNumber defaults to the parent's slot number plus one and is omitted when the parent has none. The Block schema gains slotNumber as uint64. This supersedes #806.

Block access list: the hash is computed with the EIP-7928 rules over the execution that the simulation performs. State overrides and movePrecompileToAddress are pre-state and are not recorded. System calls, calls and withdrawals use the same block access indices as a real block. Fees use the gas price fields that the simulation uses, so with validation off the coinbase is accessed but gets no balance change. The sender nonce increment is recorded whether or not validation is on.

traceTransfers: in blocks where EIP-7708 is active, no synthetic 0xeeee... logs are added. The protocol logs from 0xffff...fffe cover the same transfers. The flag still works for blocks before Amsterdam.

Fixtures are not touched. They are generated from go-ethereum and follow once geth implements slotNumber and the coinbase read, on top of #867.

Closes #868.

Simulated blocks carry slotNumber and blockAccessListHash. Define the
block access list of a simulated block and the traceTransfers behavior
when EIP-7708 is active. Add slotNumber to the Block schema.
@Soubhik-10

Soubhik-10 commented Sep 7, 2026 •

Copy link
Copy Markdown

Fees use the gas price fields that the simulation uses, so with validation off the coinbase is accessed but gets no balance change.

Can you please tell me if this is included as an empty AccountChange always or is omitted with validation off or some specific cases?

@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

@Soubhik-10 do you have a preference or argument for either?

@Soubhik-10

Copy link
Copy Markdown

@Soubhik-10 do you have a preference or argument for either?

If it's accessed we should include anyway ig?

@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

go-ethereum implementation of this text, for tracking: ethereum/go-ethereum#35668 (draft until this PR has agreement).

@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

Always included as an entry with empty change lists, as long as the block has at least one call, with or without validation. A balance change appears only when the fee is not zero. A block with no calls does not include it, same as an empty real block. I tightened the wording in the notes. The go-ethereum draft linked above does exactly this.

MysticRyuujin added a commit to MysticRyuujin/go-ethereum that referenced this pull request Sep 8, 2026
… spec

Simulated blocks now carry the slot number (parent + 1, omitted when the
parent has none), the coinbase is accessed at zero fee so it enters the
block access list like in a real block, and traceTransfers adds no
synthetic logs in blocks where EIP-7708 emits the protocol transfer log.

Implements ethereum/execution-apis#881.
@MysticRyuujin MysticRyuujin mentioned this pull request Sep 13, 2026
4 of 29 tasks
MysticRyuujin added a commit to MysticRyuujin/go-ethereum that referenced this pull request Sep 27, 2026
… spec

Simulated blocks now carry the slot number (parent + 1, omitted when the
parent has none), the coinbase is accessed at zero fee so it enters the
block access list like in a real block, and traceTransfers adds no
synthetic logs in blocks where EIP-7708 emits the protocol transfer log.

Implements ethereum/execution-apis#881.
@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

Needs ethereum/go-ethereum#35617 too

… warm set

Define the default call gas and the global gas limit use under EIP-8037, the EIP-2681 nonce limit in both validation modes, logIndex numbering that matches receipts, and the warm set after movePrecompileToAddress. Deprecate traceTransfers for EIP-7708 blocks.
@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

I added four rules in 05677f4. A hive run on the Amsterdam chain showed that clients differ on each one, and the notes did not say which behavior is correct. Please comment if your client should not follow a rule.

  1. Default call gas after EIP-8037. The default is the largest gas limit that the block can still include. That is the remaining state gas when the remaining regular gas is at least 2^24, and otherwise the smaller of the two. Each call uses up its gasUsed from the global gas limit. This matches geth. Nethermind, besu and erigon use a different amount today, so later calls get a different default gas and a different transaction hash.
  2. Nonce 2^64-1. The call fails in both validation modes, and the nonce does not wrap. EIP-2681 does not allow this state in a real block. Today geth wraps with validation off, reth rewrites the nonce to 0, and nethermind wraps but leaves the 0 out of the block access list. The rule names no error code, because error codes stay with Inconsistent JSON-RPC error codes for the same transaction-validation failure modes across methods #817.
  3. Warm set after movePrecompileToAddress. The move does not change the set of addresses that are warm at the start of a call. This matches geth, besu, erigon and nethermind. Reth warms the destination.
  4. logIndex. It uses the same numbering as receipts: a log from a failed call is not returned and does not use an index. In ethSimulate-use-as-many-features-as-possible, six failed transfers come before one success. Reth returns 0x0 for the log. Geth, besu, nethermind and erigon return 0x6, because each failed transfer still used an index. If the block were real, receipts would give 0x0. debug: specify callTracer output and add debug_traceCall #855 uses the same receipt numbering for callTracer.

I also marked traceTransfers as deprecated for EIP-7708 blocks, as @rjl493456442 suggested. The behavior does not change.

Name the request-level error for the nonce limit, exclude logs of reverted inner frames from the log index, note that traceTransfers logs are not in receipts, and use the EIP-8037 terms for the gas defaults.
@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

c485b46 tightens the wording from review:

  • The nonce limit ends the simulation with the request-level error, as the Failures section describes. Today geth wraps the nonce with validation off, and before Amsterdam a contract creation from such a sender becomes a failed call. So geth must change for this rule too.
  • logIndex also skips logs of inner call frames that reverted. traceTransfers logs take indices but are not in receipts, so the receipt numbering applies without them.
  • The gas defaults use the EIP-8037 terms execution gas and state gas, and define the remaining amounts.

The ethSimulate-use-as-many-features-as-possible fixture on main still has logIndex 0x6. It changes to 0x0 when the fixtures are refilled on #867. Nethermind master already returns 0x0 since NethermindEth/nethermind#13979, and reth does too.

@MysticRyuujin

Copy link
Copy Markdown
Contributor Author

go-ethereum drafts for the two rules that change geth: ethereum/go-ethereum#35841 (logIndex numbered like receipts) and ethereum/go-ethereum#35842 (nonce limit with validation off). Filling the fixtures with them changes only ethSimulate-use-as-many-features-as-possible and ethSimulate-overflow-nonce.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eth_simulateV1: simulated block identity is undefined after Amsterdam

2 participants