Skip to content

fix(truapi-provider)!: keep RPC providers off the catalog and light-client chains running - #1793

Open
Mikelle wants to merge 23 commits into
mw/cli-chain-truapi-providerfrom
fix/cli-provider-follow-ups
Open

Mikelle wants to merge 23 commits into
mw/cli-chain-truapi-providerfrom
fix/cli-provider-follow-ups

Conversation

@Mikelle

@Mikelle Mikelle commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Follow-ups from #1715, stacked on #1664.

  • Catalog only when asked: only a provider built with catalog() resolves an unregistered genesis from the bundled catalog, so one for remote nodes never starts smoldot. The native bindings and the gateway set it. The JS builder sets it until the first addRpcChain.
  • keep_chains_running(): a light-client chain stays running after its last connection closes. The CLI uses it instead of keeping an idle connection per chain.
  • WebSocket timeout: the stream ends after 50 s with no bytes, not 50 s per message, so a large Bulletin block on a slow link arrives. The crate dials the socket itself and keeps jsonrpsee's behaviour: each resolved address is tried in turn, and redirects are followed.
  • Allowance transactions: statuses are parsed with subxt's TransactionStatus, so "dropped" and "invalid" fail the submission at once instead of at the two-minute timeout.
  • CLI reroute check: an override is compared with the preset's URL for the same chain.

PGAS on the light client (item 4): a smoldot bug. The claim's validation checks that the PGAS asset exists, and the node correctly proves that with the value's hash. smoldot treats the check as a full read, wants the value too, and drops the transaction. Item 4 stays open until smoldot is fixed. SPEC.md now explains this.

Breaking: a JS host that uses addRpcChain and relies on the catalog for other chains has to call addNetwork for those, before addRpcChain.

Testing: workspace build, clippy, tests and make wasm pass, and the host bun tests and provider browser tests pass. The live RPC and light-client tests pass on Paseo Next v2, and the standalone gateway example reaches a real wss:// node. One unrelated macOS temp-path test fails locally.

Refs #1715

The pool sends "dropped" and "invalid" as bare strings, which the
status matcher only recognised as object keys, so a rejection surfaced
as the submit timeout about two minutes later.
An override set to a URL the preset uses for another chain skipped the
genesis check, so People pointed at the preset's Asset Hub node ran
unchecked.
The 50 s read timeout covered the wait for one whole message, so a
13 MiB Bulletin block body on a link under about 0.3 MB/s ended a
healthy connection. The backend now dials the socket itself, records
every read, and ends the stream only after 50 s with no bytes at all.
TLS uses the crate's own client config, so jsonrpsee's tls feature is
no longer needed. Redirects are not followed.
…node

A genesis nobody registered was resolved from the bundled catalog on
every provider, so a host on RPC nodes that asked for an unrouted chain
started the light client. A provider with a remote node registered now
answers only for what was registered on it. The gateway example serves
its catalog chains from a provider of their own.
…ctions

smoldot stops a chain once its last connection closes, so a host that
connects once per use started each chain again with no peers. A
provider built with keep_chains_running() adds each chain once more on
its own account, JSON-RPC disabled, and never removes it; a parachain's
hold also holds its relay. The CLI's shared light client uses it instead
of keeping an idle connection per chain open.
The node's proof is complete for what it reads: the claim's validation
checks that the PGAS asset exists, which a node proves with the asset's
trie entry and its value hash. smoldot answers the check by reading the
whole value, so it rejects that proof.
Adds changesets for the catalog rule in @parity/truapi-provider and the
CLI and allowance fixes, and states the read timeout in the CLI's
existing changeset as silence on the socket rather than between frames.
@Mikelle
Mikelle requested a review from a team as a code owner October 29, 2026 16:36
@github-actions github-actions Bot added area:docs Touches documentation area:js Touches the TypeScript packages, the playground or the explorer area:core Touches the Rust core under rust/crates labels Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Bundle size report

Compared with main at 860a5f4.

Raw Gzip Brotli
truapi-host 8.30 MiB (+300 B) 5.80 MiB (+129 B) 5.48 MiB (+338 B)
truapi-provider 4.03 MiB (-6.2 KiB, -0.2%) 1.27 MiB (-1.8 KiB, -0.1%) 960.9 KiB (+685 B, +0.1%)
truapi 7.08 MiB 1004.9 KiB 820.3 KiB
trinity-stack 562.4 KiB 225.5 KiB 186.6 KiB
Total 19.95 MiB (-5.9 KiB) 8.27 MiB (-1.7 KiB) 7.40 MiB (+1023 B)

WebAssembly modules

