Testnet live · recursion: 128-bit inner-hash stack complete, on-chain channel replay verified · 2026-07-26

Build the post-quantum migration layer before it becomes urgent

QLSA aggregates thousands of post-quantum signatures into a single constant-size proof, targeting scalable blockchain verification without trusted setup and without forcing a hard fork.

QLSA (Quantum-Layered Signature Aggregation) is an open deep-tech project exploring how ML-DSA, Merkle commitments, and STARK proofs can make post-quantum blockchain systems operationally viable.

FocusInfrastructure, not just cryptography
CI1,011 Solidity + ~560 Python + 516 Rust (152 recursive) + 71 TS — all green
Statusrecursion R0.1–R4.6 ✓ · on-chain replay + binding verified · outer-proof gas: next step

What's been built

Phase 6 complete — live on Ethereum Sepolia (2026-05-05). The V23 production pipeline proves 8 ML-DSA circuits in a single STARK (3,505 trace columns), and BatchRegistryV6 + QLSAVerifierVFRI10 finalize a batch on-chain within the per-transaction gas cap.

Milestones since then:

  • VFRI11 — Poseidon2 t=8 backend (2026-06-16): 4-word/124-bit nodes → ~262 node collision. Full-V23 verify() at depth-10 exceeds 100M gas — which is what motivated recursion.
  • Path decision (2026-06-17): skip a standalone t=16 verifier (~4× worse gas) and go straight to proof recursion — a STARK proving “I verified a VFRI11 STARK”, where the inner hash can be any width for free.
  • Recursion gadget set + soundness closure (R0.1–R3.12): QM31 arithmetic, FRI fold/OODS, inner-hash Merkle path, Fiat-Shamir absorb/draw, per-query composition. Two-expert audits closed every composition-level gap — C1 (outputs pinned in-circuit), C2 (every preprocessed-column verifier pins Tree 0 via a canonical recomputed root), 1a (all verifier-fixed FRI challenges pinned), and Merkle root binding.
  • 128-bit inner-hash ladder complete (R3.13–R3.18): t=2 → t=8 → t=16 (8-word/248-bit nodes, ~2124 ≈ 128-bit), each rung a pure hash-backend swap with compression AIR, Merkle-path AIR and single-/N-query composition.
  • The recursion verifies the real pipeline (R4.1): per-query inputs are extracted from a genuine VFRI11 FRI chain and proven against the committed last-FRI-layer root.
  • On-chain half assembled (R4.2–R4.5): the cheap Poseidon2 channel replay runs on-chain — RecursiveChannelReplay.sol reproduces the Rust reference byte-for-byte, and QLSAVerifierRecursive.sol binds an outer proof to the inner publics via keccak(traceRoot‖lastLayerRoot).
  • Measured limit (R4.6): the channel replay and the binding root verify on-chain today; driving the outer proof through the deployed VFRI11 exceeds a 29M-gas call even with a compacted outer trace. Next: a cheaper outer backend (t=4) or a per-transaction split, exactly as BatchRegistryV6 already does for V23.
