Skip to main content

Wormhole & ZK Scaling

The Wormhole system is Quantus's solution to the post-quantum signature bloat problem. It uses zero-knowledge proofs to aggregate many transfers into a compact proof. Current two-layer aggregation is ~430 QTPS; the theoretical ceiling is ~2,800 QTPS. Transfers are also private at the sender-receiver link.

The Problem

Post-quantum signatures (ML-DSA-87) are ~70x larger than ECDSA. Without wormhole aggregation, transparent ML-DSA-87 fills a 3.75 MB, 12-second block with about 510 transfers — ~43 QTPS. Every Quantus transaction is already post-quantum, so TPS and QTPS are the same number. Every block would still be dominated by signature data.

The Solution: Burn-and-Remint with ZK Proofs

Wormhole addresses allow users to transfer value without including a full Dilithium signature on-chain for each individual transaction:

Step by Step

  1. Burn: User sends coins to an unspendable wormhole address computed as H(H(salt|secret)) where H is Poseidon2
  2. Prove: User generates a ZK proof (off-chain) demonstrating they know the preimage that maps to the wormhole address, without revealing it
  3. Aggregate: Multiple users' proofs are recursively composed into a single aggregated proof using Plonky2
  4. Verify: The aggregated proof is submitted on-chain. Current fixtures serialize to ~151 KB (private batch) and ~224 KB (public batch); those sizes come from the compiled circuit, not a linear per-transfer cost. The table below is amortized on-chain payload, including public inputs.
  5. Mint: The on-chain verifier validates the proof and mints coins to the specified exit addresses. Exits pay a 4 bps volume fee (ceil-rounded per private segment; 50% burned, remainder to the miner; public batches may rebate half of the burn bucket to the aggregator).

Performance Impact

Figures from the whitepaper. Bound: 12-second blocks, 3.75 MB of transactions.

ModeBytes per transferTransfers / blockQTPS
Transparent, ML-DSA-87~7.3 KB~510~43
Transparent, ML-DSA-65~5.4 KB~690~58
Encrypted, current two-layer aggregation~266 KB per 371 transfers~5,200~430
Encrypted, theoretical ceiling~112 bytes of public inputs~33,000~2,800

Current aggregation is about 10x transparent ML-DSA-87. The ceiling is about 65x.

ZK Proof System: Plonky2

Quantus uses Plonky2, a STARK-based proof system maintained by Polygon Zero (forked and maintained by Quantus as qp-plonky2).

Key properties:

  • No trusted setup -- Unlike Groth16 or PLONK, STARKs require no ceremony
  • Recursive composition -- Proofs can verify other proofs, enabling aggregation
  • Field: Goldilocks (p = 2^64 - 2^32 + 1) -- optimized for 64-bit CPUs
  • Hash function: Poseidon2 over Goldilocks field (same as used throughout Quantus)

Circuit Architecture

The ZK circuit system is organized as a pipeline of independent crates:

Crate Inventory

CratePathPurpose
qp-zk-circuits-commoncommon/Shared gadgets, utilities, traits
qp-wormhole-inputswormhole/inputs/Input data structures for the circuit
qp-wormhole-circuitwormhole/circuit/Circuit constraint definition
qp-wormhole-proverwormhole/prover/Proof generation
qp-wormhole-verifierwormhole/verifier/On-chain proof verification
qp-wormhole-aggregatorwormhole/aggregator/Recursive proof aggregation (tree structure)
qp-wormhole-circuit-builderwormhole/circuit-builder/Build-time circuit compilation

Source: qp-zk-circuits

Privacy Model

Wormhole addresses provide transaction privacy as a structural feature, not as an add-on:

What is visible on-chain:

  • The amount burned to a wormhole address
  • The wormhole address itself
  • The exit address and amount minted
  • The aggregated proof

What is NOT visible on-chain:

  • The link between who burned and who received
  • The preimage / secret used to derive the wormhole address

This is architecturally similar to Tornado Cash's privacy model, but integrated at the protocol level rather than as a smart contract overlay.

Nullifiers

Each wormhole transaction produces a nullifier -- a value derived from the secret that is unique per transaction. The chain stores all used nullifiers and rejects any proof that reuses one. This prevents double-spending without revealing the sender's identity.

Proof Aggregation

The aggregator uses a recursive tree structure:

  1. Individual proofs are generated for each wormhole transaction
  2. Pairs of proofs are recursively verified and composed into a parent proof
  3. The tree continues until a single root proof remains
  4. Only the root proof is submitted on-chain

The aggregator handles padding (when the number of proofs isn't a power of two) and ensures that proofs from different blocks, assets, or fee policies are not incorrectly mixed.

On-Chain Verification

The verifier is compiled at build time and embedded in the node binary. The runtime's pallet-wormhole calls into the verifier to validate aggregated proofs:

  1. Parse the aggregated proof's public inputs
  2. Verify the ZK proof against the embedded verification key
  3. Check all nullifiers are unused
  4. Mint the specified amounts to the exit addresses
  5. Store the nullifiers to prevent replay

Unsigned Submission

Aggregated proofs are submitted as unsigned transactions (no signature required) because the ZK proof itself authenticates the transaction. This avoids adding another Dilithium signature on top of the proof.

Voting Circuit

The qp-zk-circuits repository also contains a voting circuit (voting/) for on-chain vote eligibility and double-vote prevention. This circuit is not yet published but shares infrastructure with the wormhole circuit.

Technical Resources

  • Repository: qp-zk-circuits (15-page DeepWiki available)
  • Proof system fork: qp-plonky2
  • Audit: Eiger ZK circuit audit (in progress)