Skip to content

Add embedded smoldot light-client transport to the provider node#297

Open
ilchu wants to merge 5 commits into
ic/event-driven-coordinatorsfrom
ic/smoldot-transport
Open

Add embedded smoldot light-client transport to the provider node#297
ilchu wants to merge 5 commits into
ic/event-driven-coordinatorsfrom
ic/smoldot-transport

Conversation

@ilchu

@ilchu ilchu commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Implements the embedded smoldot light-client transport from #222: with --chain-transport light, the provider node follows the chain through an embedded smoldot instance that syncs the relay chain and derives parachain finality from it — no operated RPC infrastructure required. The RPC transport remains the default; flipping the default is a deliberate follow-up decision after soak time, per the investigation's sequencing.

This PR is small by design: all chain access already flows through the chain_connection seam and the single-follower architecture from #291, so the transport is a construction-site change plus spec handling.

What's in it

  • ChainTransport::Light { relay_spec, para_spec } in chain_connection, each spec a SpecSource: a vetted spec file (the trust-preserving production path; for Paseo the relay spec comes from paseo-network/paseo-chain-specs) or fetched from a node's RPC at startup (dev convenience for zombienet, which regenerates genesis every run — logged loudly as trust-defeating, since it reintroduces exactly the node trust the light client exists to remove).
  • ChainHandle keeps the LightClient instance alive; the follower's stall watchdog rebuilding the connection therefore boots a fresh smoldot, so the recovery path covers even a wedged smoldot background task (the silent-hang failure mode tracked upstream as Light Client: subxt API will do nothing after smoldot thread panic. subxt#1536), not just a dropped subscription.
  • The watchdog gets a two-phase budget: the first finalized block after a (re)connect has 300s (peer discovery + warp sync — rebuilding mid-sync would discard that progress), 60s steady-state thereafter.
  • CLI: --chain-transport {rpc|light}, --relay-chain-spec, --para-chain-spec, --relay-rpc (relay-spec fetch fallback); --chain-rpc keeps double duty as the RPC endpoint and the para-spec fetch source. Resolution logic is unit-tested, including the error when the light transport has no relay-spec source.
  • scripts/test-light-client.sh: manual e2e against a running zombienet. It synthesizes the light-client para spec from zombienet's network directory, encoding three non-obvious requirements: smoldot needs raw genesis storage (zombienet's collator spec is runtimeGenesis, so the script runs chain-spec-builder convert-to-raw on it, preserving the genesis hash), the advertised boot nodes live only in the non-raw variant and get injected, and the para spec's relay_chain id must exactly match the relay spec's id (omni-node tolerates westend-local vs westend_local_testnet; smoldot does not). The same three facts will drive the real Paseo chain-spec packaging for the default flip.

Verification

283 provider-node tests pass; clippy clean. Live on this exact HEAD (i.e. including the event-driven follower and static-bindings event decoding from #291 running over the light transport): scripts/test-light-client.sh against a local zombienet — embedded smoldot synced the relay and parachain, followed finalized blocks, and /info served the provider's on-chain registration read through it.

Relates to #222. Stacked on #291.

ilchu added 3 commits July 14, 2026 02:05
The provider can now follow the chain without any operated RPC
infrastructure: --chain-transport light boots an embedded smoldot
instance that warp-syncs the relay chain and derives parachain
finality from it. The RPC transport remains the default; the switch
is config-level because all chain access already flows through the
chain_connection seam introduced with the event-driven coordinators.

- ChainTransport::Light { relay_spec, para_spec } with SpecSource
  being either a vetted spec file (production: trust-preserving) or
  fetched from a node's RPC at startup (dev convenience for zombienet,
  which regenerates genesis every run; logged loudly as trust-defeating).
- ChainHandle keeps the LightClient alive; the follower's stall
  watchdog rebuilding the connection therefore boots a fresh smoldot,
  recovering even from a wedged smoldot background task (the silent-
  hang failure mode tracked upstream as subxt#1536).
- The first finalized block after a (re)connect gets a 300s budget
  (warp sync + peer discovery) before the 60s stall watchdog applies.
- scripts/test-light-client.sh: manual e2e against a running zombienet.
  It synthesizes the light-client parachain spec from zombienet's
  network directory - convert-to-raw genesis (smoldot cannot execute
  runtimeGenesis), inject the collator's boot nodes, and align
  relay_chain with the relay spec's exact id (smoldot matches
  parachain to relay by exact id; omni-node tolerates the westend-local
  vs westend_local_testnet mismatch, smoldot does not).

Verified live: the provider ran with --chain-transport light against
a local zombienet, synced the relay + parachain via smoldot, followed
finalized blocks, and served its on-chain registration on /info.
282 provider-node tests pass; clippy clean.
The merge from the base branch brought mock-connection tests that
built ChainHandle with struct-literal syntax; the _light keep-alive
field added on this branch is private outside chain_connection, so the
RPC arm and those tests now construct handles via from_api.
@ilchu ilchu self-assigned this Jul 17, 2026
ilchu added 2 commits July 17, 2026 18:40
Enabling subxt's light-client feature put smoldot and smoldot-light
into cargo-deny's feature-resolved graph for the first time, and their
GPL-3.0-or-later WITH Classpath-exception-2.0 expression was not in
the allow list. Allowed via exceptions scoped to those two crates -
compatible with the GPL-3.0-only provider node, and the exception only
grants additional linking permissions - rather than allowing the
expression workspace-wide.

Also covers the Light transport's failure paths for the patch gate:
missing spec file, invalid spec contents rejected by smoldot, and an
unreachable spec-fetch node.
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.

1 participant