Skip to content

fix(#123): devnet bridge-first read ordering for eth_getCode / eth_getStorageAt / eth_call#126

Merged
Reiers merged 1 commit into
mainfrom
fix/devnet-eth-bridge-first
Jul 12, 2026
Merged

fix(#123): devnet bridge-first read ordering for eth_getCode / eth_getStorageAt / eth_call#126
Reiers merged 1 commit into
mainfrom
fix/devnet-eth-bridge-first

Conversation

@Reiers

@Reiers Reiers commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Closes the last correctness gap from #123 (finding 7).

The problem

On a single-node docker devnet, localEthGetCode / localEthGetStorageAt / localEthCall always burn their full retry budget (2 × 8s = 16s) before falling through to the bridge, because there are no bitswap peers to fetch cold storage-trie blocks from. Symptom: eth_getCode against the deployed PDPVerifier proxy takes 15+ seconds and any real consumer (curio-core, ethers, viem) times out first.

The devnet lotus IS the source of truth for the devnet chain (single-source by design, trust posture unchanged from #122), so the local walk adds latency without adding trust.

The fix

Handlers short-circuit straight to the auto-wired VMBridge when NetworkName == "devnet" && Bridge != nil.

Live evidence

Curio-fork docker devnet, lotus at height 17600+, tested against the actually-deployed PDPVerifier proxy 0x564Fce2BeCEdd3042F0Cb8bE787fe8230a40d276:

method pre-fix post-fix
eth_getCode 15s timeout 34 ms, real 262-char bytecode
eth_getStorageAt(slot 0) 15s timeout 28 ms, real 32-byte value
eth_call bad selector 15s timeout 13 ms, correct revert

Blast radius

  • Mainnet + calibration: byte-identical to pre-change (NetworkName == "devnet" gate).
  • Escape hatch: on devnet with no bridge (--vm-bridge-rpc=""), reads return errBridgeUnconfigured instead of silently hanging.

Refactor

Extracted the bridge-forward tail of EthCall into bridgeEthCall so the devnet short-circuit and the mainnet/calibration local-miss fallback share the same code. No behavior change to the mainnet/calibration path.

Tests

5 new unit tests in rpc/handlers/devnet_bridge_first_test.go:

  • TestEthGetCode_DevnetGoesStraightToBridge — bridge called exactly once, local skipped
  • TestEthGetStorageAt_DevnetGoesStraightToBridge — same
  • TestEthCall_DevnetGoesStraightToBridge — same, includes bridge answer round-trip
  • TestEthGetCode_DevnetNoBridgeErrorsOut — escape-hatch case surfaces error
  • TestEthCall_DevnetNoBridgeErrorsOut — same for eth_call
  • TestEthGetCode_MainnetTriesLocalFirst — mainnet path unchanged (falls back to bridge only after local miss)

Full suite: go test ./... reports 52 packages ok.

Follows

…tStorageAt / eth_call

Closes the last correctness gap from lantern#123 (finding 7).

On a single-node docker devnet the local hamt walk in localEthGetCode
/ localEthGetStorageAt / localEthCall always burns its full retry
budget before falling through to the bridge: there are no bitswap peers
to fetch cold storage-trie blocks from. Symptom: eth_getCode against
the deployed PDPVerifier proxy takes 15+ seconds (retry budget = 2 x
8s) and any real consumer (curio-core, ethers, viem) times out first.

The devnet lotus IS the source of truth for the devnet chain (single-
source by design, trust posture unchanged from #122), so the local walk
adds latency without adding trust. Handlers now short-circuit straight
to the auto-wired VMBridge when NetworkName == "devnet" && Bridge != nil.

Live smoke, curio-fork docker devnet, height 17600+:

  eth_getCode(PDPVerifier)      34 ms   (was 15 s timeout)
  eth_getStorageAt(slot 0)      28 ms   (returns real 32-byte value)
  eth_call(bad selector)        13 ms   (bridge returns correct revert)

Mainnet + calibration paths byte-identical (bridge-first block gated on
network == "devnet"). Escape hatch: if the operator explicitly drops
the bridge on devnet (--vm-bridge-rpc=""), reads return
errBridgeUnconfigured instead of silently hanging — same as pre-#123.

Extracted the bridge-forward tail of EthCall into bridgeEthCall so the
devnet short-circuit and the mainnet/calibration local-miss fallback
share the same code.

5 new unit tests in rpc/handlers/devnet_bridge_first_test.go:
- devnet EthGetCode / EthGetStorageAt / EthCall each call bridge exactly
  once (never touch local)
- devnet EthGetCode / EthCall without a bridge return errBridgeUnconfigured
- mainnet EthGetCode with no accessor still falls back to bridge (local
  path unchanged)

Full suite: go test ./... reports 52 packages ok, no failures.
@Reiers Reiers merged commit 7a572c0 into main Jul 12, 2026
1 check passed
@Reiers Reiers deleted the fix/devnet-eth-bridge-first branch July 12, 2026 11:59
@Reiers Reiers mentioned this pull request Jul 14, 2026
7 tasks
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.

1 participant