fix(#123): devnet bridge-first read ordering for eth_getCode / eth_getStorageAt / eth_call#126
Merged
Merged
Conversation
…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.
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.
Closes the last correctness gap from #123 (finding 7).
The problem
On a single-node docker devnet,
localEthGetCode/localEthGetStorageAt/localEthCallalways 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_getCodeagainst 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:eth_getCodeeth_getStorageAt(slot 0)eth_callbad selectorBlast radius
NetworkName == "devnet"gate).--vm-bridge-rpc=""), reads returnerrBridgeUnconfiguredinstead of silently hanging.Refactor
Extracted the bridge-forward tail of
EthCallintobridgeEthCallso 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 skippedTestEthGetStorageAt_DevnetGoesStraightToBridge— sameTestEthCall_DevnetGoesStraightToBridge— same, includes bridge answer round-tripTestEthGetCode_DevnetNoBridgeErrorsOut— escape-hatch case surfaces errorTestEthCall_DevnetNoBridgeErrorsOut— same for eth_callTestEthGetCode_MainnetTriesLocalFirst— mainnet path unchanged (falls back to bridge only after local miss)Full suite:
go test ./...reports 52 packages ok.Follows
cmd/lanterndaemon mpool wiring (my discovered pre-existing gap from fix(#123): close all ten devnet RPC parity gaps against local Curio devnet #125's live smoke): separate PR.