Quip v0.2 includes a substantial Substrate integration milestone: user transactions, BABE block production, and GRANDPA finality have been wired to use hybrid classical + post-quantum signatures.
The goal is to make hybrid signatures work through the same core interfaces that a Substrate node, runtime, keystore, consensus engine, browser signer, and external SDKs already expect.
At a high level, the integration covers three signing paths:
- User transaction signatures use a hybrid H3 scheme:
sr25519 + ML-DSA-44. - BABE authority signatures and BABE VRF proofs also use H3.
- GRANDPA authority signatures use a hybrid H1 scheme:
ed25519 + ML-DSA-44.
The Polkadot SDK changes are tracked in QuipNetwork/polkadot-sdk#3, with the SDK work compared against ru/tag/polkadot-stable2603.
Why This Matters
Substrate assumes compact classical public keys and signatures in many places. That assumption shows up in transaction signatures, app-crypto types, consensus authorities, keystore persistence, host functions, metadata, CLI commands, and JavaScript signing flows.
Hybrid signatures change those assumptions:
- A hybrid public key is 1344 bytes.
- A hybrid signature is 2484 bytes.
- A user transaction signature envelope carries both the full public key and signature.
- Consensus authorities need large public keys in session keys.
- BABE needs a VRF-compatible construction, not just a plain signature swap.
- Browser and Python tooling need deterministic byte-level compatibility with the runtime.
The v0.2 work is therefore an end-to-end integration pass, not only a crypto library drop-in.
Architecture Overview
The implementation is split across a small number of layers.
The Polkadot SDK fork contains the Substrate-facing hybrid crypto primitives:
- A pure crypto core with no direct Substrate dependency.
- Substrate wrappers for
Pair,Public,Signature, app-crypto, and BABE VRF types. - BABE and GRANDPA primitive updates.
- Keystore and host-function support for selecting crypto by key type and crypto ID.
- CLI support for inserting hybrid keys.
The Quip protocol repo contains the chain-specific integration:
- Runtime transaction signature type switched to the hybrid transaction signature.
- Account IDs derived from hybrid public keys.
- Genesis/session wiring for hybrid BABE and GRANDPA authorities.
- Node service integration using the patched BABE and GRANDPA paths.
- WASM, TypeScript, and Python signing packages for app integration.
This keeps the crypto core reusable while still letting the runtime and node use normal Substrate abstractions wherever possible.
User Transaction Signing
User transactions use the H3 hybrid scheme: sr25519 + ML-DSA-44.
The runtime signature envelope contains:
- the full 1344-byte hybrid public key
- the full 2484-byte hybrid signature
The account ID remains 32 bytes. It is derived as:
blake2_256("quip-account-v1" || hybrid_public_key_bytes)
Runtime verification checks both sides of that relationship:
- The public key in the envelope must hash to the claimed account ID.
- The hybrid signature must verify over the Substrate signing payload.
This lets accounts stay compact while preserving the full verification material in each signed extrinsic.
There is one important signing rule for external clients: Substrate signs payloads using SignedPayload::using_encoded. Payloads longer than 256 bytes are first hashed with blake2_256; shorter payloads are signed directly. The TypeScript signer handles this rule. Lower-level WASM and Python APIs sign the exact bytes passed to them, so clients using those APIs directly must apply the same rule before signing.
Browser and Polkadot-js Support
The browser signing stack has three parts:
- A Rust transaction crypto core compiled to WASM.
- A generated JS/WASM package.
- A Polkadot-js-compatible
Signerimplementation.
The WASM API exposes simple hex-based functions for deriving public keys, deriving account IDs, signing payloads, verifying envelopes, and deriving seeds from mnemonics.
The Polkadot-js signer implements signRaw. It applies the Substrate payload hashing rule, signs through the WASM module, and returns the hybrid signature envelope in the shape expected by Polkadot-js.
One compatibility issue needed special handling: Polkadot-js fee-estimation paths assume a much smaller fake signature. The Quip signer patches fake-signature sizing so dry-run fee estimates account for the larger hybrid envelope.
This gives web applications a practical path to hybrid transaction signing without requiring browser code to reimplement the cryptographic details.
Python Signing
The Python bindings expose the same transaction signing rules through PyO3.
The API supports:
- seed and mnemonic based key derivation
- public key derivation
- account ID derivation
- payload signing
- signature-envelope verification
- a small
HybridSignerclass for repeated signing
The Python bindings call the same Rust core as the WASM package. That matters because it gives browser, Python, and runtime code one shared source of truth for transaction envelope layout and account derivation.
BABE Integration
BABE uses the H3 hybrid scheme for authority keys and block authoring signatures.
The larger challenge is BABE VRF support. BABE relies on VRF outputs for slot eligibility and randomness, so replacing only the block seal signature is not enough.
The H3 BABE VRF design keeps the classical sr25519 VRF transcript shape, then binds the resulting VRF output to the post-quantum key:
- Build the normal BABE transcript from randomness, slot, and epoch.
- Produce the sr25519 VRF output and proof.
- Build a binding message from the BABE input and sr25519 output.
- Sign that binding message with ML-DSA-44.
- Derive the hybrid VRF output from the sr25519 output and PQ signature.
Verification checks both the sr25519 VRF proof and the ML-DSA-44 binding signature.
The BABE client authoring path now asks the keystore for a hybrid VRF signature using the BABE key type and the H3 crypto ID. The verification path checks the hybrid VRF proof and uses the verified hybrid output for BABE threshold calculations and randomness.
GRANDPA Integration
GRANDPA uses the H1 hybrid scheme: ed25519 + ML-DSA-44.
The GRANDPA public API shape stays familiar: authority IDs, authority pairs, and authority signatures remain the main primitives. The main change is that signing goes through a generic keystore path keyed by both application key type and crypto ID, rather than through an ed25519-specific signing function.
This lets GRANDPA finality votes use hybrid signatures while preserving the normal GRANDPA integration pattern in the node service.
Keystore and Operator Support
Hybrid public keys are too large for parts of Substrate’s older keystore assumptions. In particular, local keystore filenames previously worked well for short public keys but not for 1344-byte hybrid public keys.
The SDK patch keeps legacy storage for short keys and adds a long-key format:
- the filename uses a hash of the public key
- the file body stores a JSON envelope containing the full public key and secret URI
The keystore interface also gains generic methods for listing, generating, and signing with keys by application key type and crypto ID.
For operators, hybrid key insertion is supported at two levels:
- the SDK CLI accepts hybrid BABE and GRANDPA schemes
- the Quip node includes a focused hybrid key insertion command that maps schemes to the expected session key type
That reduces the chance of inserting a valid key under the wrong key type.
Runtime and Genesis
The runtime session key bundle contains hybrid BABE and GRANDPA keys.
Genesis wiring separates three roles:
- transaction account key
- BABE session key
- GRANDPA session key
Development presets derive these from known seeds. Public testnet presets import configured authority public keys and wire them through session genesis. BABE and GRANDPA authorities are populated through pallet-session, which avoids double-initializing consensus authority sets.
Runtime-driven session rotation is not enabled yet, but the session-key structure and node-side key-management path are in place for future validator lifecycle work.
Current Status
The integration currently includes:
- hybrid crypto primitives in the Polkadot SDK fork
- Substrate app-crypto wrappers for H1 and H3
- hybrid BABE primitives, authoring, verification, and runtime randomness handling
- hybrid GRANDPA authority signatures
- keystore support for large public keys
- CLI support for hybrid key insertion
- Quip runtime support for hybrid transaction signatures
- WASM and TypeScript signing support for Polkadot-js style apps
- Python signing bindings backed by the same Rust transaction crypto core
The source tree includes tests for crypto core behavior, envelope layout, account derivation, runtime extrinsic verification, WASM signing, and Python parity against golden vectors.
Open Work
The main remaining work is validation and product hardening:
- run the full test matrix across both repos at the target release heads
- measure extrinsic, digest, and finality-message size impact under realistic load
- harden browser extension key storage and signing UX
- work on signature algorithm integration with smaller footprint to address on-chain storage concerns
Goals
This work brings hybrid classical + post-quantum signatures into the core places where a Substrate chain actually depends on signatures: user transactions, block production, finality, keystore operations, and external signing tooling.
The important part is the integration boundary. Applications can sign through WASM or Python, the runtime verifies a compact account ID against a full hybrid envelope, and consensus can keep using familiar BABE and GRANDPA service paths backed by hybrid-aware SDK primitives.