fix(giga): fail-fast broadcast_tx_commit and feed newBlockFilter from the notifier - #4012
fix(giga): fail-fast broadcast_tx_commit and feed newBlockFilter from the notifier#4012shemnon wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8b5dedf. 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 #4012 +/- ##
==========================================
- Coverage 61.26% 60.21% -1.05%
==========================================
Files 2153 2051 -102
Lines 188241 176176 -12065
==========================================
- Hits 115322 106088 -9234
+ Misses 62147 60303 -1844
+ Partials 10772 9785 -987
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
The broadcast_tx_commit fail-fast change is sound, but the notifier fan-out added in evmrpc/notifier.go is broken in two ways: publish returns before invoking listeners on the (normal) non-full-channel path, so eth_newBlockFilter receives nothing, and the panic guard uses a function-scoped defer inside the listener loop, so a panicking listener silently skips all later ones. Several of the PR's own new tests should be failing as a result.
Findings: 2 blocking | 1 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
- 2 blocking issue(s) flagged inline on specific lines.
Non-blocking
- None at the file/PR level.
- 1 suggestion(s)/nit(s) flagged inline on specific lines.
Autobahn has no EventBus wait for inclusion, so BroadcastTxCommit returns unsupported before InsertTx. eth_newBlockFilter reads committed Autobahn hashes from BlockHeaderNotifier instead of /events.
4651d51 to
acefef1
Compare
BroadcastTxCommit now fail-fasts before InsertTx, which would drop eth_sendRawTransaction when evm.slow=true. Route that path to BroadcastTx when Autobahn is present so only -b block breaks. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@seidroid review |
There was a problem hiding this comment.
Both previously-flagged blockers are genuinely fixed — publish now falls through to the listener fan-out and each listener is individually wrapped in runWithRecovery — and the evm.slow + Autobahn interaction raised last round is handled by the new SendConfig.autobahn flag with tests for both branches. Remaining findings are non-blocking: unbounded per-filter hash accumulation done on the consensus commit goroutine, an Autobahn signal inferred from notifier presence that an in-tree TODO plans to invalidate, and a test that hangs rather than fails on regression.
Findings: 0 blocking | 3 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- None at the file/PR level.
- 3 suggestion(s)/nit(s) flagged inline on specific lines.
| if f.typ != BlocksSubscription { | ||
| continue | ||
| } | ||
| f.blockHashes = append(f.blockHashes, hash) |
There was a problem hiding this comment.
[suggestion] blockHashes grows without bound and the growth happens on the consensus commit goroutine.
appendBlockHash is reached from App.Commit → PublishStashed → publish, so every committed block does O(#filters) work while holding filtersMu (also taken by cleanupExpiredFilters, NewBlockFilter, and the log branch of GetFilterChanges). Before this PR a BlocksSubscription held only a cursor string and all the work was on the RPC goroutine.
A block filter that is created and never polled accumulates one 32-byte hash per block until the 120s FilterTimeout (evmrpc/config/config.go:331) expires it, and NewBlockFilter is unauthenticated with no cap on filter count and no globalRPSLimiter check. At Autobahn block rates that is a few tens of KB per idle filter, so it is amplification rather than an outright DoS — but it is new memory proportional to (block rate × filters) that nothing bounds.
Capping the slice and dropping the oldest entries on overflow would match the overwrite-on-full philosophy the notifier already documents for recv(), e.g. bound it by a.filterConfig.maxBlock.
| globalBlockCache := NewBlockCache(3000) | ||
| cacheCreationMutex := &sync.Mutex{} | ||
| sendAPI := NewSendAPI(tmClient, txConfigProvider, NewSendConfig(config.Slow, config.EnableSimulation), k, beginBlockKeepers, ctxProvider, homeDir, simulateConfig, app, antehandler, ConnectionTypeHTTP, methodTimeout, globalBlockCache, cacheCreationMutex, watermarks) | ||
| sendAPI := NewSendAPI(tmClient, txConfigProvider, NewSendConfig(config.Slow, config.EnableSimulation, blockHeaderNotifier != nil), k, beginBlockKeepers, ctxProvider, homeDir, simulateConfig, app, antehandler, ConnectionTypeHTTP, methodTimeout, globalBlockCache, cacheCreationMutex, watermarks) |
There was a problem hiding this comment.
[suggestion] blockHeaderNotifier != nil is used as the "is Autobahn" signal, but the notifier's own construction site says that equivalence is temporary.
app/app.go:694 creates the notifier only when tmConfig.AutobahnConfigFile != "", so this is accurate today. But the comment immediately above it (app/app.go:685-693) describes the plan to feed non-Autobahn newHeads from the notifier too, gated only on encoder-parity work: "Until that's verified, keep this gate so non-Autobahn newHeads semantics are unchanged by this PR."
When that gate is removed, SendConfig.autobahn silently becomes true on Comet nodes and evm.slow stops waiting for inclusion — a behaviour change with no compile error and no failing test to catch it, since TestSendRawTransactionSlowOnCometUsesBroadcastTxCommit constructs SendConfig directly rather than going through NewEVMHTTPServer. (FilterAPI switching to the notifier path on Comet at that point is probably fine or even desirable; the send path is the one that regresses.)
Threading the actual Autobahn flag through, or at minimum a comment here and at app/app.go:694 recording that the two are coupled, would keep the two changes from drifting apart.
| ) | ||
| require.NoError(t, err) | ||
|
|
||
| // Setup: empty KV indexer; TimeoutBroadcastTxCommit stays 0 so a wait regression hangs. |
There was a problem hiding this comment.
[suggestion] Leaving TimeoutBroadcastTxCommit at 0 means a regression hangs the whole package instead of failing this test.
The intent is clear and the comment is explicit about it, but with no timeout and no deadline on t.Context(), a reintroduced InsertTx + event-log wait blocks until the Go test binary's 10-minute panic, which takes down every other test in internal/rpc/core with a stack dump rather than producing a targeted failure.
Setting a short Config.RPC.TimeoutBroadcastTxCommit (or a context.WithTimeout) gives the same detection — a regression returns some other error and require.ErrorIs(err, ErrBroadcastTxCommitUnsupported) fails — while keeping the failure local and fast.
Superseded: latest AI review found no blocking issues.

Summary
BroadcastTxCommit(-b block) returnsErrBroadcastTxCommitUnsupportedbeforeInsertTxinstead of waiting on an empty EventBus/KV indexer (CON-352 hang). Comet is unchanged.eth_newBlockFilter/eth_getFilterChangestake Autobahn block hashes fromBlockHeaderNotifier(same FinalizeBlock hash asnewHeads/eth_getBlockBy*), not/events. HTTP FilterAPI now gets the notifier./tx,tx_search, TMsubscribe, and/eventsfail-fast is not in this PR (fork-gated).Linear: CON-409
Related: CON-352 (do not merge EventBus PR 3998 as the fix)
Test plan
go test ./sei-tendermint/internal/rpc/core/ -count=1 -run 'TestBroadcastTxCommit'— Autobahn fail-fast; Comet still hits mempool, not the sentinelgo test ./evmrpc/ -count=1 -run 'TestBlockHeaderNotifier_Subscribe|TestFilterAPI_NewBlockFilter|TestFilterBlockFilter'— notifier fan-out, Autobahn block filter hashes, Comet Events path unchangedseid tx … -b blockagainst an Autobahn node errors immediately;-b syncstill workseth_newBlockFilter+eth_getFilterChangeson Autobahn returns the overlay block hash, not0x000…0Made with Cursor