Corona — Module-LWE Threshold Signatures (Ringtail-Derived)
Lux's foundational post-quantum threshold-signature scheme and its native verifier precompile — a two-round Module-LWE construction derived from Ringtail (ePrint 2024/1113). The first of the Lux lattice threshold family by development age. Corona and Pulsar (LP-4450) are both Module-LWE; they differ by base signature (Raccoon vs ML-DSA), parameter regime, and codebase — the permissionless lifecycle is shared across all Lux legs, not a distinguisher. Raw precompile 0x012206 per LP-0120.
Implementation status (code-audited 2026-07-03): SHIPPED corona repo: Module-LWE two-round threshold (Ringtail/Raccoon line), slot 0x012206.
Abstract
Corona is Lux's two-round Module-LWE threshold signature scheme —
the foundational member of the Lux lattice threshold-signature family by
development age — together with its native EVM verifier precompile.
Corona is Lux's first-party fork of Ringtail (IACR ePrint
2024/1113), parameterised over a module of dimension M = 8 × N' = 7
(a public matrix A ∈ R_q^{8×7} over the ring
R_q = Z_q[X]/(X^256 + 1), modulus q = 0x1000000004A01). Corona was
the first Lux PQ threshold construction implemented; Pulsar (LP-4450)
followed, and Magnetar (LP-4540) added a hash-based cross-family leg.
Corona and Pulsar are both Module-LWE threshold schemes. They are not "rank-1 vs rank-k": both operate over a module of rank greater than one. They differ instead by parameter regime, lifecycle, and codebase. Corona is the Ringtail-derived scheme: its own large parameter set, ~33–35 KB signatures, and a permissionless, leaderless distributed key generation built for validator sets that change over time. Pulsar is the production-tuned variant whose parameters make it byte-equal to FIPS 204 ML-DSA-65 (~3.3 KB signatures) for FIPS interoperability. (As a general lattice fact, Ring-LWE is Module-LWE at rank one — but that is not Corona's instantiation; Corona is a genuine module construction at rank ~7–8.)
Corona produces a compact two-round threshold signature (one
message-independent precomputation round, one signing round, then a
non-interactive combine) and is trustless at both ends: a
genuinely dealerless Pedersen-VSS distributed key generation in which
no party ever holds the master secret (keyera.BootstrapPedersen, built
on the shared luxfi/dkg library), and no-reconstruct t-of-n
signing in which the secret is never reassembled — not even transiently
at a coordinator (audited and certified in Corona v0.10.0). Hardness is
Module-LWE with Module-SIS commitments. It is verified by the raw
precompile at 0x012206 (LP-0120): signature ~33–35 KB, verification
~1.6 ms; the combined output verifies under the stock Ringtail/Raccoon
verifier (no bespoke threshold verifier). Corona is the natively
dealerless intra-lattice diversity leg of the Aurora certificate
profile (LP-4900) and the Polaris profile (LP-4910), composed alongside
Pulsar under Quasar.
Corona extends the Ringtail two-round lattice threshold construction (IACR ePrint 2024/1113) to public, permissionless validator sets. The academic construction assumes a fixed, known set of parties; Corona's contribution is making it work when validators join and leave permissionlessly — leaderless distributed key generation with no trusted dealer, dynamic resharing across a changing validator set, and on-chain Q-Chain ceremony binding. That is the engineering a public chain needs and an academic threshold scheme does not provide.
Use cases: Quasar consensus validator threshold sigs; threshold wallets; DAO governance; cross-chain bridges; enterprise custody.
Motivation
The Lux PQ-finality stack needs more than one threshold signature primitive so that a structural advance against any single construction does not by itself forge a QuasarCert. Corona is the Ringtail-derived Module-LWE scheme: a well-studied lattice (the same cyclotomic-ring NTT structure as ML-KEM / ML-DSA) and the historical starting point for Lux threshold signing. It is profile-selectable, not the default floor — Pulsar (Module-LWE tuned to FIPS 204 byte-equality) is the strict-PQ floor; Corona adds intra-lattice diversity in Aurora, and Magnetar adds true cross-family (hash) diversity in Polaris. Because Corona and Pulsar are both Module-LWE, their composition in Aurora is implementation-level defence-in-depth (distinct parameters, codebases, samplers), not family-disjoint hardness — see the Rationale.
Specification
Scheme
- Hardness. Module-LWE (with Module-SIS commitments) over the ring
R_q = Z_q[X]/(X^256 + 1), with a module of dimensionM = 8 × N' = 7(public matrixA ∈ R_q^{8×7}; secrets ∈ R_q^{7}; errore ∈ R_q^{8};b = A·s + e). NIST Category 1. - Rounds. Two-round signing: Round 1 (message-independent precomputation / commitment), Round 2 (signing share over the message), then a non-interactive Combine into a single aggregate. Two rounds is optimal — no dealer-free threshold scheme can do better.
- DKG (genuinely dealerless). Permissionless, leaderless distributed
key generation via Pedersen-VSS (
keyera.BootstrapPedersen), built on the shared no-reconstruct DKG libraryluxfi/dkg(VSS share-dealing, equivocation gate, identifiable-abort blame) and anchored to the Q-Chain ceremony root, with a production lifecycle (generation, resharing, snapshots). No party ever holds the master secrets: each party aggregates only its own share, and the group key is finalised from the public aggregateb = A·s + e″(Path-(a) noise flooding, domain tagCORONA-BOOTSTRAP-PEDERSEN-NOISEFLOOD-v1). Corona is the natively-dealerless leg of the stack. - No-reconstruct signing. The
t-of-nsigning path never forms the full secrets = Σ_j λ_j·s_j, not even transiently at the combiner: each signer emits only a masked partial,SignFinalizesums the masked partials (z_sum = Σ_j z_j), andsappears only as the coefficient of the public challengecinside the aggregate. Certified in Corona v0.10.0 by structural (AST call-graph), behavioural (no secret-bearing wire field), and independent-verifier gates (threshold/no_reconstruct_sign_test.go). Dealerless keygen + no-reconstruct signing make Corona a strict trustless lane, eligible for the strict-PQ posture. - Verifier. Raw precompile
0x012206(LP-0120). Under the P3Q unified rollup-batch verifier (LP-218 / LP-220) Corona is kind byte0x02at slot0x012205; the raw0x012206path remains for non-rollup-batch use. - Sizes (LP-0120). Signature ~33–35 KB; verification ~1.6 ms. Corona is not byte-equal to any FIPS standard — that byte-equality is a property of Pulsar (LP-4450), not Corona.
The canonical parameter set is fixed by the reference implementation and
its KAT vectors (luxfi/corona, a fork of luxfi/ringtail):
ring R_q = Z_q[X]/(X^256 + 1) (degree 256), module dimensions
M = 8, N' = 7, modulus q = 0x1000000004A01 (a 48-bit NTT-friendly
prime), key-generation Gaussian width σ_E = 6.108, challenge ternary
weight κ = 23, rejection bound B = 2^48.6. This LP fixes the role,
slot, rounds, DKG model, family relationships, and the precompile ABI
below; the sampler internals track the reference code.
Precompile address
0x012206
The raw verifier slot 0x012206 is owned by LP-0120. (Historical note:
the original LP-7324 precompile shipped at the EVM-style string address
0x020000000000000000000000000000000000000B; the canonical raw-slot
address is 0x012206. See Provenance.)
Input format (packed)
| Offset | Length | Field | Description |
|---|---|---|---|
| 0 | 4 | threshold | Required signers (big-endian uint32) |
| 4 | 4 | totalParties | Total participants (big-endian uint32) |
| 8 | 32 | messageHash | Hash of message |
| 40 | var | signature | Corona threshold signature (~33 KB per LP-0120) |
Minimum size: 40 B + signature.
Signature contents
Round 1 commitments (all signers) + Round 2 lattice-based shares + aggregated threshold signature + participant bitmap.
Output
32-byte word: 0x...0001 valid, 0x...0000 invalid.
Gas
gas = 150,000 + (totalParties × 10,000)
Examples: 3-of-5 → 200,000 gas; 10-of-15 → 300,000; 67-of-100 → 1,150,000.
150K base for Module-LWE verification + 10K per party (commitment verification, share validation, aggregation).
Solidity interface
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ICorona {
function verifyThreshold(
uint32 threshold,
uint32 totalParties,
bytes32 messageHash,
bytes calldata signature
) external view returns (bool valid);
}
library CoronaLib {
ICorona constant CORONA = ICorona(0x0000000000000000000000000000000000012206);
function verifyOrRevert(
uint32 threshold,
uint32 totalParties,
bytes32 messageHash,
bytes calldata signature
) internal view {
require(
CORONA.verifyThreshold(threshold, totalParties, messageHash, signature),
"Corona: invalid threshold signature"
);
}
function estimateGas(uint32 totalParties) internal pure returns (uint256) {
return 150_000 + (uint256(totalParties) * 10_000);
}
}
abstract contract CoronaVerifier {
ICorona internal constant corona = ICorona(0x0000000000000000000000000000000000012206);
modifier validCoronaSignature(
uint32 threshold,
uint32 totalParties,
bytes32 messageHash,
bytes calldata signature
) {
require(
corona.verifyThreshold(threshold, totalParties, messageHash, signature),
"Invalid Corona threshold signature"
);
_;
}
}
Example
contract ThresholdWallet is CoronaVerifier {
uint32 public threshold = 2;
uint32 public totalOwners = 3;
function withdraw(
address to,
uint256 amount,
uint256 nonce,
bytes calldata thresholdSig
) external {
bytes32 txHash = keccak256(abi.encode(to, amount, nonce));
CoronaLib.verifyOrRevert(threshold, totalOwners, txHash, thresholdSig);
payable(to).transfer(amount);
}
}
Rationale
Corona vs other threshold schemes
| Scheme | Post-Quantum | Rounds | Trusted Dealer | Hardness |
|---|---|---|---|---|
| Corona | Yes | 2 | No | Module-LWE (Ringtail-derived; M=8, N'=7) |
| Pulsar (LP-4450) | Yes | 2 | No | Module-LWE (FIPS 204 byte-equal) |
| Magnetar (LP-4540) | Yes | — | No | Hash (SLH-DSA, FIPS 205) |
| FROST | No | 2 | No | Discrete Log |
| CGGMP21 | No | 5+ | No | Discrete Log |
| BLS | No | 1 | Yes | Pairing |
Corona and Pulsar (LP-4450) are both Module-LWE constructions; they differ by base signature (Raccoon vs ML-DSA), parameter regime, and codebase — not by module rank, and not by lifecycle (the permissionless, leaderless, dynamically-resharable adaptation is shared across Corona, Pulsar, and Magnetar). Pulsar ‖ Corona is therefore implementation-level defence-in-depth (different code, different parameter sets, different rejection samplers), not family-disjoint hardness — a structural advance against the cyclotomic-ring module lattice problem could in principle touch both legs. Magnetar (hash) is the only true cross-family leg. Among classical comparators, FROST cost is ~50K + 5K per signer; Corona is the price of quantum resistance on the same dealer-free, two-round profile.
Provenance and lineage
Corona is Lux's first-party fork of Ringtail (IACR ePrint 2024/1113,
IEEE S&P 2025), which thresholdizes the Raccoon signature family — a
masking-friendly Fiat–Shamir-with-aborts Module-LWE scheme, distinct from
Dilithium/ML-DSA, with a 48-bit modulus chosen for threshold-friendly
rounding. Ringtail builds on Threshold Raccoon (EUROCRYPT 2024) and
adapts the random-linear-combination and full-rank-commitment techniques
of MuSig-L (CRYPTO 2022) and FROST; it is the standard-assumption
counterpart of EKT (Espitau–Katsumata–Takemure, CRYPTO 2024), which
relied on the non-standard algebraic one-more LWE assumption. Corona's
parameters are bit-identical to Ringtail's 128-bit set (paper Table 2):
ring degree N=256, q = 2^48 + 2^14 + 2^11 + 2^9 + 1 = 0x1000000004A01,
(N', M, D̄) = (7, 8, 48), κ=23, σ=6.108. Its full proof reduces to
self-target Module-SIS and Hint-Module-LWE, both reducing to standard
Module-LWE / Module-SIS.
The name "Ringtail" is the raccoon-family mammal (the scheme
thresholdizes Raccoon) — not a reference to Ring-LWE. Corona is
Module-LWE at module rank 7 (a genuine matrix A ∈ R_q^{8×7}); it would
coincide with Ring-LWE only in the degenerate rank-1 case, which it does
not use.
A Corona signature is therefore a Raccoon signature and does not verify under FIPS 204. The Corona↔Pulsar split is at the base signature: Corona thresholdizes Raccoon (standard-assumption, practical, not FIPS-compatible), whereas Pulsar (LP-4450) thresholdizes ML-DSA/Dilithium to stay byte-equal to FIPS 204 — the harder, FIPS-compatible target, since ML-DSA's rejection sampling and hint bits do not split cleanly across parties.
Two rounds
Optimal — no threshold scheme can do better than 2 rounds without a trusted dealer.
Quasar integration
Corona provides the intra-lattice diversity leg of Quasar certificate profiles: Aurora (LP-4900) is Pulsar ‖ Corona, Polaris (LP-4910) is Pulsar ‖ Corona ‖ Magnetar. The Pulsar leg (LP-4450) is the PQ floor; Corona is composed alongside it by reference under Quasar (LP-0110), never redefined there.
Backwards Compatibility
New precompile; no compatibility issues. Migration from classical threshold (FROST/CGGMP21) — dual signatures during transition, then migrate keys to a PQ scheme (Corona or Pulsar), then deprecate the classical leg.
Test Cases
Test 1 — Valid 2-of-3. Input: threshold=2, totalParties=3,
messageHash = keccak256("Test message for threshold signature"),
valid signature from 2 of 3. Output: 0x...0001. Gas: 180,000.
Test 2 — Insufficient signers (1-of-3). Threshold=2 but signature
from 1 party. Output: 0x...0000.
Test 3 — Invalid share. Threshold=2, totalParties=3, one corrupted
share. Output: 0x...0000.
Test 4 — Large threshold (67-of-100). Output: 0x...0001.
Gas: 1,150,000.
Reference Implementation
Stack:
Solidity Interface
ICorona.sol → CoronaLib.sol → CoronaVerifier.sol
│ staticcall
EVM Precompile (Go)
precompile/corona/contract.go → Run() → Verify()
│ calls
Corona Protocol (Go)
corona/ → 2-round threshold + DKG + Shamir over Module-LWE
│ uses
Lattice Primitives
lattice/ → Module-LWE, Module-SIS, NTT (matrix A ∈ R_q^{8×7})
EVM precompile — ~/work/lux/precompile/corona/
| File | Lines | Purpose |
|---|---|---|
contract.go | 257 | Precompile at raw slot 0x012206 |
module.go | 50 | EVM registration |
contract_test.go | 236 | Test suite |
ICorona.sol | 288 | Solidity interface + library |
README.md | 501 | Docs |
Corona protocol — ~/work/lux/corona/
| Path | Purpose |
|---|---|
keyera/bootstrap_pedersen.go | Dealerless Pedersen-VSS key-era bootstrap (BootstrapPedersen) — no party holds s; share-dealing + blame via luxfi/dkg |
keyera/ | Key-era lifecycle (generation, resharing, snapshots; Path-(a) noise-flood finalize) |
threshold/threshold.go | t-of-n threshold signer over Module-LWE (ring via lattice/v7) |
threshold/no_reconstruct_sign_test.go | No-reconstruct certification (AST call-graph + behavioural + independent-verifier gates) |
sign/sign.go | 2-round signing; SignFinalize sums masked partials (no secret s formed) |
sign/verify.go | Non-destructive stock verification |
sign/aggregate.go | Threshold aggregation |
sign/types.go | Signature + public key types |
share/lagrange.go | Lagrange coefficients (λ_j) for masked-share aggregation |
network/ | Party communication |
Core functions:
// Dealerless key-era bootstrap (no trusted dealer; no party ever holds s):
func BootstrapPedersen(suite hash.HashSuite, t int, validators []string,
groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader,
) (*KeyEra, *BootstrapTranscript, error)
// No-reconstruct t-of-n signing (combiner sums masked z partials; s never formed):
func SignRound1(share *Share, message []byte) (*Round1Output, error)
func SignRound2(share *Share, round1Outputs []*Round1Output) (*SignatureShare, error)
func SignFinalize(z map[int]Vector, ...) (Signature) // z_sum = Σ_j z_j
func Verify(groupKey *GroupKey, message []byte, sig *Signature) bool
Lattice primitives — ~/work/lux/lattice/
| Path | Purpose |
|---|---|
ring/ring.go | Cyclotomic ring R_q = Z_q[X]/(X^256 + 1) |
ring/ntt.go | Forward NTT |
ring/intt.go | Inverse NTT |
mlwe/sample.go | Discrete Gaussian sampling |
mlwe/keygen.go | Module-LWE key generation (matrix A ∈ R_q^{8×7}) |
mlwe/commit.go | Module-LWE commitment (b = A·s + e) |
sis/hash.go | Module-SIS commitment hash |
params/ | Security parameters |
Module-LWE parameters (NIST Category 1; the real symbol names from
luxfi/corona sign/config.go):
const (
M = 8 // Module rows (public matrix A ∈ R_q^{M×N})
N = 7 // Module cols (secret s ∈ R_q^{N})
LogN = 8 // Ring degree 2^LogN = 256 → R_q = Z_q[X]/(X^256+1)
Q = 0x1000000004A01 // 48-bit NTT-friendly prime (q ≡ 1 mod 2·256)
)
// Gaussian width σ_E = 6.108, challenge ternary weight κ = 23,
// rejection bound B ≈ 2^48.6 (Ringtail 128-bit set, paper Table 2).
// LWE dimension d = M·256 = 2048; BKZ blocksize β ≈ 430.
// Classical ~2^142; quantum (Grover-enhanced sieving) ~2^130.
Note: in sign/config.go the symbol N = 7 is the module-column count
(not the ring degree); the ring degree is 2^LogN = 256. Earlier drafts
of this LP overloaded N for both — corrected here to the shipped names.
Quasar integration — ~/work/lux/consensus/protocol/quasar/
| File | Purpose |
|---|---|
epoch.go | EpochManager (Corona key rotation) |
corona.go | Threshold Corona signing for finality certs |
hybrid_consensus.go | Multi-cert finality (BLS + lattice legs) |
epoch_test.go | 8 epoch tests |
EpochManager:
type EpochManager struct {
currentEpoch uint64
currentKeys *EpochKeys
lastKeygenTime time.Time
epochHistory map[uint64]*EpochKeys // cross-epoch verify
historyLimit int // default 3
currentValidators []string
threshold int
}
const (
MinEpochDuration = 1 * time.Hour // rate limit
MaxEpochDuration = 24 * time.Hour // forced rotation
HistoryLimit = 3 // epochs preserved
)
func (em *EpochManager) VerifySignatureForEpoch(message string, sig *Signature, epoch uint64) bool
Tests passing
- Precompile: valid threshold sig, insufficient threshold rejection, invalid share detection, large threshold (10-of-15), gas verification, edge cases.
- Protocol: 2-round correctness, t-of-n aggregation, non-malleability, cross-party verification.
- Epoch: creation/rotation, cross-epoch verify, rate-limit enforcement, history pruning.
Machine-checked proofs
proofs/lean/Crypto/Corona.lean (Module-LWE threshold correctness) over
proofs/lean/Crypto/Threshold_Lagrange.lean (shared algebraic core).
Repositories
| Component | Repository |
|---|---|
| Precompile | github.com/luxfi/precompile/corona/ (raw slot 0x012206) |
| Corona library | github.com/luxfi/corona/ (v0.10.1) |
| Shared no-reconstruct DKG | github.com/luxfi/dkg/ (v0.2.0 — Pedersen-VSS + blame) |
| Lattice primitives | github.com/luxfi/lattice/v7 (v7.1.0) |
| C/C++/GPU backends | luxcpp/crypto/corona/ (CUDA / Metal / WGSL) |
| Epoch / Quasar finality | github.com/luxfi/consensus/protocol/quasar/ |
| Solidity interface | ~/work/lux/standard/contracts/precompiles/corona/ |
Security Considerations
Quantum
Module-LWE hardness — no efficient quantum algorithm. Module-SIS for commitments — also quantum-resistant. NIST Category 1 (~128-bit classical, ~130-bit quantum); worst-case lattice reduction over the module.
Threshold
Safety: adversary controlling < threshold parties learns NOTHING about
the private key; corrupted shares don't help forge. Liveness: any
threshold parties produce a signature; robust against n − threshold
offline.
Distributed key generation (dealerless) and no-reconstruct signing
Keygen (keyera.BootstrapPedersen, over luxfi/dkg):
1. Each party deals a Pedersen-VSS sharing of its own contribution.
2. Equivocation gate + Pedersen verification; identifiable-abort blame.
3. Each party aggregates ONLY its own share s_j of the master s.
4. Group key from the public aggregate b = A·s + e″ (noise-flooded).
→ No party ever holds s (no-reconstruct keygen).
Signing (no-reconstruct):
1. Each signer emits a masked partial z_j (its share + Lagrange coeff λ_j).
2. SignFinalize sums masked partials: z_sum = Σ_j z_j.
3. s = Σ_j λ_j·s_j appears ONLY as the coefficient of the public c.
→ No party/coordinator ever forms s, even transiently.
Neither keygen nor signing reconstructs the master secret. This is the
strict trustless-lane property certified in Corona v0.10.0
(threshold/no_reconstruct_sign_test.go: AST call-graph + behavioural +
independent-verifier gates, with negative controls).
Side-channel
Constant-time lattice ops; blinded share generation; secure memory clearing; no timing-dependent branches.
Quantum attack scenarios
| Attack | Classical | Post-Quantum |
|---|---|---|
| Break one share | Safe (DL) | Safe (Module-LWE) |
| Break threshold | Safe (DL) | Safe (Module-LWE) |
| Break commitment | Safe (hash) | Safe (Module-SIS) |
| Forge signature | Safe (DL) | Safe (Module-LWE) |
| Shor's algorithm | Breaks DL | Module-LWE unaffected |
Key management
Shares stored encrypted at rest; never combined; rotated periodically (forward security); backed up redundantly (liveness); HSM/TEE when possible.
Epoch-based rotation
| Constant | Value | Purpose |
|---|---|---|
MinEpochDuration | 1 hour | Rate limit |
MaxEpochDuration | 24 hours | Forced rotation |
HistoryLimit | 3 epochs | Cross-epoch verify window |
Triggers: validator-set change (rate-limited to 1/hour); forced
expiration at 24 h; manual RotateEpoch(validators, force=true).
Counter is uint64 → 2.1 trillion years at 1/hr.
func (em *EpochManager) RotateEpoch(validators []string, force bool) error {
if !force && time.Since(em.lastKeygenTime) < MinEpochDuration {
return ErrTooSoon
}
if time.Since(em.lastKeygenTime) > MaxEpochDuration {
force = true
}
groupKey, shares := coronaDKG(validators, threshold)
em.pruneHistory()
return nil
}
Multi-leg certificate finality
Corona is one lattice leg of a Quasar certificate profile (BLS fast lane
- Pulsar PQ floor + Corona intra-lattice diversity). Every configured leg must validate for finality:
func ValidateBlock(block *Block) bool {
if !bls.Verify(block.BLSSignature) { return false }
if !corona.Verify(block.CoronaSignature) { return false }
return true
}
Secure contract usage
// GOOD: verify before state changes
function withdraw(bytes calldata sig) external {
require(corona.verify(sig), "Invalid sig");
// safe state modification
}
// BAD: state change before verification (reentrancy)
function withdraw(bytes calldata sig) external {
updateState();
require(corona.verify(sig), "Invalid sig");
}
Replay-protected vault example:
contract QuantumSafeVault is CoronaVerifier {
uint32 public threshold;
uint32 public totalOwners;
mapping(bytes32 => bool) public usedNonces;
function withdraw(
address to,
uint256 amount,
uint256 nonce,
bytes calldata sig
) external nonReentrant {
bytes32 messageHash = keccak256(abi.encode(
"CoronaVault-v1",
block.chainid,
address(this),
to, amount, nonce
));
require(!usedNonces[messageHash], "Nonce already used");
usedNonces[messageHash] = true;
CoronaLib.verifyOrRevert(threshold, totalOwners, messageHash, sig);
(bool success,) = to.call{value: amount}("");
require(success, "Transfer failed");
}
}
Network usage map
| Component | Location | Usage |
|---|---|---|
| Precompile | precompile/corona/ | On-chain verification |
| Corona library | corona/ | Threshold signing |
| Lattice primitives | lattice/ | Module-LWE, Module-SIS, NTT |
| Epoch management | consensus/protocol/quasar/epoch.go | Validator key rotation |
| Quasar finality | consensus/protocol/quasar/hybrid_consensus.go | Multi-cert finality |
| C-Chain | node/vms/coreth/ | Smart-contract verification |
| Bridge custody | node/bridges/ | PQ bridge security |
| Warp messaging | node/vms/platformvm/warp/ | Cross-chain (L1 / L2) PQ sigs |
Economic Impact
Gas comparison:
| Scheme | 3-of-5 | 10-of-15 | Security |
|---|---|---|---|
| Corona | 200,000 | 300,000 | Post-quantum |
| FROST | 75,000 | 125,000 | Classical |
| CGGMP21 | 125,000 | 225,000 | Classical |
2-3× higher gas for quantum security. Worth it for high-value assets (> $1M), long-term storage (> 5y), critical infrastructure, regulated PQ. For Quasar validators using the Aurora/Polaris profiles: ~200K gas per Corona verification per block, $0.01-$0.10 cost.
Implementation Notes
import "github.com/luxfi/corona/sign"
func verifyCorona(threshold, totalParties uint32, msgHash []byte, sig []byte) bool {
return sign.Verify(sig, msgHash, threshold, totalParties)
}
Corona package: 2-round signing, distributed keygen, Shamir, NTT-based polynomial ops, network stack.
Parameter validation: threshold > 0; threshold ≤ totalParties;
totalParties ≥ 2; recommended threshold ≥ totalParties/2 + 1
(honest majority); maximum totalParties ≤ 1000 (practical).
Byzantine threshold: threshold > totalParties × 2/3; liveness:
totalParties − threshold < totalParties/3. Optimal: 67-of-100.
Provenance
Corona is the first Lux lattice threshold scheme by development age. This LP is the canonical, comprehensive Corona specification (scheme + precompile).
Renumbering history. The construction was first specified as LP-7324
("Corona Threshold Signature Precompile") and shipped at the EVM-style
string address 0x020000000000000000000000000000000000000B. During the
unified-4xxx crypto consolidation it was briefly mis-titled "Pulsar" at
LP-4440 (a labelling error: the body was always two-round /
luxfi/corona / 0x012206 Corona content). LP-4440 is now corrected to
its true identity — Corona — and the canonical raw verifier slot is
0x012206 (owned by LP-0120). A separate, clean Corona algorithm stub
previously lived at LP-4470; LP-4440 is now the single canonical Corona
LP. The LP-7324 slot remains a permanent pointer to LP-4440.
Family relationships. Corona and Pulsar (LP-4450) are both Module-LWE threshold signatures; they differ by parameter regime, lifecycle, and codebase, not by module rank. Corona is the Ringtail-derived scheme (large ~33–35 KB signatures, permissionless leaderless DKG); Pulsar is the production-tuned variant whose parameters make it byte-equal to FIPS 204 ML-DSA-65 (~3.3 KB) and is the strict-PQ floor. Corona and Magnetar (LP-4540, hash-based) are profile-selectable diversity legs. The three are composed by the Aurora (LP-4900, Pulsar ‖ Corona) and Polaris (LP-4910, Pulsar ‖ Corona ‖ Magnetar) certificate profiles under Quasar.
Academic origin. Corona is Lux's first-party fork of Ringtail
(IACR ePrint 2024/1113), a practical two-round Module-LWE threshold
signature. Corona is Lux's distinct production scheme: cite ePrint
2024/1113 for the construction and luxfi/corona for the scheme — they
are separate artifacts. (Earlier drafts mislabelled the academic paper
as "Corona" and mis-attributed it to Pulsar; corrected here — Corona is
the Ringtail-derived fork.)
References
- Boschini, Kaviani, Lai, Malavolta, Takahashi, Tibouchi, Ringtail: Practical Two-Round Threshold Signatures from LWE (IACR ePrint 2024/1113; IEEE S&P 2025). The two-round Module-LWE threshold construction Corona forks.
- Module-LWE: Langlois, Stehlé, Worst-Case to Average-Case Reductions for Module Lattices (Designs, Codes and Cryptography 2015).
- Implementation:
github.com/luxfi/corona(v0.10.2, a fork ofgithub.com/luxfi/ringtail),precompile/corona/. - Shared base:
github.com/luxfi/dkg(the one no-reconstruct Pedersen-VSS DKG library) andgithub.com/luxfi/mlwe(v0.2.1) are both consumed by Corona and Pulsar (LP-4450) — the one-and-one-way DRY base. As of corona v0.10.2 Corona consumesmlwe/transcript(SP 800-185 cSHAKE/KMAC) andmlwe/share(Shamir Lagrange + prime field) — a byte-preserving de-dup (19/19 KAT oracles byte-identical). Corona's ring arithmetic staysgithub.com/luxfi/lattice/v7(its 48-bit Ringtail ring); only mlwe's FIPS-204 23-bit ring (mlwe/ring/mldsa) is Pulsar-specific — wrong modulus for Corona's 48-bit ring, so Corona does not consume that one package. - LP-0120: Quasar mainnet defaults — owns raw slot
0x012206. - LP-4400: ML-DSA family root (non-threshold PQ sig).
- LP-4450: Pulsar — Module-LWE threshold ML-DSA, FIPS 204 byte-equal (the production-tuned Module-LWE sibling of Corona).
- LP-4540: Magnetar — hash-based threshold SLH-DSA (cross-family leg).
- LP-4900 / LP-4910: Aurora / Polaris certificate profiles.
- LP-4710: FROST (classical threshold for comparison).
- LP-4720: CGGMP21 (classical ECDSA threshold).
Copyright
Copyright and related rights waived via CC0.