Raw Gzip Brotli
truapi-host/wasm/web/truapi_server_bg.wasm 3.19 MiB (+300 B) 1.10 MiB (+129 B) 843.4 KiB (+338 B)
truapi-host/wasm/web/truapi_verifiable_bg.wasm 4.89 MiB 4.64 MiB 4.61 MiB
truapi-provider/truapi_provider_bg.wasm 3.98 MiB (-6.5 KiB, -0.2%) 1.26 MiB (-1.9 KiB, -0.1%) 953.1 KiB (+604 B, +0.1%)
trinity-stack/wasm/trinity_stack_bg.wasm 525.1 KiB 217.1 KiB 179.3 KiB
Changed files (3)
Raw Gzip Brotli
truapi-provider/truapi_provider_bg.wasm 3.98 MiB (-6.5 KiB, -0.2%) 1.26 MiB (-1.9 KiB, -0.1%) 953.1 KiB (+604 B, +0.1%)
truapi-provider/truapi_provider.js 46.6 KiB (+310 B, +0.7%) 9.1 KiB (+91 B, +1.0%) 7.8 KiB (+81 B, +1.0%)
truapi-host/wasm/web/truapi_server_bg.wasm 3.19 MiB (+300 B) 1.10 MiB (+129 B) 843.4 KiB (+338 B)

Commit: 79f58a2

Dialing the socket in the crate dropped behaviour jsonrpsee's own dial
had. Each address a host resolves to is tried in turn with its own
timeout, so a dead IPv6 route or a backend that refuses the upgrade
falls back to the next address. Redirects are followed up to five
times, a URL that is not ws or wss is refused before anything is
dialed, and vectored writes reach the socket. The jsonrpsee tls feature
is declared again: the transport accepts a wss URL only with it on,
which another crate in the CLI build had been enabling.
Whether a provider resolved an unregistered genesis from the bundled
catalog was inferred from whether any registration was a remote node,
so a provider with nothing registered still started the light client for
any bundled genesis. The catalog is now an explicit builder option,
catalog(), which the native bindings and the gateway example set. The
JS builder sets it until the first addRpcChain, as before. load_database
reads nothing for a chain the provider cannot connect to, and relay_of
consults the catalog only on a provider that uses it. The changeset
states the order addNetwork and addRpcChain have to be called in.
The submission watch reads each notification as subxt's
TransactionStatus, which models both the bare-string and the object
forms the pool sends, instead of matching JSON keys by hand.
…r path

A failed hold and a failed add of the connection's own chain release the
relay reference through the same branch, and LightState::new documents
what keep_running does.
load_database answers false for a chain the provider cannot connect to,
so the storage tests register the chain they load and save, as a host
does before connecting to it.
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

CI Status: 24 required jobs green, 22 passed and 2 skipped by path filter.

All job results
job result
android-app success
android-bindings success
bundle-size success
changes success
changeset-guard success
cli-package success
codegen success
e2e skipped
explorer success
headless-install success
host-wasm success
ios-app success
ios-bindings success
licenses success
playground success
provider-android-bindings success
release-guard success
rust success
ts-client success
ts-debugger success
ts-host success
ts-trinity-stack success
wasm-provider success
workflow-lint skipped

Signing credentials: valid as of 2026-10-30

Commit 79f58a28 · run log

A failure after a redirect names the redirected URL, redacted, since
the caller reports only the URL it asked for, and a redirect location
never reaches an error unredacted. A dial timeout names the step that
hung: resolving the host, connecting, or the TLS handshake. The slow
message test trickles bytes at an interval that does not divide the
read timeout.
…ect to

load_database fails as unknown for a chain connect would refuse, rather
than answering as if nothing were stored, and the JS connect path
returns that error instead of logging it as unavailable storage.
Whether the catalog covers a chain is read from the catalog's statics.
SPEC.md states that neither CLI backend resolves from the catalog.
Each comment says what the code cannot, once, instead of restating the
mechanism or the reason a second time.
… entry default

The catalog tests look up Paseo Next v2's genesis hashes through one
helper, and an AddedChain entry starts from its derived default.
@Mikelle Mikelle self-assigned this Oct 29, 2026
…feature

The EmbeddedChainProvider doc linked to catalog(), which only exists
with networks, so rustdoc failed on the default features that codegen
documents the crate with.
@TarikGul TarikGul added the size:XXL 1000 lines or more; split it, or say in the description why it cannot be label Oct 30, 2026
@TarikGul TarikGul added the status:waiting-on-review Ready for a reviewer label Oct 30, 2026
@Mikelle
Mikelle requested a review from a team as a code owner October 30, 2026 17:05

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core Touches the Rust core under rust/crates area:docs Touches documentation area:js Touches the TypeScript packages, the playground or the explorer size:XXL 1000 lines or more; split it, or say in the description why it cannot be status:waiting-on-review Ready for a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants