Bound /store/*/subspace ABCI queries - #4009
Conversation
Reject empty prefixes, cap pair/byte accumulation during iteration, and limit concurrent SS fast-path scans so unauthenticated callers cannot OOM a node with wide subspace queries. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
PR SummaryMedium Risk Overview Implementation centralizes iteration in Operator impact: indexers or tooling that relied on wide Reviewed by Cursor Bugbot for commit 260ccf0. Bugbot is set up for automated code reviews on this repo. Configure here. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4009 +/- ##
==========================================
- Coverage 61.34% 60.28% -1.06%
==========================================
Files 2163 2059 -104
Lines 188757 176566 -12191
==========================================
- Hits 115792 106445 -9347
+ Misses 62256 60410 -1846
+ Partials 10709 9711 -998
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.
Solid, well-tested DoS hardening of the storev2 /subspace ABCI path: pair/byte caps, an empty-prefix rejection, an SS-path semaphore, and complete config-characterization wiring (flags, defaults, app.toml template, both goldens, and the fuzz seed re-indexing all line up). Three non-blocking issues: the scan ignores the repo's context-aware iterator helper, the empty-prefix guard is duplicated at callers instead of the shared choke point, and the 0-means-default cap semantics are undocumented and contradict the neighbouring rate-limit key.
Findings: 0 blocking | 5 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- [suggestion] The new in-flight semaphore only covers the SS fast path. Since
RequireProof("/subspace")is false,/subspacereaches the commitment path wheneverrs.ssStore == nil(SS disabled), and that path gets no concurrency limit — only the pair/byte caps. Either extendtryAcquireSubspaceQueryPermitto cover it, or state the SS-only scope in a comment onsubspaceQuerySemso the gap reads as deliberate. - [suggestion] This is a fail-closed behavior change on a public query surface: a
/store/{name}/subspacequery that previously returned 5k pairs now returnsErrSubspaceCapExceededinstead of data. Worth an explicit release note so operators running indexers or tooling on wide prefixes know to narrow their prefixes or raisesc-subspace-max-pairs/sc-subspace-max-bytesbefore upgrading. - 3 suggestion(s)/nit(s) flagged inline on specific lines.
Thread request context through IteratorOn on the SS path, centralize the empty-prefix guard in ScanSubspace, and document <=0 default semantics for subspace limits in app.toml. Co-authored-by: Cursor <cursoragent@cursor.com>
Cancelled or timed-out /subspace queries were panicking on the SS path because state.Store fatally handles iterator-construction failures. Co-authored-by: Cursor <cursoragent@cursor.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 260ccf0. Configure here.
| if err != nil { | ||
| panic(fmt.Errorf("failed to marshal KV pairs: %w", err)) | ||
| } | ||
| return bz, nil |
There was a problem hiding this comment.
Cancelled subspace scans can succeed
High Severity
ScanSubspace exits the loop when Valid() becomes false and then marshals whatever pairs it already collected. It never checks iterator.Error() or ctx.Err() after iteration, so an SS MVCC skip that stops on deadline, cancel, or I/O is returned as a successful complete result. Clients get a code-0 truncated subspace instead of an error, which breaks the fail-closed contract.
Triggered by learned rule: sei-db: iterator factories must return (dbm.Iterator, error), not swallow errors
Reviewed by Cursor Bugbot for commit 260ccf0. Configure here.
There was a problem hiding this comment.
Worth double clicking into this one @amir-deris


Summary
Closes PLT-799. The
/store/{name}/subspaceABCI path on storev2 SS nodes iterated an entire KV prefix into memory with no empty-data guard, no result cap, and no concurrency limit.This PR makes
/subspacefail closed on unbounded work:storev2/query.ScanSubspacebefore any iteration (covers state and commitment paths).storev2/queryhelper: stop once pair count or accumulated key+value bytes exceed configured limits (defaults: 1,000 pairs / 4 MiB). Returns typedstore.ErrSubspaceCapExceeded— no silent truncation.IteratorOnso SS MVCC skip loops honor request cancellation and deadlines (not just between yielded pairs).subspaceQuerySem, separate fromhistProofSem): saturated callers getErrConflict, same as historical proof.subspace_query_rejected{reason=semaphore|cap_exceeded}for operator visibility.Query-only change — no AppHash or chain-upgrade impact.
Operator note: wide
/subspacequeries that previously returned large result sets may now fail withErrSubspaceCapExceeded. Narrow prefixes or raisesc-subspace-max-pairs/sc-subspace-max-bytesbefore upgrading if indexers or tooling depend on wide scans.New config (
[state-commit])sc-subspace-query-max-inflight<=0resolves to defaultsc-subspace-max-pairs<=0resolves to default; no unlimited settingsc-subspace-max-bytes<=0resolves to default; no unlimited settingWired through
parseSCConfigs,app.tomltemplate, and config characterization goldens.Test plan
go test ./sei-cosmos/storev2/query/...— pair/byte caps, ctx cancel, typed error + ABCI code preservationgo test ./sei-cosmos/storev2/commitment/...— empty prefix rejected, narrow prefix succeeds,/keyunaffectedgo test ./sei-cosmos/storev2/rootmulti/...— semaphore saturation, integration paths, empty prefixgo test ./app/ -run 'TestDefaultsMatchTheRecordedValues|TestKeyNamesMatchTheRecordedNames|FuzzParseSCConfigs'go test ./sei-db/config/...