Implement tx write in Autobahn rpc-only node (CON-309)#3525
Implement tx write in Autobahn rpc-only node (CON-309)#3525wen-coding wants to merge 12 commits into
Conversation
Adds an autobahn-role config (validator|rpc-only). With autobahn-role= "rpc-only", a non-validator RPC node loads the committee from autobahn.json as a routing table only — no consensus participation, no block execution, no validator key required. eth_sendRawTransaction submitted to such a node is recovered, sender-shard- mapped via Committee.EvmShard, and forwarded over HTTP to the shard owner's EVM RPC. The rest of the giga stack (consensus, producer, data, service) stays nil; Run is a no-op; block-read methods return a sentinel error. InitRPCOnly bootstraps the app once at startup so x/evm params (chain ID, signer config) are populated. app.go pre-fires the EVM HTTP/WS start gate since rpc-only nodes don't call ProcessBlock in the current milestone — see TODO(autobahn-read-path) in NewGigaRouter for the read-side scope. CI: wires PR #3234's make autobahn-integration-test into the workflow as a new top-level job (it owns its own cluster via TestMain, so can't share the matrix's cluster), and adds a TestAutobahn/RPCOnlyForwarding sub-test that verifies an actual signed tx round-trips through the proxy: rpc-only sidecar → shard owner → block inclusion → receipt on validator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview Tendermint / node: New App: Replaces racy HTTP/WS start flags with Docker / CI: Rpc init script generates Reviewed by Cursor Bugbot for commit e25d6b1. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3525 +/- ##
==========================================
- Coverage 59.12% 58.27% -0.85%
==========================================
Files 2204 2131 -73
Lines 182630 174339 -8291
==========================================
- Hits 107983 101599 -6384
+ Misses 64942 63707 -1235
+ Partials 9705 9033 -672
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Guard autobahnRPCOnly on AutobahnConfigFile != "" so a stray autobahn-role without a config file doesn't pre-fire the EVM gate (matches node.go's gigaRPCOnly construction and the AutobahnRole godoc, which already says the role is ignored when the config file is empty). - Drop time.Sleep + time.After from the rpc-only Run-cancel test; a pre-cancelled context proves the unblock path without any goroutine- timing synchronization. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ProcessBlock's deferred gate-fire didn't cover rpc-only nodes because they never execute a block. Factor the gate-fire into a helper and call it from InitChainer as well — fresh-start validators reach it via the handshaker / runExecute InitChain call, rpc-only nodes via GigaRouter.InitRPCOnly. Both paths converge on the same chain event. The *Sent flags keep the second fire a no-op. Drops the autobahnRPCOnly field on App and the consensus-mode branch in RegisterLocalServices that bugbot flagged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bugbot caught the leftover copy-paste from buildGigaConfig — the rpc-only variant intentionally skips the membership check, so nodeKey was never read. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bot caught a latent issue: InitRPCOnly's early-return (when the app already has committed state) skipped the InitChain call, so the InitChainer defer never fired and the EVM RPC goroutines would block forever. Today the path is unreachable (rpc-only never commits) but read-side scope changes that. Wrap BaseApp.Info to also fire the gate when LastBlockHeight > 0. The trigger is the app's own committed state, not a consensus-engine flag, so no cross-layer branching. Pairs naturally with InitChainer's defer: fresh start fires via InitChain, restart-with-state via Info, steady- state via ProcessBlock. Verified: make autobahn-integration-test passes all 6 sub-tests including RPCOnlyForwarding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0cb13f5. Configure here.
Bugbot caught the validator-address-map construction was copy-pasted between buildGigaConfig and buildRPCOnlyGigaConfig. Pull it into a single loadAutobahnCommittee helper that returns the parsed file config + the committee map; both callers compose the rest of their config from there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The override is a sei-tendermint-specific concession (rpc-only nodes have no ProcessBlock to fire the gate from), not a general improvement to Info. Calling that out in the header so a future reader doesn't wonder why we touched an ABCI method that looks innocent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review caught that the unconditional Info wrapper fires the gate before CometBFT Handshaker's ReplayBlocks runs, binding EVM HTTP/WS while replay is mid-flight — strictly worse staleness window than the original ProcessBlock-defer trigger (which fires after the first replayed block commits). Re-introduce autobahnRPCOnly as a single bool on App, set from tmConfig (guarded on AutobahnConfigFile != ""), and gate the Info-side fire on it. Autobahn nodes skip the Handshaker entirely, so the gating is also what makes the override safe for the mode it exists for. Also addresses smaller review feedback: - LastCommittedBlockNumber: reword to match /status's actual committed > 0 guard; the "LastCommitted >= Latest" framing was overstated and fragile. - rpc_only_test.go: rename identical-string constants to expose the routing intent at call sites (validatorEVMRPCURLOnHost vs evmRPCURLOnContainerLocalhost — one is host-curled, the other goes through docker exec into the rpc-only sidecar). Verified: make autobahn-integration-test passes all 6 sub-tests including RPCOnlyForwarding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI fresh-cluster runs failed at TestAutobahn/RPCOnlyForwarding with
"sei_associate error: : unknown" because the V/R/S hex encoding
differed from what `seid tx evm associate-address` produces:
crypto.Sign returns sig[64] as a raw byte and hex.EncodeToString of
[]byte{0x00} produces "00", but the CLI uses big.Int.Bytes() which
strips leading zeros to "" for V=0. The chain's signature
verification rejects the encoding mismatch (CheckTx code != 0).
Match the CLI exactly: round-trip through big.Int.Bytes() for R, S,
and V. Local runs previously passed because they hit the
"balance > 0 → skip associate" branch against a long-lived cluster
where admin was already associated from prior runs.
Also silence `git describe --tags 2>/dev/null` in Makefile — the
"fatal: No names found, cannot describe anything" line was cluttering
every CI log and surfaced from a shallow clone with no tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/app.go: replace racy *Sent flag pair on signalEVMRPCReady with sync.Once. The Info-side fire site makes the race reachable from any concurrent /abci_info RPC call once read-path lands; cheap to fix now. Also restore the InitChainer doc comment that an earlier edit orphaned onto the helper above it. - sei-tendermint/internal/p2p/giga_router.go: change LastCommittedBlockNumber's rpc-only sentinel from -1 to 0 so /status's JSON response carries a non-negative integer for downstream clients. status.go's "committed > 0" guard still silently skips it, so the invariant warning stays quiet. Update unit test. - integration_test/autobahn: fold teardownRPCOnlyNode into teardownCluster. TestMain no longer repeats the kill in the two error paths + the success path; adding future sidecars goes in the same centralized teardown. Verified: make autobahn-integration-test passes all 6 sub-tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
Adds an
autobahn-roleconfig (validator|rpc-only). Withautobahn-role="rpc-only", a non-validator RPC node loads the committee fromautobahn.jsonas a routing table only — no consensus participation, no block execution, no validator key required.eth_sendRawTransactionsubmitted to such a node is recovered, sender-shard-mapped viaCommittee.EvmShard, and forwarded over HTTP to the shard owner's EVM RPC. The rest of the giga stack (consensus, producer, data, service) stays nil;Runis a no-op; block-read methods return a sentinel error.InitRPCOnlybootstraps the app once at startup so x/evm params (chain ID, signer config) are populated.app.gopre-fires the EVM HTTP/WS start gate since rpc-only nodes don't callProcessBlockin the current write-only milestone — seeTODO(autobahn-read-path)inNewGigaRouterfor the read-side scope.Test plan
go vet+gofmt -s -lcleango test ./sei-tendermint/internal/p2p/ -run TestGigaRouter_RPCOnlypassesmake autobahn-integration-testlocally: all 6 sub-tests pass (BlockProduction, BankTransfer, RPCOnlyForwarding, LivenessUnderMaxFaults, HaltsBeyondMaxFaults, Recovery)autobahn-integration-testsjob greenFollow-up
Read-path milestone: rpc-only nodes subscribe to finalized blocks and serve receipts/logs locally. At that point the
autobahnRPCOnlygate-bypass inapp.go, the read-path sentinel errors ingiga_router.go, and the data/consensus skips inNewGigaRoutercan be partially reverted — see the singleTODO(autobahn-read-path)inNewGigaRouterfor the consolidated plan.🤖 Generated with Claude Code