epic: substrate chain adapter, quantus first #3

Closed
opened 2026-09-15 12:35:01 +00:00 by grenade · 3 comments
Owner

Goal

A ChainAdapter for Substrate chains that is driven by runtime metadata plus a small per-chain profile, so that Quantus mainnet, Heisenberg and any later post-quantum Substrate chain are profiles rather than crates. It talks only to our own nodes over TLS, since public endpoints do not exist.

Facts the design rests on

  • quantus-cli already carries a working subxt Config with the Dilithium signature enum and the two custom transaction extensions (ReversibleTransactionExtension, WormholeProofRecorderExtension, neither contributes signed bytes). Lift it; do not re-derive it.
  • subxt 0.50 needs that custom Config; 0.51 adds a custom_extension builder. Track it, do not wait for it.
  • The signing context depends on specVersion; the adapter reads the runtime version at connect and on every runtime upgrade.
  • Incoming transfers may be signed by either scheme variant; both decode.
  • High-security accounts must use the reversible-transfers extrinsics and are blocked from plain transfers by the extension; the adapter must know which kind of account it is sending from.

Operator-owned decisions

  • Endpoint list and TLS: which hosts, and whether mTLS or a bearer token authenticates the wallet to our nodes. Needed before the RPC child; recorded on that issue.
  • History source: block scanning in the wallet, or an indexer on our infra (blackbeard.observer already ingests every block). Default: indexer, exposed through the relay; the wallet never scans.

Definition of done

Against a dev node in CI and against Heisenberg from a laptop: connect, read balance and nonce, build a transfer_keep_alive, sign with the wallet's ML-DSA-65 key, submit, watch it to finality, and see the balance move. The same binary does the same against mainnet with only a profile change.

## Goal A `ChainAdapter` for Substrate chains that is driven by runtime metadata plus a small per-chain profile, so that Quantus mainnet, Heisenberg and any later post-quantum Substrate chain are profiles rather than crates. It talks only to our own nodes over TLS, since public endpoints do not exist. ## Facts the design rests on - quantus-cli already carries a working subxt `Config` with the Dilithium signature enum and the two custom transaction extensions (`ReversibleTransactionExtension`, `WormholeProofRecorderExtension`, neither contributes signed bytes). Lift it; do not re-derive it. - subxt 0.50 needs that custom `Config`; 0.51 adds a `custom_extension` builder. Track it, do not wait for it. - The signing context depends on `specVersion`; the adapter reads the runtime version at connect and on every runtime upgrade. - Incoming transfers may be signed by either scheme variant; both decode. - High-security accounts must use the reversible-transfers extrinsics and are blocked from plain transfers by the extension; the adapter must know which kind of account it is sending from. ## Operator-owned decisions - Endpoint list and TLS: which hosts, and whether mTLS or a bearer token authenticates the wallet to our nodes. **Needed before the RPC child; recorded on that issue.** - History source: block scanning in the wallet, or an indexer on our infra (blackbeard.observer already ingests every block). **Default: indexer, exposed through the relay; the wallet never scans.** ## Definition of done Against a dev node in CI and against Heisenberg from a laptop: connect, read balance and nonce, build a `transfer_keep_alive`, sign with the wallet's ML-DSA-65 key, submit, watch it to finality, and see the balance move. The same binary does the same against mainnet with only a profile change.
grenade added the epic label 2026-09-15 12:38:42 +00:00
Author
Owner

Children, in dependency order: #18 (chain profiles) → #19 (subxt config) and #20 (RPC to our nodes) → #21 (balances and heads) → #22 (transfer with reversal). #23 (history via indexer) and #24 (dev node in CI) run in parallel from #18 and #19 respectively.

Children, in dependency order: #18 (chain profiles) → #19 (subxt config) and #20 (RPC to our nodes) → #21 (balances and heads) → #22 (transfer with reversal). #23 (history via indexer) and #24 (dev node in CI) run in parallel from #18 and #19 respectively.
Author
Owner

Added #53 (public node endpoints on oolon, throttled, routed to bob), the infrastructure half of the decision recorded on #20. It has no code dependency and can start now; #20 consumes its result.

Added #53 (public node endpoints on oolon, throttled, routed to bob), the infrastructure half of the decision recorded on #20. It has no code dependency and can start now; #20 consumes its result.
Author
Owner

Every child is closed: #18, #19, #20, #21, #22, #23, #24 and #53. The chain layer as it stands: profiles shipped as TOML with operator overrides; a subxt config for ML-DSA signatures under the FIPS 204 context from spec 148; a connection manager with failover, backoff, a polling runtime-version watch and a measured block interval; balances and pending reversible transfers read at the best block with the finalized height alongside (the PoW finality lag is about 100 blocks); transfers, reversible transfers and cancels prepared for review, priced, signed by the session and watched; history from blackbeard.observer cached in SQLite; a pinned quantus-node --dev in CI; and the public names wss://quantus.blackbeard.observer and wss://planck.blackbeard.observer on oolon, throttled per client. Closing the epic.

Every child is closed: #18, #19, #20, #21, #22, #23, #24 and #53. The chain layer as it stands: profiles shipped as TOML with operator overrides; a subxt config for ML-DSA signatures under the FIPS 204 context from spec 148; a connection manager with failover, backoff, a polling runtime-version watch and a measured block interval; balances and pending reversible transfers read at the best block with the finalized height alongside (the PoW finality lag is about 100 blocks); transfers, reversible transfers and cancels prepared for review, priced, signed by the session and watched; history from blackbeard.observer cached in SQLite; a pinned `quantus-node --dev` in CI; and the public names `wss://quantus.blackbeard.observer` and `wss://planck.blackbeard.observer` on oolon, throttled per client. Closing the epic.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: blackbeard/wallet#3