Recursion audit R3.13–R4.1 — HIGH input/output binding fixed (2026-07-16)Crypto+code audit of the t=8/t=16 stack + VFRI11 bridge. [HIGH — fixed] poseidon2_t{8,16}_air verify_compress bound (left,right,node) only via Fiat-Shamir → a false compress(FAKE)=node claim could verify (latent, test-only, not production); now pinned in-circuit (raw_pin/node_pin + gated equality, regressions test_forged_input_cannot_prove). + t=16 matrix naive cross-checks + stale-doc/capacity fixes. All else clean. 152 recursive tests, 513 total.
Recursion R4.1 — verifies the REAL VFRI11 pipeline (2026-07-16)Shared vfri11_fri_chain (byte-identical to the ABI hint generator) + gen_vfri11_recursion_inputs: the t=8 composition proves the inner proof's per-query decommitments against the genuine committed last-FRI-layer root. "Root vs committed FRI-layer root" closed at the Rust level; index-oriented twiddle-inverse trick + hard extraction invariant.
Recursion R3.13–R3.18 — 128-bit inner-hash ladder COMPLETE (2026-07-13/16)t=8 (4-word/124-bit → 2^62) and t=16 (8-word/248-bit → ~2^124 ≈ 128-bit, Stwo's native Poseidon2-16 width): compression AIRs + Merkle-path AIRs + single-/N-query compositions. Each rung a pure hash-backend swap binding leaf = hash(finalFold) end-to-end in-circuit. 150→152 recursive tests.
Recursion audit R3.12 — Merkle root binding closed + input caps (2026-07-10)Crypto+code audit of R3.8–R3.11. [C1 root — fixed] claimed root pinned in-circuit (is_root/root preproc columns, is_root·(s0 − root_pinned)=0); before, a malicious prover could prove a FALSE root claim with adversarial siblings (only reuse was blocked). depth is an explicit public input, matching on-chain MerkleVerifier.verify. Regression test_forged_root_cannot_prove. [Code — fixed] MAX_QUERIES/MAX_NUM_FOLDS/MAX_DEPTH/log_size/capacity caps close panic/OOM paths (div-by-zero at depth 0, OOB preproc writes, 2^40 allocs). 104 recursive tests, 453 total.
Recursion R3.10–R3.11 — FRI cherry-pick (1a) fully closed (2026-06-17)Design realization: the cheap Poseidon2 channel stays on-chain → challenges are public inputs, no logup needed. QueryChallenges bundle (alpha, z_x, px, inv) pinned via 17 preprocessed columns + in-circuit equality constraints; threaded through single/multi/composition. test_forged_alpha_cannot_prove, test_forged_zx_inv_px_cannot_prove.
Recursion R3.8–R3.9 — multi-gadget composition, single + N-query (2026-06-17)composition.rs: recursive_verifier + merkle_path in ONE multi-component STARK — fold chain → hashLeaf → Merkle path, value-bound end-to-end; N-query (VFRI11 shape) via multi-path merkle (N paths in one component, AIR unchanged).
Recursion R3.7 — C1/C2 CLOSED codebase-wide (2026-06-17)[C1] outputs pinned via fin preproc columns + is_output-gated constraint; [C2] canonical Tree-0 pinning (canonical_preproc_root) in ALL preprocessed-column verifiers: 4 recursion sub-gadgets + 5 production V23 is_init_uh verifiers + Poseidon2 hash-chain. Forged selectors no longer verify.
Recursion audit — 2 confirmed soundness blockers (2026-06-17)Two-expert audit (crypto + code) of the full recursive gadget set. The gadgets are correct arithmetic relation provers, but composition is not yet sound vs a malicious prover: [C1] public outputs (root/finalFold/digest) bound only via Fiat-Shamir, no in-circuit constraint; [C2, reproduced] preprocessed selectors/constants prover-supplied & unpinned (forged is_step verifies). Same pattern in mature V23/VFRI — per-circuit follow-up. Robustness fixes applied (caps, guards, asserts). Must close C1/C2 before production wiring (R3.7). Resolved: see R3.7–R3.12 above.
Recursion R3.6 — Fiat-Shamir draw AIR (2026-06-17)transcript_draw_air.rs: proves the Poseidon2 t=2 duplex-sponge squeeze chain (drawSecureFelt/drawQueries core) — the dual of channel_air absorb. 8 main + 7 preproc cols; matches P2Channel::draw_pair. 11 Rust tests. Full recursion gadget set complete: 88 tests.
Recursion R3.5 — multi-query aggregation (2026-06-17)prove_recursive_queries: N per-query FRI chains in ONE STARK — N blocks of (1+K) rows, the same AIR (per-row selectors gate each block), all finalFolds bound. 5 Rust tests.
Recursion R3.3–R3.4 — per-query recursive verifier + integration (2026-06-17)recursive_verifier.rs: OODS± + circle fold + K line folds in ONE AIR, data flow enforced by a cross-row constraint (42 main + 2 preproc). integration.rs: recursive_verifier → qm31_leaf_hash → merkle_path_air — full per-query FRI verification value-bound across 3 sub-proofs. 9+3 Rust tests.
Recursion R3.2 — FRI fold chain AIR (2026-06-17)fri_fold_chain_air.rs: K line-fold rounds chained via a cross-row constraint input[k]=output[k−1] (21 main + 1 preproc). 9 Rust tests.
Recursion R3.1 — per-query FRI step AIR (2026-06-17)recursive/query_step_air.rs: first composition gadget — chains OODS± + circle fold in one row per query. 42 cols, 16 degree-2 constraints; step_ref() cross-checks against oods_air + fold_air references; generic qmul<F> deduplicates QM31-mul expansion. 7 Rust tests.
Recursion R2 — Merkle path + Fiat-Shamir transcript AIRs (2026-06-17)merkle_path_air.rs: proves leaf@index+siblings→root via Poseidon2 t=2 compression (10 main + 4 preproc cols; index-bit child selection; cross-row cur chaining). channel_air.rs: proves Poseidon2 duplex sponge absorption — mixU32s core (7 main + 4 preproc cols). 10+9 Rust tests.
Recursion R1 — FRI fold + OODS quotient AIRs (2026-06-17)fold_air.rs: proves folded=(f₊+f₋)+α·(f₊−f₋)·inv (21 cols; helper column p lowers degree to 2). oods_air.rs: proves fₚ·(px−z_x)=compValue−oodsCombo (17 cols; multiplicative form avoids QM31 inverse). 8+8 Rust tests.
Security + code audit — VFRI11/t=8/recursion (2026-06-17)Two-expert review (crypto/blockchain + Rust/systems) of the diff. No Critical/High/Medium. QM31-mul AIR soundness hand-verified (4 degree-2 constraints exactly pin each z limb). Fixed: deploy_v6.sh --network flag (silently ignored → wrong-network deploy risk), CLI arg validation, web3 HTTP timeouts, .env.deployed 0600, t=8 sponge % P input reduction (Rust parity), QM31 limb-canonicity precondition.
Recursion R0.1 — QM31-mul AIR (2026-06-17)recursive/qm31_mul_air.rs proves z = x·y in QM31 = CM31[u]/(u²−R), R = 2+i. 12 cols, 4 degree-2 constraints, no preproc; full Stwo prove/verify roundtrip + cross-check against the u128 reference (u²=R, 1·y=y). The load-bearing primitive for circleFold/lineFold/OODS. Decision: skip the standalone t=16 verifier — t=16 becomes the recursion's inner hash AIR (constant on-chain cost).
Poseidon2 t=8 backend + VFRI11 (2026-06-16)poseidon2_t8.rs + Poseidon2M31T8.sol / Poseidon2MerkleVerifierT8 (4-word/124-bit nodes → ~2^62 collision) / Poseidon2ChannelT8 (217-bit capacity), cross-checked bit-exact. QLSAVerifierVFRI11 = VFRI10 protocol on t=8 (identical ABI, version marker 5); generic verify()==true at ~13.1M gas. Gas finding: full-V23 t=8 verify >100M gas → production needs recursion.
Security + code audit (2026-06-14)Two-expert audit (crypto/blockchain + systems). Off-chain replay guard (ReplayedTxError — a batched tx can no longer be re-batched), submit error-text hardening, /stats overflow metric, #[cfg(test)] gating test fixtures out of the release library, and an FRI tree_depth ∈ 2..=30 guard. 6 fixed, 1 false-positive, 2 documented research-defaults.
VFRI10 — Poseidon2 t=4 backend (2026-06-14)QLSAVerifierVFRI10 wires the t=4 wide Merkle (Poseidon2MerkleVerifierT4) + t=4 Fiat-Shamir channel (Poseidon2ChannelT4) into the VFRI9 proof path (byte-identical ABI, version marker 4). V23 cross-bound Rust/PyO3/Python pipeline + dual-group on-chain E2E via BatchRegistryV5. Each V23 group verify() ≤16.7M gas; lifts the node/transcript collision wall above the t=2 ceiling (~2^31).
VFRI9 aggregator pipeline + SDK (2026-06-12)BatchResult.has_vfri9 + vfri9 proof/commitment/hints fields; Batcher._try_prove calls prove_mldsa_sig_vfri9_stark; API /batch/run, /batch/{id}/witness, /batches expose has_vfri9; Python SDK WitnessStatus/BatchStatus extended; JS SDK WitnessStatus/BatchStatus gain hasVfri9 + RawBatchStatus deduplication. pyo3 0.24→0.29: RUSTSEC-2026-0176/0177 fixed.
VFRI9 — last-layer FRI check + wide Poseidon2 (2026-06-10)QLSAVerifierVFRI9: prover supplies all 2^(treeDepth−K) final FRI evaluations; verifier rebuilds Merkle tree with wide nodes (62-bit: (s0<<32)|s1) and asserts root == friLayerRoots[K] — closes bounded-degree soundness gap in VFRI5–VFRI8. Full-root Fiat-Shamir: mixRootFull absorbs all 32 bytes. BatchRegistryV5 dual-VFRI9.
Security & code audit (2026-06-10)21 findings (18 fixed): VFRI8 OODS guard, num_folds cross-bound param, short-proof calldataload guard in BatchRegistryV4/V5, API rate limit on /stats+/config, VFRI8 witness_commitment fallback, _sender_txs eviction sync, dead _call_prover removed. Aggregator liveness: prover-crash recovery (≤3 retries, txs returned to mempool), prepend_batch overflow accounting, Bearer-token API auth.
VFRI8 — Poseidon2 trace commitment (2026-06-10)QLSAVerifierVFRI8: Poseidon2 Merkle trees + Poseidon2 Fiat-Shamir channel. 20-query on-chain FRI ≤15M gas. BatchRegistryV5 dual-VFRI8.
Code audit (2026-06-03)HttpClient._decode_json() guards all 7 JSON call-sites — proxy HTML responses (nginx 2xx) no longer crash the SDK. testnet/e2e.py sender_key recomputation eliminated; unused import hashlib removed. 239 Python tests green.
Cryptographic coreML-DSA-65/44/87, SHA3-512 Merkle, address derivation, pubkey validation, batch aggregation
STARK prover (Stwo 2.2.0) — V23Circle STARK, Poseidon2-over-M31 AIR, 8 ML-DSA circuits in 1 FRI commitment (3,505 trace columns): NttBatch + AzFull + Ct1Full + InttBatch + WPrimeFull + NormCheck + UseHintV2 + RangeQBatch (az_hat ∈ [0,Q))
Smart contracts — deployed SepoliaBatchRegistryV2/V3/V4/V5 (nonce replay protection, MAX_SENDERS DoS cap), QLSAVerifierFull + VFRI7–VFRI10 (O(1)-gas FRI, cross-proof binding, Poseidon2 t=2/t=4 Merkle + channel, last-layer check), 950 Solidity tests
Aggregator node + APIThread-safe mempool (atomic drain), auto-batcher, FastAPI REST, rate limiting (100 tx/min per IP, proxy-aware), pk/sig length validation
SDKs (Python + TypeScript)Wallet, LocalClient + HttpClient, WitnessStatus, TypeScript AggregatorClient — mypy strict
Testnet E2E (Sepolia, 2026-05-05)First batch: 4 tx, 3234-byte proof, 9.16 s finalization. submitBatchWithNonces on-chain.
On-chain FRI verifier library stackCM31 · QM31 · Blake2sYul · MerkleVerifier · TwoChannel · CirclePoint · QLSAVerifierV4–V13 · VFRI/VFRI2/VFRI3/VFRI4/VFRI5/VFRI6 — full K-round FRI + OODS + composition tree + O(1)-gas off-chain OODS combo
Security audit round 1 (2026-05-22)Constant-time Merkle (hmac.compare_digest), rate-limit thread-safety, MAX_SENDERS=3000 DoS cap (O(n²) loop), y=0 circle guard (M31.inv panic), _MAX_HISTORY=1000 memory cap, PRIVATE_KEY env fallback
BatchRegistryV4 — dual-VFRI7 (2026-05-30)Requires both LOG=10 (1298 cols) and LOG=8 (2206 cols) VFRI7 proofs. Cross-proof binding: boundRoot10 = keccak256(batchRoot ‖ traceRoot8) — mixing proofs from different witnesses fails Merkle verification
Security audit round 2 (2026-05-30)O(1) batch history (deque + dict index, eliminates O(n) scan DoS), N_FRI_QUERIES range validation, IP address validation for TRUSTED_PROXIES, ML-DSA pubkey size guard, UUID input validation, GET /batch/* rate limit, module-level logger — 24 total findings resolved

The Problem

Direct migration from classical signatures to post-quantum signatures can severely inflate block payloads. That turns protocol safety progress into an operational scaling problem.

Signature Size 3000 tx block Operational impact
ECDSA (current)~70 bytes~220 KBFits today's assumptions
ML-DSA-65 (FIPS 204)~2,420 bytes~7.2 MBSevere throughput penalty
~30–40×Payload overhead from direct migration
FIPS 204Standardized signature base
Hash-firstNo trusted setup in the proving path
L2-readyDeployment path without hard fork

The Solution

QLSA is not a new signature scheme. It is a post-quantum aggregation layer that aims to make large signature sets proof-friendly, compact, and verifiable at fixed on-chain cost.

What QLSA does

  • Aggregates N ML-DSA signatures into one batch commitment
  • Uses Merkle commitments to structure batch data
  • Generates a STARK proof for batch correctness
  • Allows fixed-cost on-chain verification independent of batch size

Why the architecture matters

  • Heavy proving remains off-chain
  • Public inputs can be kept minimal with commitment roots
  • MVP can ship before full ML-DSA-inside-AIR is solved
  • The system remains crypto-agile across future upgrades

Architecture

The system is split into three layers so that user flows stay familiar, proving stays off-chain, and on-chain verification remains lean.

Layer 1

Signing

Wallet-level signing using post-quantum keys.

  • ML-DSA-65 for signatures
  • Address derived from SHA3-256(pubkey)
  • Intended to preserve a familiar wallet-style UX
Layer 2

Aggregation

Batch formation and proving pipeline.

  • Collect transactions off-chain
  • Verify ML-DSA signatures off-chain; V23 proves arithmetic witness (8 circuits) inside STARK
  • Build Merkle tree with SHA3-512; root bound into Fiat-Shamir channel (V22+)
  • Generate STARK proof — c̃ + merkle_root mixed; RangeQBatch proves az_hat ∈ [0,Q)
Layer 3

Verification

Constant-cost contract-side validation.

  • Verify proof in Solidity verifier contract
  • Store merkle_root and finalize the batch
  • onchain_commitment = Blake2s(proof[:32] ∥ c_tilde[:32])[:16] — 128-bit binding
  • MVP-6: VFRI8 — Poseidon2 Merkle + Fiat-Shamir channel; 20-query verification ≤15M gas. Both LOG=10 and LOG=8 trace groups verified on-chain with 130-bit soundness.

MVP Strategy

The project intentionally separates realistic implementation milestones from the hardest research component.

StageWhat STARK provesML-DSA in STARKStatus
MVP-1Core crypto & MerkleNo✓ Done
MVP-2Hash-chain + Poseidon2 AIR (Stwo)No✓ Done
MVP-2bML-DSA batch proof (Rust + STARK)Off-chain✓ Done
MVP-3+All 8 circuits inside AIR + RangeQBatch (V23)Yes (V23)✓ Complete
MVP-4VFRI4/5/6 — O(1)-gas on-chain FRI verifier; BatchRegistryV4Yes✓ Done (2026-05-22)
MVP-5VFRI7 cross-proof binding; security audit round 2; code quality hardeningYes✓ Done (2026-05-30)
VFRI8/9Poseidon2 Merkle+channel; last-layer FRI check; wide nodes (62-bit); aggregator liveness; API authYes✓ Done (2026-06-10)
MVP-6 groundworkPoseidon2 t=4 Rust+Solidity; VFRI9 pipeline; pyo3 CVE fix; JS SDK parityYes✓ Done (2026-06-12)
MVP-6 VFRI10t=4 verifier + V23 pipeline + dual-group E2E; security/code auditYes✓ Done (2026-06-14)
MVP-6 t=8BatchRegistryV6 per-group split; Poseidon2 t=8 backend; VFRI11 (~2^62 nodes)Yes✓ Done (2026-06-16)
Recursion R0.1QM31-mul AIR gadget; path decision: skip standalone t=16, t=16 → inner hash of recursionYes✓ Done (2026-06-17)
Recursion R1FRI fold AIR + OODS quotient AIR — 8+8 Rust testsYes✓ Done (2026-06-17)
Recursion R2Poseidon2 Merkle auth-path AIR + Fiat-Shamir sponge-transcript AIR — 10+9 Rust testsYes✓ Done (2026-06-17)
Recursion R3.1Per-query FRI step (first composition gadget: OODS± + circle fold) — 7 Rust testsYes✓ Done (2026-06-17)
Recursion R3.2–R3.6FRI fold chain, per-query verifier, integration, N-query aggregation, Fiat-Shamir draw — full gadget set (88 tests)Yes✓ Done (2026-06-17)
Recursion auditC1/C2 codebase-wide (R3.7), 1a (R3.10–11), Merkle root binding (R3.12), compression input/output binding (R3.13–R4.1)Yes✓ Done (2026-07-16)
Recursion R3.13–R4.1128-bit inner-hash ladder (t=8/t=16 compression + path + composition) + verifies real VFRI11 pipeline — 152 recursive testsYes✓ Done (2026-07-16)
Recursion R3.7–R3.12Composition (single + N-query, VFRI11 shape), QueryChallenges pinning, root binding — 104 recursive testsYes✓ Done (2026-07-10)
Recursion R3–R4Full recursive verifier composition → QLSAVerifierRecursive (~5M gas constant)Yes⏳ In Progress
MVP-6VFRI8: Poseidon2 Merkle + channel; 20-query ≤15M gas; BatchRegistryV5; security & code auditYes✓ Done (2026-06-10)

Key risks

  • ML-DSA inside STARK: All 8 circuits complete in V23 (NttBatch + AzFull + Ct1Full + InttBatch + WPrimeFull + NormCheckBatch + UseHintBatchV2 + RangeQBatch), 3,505 trace columns, 1 FRI commitment. c̃ + Merkle root Fiat-Shamir bound; RangeQBatch closes AzFull multiplication soundness gap.
  • Aggregator trust model: the MVP relies on off-chain verification before challenge systems exist.
  • Adoption timing: the need is real, but mainstream migration may still take years.
Research-driven Benchmark-first Security-aware

Tech Stack

The technical stack reflects a staged path: fast iteration now, stronger proving infrastructure later.

Cryptographic core

  • ML-DSA-65
  • SHA3-512 for Merkle hashing
  • SHA3-256 for address derivation
  • Python 3.10+
  • liboqs-python >= 0.14.0

STARK layer

  • Stwo 2.2.0 — Circle STARK over M31 field
  • Poseidon2-over-M31 AIR (t=2, α=5, 8 full rounds, 7 columns, 6 constraints ≤ deg 3)
  • V23 single STARK: 8 ML-DSA circuits, 3,505 trace columns, 1 FRI commitment
  • ML-DSA-65 FIPS 204 verifier in Rust; c̃ + Merkle root Fiat-Shamir binding
  • FRI blowup ×64, N_FRI_QUERIES=20, POW_BITS=10 — 130-bit soundness; zeroize crate for secure key wipe
  • nightly-2025-07-01 Rust toolchain

Contracts & infra

  • Solidity + Hardhat 2.28
  • VFRI8 verifier stack: CM31 · QM31 · Blake2s · Poseidon2M31 · Poseidon2MerkleVerifier · Poseidon2Channel · CirclePoint · QLSAVerifierVFRI8
  • BatchRegistryV5 — dual-VFRI8 with cross-proof binding
  • FastAPI aggregator + rate limiting (proxy-aware, stale eviction)
  • Python SDK (Wallet, LocalClient, WitnessStatus) + TypeScript SDK

Performance targets

Batch size3,000 tx
Proof size90–200 KB
On-chain verificationO(1)
ML-DSA-65 sign (avg)0.12 ms / tx
Merkle build N=30007.1 ms
Full pipeline N=3000354 ms
V23 STARK columns3,505 (649+1523+295+649+24+15+61+288+1 preproc)
20-query VFRI9 gas≤15M gas (wide Poseidon2 Merkle, last-layer check)
Solidity tests1,011 passing
Python tests~560 passing (with PyO3) / ~350 without
Rust tests516 passing (152 recursive)
TS tests71 passing
CI pipeline6 / 6 jobs green

Repository snapshot

QLSA/
├── core/          # ML-DSA, Merkle, batch
├── aggregator/    # Mempool, batcher, FastAPI
├── stark/         # Python prover/verifier wrappers
├── stark_stwo/    # Stwo STARK + ML-DSA AIR circuits
├── contracts/
│   └── src/verifier/
│       ├── CM31.sol       # GF((2³¹−1)[i])
│       ├── QM31.sol       # quartic extension
│       ├── Blake2s.sol    # pure-Solidity
│       ├── Poseidon2M31.sol      # t=2 over M31
│       ├── Poseidon2M31T4.sol    # t=4 over M31 (VFRI10)
│       ├── Poseidon2MerkleVerifier{,W,T4}.sol  # t=2 / wide / t=4 nodes
│       ├── Poseidon2Channel{,T4}.sol  # Fiat-Shamir (t=2 / t=4)
│       ├── MerkleVerifier.sol
│       ├── TwoChannel.sol # Fiat-Shamir (Blake2s)
│       └── CirclePoint.sol # CanonicCoset + FRI fold
├── sdk/python/    # Wallet, LocalClient
├── sdk/js/        # TypeScript AggregatorClient
├── testnet/       # E2E demo, deploy, submit
└── tests/         # 1011 Sol + ~560 Py + 516 Rust + 71 TS

Roadmap

The roadmap prioritizes a credible implementation path before expanding into the hardest proving research.

Phase 1 — Core

✓ Complete

ML-DSA-65/44/87 keys, signing, transaction model, SHA3-512 Merkle tree, batch aggregation.

Phase 2 — STARK Prototype

✓ Complete

Stwo 2.2.0 Circle STARK — Poseidon2-over-M31 AIR (t=2, α=5, 8 full rounds, 7 main columns, 4 preproc columns, 6 constraints ≤ deg 3), ML-DSA-65 batch verifier in Rust (FIPS 204), FRI blowup ×4. 128-bit Blake2s commitment. 210 Rust tests green.

Phase 3 — Smart Contracts

✓ Complete

QLSAVerifierFull, QLSAVerifierBound (Merkle-root-bound Blake2s, bytes16 128-bit commitment), BatchRegistry, BatchRegistryV2 — all deployed (Hardhat). Contract tests green.

Phase 4 — Aggregator Node

✓ Complete

In-memory mempool (thread-safe, atomic drain), auto-batcher with transaction recovery, FastAPI REST endpoint. Full STARK prove→submit pipeline via PyO3 native FFI.

Phase 5 — SDK

✓ Complete

Python SDK (wallet + async client) and TypeScript/JavaScript SDK with full input validation and typed API. wipe_key() backed by Rust zeroize crate (volatile writes) for secure key wipe.

Phase 6 — Testnet

✓ Complete

Live on Ethereum Sepolia (2026-05-05). QLSAVerifierBound: 0xF2dcad845F0fb4a11106E4c4cF23D260a3CB65Dd · BatchRegistryV2: 0xF44A51CF1d7620e070ecaf611685418DE4679879. First batch: tx 0x5a9215d2aeb4…bd82, 3234 bytes, finalized in 9.16 s. Security hardening: nonce registry, rate limiting, pubkey validation.

MVP-3+ — ML-DSA Native in AIR (V22/V23)

✓ Complete

All 8 ML-DSA-65 arithmetic circuits unified in one FRI commitment (V23, 3,505 trace columns): NttBatch (LOG=10, 649 cols) + AzFull (LOG=8, 1523 cols) + Ct1Full (LOG=8, 295 cols) + InttBatch (LOG=10, 649 cols) + WPrimeFull (LOG=8, 24 cols) + NormCheckBatch (LOG=8, 15 cols) + UseHintBatchV2 (LOG=8, 61 cols + 1 preproc) + RangeQBatch (LOG=8, 288 cols). Fiat-Shamir transcript: c̃ → merkle_root → Tree0 → Tree1. RangeQBatch closes AzFull multiplication soundness gap.

MVP-4 — Mainnet-grade On-chain Verifier

✓ Complete (2026-05-22)

Full on-chain Circle STARK FRI verifier (Solidity). Verifier library stack: CM31 · QM31 · Blake2sYul · MerkleVerifier · TwoChannel · CirclePoint · QLSAVerifierV4–V13 · VFRI–VFRI6. QLSAVerifierVFRI6: O(1)-gas off-chain OODS combo — 649, 1298 and 2206-column traces all verified within 15M gas. BatchRegistryV4: dual-VFRI6 registry.

MVP-5 — Cross-Proof Binding & Security Hardening

✓ Complete (2026-05-30)

QLSAVerifierVFRI7: adds mixRoot(merkleRoot) before drawQueries — FRI query indices depend on the batch Merkle root, preventing proof-mixing attacks. BatchRegistryV4: boundRoot10 = keccak256(batchRoot ‖ traceRoot8) — cross-binding both LOG groups. Security audit round 2: 24 findings resolved. Code audit 2026-06-03.

VFRI8 + VFRI9 — Poseidon2 + Last-layer FRI (2026-06-10)

✓ Complete

VFRI8: Poseidon2 Merkle trees + Poseidon2Channel — 20-query on-chain FRI ≤15M gas. VFRI9: last-layer bounded-degree check (prover supplies all 2^(treeDepth−K) final evaluations; verifier rebuilds tree with wide 62-bit nodes and asserts root match) — closes the FRI soundness gap in VFRI5–VFRI8. Full-root Fiat-Shamir (mixRootFull: all 32 bytes). Aggregator liveness: prover-crash recovery (≤3 retries), prepend_batch overflow accounting, Bearer-token API auth. Security & code audit: 21 findings, 18 fixed. BatchRegistryV5: dual-VFRI9. 917 Solidity + 552 Python + 315 Rust + 71 TS.

MVP-6 — VFRI10 (Poseidon2 t=4 backend) + audit (2026-06-14)

✓ Complete

The Poseidon2 t=4 permutation (poseidon2_t4.rs + Poseidon2M31T4.sol, cross-checked bit-exact, 124-bit compress, collision ~2^62) is wired into the full proof path: QLSAVerifierVFRI10 swaps the t=2 Merkle/channel for Poseidon2MerkleVerifierT4 + Poseidon2ChannelT4 (byte-identical VFRI9 ABI, version marker 4). V23 cross-bound Rust bridges + PyO3 + Python (prove_mldsa_sig_vfri10_stark) + on-chain dual-group E2E via BatchRegistryV5 — each V23 group verify() ≤16.7M gas (dual-group submitBatch needs a per-group registry or mainnet's 30M). Security + code audit: off-chain replay guard, submit error-text hardening, /stats overflow metric, release-build test-fixture gating, FRI tree_depth guard. 950 Solidity + ~560 Python + 323 Rust + 71 TS.

FAQ

Short answers to the questions most people will ask when they first encounter the project.

Is QLSA a new signature algorithm?

No. QLSA is an aggregation and verification architecture built around post-quantum signatures such as ML-DSA.

Why not just replace ECDSA directly?

Because the size increase of post-quantum signatures can cause major throughput and cost problems if deployed naively.

Does QLSA already prove ML-DSA inside STARK?

Yes — V23 is the production pipeline. All 8 ML-DSA-65 arithmetic circuits are unified in one FRI commitment (3,505 trace columns): NttBatch + AzFull + Ct1Full + InttBatch + WPrimeFull + NormCheckBatch + UseHintBatchV2 + RangeQBatch. c̃ and batch Merkle root are both mixed into Fiat-Shamir, binding each proof to one specific witness and batch.

What on-chain verifier exists?

The latest is QLSAVerifierVFRI10 + BatchRegistryV5 (2026-06-14): the VFRI9 proof protocol (last-layer FRI check + full-root Fiat-Shamir) on the Poseidon2 t=4 hash backend, lifting the node/transcript collision wall above the t=2 ceiling (~2^31) toward 128-bit. Each V23 trace group (LOG=10 / LOG=8) verify() fits within 16.7M gas; the dual-group call needs a per-group registry or mainnet's 30M block limit. Off-chain FRI soundness: LOG_BLOWUP=6 × 20 queries + POW_BITS=10 = 130-bit.

Is this production-ready?

No. QLSA is research-stage software (not audited by external parties; an internal two-expert audit ran 2026-06-14). Infrastructure is fully implemented and tested (950 Solidity + ~560 Python + 323 Rust + 71 TS tests). Not recommended for production use or real funds.

Interested in PQC, ZK proofs, or blockchain infrastructure?

QLSA is an open research and engineering effort. Contributions, technical feedback, research discussion, and collaboration are welcome.