Draft. No backwards compatibility. No flag day.
Activated at the genesis of the new final Lux network: **2025-12-25
16:20 Pacific (unix 1766708400)**. The pre-Quasar Edition Lux network
(2020–2025) had no P3Q precompile family and is a separate network out
of scope.
LP-218 introduces P3Q at EVM slot 0x012205 — the rollup-batch-aware
PQ verifier family. The precompile takes a 1-byte kind
discriminator at the head of calldata and dispatches to a
primitive-specific P3QVerifier implementation that performs the
threshold-signature verification with an audited per-kind gas charge.
LP-218 specifies kind 0x01 (Pulsar, Module-LWE / FIPS-204 ML-DSA-65
threshold).
A rollup that wants cross-family-maximum PQ posture — for example
custody-grade, treasury, or sovereign-archival rollups — must verify
signatures from independent PQ families so that a structural break
against any one family does not compromise the rollup. LP-220
specifies two additional kinds on the same slot:
kind | Name | Family | Verifies |0x012205 | 0x01 | Pulsar (P3Q kind 0x01) | Module-LWE | Pulsar / FIPS-204 ML-DSA-65 threshold |0x012205 | 0x02 | Corona (P3Q kind 0x02) | Module-LWE | Corona threshold signatures |0x012205 | 0x03 | Magnetar (P3Q kind 0x03) | Hash-based | Magnetar / FIPS-205 SLH-DSA threshold |All three share one slot, one ABI shape, one dispatch path:
// EVM precompile at slot 0x012205 — unified PQ verifier family
function verify(
uint8 kind,
bytes calldata sig,
bytes32 messageHash,
bytes calldata groupPubKey
) external view returns (bool);
One slot, one address, three primitive kinds. A rollup using PQ-heavy
mode submits one P3Q verify call per kind per RollupBatchTx; a
rollup using PQ-fast mode submits only kind 0x01. Cross-family
resilience is opt-in at the rollup-genesis layer via CertModeFloor
(LP-218 §"Inherited cert mode").
The earlier LP-220 draft allocated separate slots 0x012206 (Corona)
and 0x012207 (Magnetar) for the rollup-batch wrappers and reused
LP-0120's same-address raw primitive verifiers as the underlying
implementation. **2026-06-03 decomplection collapses this into a
single P3Q slot at 0x012205 with kind-byte dispatch.** One slot, one
audit surface, one Solidity ABI shape, one operator-known address,
one dispatch path in EVM. Adding a fourth PQ family (a code-based
threshold sig, say) becomes one new P3QVerifier impl + one new
kind byte; no new precompile slot allocation.
Slots 0x012206 and 0x012207 previously listed in LP-220 drafts
for rollup-batch wrappers are freed: they remain owned by LP-0120
as the raw primitive verifier slots for the Corona and Magnetar
primitives (their original LP-0120 role, distinct from the rollup-
batch family ABI). No part of the LP-218/LP-220 P3Q surface
references those slots after this decomplection.
This honors LP-200 §"One and only one way to do everything." A
Solidity contract that needs Corona-kind verify for a rollup batch
calls P3Q.verify(0x02, sig, h, pk) at 0x012205. A Solidity
contract that needs the raw Corona primitive (a non-rollup-batch use
case) calls the LP-0120 primitive verifier at 0x012206 directly.
The P3Q family ABI is the single canonical entry point for
rollup-batch verification across all PQ kinds.
One slot, three primitive kinds dispatched on the leading byte:
kind=0x01 Pulsar | kind=0x02 Corona | kind=0x03 Magnetar |0x012205 | 0x012205 | 0x012205 |luxfi/pulsar/verify | luxfi/corona/sign.Verify (bespoke; NOT byte-equal to FIPS-204) | luxfi/magnetar/verify (FIPS-205 conformant) |P3Q.verify(0x01,sig,h,pk)→bool | P3Q.verify(0x02,sig,h,pk)→bool | P3Q.verify(0x03,sig,h,pk)→bool |P3QVerifier.GasCost()) | 50,000 | 5,000,000 | 30,000 |Three independent cryptographic foundations:
0x01 Pulsar): hardness reduces to Module-LWE + Module-SIS0x02 Corona): hardness reduces to Module-LWE0x03 Magnetar): hardnessreduces to collision resistance of the underlying hash function
A simultaneous break would require independent advances in
module-lattice cryptanalysis (Module-LWE, which both Pulsar and Corona
rest on) AND hash function collision attacks (Magnetar). Pulsar and
Corona use different Module-LWE parameter sets and codebases, so a
parameter-specific break need not hit both — but they are not
family-disjoint; only Magnetar provides cross-family independence.
This is the cross-family-maximum posture LP-217's PQ-heavy mode requires.
LP-220 introduces a Go-side common abstraction at
~/work/lux/standard/precompiles/p3q_family/:
type P3QVerifier interface {
// Verify returns true iff sig is a valid threshold signature
// from groupPubKey on messageHash.
Verify(sig []byte, messageHash [32]byte, groupPubKey []byte) bool
// GasCost returns the precompile gas charge for one verify.
GasCost() uint64
// Kind returns the calldata discriminator byte (0x01 Pulsar,
// 0x02 Corona, 0x03 Magnetar).
Kind() byte
// PrimitiveName returns "pulsar", "corona", or "magnetar".
PrimitiveName() string
}
Each P3QVerifier implementation registers at boot into a kind-byte
keyed dispatch table loaded by the single P3Q precompile at slot
0x012205. When the precompile is called, it:
1. Reads the leading kind byte from calldata.
2. Looks up the registered P3QVerifier for that kind.
3. Charges Verifier.GasCost() (per-kind, returned by the impl).
4. Calls Verifier.Verify(sig, messageHash, groupPubKey).
5. Returns the boolean result encoded as a 32-byte EVM ABI bool.
This is the single point of truth for the family — three Verifier
implementations, one slot, one dispatch path, one ABI shape, one
audit surface. Adding a fourth PQ-family kind (a hypothetical
Code-based threshold via HQC, for example) is one new P3QVerifier
impl and one new kind byte registered in the same dispatch table;
no new precompile slot, no new Solidity function shape, no new
audit boundary.
kind=0x02)0x012205 with leading kind=0x02. Same physical precompile
address as Pulsar (LP-218 kind 0x01) and Magnetar (kind 0x03).
Dispatch resolves to the Corona P3QVerifier impl.
// EVM precompile at slot 0x012205, kind = 0x02
bool ok = P3Q.verify(
0x02, // Corona kind
coronaSig, // serialized Corona threshold sig
messageHash, // H(prev_root || new_root || batch_hash)
groupPubKey // Corona group public key
);
Corona threshold signatures serialize per the Corona spec (not
byte-equal to any NIST standard; Corona is a Lux-defined Module-LWE
threshold scheme). The P3Q precompile reads:
5,000,000 gas — ~100× the Pulsar kind. Returned by Corona's
P3QVerifier.GasCost() at dispatch.
Per the parity audit's reference benchmark, Corona's verify cost
ratio versus Pulsar's verify cost is approximately 141× on CPU and
~10× on Blackwell sm_120. Corona is genuinely heavier than Pulsar
because Corona verify requires NTT inverse + polynomial multiplication
mod q with larger coefficients (33 KB sig vs 3.3 KB).
The gas calibration uses the CPU verify cost as the baseline (per
LP-218's calibration discipline) — at 1.6 ms CPU vs 80 μs for Pulsar,
the gas ratio is 20× the per-instruction rate. Adjusted for inherent
PQ-verify gas premium (LP-218 §"Honest cost comparison" notes the 10×
factor over BLS), the absolute charge lands at 5,000,000 gas.
This is the most expensive kind in the P3Q family; a rollup that
wants Corona-kind verify per batch should budget its gas accordingly.
The Go reference implementation at
~/work/lux/standard/precompiles/p3q_family/corona.go wraps
luxfi/corona/sign.Verify. The core is bespoke (NOT FIPS-204
byte-equal — Corona is a separate Module-LWE construction, not a NIST
standard).
GPU acceleration at ~/work/lux/accel/cuda/corona.cu provides the
batched-verify path used by LP-203 GPU-native verify for waves of
Corona sigs. The batched kernel amortizes the NTT setup across the
batch (~50 ms for N=64 on Blackwell vs N×15 ms = 960 ms for the
naive serial verifier — 19× speedup).
A rollup that wants Module-LWE parameter/implementation diversity
configures its sequencer to sign each batch with BOTH a Pulsar
threshold sig AND a Corona threshold sig (two Module-LWE schemes,
distinct parameter sets and codebases). The Z-Chain rollup contract
calls P3Q.verify(0x01, ...) and P3Q.verify(0x02, ...) at the same
precompile address 0x012205 in sequence. A parameter- or
implementation-specific break of Pulsar need not compromise Corona; the
rollup remains cryptographically anchored. For true family-disjoint
protection, add Magnetar (hash).
The cost: ~5,050,000 gas per batch (Pulsar kind 50k + Corona kind 5M).
Acceptable for value-bearing rollups (custody, treasury, regulated
securities); prohibitive for high-volume rollups (gaming, social).
kind=0x03)0x012205 with leading kind=0x03. Same physical precompile
address as Pulsar (kind 0x01) and Corona (kind 0x02). Dispatch
resolves to the Magnetar P3QVerifier impl.
// EVM precompile at slot 0x012205, kind = 0x03
bool ok = P3Q.verify(
0x03, // Magnetar kind
magnetarSig, // serialized SLH-DSA threshold sig
messageHash, // H(prev_root || new_root || batch_hash)
groupPubKey // Magnetar group public key
);
Magnetar threshold signatures are FIPS-205 SLH-DSA conformant at the
underlying-sig level, with a Lux-defined threshold-aggregation wrapper
per LP-181. The P3Q precompile reads the leading kind then the
Magnetar-specific bytes:
30,000 gas — comparable to the Pulsar kind's 50k. Returned by
Magnetar's P3QVerifier.GasCost() at dispatch.
SLH-DSA verify is fast — ~1.92 ms CPU, ~50 ms on Blackwell (Blackwell
hash-tree walking is bottlenecked by memory bandwidth, not compute).
The sign operation is dramatically slower; see "Parameter set choice"
below.
The 30,000 gas charge reflects the modest CPU verify cost.
SLH-DSA has two parameter sets at the 192-bit security level:
The Quasar 4-scheme matrix bench (2026-06-04, /tmp/quasar-matrix-bench)
measured Magnetar-192s as the wall-clock bottleneck for every
PQ-heavy cell — pq-heavy at N=64 on Spark = 1.33 s, blowing past the
1-s sub-second-finality target. Switching to 192f drops the
per-validator sign from 795 ms to 36 ms (22× faster) and restores the
sub-1-s budget.
Production lock: LP-220-conformant deployments MUST configure
Magnetar with SLH-DSA-192f. The threshold-init API at
~/work/lux/threshold/protocols/magnetar/ accepts a parameter-set
argument; pq-heavy chains pass magnetar.ParamSetFast192. Operators
choosing 192s for the 2.2× smaller signature opt into multi-second
finality and MUST run mode=PQ-strict (no Magnetar leg) for the
sub-second tier; 192s is reserved for long-archival anchoring use
cases that don't require fast finality.
Signing for either variant happens off-chain at the sequencer;
on-chain verification under P3Q is fast for both sets (~1.92 ms CPU).
The parameter set affects only the wall-clock of the per-validator
signing ceremony.
The Go reference implementation at
~/work/lux/standard/precompiles/p3q_family/magnetar.go wraps
luxfi/magnetar/verify. The core is FIPS-205 SLH-DSA conformant.
GPU acceleration at ~/work/lux/accel/cuda/magnetar.cu provides
batched verify for waves of SLH-DSA sigs. The batched kernel
amortizes the WOTS+ tree-walk across the batch (~80 ms for N=64).
A rollup that wants hash-based family independence from lattice
families configures its sequencer to sign each batch with a Magnetar
threshold sig in addition to Pulsar. The Z-Chain rollup contract
calls P3Q.verify(0x01, ...) then P3Q.verify(0x03, ...) at slot
0x012205. SLH-DSA security relies only on the collision resistance
of its underlying hash function (SHA-256 or SHA-3 configurations);
it is immune to advances in lattice cryptanalysis and would survive
even a complete break of the Module-LWE family (both Pulsar and Corona).
Magnetar is the last-line PQ — the kind of choice for
sovereign-archival commitments, long-tenor custody attestations, and
regulator-facing audit trails. The cost (30k gas per verify + 35 KB
signature size) is reasonable for low-frequency, high-value batches.
A rollup using PQ-heavy mode (LP-217 / LP-218 `CertModeFloor =
0x03`) submits four verifications to its rollup contract — one BLS
classical leg plus three P3Q calls at the same slot, each with a
different kind:
0x0a (or pairing precompile) | Pairings (classical) | 5,000 |0x012205 kind=0x01 | Module-LWE | 50,000 |0x012205 kind=0x02 | Module-LWE | 5,000,000 |0x012205 kind=0x03 | Hash-based | 30,000 |Total: 5,085,000 gas per batch in the cross-family-maximum posture.
A break of any single family does not compromise the rollup batch
because three independent verifications remain. This is the security
ceiling the LP-217 PQ-heavy mode codifies, and LP-220 supplies the
two additional PQ-family kinds on the unified P3Q slot to make it
operationally callable.
The composition is additive: a PQ-heavy rollup contract calls the
BLS precompile and three P3Q.verify(kind, ...) calls at slot
0x012205 in sequence inside its validateBatch function. If any
returns false, the batch is rejected.
For value-bearing rollups that don't need full cross-family-maximum
but do want PQ insurance, LP-217's PQ-strict mode requires only
BLS + Pulsar + Corona. The rollup contract calls
P3Q.verify(0x01, ...) + P3Q.verify(0x02, ...); gas cost
~5,050,000 per batch.
A rollup operator optimizing for cost may pin strict-PQ-strict
(LP-217 0x11 — drops BLS but keeps Pulsar + Corona). Gas cost
reduces to ~5,050,000 - 5,000 = ~5,045,000 per batch. Marginal
savings; not the optimization point.
A high-throughput rollup pinning PQ-fast (LP-218 `CertModeFloor =
0x01`) verifies only Pulsar: gas cost ~50k per batch. The Corona and
Magnetar kinds are not called.
Honest summary of the verify-cost landscape across the P3Q kinds plus
the classical baselines:
ecrecover (secp256k1) | 0x01 | 3,000 | ~10 μs | ~3 μs |0x012205 0x01 | 50,000 | ~80 μs | ~50 μs |0x012205 0x03 | 30,000 | ~1.92 ms | ~50 ms |0x012205 0x02 | 5,000,000 | ~1.6 ms | ~15 ms |The Corona kind is the bottleneck. Its CPU verify time (1.6 ms) is
comparable to Magnetar's (1.92 ms), but its gas charge is 167×
Magnetar's. Why the disparity?
Two factors:
1. GPU acceleration ratio. Magnetar verify on Blackwell is
dominated by memory bandwidth (~50 ms for the WOTS+ tree-walk);
Corona verify on Blackwell is compute-bound on the NTT and
parallelizes harder (~15 ms). The GPU win is larger for
Corona than Magnetar in absolute terms — Corona's CPU-to-GPU
ratio is ~107× (1.6 ms / 15 ms = 107), Magnetar's is ~38×
(1.92 ms / 50 ms = 38).
2. Validator hardware assumption. Gas calibration assumes a
CPU-class verifier (per LP-218's calibration discipline —
validators are not required to have Blackwell GPUs). Under that
assumption, Corona's effective per-validator cost is closer to
the audit's measured 1.6 ms ≈ 141× the Pulsar CPU verify time.
The gas charge reflects this: 5M gas vs Pulsar's 50k gas
≈ 100× ratio (rounded for clean accounting).
Operators with all-Blackwell validator sets may petition for a
Blackwell-discounted Corona gas charge in a future amendment;
LP-220 does not codify it. The conservative CPU-baseline gas charge
applies until validator-hardware uniformity is established.
The gas cost ladder makes the LP-217 mode tradeoff explicit:
The 100× cost cliff between PQ-fast and PQ-strict is intentional —
Corona's gas charge naturally prices cross-family insurance. A
rollup that does not need Module-LWE-parameter-diversity (not family-independence pays only
PQ-fast prices; a rollup that does, pays PQ-strict prices.
This is the canonical Pareto frontier the LP-217 cert profile modes
codify. LP-220 just supplies the additional P3QVerifier kinds on
the unified P3Q slot 0x012205 that make each mode operationally
callable from a rollup contract.
kind byte | caller passes kind not in {0x01, 0x02, 0x03} | P3Q returns false; rollup contract may revert | caller fix; activation predates any live rollup so no backwards-compat layer |P3Q.verify(0x02,...) returns false; rollup contract may revert | client re-submits with valid sig; sequencer fix |P3Q.verify(0x03,...) returns false; rollup contract may revert | client re-submits with valid sig |P3Q.verify(0x02,...) returns false for all subsequent batches until rotation tx lands | sequencer rotation tx updates group pubkey; backfill batches re-verify |P3QVerifier registration | validator fails to validate batch; rollback to LP-201 normal-path | validator config audit; operator rebuilds with full P3Q dispatch table |0x01 verifies |0x03 verifies |~/work/lux/accel/cuda/ kernels |0x012205 — LP-218 specifies kind 0x01 (Pulsar) and the unified ABI; LP-220 specifies kinds 0x02 (Corona) and 0x03 (Magnetar) on the same slot |CertModeFloor |0x012204 (raw Pulsar), 0x012206 (raw Corona), 0x012207 (raw Magnetar) for non-rollup-batch use; LP-218/LP-220's P3Q family lives exclusively at 0x012205. |LP-220 completes the on-chain PQ-verify surface. There is no fourth
PQ family in the current Quasar stack — Code-based KEMs (HQC) are
KEMs, not signature schemes; their on-chain role per LP-0120 is
key encapsulation, not batch authentication. A hypothetical
Code-based-signature PQ family (Hamming-Quasi-Cyclic-Sig, McEliece-
Sig variants) would add a fourth kind byte on the same P3Q slot
0x012205 if it ever entered the Lux stack; LP-220's P3QVerifier
framework would absorb it without a new slot allocation.
activates: 2025-12-25T16:20:00-08:00
activates-unix: 1766708400
P3Q at slot 0x012205 with kinds 0x02 (Corona) and 0x03 (Magnetar)
is callable from the genesis block of the new final Lux network.
Rollup deployments that target PQ-strict or PQ-heavy mode
(CertModeFloor = 0x02 or 0x03) call P3Q.verify(kind, ...) from
height 0 with the appropriate kind byte.
0x01's underlyingprimitive)
0x03's underlyingprimitive)
kinds LP-220 supplies)
that determine which P3Q kinds a rollup contract calls)
P3Q slot 0x012205 and kind 0x01 Pulsar; LP-220 specifies kinds
0x02 Corona and 0x03 Magnetar on the same slot)
via the P3Q family at each participating rollup's chosen kind set)
Slots 0x012204, 0x012206, 0x012207 remain owned by LP-0120
for the raw Pulsar / Corona / Magnetar primitive verifiers (the
non-rollup-batch use case). The LP-218/LP-220 P3Q family lives
exclusively at 0x012205.
0x03 verifier conformant)0x01 verifier conformant)