Research project. Unaudited.
Measured on a local QRL 2.0 network on 25 August 2026.
Post-quantum STARK verifier for QRL 2.0
QuantaStark verifies Plonky3-compatible STARK proofs on the 512-bit QRL 2.0 virtual machine. The verifier is written in Hyperion and the proof system is hash-based from the prover to the bridge, so the proofs are post-quantum by construction. One verification of the c3 preset costs 2,185,178 gas at 212 trace rows and 4,463,257 gas at 220, within the 20,000,000 gas block cap.
The QRL 2.0 virtual machine offers hashing, modular exponentiation and ML-DSA-87 signature verification as precompiles and has no pairing operation, so a validity proof on QRL takes the hash-and-field-arithmetic shape of a STARK. QuantaStark exists to measure what such proofs cost on QRL 2.0 and to ground an L2 design in those costs. The repository holds the verifier, the Rust prover and its test vectors, a fact registry, a bridge skeleton for the L1 side of a rollup, and the architecture decision derived from the measurements.
- Source
- github.com/DigitalGuards/QuantaStark, GPL-3.0
- Documents
- L2 architecture decision, gas report, verifier, protocol, bridge
- Authors
- DigitalGuards
1 Measurements
Every number is a receipt or a gas estimate from the gqrl developer node at the
20,000,000 block gas limit, optimizer runs 200, with one verifier
deployment per parameter preset. Transaction gas is the whole
verifyAndLog transaction through the gas meter; the last column is the
gasleft() delta around the verifier call. The generated report has all 44
cells under three optimizer settings.
| Cell | Proof bytes | Transaction gas | Inside the verifier |
|---|---|---|---|
fib_c3_n12
34 queries, log_blowup 3, 212 rows
|
43,440 | 2,185,178 | 1,447,427 |
fib_c3_n20
34 queries, 220 rows
|
105,873 | 4,463,257 | 2,710,754 |
fib_c2_n20
50 queries, log_blowup 2, 220 rows
|
134,801 | 5,920,483 * | 3,696,128 |
fib_c1_n12
100 queries, log_blowup 1, 212 rows
|
82,672 | 4,937,007 | 3,561,810 |
fib_c1_n20
100 queries, 220 rows
|
226,641 | 10,534,049 * | 6,807,827 |
* The transaction exceeds the 131,072-byte transaction-pool cap of the execution client,
so its gas comes from qrl_estimateGas and qrl_call. Source:
docs/GAS-REPORT.md.
Block cap. 40 of the 44 measured cells sit at or below the
8,000,000 gas target. The largest,
fib_c1-binary_n20 with a 388 KB proof, takes
17,065,391 gas, 85 percent of the
20,000,000 consensus block cap.
Transaction-pool limit. The execution client refuses transactions above 131,072 bytes, so proofs above about 123 KB need a raised pool cap or staged verification before they can be submitted as one transaction. Calldata is 20 to 43 percent of a verification transaction, and the FRI fold chains are 52 percent of the verifier's own execution at 212 rows.
Code size and tests. The verifier is 14,458 bytes of runtime code against a 24,576-byte cap. It accepts every valid vector of the sixteen presets and rejects every mutated vector with its recorded custom error. 618 unit tests check the JavaScript references against the Rust vectors on a stock machine; the contract suites run against a live QRVM.
Release gate. The Kurtosis composition, a full execution, beacon and
validator network on chain 3151909, deployed every preset and verified
fib_c3_n20 end to end on 25 August 2026, and its re-measurement of all 44
cells matched the developer node cell for cell. QRVM gas is deterministic, so the numbers
carry over to the public network unchanged.
2 How it works
The proof system is Plonky3 uni-stark over Goldilocks with the quadratic extension and two-adic FRI. Each choice below was priced against the QRVM gas table before it went into the verifier.
Prover
A Rust workspace produces the proofs, the test vectors and their mutation sets. The crate
version, Plonky3 0.7.0-rc.1, is pinned in
prover/PLONKY3_VERSION and recorded in every compile manifest, so a proof and
the verifier that checks it always name the same upstream release. A mirror verifier in
the same workspace reproduces the upstream transcript byte for byte.
Hashing
Every hash in the proof system, Merkle leaves, Merkle compression and the Fiat-Shamir
transcript, is keccak256 through the KECCAK256 opcode at 30 + 6 gas per
64-byte word. The SHAKE256 precompile costs 388 gas per call; the bridge uses it where
ML-DSA-87 needs a 64-byte digest. A proof verifies a few thousand hashes, so the cheaper
hash decides the design.
Field arithmetic
Goldilocks, p = 2^64 - 2^32 + 1, needs the fewest extension-field operations
of the common STARK fields. Integers are uint512 at the Hyperion level, the
hot loops run in Yul on native 512-bit words, and field inversion goes through the
0x05 modexp precompile.
Openings and calldata
Query openings share one pruned Merkle multi-proof per commitment, and FRI folds with
arity 8, the cheapest schedule in both bytes and gas. Proofs travel as packed
bytes calldata, 8-byte little-endian Goldilocks elements and 32-byte digests
exactly as Plonky3 serialises them, and the parser enforces the exact proof length before
any query data is read.
Presets
One verifier deployment per parameter preset: the six FRI constants at the top of
StarkVerifier.hyp are substituted at compile time. The committed values are
preset c3 (log_blowup 3, 34 queries, 16 proof-of-work bits); sixteen presets are measured.
Facts
StarkFactRegistry turns a verified proof into a storage fact keyed by
keccak256(verifier, programId, keccak256(publicValues)). Proof bytes contain
witness bytes the verifier skips and are therefore malleable, so the key is built from the
public values and the bridge looks facts up by that key.
Proof flow
- The prover (Rust, Plonky3 0.7.0-rc.1) produces the proof and its public values.
-
The proof travels as packed calldata: 43,440 bytes for
fib_c3_n12. -
StarkVerifier.verify(bytes proof, bytes publicValues), one deployment per preset, decodes and scans the layout, replays the keccak transcript, checks the constraints at the out-of-domain point and runs FRI: input batches, reduced openings, fold chains, round Merkle checks. Any failed check reverts with a custom error. -
StarkFactRegistry.registerFactcalls the verifier and stores the fact keyed by the public values. -
StateBridge.submitBatchlooks the fact up, or registers it in the same transaction, and advances the state root.
Normative details: docs/PROTOCOL.md (transcript, openings, calldata layout) and docs/VERIFIER.md (memory layout, check order, phase measurements).
3 Bridge preview
StateBridge is the L1 contract that consumes registry facts: it advances a
state root per proven batch, records deposits in an append-only accumulator and pays
withdrawals that carry a Merkle inclusion proof and an ML-DSA-87 signature. The panel
below shows its interface with every control disabled.
StateBridge preset c3
Network: none. Awaiting the QRL 2.0 public testnet relaunch.
- State root
- 0x0000…0000 (genesis)
- Batch index
- 0
- Deposits
- 0
- Verifier
- StarkVerifier[c3]
What the bridge does
Three flows, written in high-level Hyperion. Gas is the receipt on the developer node with a four-leaf withdrawal tree.
Deposits. deposit() hashes the sender, the amount and the
deposit index into a leaf, folds it into an append-only accumulator and emits
Deposited. An L2 batch later proves that it consumed the deposits in order.
Batches.
submitBatch(prevRoot, newRoot, publicValues, proof) requires
prevRoot to equal the current state root and the public values to encode both
roots as sixteen Goldilocks limbs. The registry verifies the proof, or finds the fact
already registered, and the state root advances: 155,618 gas plus
the verifier's own gas.
Withdrawals. withdraw proves a leaf under the state root and
checks an ML-DSA-87 signature through precompile 0x03 over a SHAKE256 digest
(precompile 0x06) of the leaf and the recipient, with the context
QP-WITHDRAW-v1. The leaf is marked spent before the transfer.
345,791 gas, 125,000 of them in the
precompile.
Still missing from the skeleton: an escape hatch, a challenge window, forced inclusion, a sequencer role, pausing and upgradeability. docs/BRIDGE.md lists every simplification, and the architecture document gives the order in which they get added.
4 Status and roadmap
QuantaStark is a DigitalGuards research project: experimental, unaudited and subject to breaking changes. It exists to measure what post-quantum validity proofs cost on QRL 2.0 and to inform an L2 design. Do not use anything in the repository to secure funds.
| Item | State |
|---|---|
| Verifier, prover, vectors, fact registry, bridge skeleton | Complete and measured on the developer node (milestones M0 to M8). Every valid vector of the sixteen presets verifies; every mutated vector reverts with its recorded error. |
| Release gate |
Run on the Kurtosis composition, chain 3151909, on 25 August 2026: all presets
deployed, fib_c3_n20 verified end to end, 44 cells re-measured.
|
| L2 architecture decision | Written from the measured numbers (milestone M9), with a decision table and the open questions. |
| Soundness | Every current preset is an experimental benchmark profile: 116 to 118 conjectured bits under the ethSTARK conjecture, with about 66 to 70 bits in the rough proven regime. A production profile remains open. |
| Privacy | The initial track is transparent validity and scaling. Proof layout version 1 does not provide zero knowledge. |
| Circuit hash | Selection is open. Poseidon2 remains an experimental benchmark candidate while M10 reviews the exact parameters and keeps the proof layout hash-agile. |
| External review | None to date. An audit of the verifier and the circuits is part of the plan. |
| Public deployment | Waits for the QRL 2.0 public testnet relaunch. |
Recommendation. Build a validity rollup whose data availability lives in
QRL calldata and whose final on-chain proof is the c3 STARK that
StarkVerifier already checks. Aggregate batch proofs with two-layer
recursion, a reviewed circuit hash inside and keccak256 at the last layer. Bind the
selected suite to a versioned identifier, let L2 accounts use a reviewed hash-based
signature scheme, and keep ML-DSA-87 at the L1 boundary through the bridge. Start with a
centralized sequencer and a single prover, with forced inclusion and an escape hatch on
L1, then move to a proof market and to sequencer decentralization.
Next steps, in order
- M10 Hash security gate and layout version 2. Add a versioned hash-suite identifier and multi-commitment layout with quotient chunks. Benchmark candidate suites against a conservative keccak baseline before selection.
-
M11
Data binding in the bridge. A keccak AIR,
postDataand the public-values schema inStateBridge, the withdrawal root and the forced-inclusion inbox. - M12 Recursion prototype. Selected circuit hash in the inner layer, keccak256 in the final layer, with the Fibonacci AIR as the payload.
- M13 Payment AIR set. Hash-based signatures and the lookup layer; wallet key derivation and index persistence.
- M14 Escape hatch, governance, defense in depth. External review of the verifier and the circuits, then a public testnet deployment of the sequencer, the prover and the submitter.