Skip to content

perf(evm): discover non-Anvil fork endpoints once - #17280

Merged
mattsse merged 5 commits into
masterfrom
mattsse/fork-endpoint-probe-once
Oct 3, 2026
Merged

mattsse merged 5 commits into
masterfrom
mattsse/fork-endpoint-probe-once

Conversation

@mattsse

@mattsse mattsse commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Fork resolution snapshots the endpoint identity (eth_chainId plus the anvil_nodeInfo probe) before and after every remote read, so an Anvil reset or profile change cannot slip in between. An endpoint without Anvil node info has nothing to revalidate, since its identity is derived from the chain ID alone, yet each step repeated the snapshot: cast run discovered the endpoint in run and again in TracingExecutor::get_fork, took three snapshots around the block and gas price reads in env_resolved, and two more around the block read in create_fork. That is 9 eth_chainId and 9 anvil_nodeInfo requests, sent sequentially before the replay starts. cast call --trace and forge test --fork-url go through the same sequence.

Such endpoints are now probed once: discovery returns the first snapshot without a confirming one when it finds no Anvil node info, and later snapshots reuse the cached discovery. Any probe failure still means "no Anvil" until node info is returned, endpoints identified as Anvil keep their strict revalidation around every read, and discover_fork_endpoint still always inspects the endpoint. The remote trace paths (--debug-trace-transaction, --debug-trace-call) use the same reuse for their before/after identity check. The one behavior difference is an Anvil endpoint whose first discovery probe fails: it is now treated as non-Anvil for the rest of the invocation instead of being picked up by the confirming snapshot. Resolution without a cached discovery still reads both snapshots and identifies it.

Results

Cold cast run 0x29582b47311ef26dc1d0677448ff520d646ac1d0489ca310d70294343991130b --rpc-url https://ethereum.reth.rs/rpc (fork cache for the block removed before every run), release builds:

master this PR
eth_chainId requests 9 1
anvil_nodeInfo requests 9 1
total RPC requests 57 41
wall time, median of 20 interleaved runs 1.69 s 1.29 s

Probe requests (eth_chainId + anvil_nodeInfo) on the other paths against the same endpoint:

master this PR
cast run --quick 9 + 9 1 + 1
cast call --trace 9 + 9 1 + 1
cast run --debug-trace-transaction 6 + 6 1 + 1
cast call --debug-trace-call 6 + 6 1 + 1
forge test --fork-url 9 + 9 1 + 1

Fork resolution snapshots the endpoint identity (`eth_chainId` plus the
`anvil_nodeInfo` probe) around every remote read so that an Anvil reset
cannot slip between them. An endpoint whose discovery found no Anvil
identity has nothing to revalidate, but each step still repeated the
snapshot: `cast run` sent 9 `eth_chainId` and 9 `anvil_nodeInfo`
requests before replaying anything.

Reuse the cached discovery for such endpoints. Discovery itself is
unchanged, endpoints identified as Anvil keep their strict
revalidation, and `discover_fork_endpoint` still always inspects the
endpoint.

Cold `cast run` of a mainnet swap against a remote RPC drops from 9+9
to 2+2 probe requests and from 1.66s to 1.34s (median of 20 interleaved
runs).
@mattsse
mattsse marked this pull request as ready for review October 2, 2026 11:40
Discovery read a second snapshot to confirm the first. Without Anvil
node info the identity depends on the chain ID alone, so the second
`eth_chainId` and `anvil_nodeInfo` requests confirmed nothing a remote
node can change. Return the first snapshot instead.

An Anvil endpoint whose first probe fails is now discovered as
non-Anvil. Resolution without a cached discovery still reads both
snapshots and identifies it.

Cold `cast run` of a mainnet swap against a remote RPC now sends 1+1
probe requests (9+9 on master): 1.69s -> 1.29s (median of 20
interleaved runs).
Comment thread crates/evm/core/src/opts.rs
@stevencartavia
stevencartavia requested a review from mablr October 2, 2026 17:16
stevencartavia
stevencartavia previously approved these changes Oct 2, 2026
@mattsse
mattsse merged commit 3b57188 into master Oct 3, 2026
28 checks passed
@mattsse
mattsse deleted the mattsse/fork-endpoint-probe-once branch October 3, 2026 04:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants