Summary
The first live --vm-bridge-rpc-disable attempt on cc-smoke (2026-06-15) caught a real curio SP CreateDataSet tx that failed bridge-off. Stage 4/5 handlers are correct in isolation (unit + synthetic-tx live proof), but the real SP send flow exposed two concrete gaps that must close before bridge-off can work on a writing SP. Spun out of #45 so the write-path tracker stays clean.
Evidence (cc-smoke, calibration, 2026-06-15 15:07:44 UTC)
ERROR rpc error in RPC call to 'eth_sendRawTransaction': FEVM method requires --vm-bridge-rpc pointing at a Forest/Lotus node
INFO curio/message sender_eth.go:470 sent transaction {"hash":"0x000...0", "task_id":40002, "send_error":"send error: FEVM method requires --vm-bridge-rpc...", "poll_loops":6}
ERROR pdpv0 handlers_create.go:137 Failed to send transaction: ...
The SP's SenderETH.Send hit errBridgeUnconfigured, meaning our local EthSendRawTransaction path declined and fell through to the (disabled) bridge. No local path declined, bridging debug line fired.
Ruled out
- Not a dual-ChainAPI bug: single instance (
start.go:108), Mpool wired synchronously in startGossipHead before rpcserver.New binds (:261), ethAPI.EthSendRawTransaction → e.full.EthSendRawTransaction confirmed.
- Not a boot race: tx was ~5 min post-boot;
gossipsub mempool publisher wired logged at boot.
Item 1 — local send declines on the real curio tx encoding
The curio SenderETH builds the tx as a go-ethereum types.DynamicFeeTx → MarshalBinary (see curio tasks/message/sender_eth.go). Our synthetic test tx round-trips through ParseSignedEIP1559 fine, but the real one declined (parse/convert failed, or hex decode returned derr != nil and skipped the block silently).
Suspected mismatch: access-list encoding (EIP-2930 list present/empty), v/r/s signature normalization, or chainID placement in our RLP decoder vs go-ethereum's canonical 0x02 || rlp([...]) envelope.
To do:
Item 2 — eth_maxPriorityFeePerGas is an UNCOVERED write-path bridge dependency
The SP's full pre-send chain (curio sender_eth.go):
EstimateGas -> eth_estimateGas (#45 Stage 2, LOCAL ✅)
HeaderByNumber(nil) -> eth_getBlockByNumber latest (base fee; should be LOCAL)
SuggestGasTipCap -> eth_maxPriorityFeePerGas (STILL BRIDGE-ONLY ❌)
NetworkID -> eth_chainId / net_version (LOCAL ✅)
SendTransaction -> eth_sendRawTransaction (#45 Stage 4, see Item 1)
SuggestGasTipCap → eth_maxPriorityFeePerGas is still bridge-only. This means Stage 6 is not lower-priority — it is directly on the write path and must be local before any bridge-off succeeds, even after Item 1 is fixed.
To do:
Acceptance / re-test gate
Refs
Summary
The first live
--vm-bridge-rpc-disableattempt on cc-smoke (2026-06-15) caught a real curio SP CreateDataSet tx that failed bridge-off. Stage 4/5 handlers are correct in isolation (unit + synthetic-tx live proof), but the real SP send flow exposed two concrete gaps that must close before bridge-off can work on a writing SP. Spun out of #45 so the write-path tracker stays clean.Evidence (cc-smoke, calibration, 2026-06-15 15:07:44 UTC)
The SP's
SenderETH.SendhiterrBridgeUnconfigured, meaning our localEthSendRawTransactionpath declined and fell through to the (disabled) bridge. Nolocal path declined, bridgingdebug line fired.Ruled out
start.go:108),Mpoolwired synchronously instartGossipHeadbeforerpcserver.Newbinds (:261),ethAPI.EthSendRawTransaction → e.full.EthSendRawTransactionconfirmed.gossipsub mempool publisher wiredlogged at boot.Item 1 — local send declines on the real curio tx encoding
The curio
SenderETHbuilds the tx as a go-ethereumtypes.DynamicFeeTx→MarshalBinary(see curiotasks/message/sender_eth.go). Our synthetic test tx round-trips throughParseSignedEIP1559fine, but the real one declined (parse/convert failed, or hex decode returnedderr != niland skipped the block silently).Suspected mismatch: access-list encoding (EIP-2930 list present/empty),
v/r/ssignature normalization, or chainID placement in our RLP decoder vs go-ethereum's canonical0x02 || rlp([...])envelope.To do:
EthSendRawTransaction(distinct lines for: hex-decode fail,Mpool == nil,ParseSignedEIP1559err,ToSignedFilecoinMessageerr) so the next bridge-off window pinpoints the exact branch.SenderETHor sniff the bridge call) and round-trip it offline throughParseSignedEIP1559→ fix the decoder to accept it.chain/ethtxusing a real captured curioDynamicFeeTxpayload.Item 2 —
eth_maxPriorityFeePerGasis an UNCOVERED write-path bridge dependencyThe SP's full pre-send chain (curio
sender_eth.go):SuggestGasTipCap → eth_maxPriorityFeePerGasis still bridge-only. This means Stage 6 is not lower-priority — it is directly on the write path and must be local before any bridge-off succeeds, even after Item 1 is fixed.To do:
eth_maxPriorityFeePerGas(conservative tip estimate from recent tipset base-fee trend, or a safe floor). Over-estimate is acceptable; under-estimate is the hazard, same invariant as estimateGas.eth_getBlockByNumber("latest")returns a non-nilbaseFeelocally (the SP needsheader.BaseFee); add to the bridge-off coverage checklist.Acceptance / re-test gate
--vm-bridge-rpc-disableand zero bridge errors.Refs