Skip to content

[WIP] statement-store: implement the new JSON-RPC API - #3330

Closed
AndreiEres wants to merge 13 commits into
mainfrom
ae-statement-unstable-submit
Closed

[WIP] statement-store: implement the new JSON-RPC API#3330
AndreiEres wants to merge 13 commits into
mainfrom
ae-statement-unstable-submit

Conversation

@AndreiEres

@AndreiEres AndreiEres commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Implements the statement store JSON-RPC API from json-rpc-interface-spec#185, implemented in polkadot-sdk by #11989. The legacy statement_submit / statement_subscribeStatement / statement_unsubscribeStatement stay untouched.

One commit per step:

  • statement_unstable_submit
  • several topic filters per subscription (internal, no behaviour change)
  • reject an empty matchAll, which the specification forbids
  • statement_unstable_subscribe / unsubscribe
  • statement_unstable_add_filter / remove_filter + subscribeEvent
  • stop when a subscription's deduplication cache overflows
  • e2e test walking the whole subscription lifecycle

submit validation

Mirrors polkadot-sdk's Store::submit order, limited to checks needing neither a local store nor chain state:

Result Condition
-32602 bytes don't decode into a statement
invalid / alreadyExpired expiry >> 32 is in the past
invalid / encodingTooLarge over MAX_STATEMENT_SIZE (1 MiB − 1)
invalid / noProof no authenticity proof
-32603 no connected peer to broadcast to
new broadcast to at least one peer

known and rejected are never returned: both report a store decision, and a light client keeps no store. known is in fact unreachable through local RPC submission in polkadot-sdk too, since StatementSource::Local can always be resubmitted.

badProof is not returned either, and deliberately so: telling a bad proof from a good one means verifying a signature, which is more CPU than a light client should spend on a submission. Only the presence of a proof is checked. The trade-off is that smoldot will relay a statement whose signature is invalid, and peers that verify it answer with a reputation penalty.

Subscriptions

One registry serves both APIs: a legacy subscription is a subscription holding a single filter. Filters are capped at 128 per subscription; add_filter answers {"result":"limitReached"} beyond that, and -32801 for an unknown subscription. topicFilter accepts only "any" and {"matchAll": [1..4 topics]}matchAny is turned down with -32602, as in polkadot-sdk.

add_filter emits replayDone immediately over an empty snapshot, replayStatements never being emitted. Peers then resend everything matching the updated affinity filter, and the per-subscription deduplication drops what was already delivered, so only unseen statements reach the client through newStatements.

A subscription is stopped, with a stop event, once its deduplication cache is full: remembering one more statement would evict the oldest entry, after which that statement could be reported twice. With the default maxSeenStatements of 65536 a subscription therefore dies after roughly that many distinct statements and the client resubscribes. Legacy subscriptions keep evicting silently, the old API having no way to report this.

-32800 is never returned, as the number of statement subscriptions per client is not capped. The specification requires accepting at least 2 and only permits erroring beyond that, so this is compliant.

To raise on the spec PR

Three points where a storeless light client diverges from what the specification seems to assume:

  1. replayDone arrives before the effective backlog. Smoldot's store is genuinely empty, so the replay is legitimately empty, but a client that treats replayDone as "I now have what the server had" concludes it too early — the backlog arrives afterwards through newStatements, delayed by up to the affinity update interval.
  2. Adding a filter to an existing subscription never re-announces already-delivered statements under the new filter id, since re-emitting them through newStatements is what the at-most-once guarantee forbids. A complete backlog for a filter requires a fresh subscription.
  3. known is unreachable through local RPC submission in the reference implementation, so the specification documents an outcome that never occurs for this method.

Noted, not addressed here

Smoldot's codec still accepts Proof::OnChain, which polkadot-sdk removed in favour of cryptographic-only proofs and now rejects at decode. Smoldot will therefore relay statements that SDK nodes drop. Worth its own issue.

🤖 Generated with Claude Code
Closes #3319

@AndreiEres AndreiEres changed the title [WIP] Add statement_unstable_submit JSON-RPC function [WIP] statement-store: implement the new JSON-RPC API Aug 5, 2026
@lexnv

lexnv commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@AndreiEres would this PR get simplified by the suggestion in: paritytech/json-rpc-interface-spec#185 (comment)?

@AndreiEres

Copy link
Copy Markdown
Contributor Author

Split into reviewable parts, identical content in total:

Parts 2-5 are stacked drafts, each based on the previous; they get retargeted and marked ready as their bases merge. The spec-divergence notes from this description live on in the doc comments and the parts' descriptions.

@AndreiEres AndreiEres closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

statement-store: Implement RPCv2 specification

2 participants