Commit Graph
184 Commits
Author SHA1 Message Date
zeekayandHanzo Dev f09d8a1b3e deps: pulsar v1.9.0 pkg/ layout + threshold v1.12.0 dealerless daemon
Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 16:27:27 -07:00
zeekay 83f286b3f0 bump: dealerless pulsar v1.7.1 / corona v0.10.2 / dkg v0.3.5 / mlwe v0.2.1
Move the consensus Quasar PQ integration to the dealerless threshold-signature
stack (RSS keygen + no-reconstruct signing + dealer ripped). Refresh the
re-tagged luxfi/pq v1.0.3 go.sum entry.

Fix a latent bug the bump exposed: ValidateMithrilSigningCommittee documented +
tested an N<=6 consensus small-committee cap but only called rss.ValidateCommittee
to enforce it. rss loosened its gate from a hard N<=6 to the norm bound
(tau*C*eta < gamma2, up to N<=63) at dkg v0.3.4+, so (2,7) — norm-viable — began
passing and the consensus policy cap silently vanished. Restored the explicit
N<=MithrilMaxCommittee cap (layered before the rss crypto bound), preserving the
small-committee security design (Avalanche subsampling carries chain safety;
Pulsar emits compact PQ evidence). Quasar suite green.
2026-06-28 19:08:08 -07:00
zeekay 8906da502e bench(quasar): measured per-lane + composed PQ policy-tier benchmarks
Adds REAL go test -bench measurements (M1 Max) of VERIFY latency, cert
SIZE, and SIGN cost for every Quasar finality lane, composed into the five
named policy tiers (BLS_FAST .. POLARIS_MAX). Drives the operator policy-tier
+ deployment decision with data, not estimates.

- bench_pq_policy_tiers_test.go: per-lane verify/sign sub-benchmarks over the
  PRODUCTION verify functions and real crypto (Beam BLS, Pulsar TALUS compact,
  Pulsar sampled r-of-m, Corona Ring-LWE, Magnetar-Quorum N SLH-DSA, P3Q direct
  N ML-DSA) + a composed-tier summary table (verify ns, cert bytes, x-overhead
  vs BLS) printed via fmt.Printf (go test truncates large b.Log in benchmarks).
- Widen 8 shared real-key test harness helpers from *testing.T to testing.TB
  (the canonical Go idiom) so benchmarks reuse the exact production cert
  builders; strict interface widening, zero behaviour/caller change.

No production code touched; test/bench-only. Suites green except the
pre-existing flaky quantum-bundle threshold tests (a different one fails each
run on clean main too).
2026-06-28 06:50:50 -07:00
zeekay 4ffca87229 merge Magnetar-Quorum Track A (v1.28.0): trustless P3Q rollup of independent FIPS-205 SLH-DSA sigs, cross-family diversity invariant, POLARIS_HASH_PQ/POLARIS_MAX tiers 2026-06-28 01:17:06 -07:00
zeekay 001c57dc1b quasar: Magnetar-Quorum proof suite (16 tests, stock FIPS-205, trustless)
Proves the Track-A claim end to end with real SLH-DSA-192s keys:
  - independent FIPS-205 sigs -> MagnetarQuorumCert -> VerifyMagnetarQuorum
    accepts (standalone + leg + full VerifyConsensusCert envelope);
  - each inner record verifies under the STOCK FIPS-205 verifier (magnetar
    VerifyCtx over circl/slhdsa) with no Lux machinery — the trustless,
    independently-auditable wire-identity claim;
  - tamper (flipped sig, re-committed), sub-threshold, and root-mismatch are
    hard rejects; the BFT floor is the verifier's, not the cert's;
  - cross-family diversity: an ML-DSA cert set is refused at BOTH build and
    verify (ErrMagnetarNotHashBased) — a Module-LWE break can't satisfy the
    hash-based leg;
  - the STARK succinct seam fails closed (ErrMagnetarBackendNotAuditGated);
  - suite dispatch can't cross lanes (P3Q suite rejected), hit an unknown
    suite, downgrade the param set, or land on the wrong leg kind;
  - POLARIS_HASH_PQ / POLARIS_MAX require the right legs and admit Magnetar
    ONLY as the trustless rollup, NEVER a threshold sig;
  - structural no-dealer/no-shared-secret: disjoint committees with fully
    independent keypairs each verify; no shared public key anywhere.

All 16 green (53s).
2026-06-28 01:08:38 -07:00
zeekay 1b2b8d79b3 quasar: POLARIS_HASH_PQ + POLARIS_MAX policy tiers (admit trustless Magnetar-Quorum)
Two new finality postures wiring the hash-based diversity lane:

  POLARIS_HASH_PQ  = Beam ∧ Pulsar ∧ Magnetar-Quorum
  POLARIS_MAX      = Beam ∧ Pulsar ∧ Corona ∧ Magnetar-Quorum
                     (Module-LWE ∥ Ring-LWE ∥ hash-based diversity)

The Magnetar leg is admitted ONLY as the trustless EvidenceMagnetarRollup
(independent per-validator FIPS-205 sigs; no dealer/DKG/shared secret), ONLY
under the policy's SLH-DSA param. Magnetar-Threshold (THBS-SE reveal-and-
aggregate, a signing-time TCB) is structurally excluded: SLH-DSA may not be
proven by a threshold-sig mode (I7), and no SLH-DSA threshold-sig mode exists.
POLARIS_MAX also extends the Corona allow-gate (previously STRICT-only).

  - magnetarParam field (default SLH-DSA-192s) + WithMagnetarParam override.
  - RequiredLegs / Allows updated for both postures; Pulsar required as the
    compact threshold sig (the '∧ Pulsar'), Magnetar as the rollup.

Full quasar suite green (172s).
2026-06-28 01:03:55 -07:00
zeekay 4fd10f9a98 quasar: VerifyMagnetarQuorum — the Track-A P3Q relation (transparent + STARK seam)
The Magnetar-Quorum relation: prove a >= policy-threshold WEIGHTED quorum of
INDEPENDENT validator SLH-DSA signatures each verify under stock FIPS-205 over
the same consensus subject M, every signer a committed validator-set member.

  - VerifyMagnetarQuorum(policy, validators, cert, M, mc) — the standalone
    public verifier the directive names; VerifyMagnetarQuorumLeg is the
    LegEvidence dispatch form, wired into dispatchEvidence (EvidenceMagnetarRollup).
  - Direct path (live today, trustless transparent aggregation): bind rollup
    root -> raw SLH-DSA cert set; enforce the cross-family diversity invariant
    (every inner record is SLH-DSA / FIPS-205, ErrMagnetarNotHashBased); then
    COMPOSE the audited VerifyWeightedSigSet (N stock FIPS-205 verifies +
    weighted-Merkle validator-set binding + BFT threshold floor + envelope
    position). No dealer / DKG / shared secret — structurally independent keys.
  - STARK path (AssumptionPQSuccinct): the OPTIMIZATION SEAM. Pin the public
    statement so an audited backend can only ever attest THIS relation, then
    FAIL CLOSED (ErrMagnetarBackendNotAuditGated) — the raw set stays
    challengeable via Direct. Succinctness makes the lane compact, not more
    trustless. No classical-assumption path for a hash-based lane.

The full SLH-DSA-verify STARK circuit is deferred; the trustless lane is the
Direct transparent-aggregation path, with the p3q-succinct proof clearly
labelled as the follow-on compaction (still trustless — no dealer/shared-secret).
2026-06-28 00:59:23 -07:00
zeekay d1ddbcc84d quasar: MagnetarQuorumCert — trustless Track-A hash-based PQ lane (type + evidence kind)
Magnetar-Quorum (Track A) is the trustless-TODAY hash-based PQ finality lane:
validators hold INDEPENDENT ordinary FIPS-205 SLH-DSA keypairs (no DKG, no
dealer, no shared seed) and sign the same consensus subject; a P3Q rollup
proves a >= policy-threshold WEIGHTED quorum of those independent signatures
verified under the STOCK FIPS-205 verifier. It sidesteps threshold SLH-DSA
entirely — trustless the moment the rollup relation is sound.

This commit adds the value-level vocabulary and the wire type:
  - MagnetarQuorumCert{SuiteID, RollupRoot, CertSet, Proof} + deterministic,
    fail-closed codec (magnetar_codec.go), mirroring the P3Q ML-DSA pattern.
  - MagnetarRollupRoot + magnetarPublicStatement domain-separated commitments.
  - BuildMagnetarQuorumCert (Direct constructor) enforcing the cross-family
    diversity invariant: every signer record must be SLH-DSA (FIPS-205).
  - EvidenceMagnetarRollup evidence mode (own primitive, not bolted onto Pulsar).
  - EvidenceMagnetarP3QSLHDSARollup kind 'magnetar-p3q-slhdsa-rollup' + lane
    mapping to (LegMagnetarSLHDSA, EvidenceMagnetarRollup).
  - Suites Lux-Magnetar-SLHDSA{192s,256s}-Direct-v1 + -192s-STARK-v1 (succinct
    seam). NO classical-assumption suite — a hash-based diversity lane must
    never rest on a pairing.

Composes the audited, scheme-generic WeightedQuorumCert (which already
dispatches SLH-DSA records to stock magnetar.VerifyCtx); verifier wired next.
2026-06-28 00:57:30 -07:00
zeekay 643628d420 quasar: three-tier sampled finality policy (FAST / HYBRID_PQ / PQ_ROOT)
Composable lane-verifier closures mapped onto the existing Quasar
evidence-mode table:
  FAST      = Beam only                       -> PolicyBLSFast
  HYBRID_PQ = Beam AND VerifyPulsarSampled    -> PolicyHybridPQCheckpoint (DEFAULT)
  PQ_ROOT   = P3Q >=2/3 weighted ML-DSA(+Beam)-> PolicyRecoveryMode

The policy owns ONLY the tier->required-lane AND structure; it constructs
no M and re-implements no Beam/P3Q. Each lane is a fully-bound closure the
caller wires (PulsarSampledLane adapts VerifyPulsarSampled). Fail-closed:
a missing required lane is ErrTierLaneMissing, never an implicit pass; the
first failing lane's error surfaces unchanged so errors.Is sees the
underlying cause. 8 tests incl. real end-to-end propagation of
ErrSampledPlanMismatch / ErrInsufficientCommittees through the tier.
2026-06-28 00:50:42 -07:00
zeekay 7c7627a5db quasar: VerifyPulsarSampled — the r-of-m sampled-cert verifier (6 checks)
Trust-minimised by re-derivation: the verifier re-derives the committee
plan from the unbiasable seed (so a cert naming an unsampled committee is
rejected) and re-derives the subject M from that plan's committeePlanHash
(so a cert whose committees signed any other M is rejected). The six
checks: (a) committeePlanHash bound into the re-derived M; (b) every
counted committee InPlan; (c) each a dealerless Mithril key-era (mode +
(t,n) + PubKeyHash binding the resolved key); (d) each sig stock-verifies
via verifyPulsarLeg (pulsarwire.VerifyBytes, unmodified FIPS-204) over M;
(e) >= r DISTINCT independent committees; (f) all over the same M.

Per-committee failures are EXCLUSIONS (offline-like) not whole-cert
errors — the r-of-m model tolerates up to m-r. CommitteeKeyResolver is
the one trusted input (registry of dealerless-DKG-activated keys); the
cert binds itself to its exact key bytes. 15 tests: positive + full-m +
r-of-m boundary + liveness slack + adversarial battery (out-of-plan,
wrong-M, non-dealerless, pubkeyhash mismatch, wrong (t,n), duplicates,
structural rejects, nil guards, wrong finality position).
2026-06-28 00:48:15 -07:00
zeekay fc1063a3ea quasar: Pulsar sampled-cert exact r-of-m binomial security tool
Two-stage exact-rational (math/big) security math: per-committee capture
p = Sigma_{i=t..n} C(n,i) f^i (1-f)^(n-i), and r-of-m forgery
P_fail = Sigma_{j=r..m} C(m,j) p^j (1-p)^(m-j) — the EXACT binomial tail,
not the p^r approximation (m=r reduces to p^r, proven exactly). Default
Pulsar-HYBRID-PQ-v1 (n=8,t=7,m=12,r=8,f=1/3) = per-committee 2^-8.59,
r-of-m 2^-59.80; all-of-r (m=r=8) 2^-68.74. ratLog2 via big.Float
mantissa/exponent stays accurate below the float64 underflow floor.

The exact tool CORRECTS the n=16 family figures from the design notes:
n=16,t=12 is 2^-10.30 (not 2^-7.9), n=16,t=14 is 2^-16.36 (not 2^-13.5)
— n=16 families are stronger per committee than previously tabulated.
n=8 figures confirmed. Exact-invariant + monotonicity + tabulation tests.
2026-06-28 00:41:22 -07:00
zeekay b84a65e1fb quasar: Pulsar sampled-cert subject M (binds committeePlanHash + beamQCHash)
The one canonical subject every r-of-m sampled Mithril committee signs.
Binds the finalized position (chainID/height/round/blockID/stateRoot),
the Beam QC over that block (beamQCHash, non-transferable across QCs),
the P-Chain-pinned signer set (signerSetID/pChainHeight), the policy
posture, and the frozen committeePlanHash — so committees are
un-reselectable after the block is seen. Distinct subject domain
('Lux/PulsarSampledSubject/v1') makes a sampled-committee signature
non-transferable vs the envelope single-committee Pulsar leg. Full
field-binding + non-transferability tests.
2026-06-28 00:36:38 -07:00
zeekay 57027712a9 quasar: Pulsar sampled-cert unbiasable sortition + committeePlanHash
Deterministic, verifiable stake-weighted committee selection from an
UNBIASABLE seed (hash of already-final consensus state, never a
proposer-grindable value), with the committeePlanHash that commits the
entire sampling plan for binding into the finality subject M. m=12
committees of n=8 stake-weighted-without-replacement members each
(Pulsar-HYBRID-PQ-v1), SHAKE256 DRBG with rejection-sampled unbiased
draws. Fail-closed params + tests (determinism, unbiasable-inputs-change-
plan, distinctness, stake-proportionality, fail-closed).
2026-06-28 00:34:46 -07:00
zeekay 40f130e80f quasar: Pulsar sampled-cert types (PulsarCommitteeCert, PulsarSampledCert)
The ML-DSA analogue of Avalanche's repeated-small-sample path: r-of-m
dealerless Mithril committees each sign the SAME finalized subject M;
post-quantum confidence is committee-capture probability accumulated over
repetition r (~p^r), NOT one committee as a finality quorum. Compact: r~8-16
ordinary FIPS-204 signatures, never 1000+. Cert value types only -- sortition,
subject, verify, policy decomplected into sibling files.
2026-06-27 23:32:04 -07:00
zeekay 3fe526cd9b quasar: Mithril RSS signing-committee size bound (2<=T<=N<=6)
The dealerless RSS Pulsar (ML-DSA) leg is only stock-FIPS-204-signable for a
small committee (C(N,N-T+1) replicated shares grow combinatorially). Adds the
consensus-side bound — delegated to luxfi/dkg v0.3.0 rss.ValidateCommittee for
ONE definition — plus ClampMithrilCommitteeSize and the grouped-epoch sortition
cap, with the full security argument: consensus quorum != signing committee, so
broad security comes from Avalanche subsampling + rotating committees + 2/3
group quorum + dual-PQ AND-mode, never from committee size. Default group size
(3) already complies; the cap only constrains over-large configs. quasar suite
green (77s).

NOTE: pre-existing luxfi/[email protected] go.sum mismatch (force-moved tag) breaks the
whole-repo 'go build ./...' on main independent of this change (proven on clean
HEAD); the quasar package and its full test suite are green.
2026-06-27 23:12:57 -07:00
zeekay 820d3c508d consensus: consume TALUS no-leak signer (pulsar v1.2.0 / threshold v1.10.2 / corona v0.8.0); rewire quasar Pulsar-leg tests to stock FIPS-204
pulsar v1.2.0 removes the small-committee threshold-signer surface
(NewThresholdSigner / ThresholdSigner / Round1Message / Round2Message /
Combine) in favour of the TALUS no-reconstruct BCC/CEF path. The Pulsar
leg of a Quasar cert verifies under the UNMODIFIED FIPS-204 verifier, so
a single-key signature under the group key is valid leg evidence — no
DKG, no ceremony is needed to GENERATE valid leg bytes for a
cert-composition test.

Replace signPulsarThresholdLeg (deleted threshold ceremony) with
signPulsarLegStock: GenerateKey + Sign via threshold/protocols/pulsar's
stock single-key path, returning the same *pulsar.Signature /
*pulsar.PublicKey alias types that ComposePolaris and
pulsarGroupPK.MarshalBinary already consume. The negative tests
(removed / tampered / missing-key Pulsar leg) still reject, so the leg
bytes remain load-bearing, not a stub.

Corona keygen (threshold/protocols/corona.GenerateKeys) is unchanged —
the trusted-dealer fixture name survives in the threshold corona adapter,
so no rename was needed in these consensus tests.

Production build unaffected (0 non-test refs to the removed APIs).
go test ./protocol/quasar/... ./protocol/auth/... ./config/... green;
go vet ./protocol/quasar/ clean.
2026-06-27 20:00:00 -07:00
zeekay 823c7baf09 quasar: decomplect PulsarKeyEra -> KeyEra (value-not-place, one type all lanes)
The compact group-key era was named for a place (Pulsar) when it is a
generic value serving every threshold lane, scheme-qualified. Drop the
Pulsar prefix and generalise the verifier so ONE type + ONE entry point
covers both the Pulsar (FIPS-204 ML-DSA) and Corona (Ring-LWE Ringtail)
lanes. Pure decomplect: same behaviour, value-not-place naming.

- Type:  PulsarKeyEra -> KeyEra  (file pulsar_key_era.go -> key_era.go)
- Field: MLDSAPubKey []byte -> GroupPubKey []byte  (scheme-interpreted via
  SchemeID; not ML-DSA-specific). The unrelated Validator.MLDSAPubKey
  (*mldsa.PublicKey, a per-validator identity key) is left untouched.
- Registry: PulsarKeyEraRegistry -> KeyEraRegistry (lane-agnostic; holds
  Pulsar and Corona eras alike), NewPulsarKeyEraRegistry -> NewKeyEraRegistry.

Generalised verifier dispatch:
- VerifyThresholdLeg(ev, M, era) is the ONE compact-threshold verify entry
  point. It resolves the era's lane through the suite registry
  (era.eraSuite, Pulsar|Corona only) and routes the group-signature check
  to that lane's boring verifier: pulsarwire.VerifyBytes for Pulsar,
  coronaThreshold.Verify for Corona (via new verifyCoronaGroupKeyLeg, which
  decodes the era's wire-byte group key and reuses verifyCoronaLeg). Both
  the era scheme and the evidence suite are resolved through the registry
  and must name the SAME lane + param set, so no suite string can reach the
  wrong verifier (reuses LookupSuite/resolveSuiteForLane lane safety).
- VerifyPulsar is retained as a thin Pulsar-suite wrapper that refuses any
  non-Pulsar era at the door, then defers to VerifyThresholdLeg.

Tests: existing boring-verifier + suite-dispatch-safety (VerifyPulsar) and
dualpq tests hold unchanged; KeyEra registry test now proves lane-agnostic
acceptance (corona era registers, non-threshold Beam refused); new
TestCompact_VerifyThresholdLeg_CoronaLane proves a corona-suite era
verifies via the corona path through the same KeyEra/dispatch with a real
multi-node Ring-LWE signature, and that a pulsar suite can never reach the
corona verifier.

GOWORK=off go build ./... green; go test ./protocol/quasar/ green.
2026-06-27 12:38:58 -07:00
zeekay 8a71526f87 quasar: bind P3Q rollup to the committed validator set; trim speculative API
SECURITY FIX (self-review): the first P3Q direct verifier checked N ML-DSA
signatures and summed their CLAIMED weights but never bound the (validatorID,
pubkey, weight) leaves to the committed validator set — a zero-stake attacker
could forge a rollup of fresh keys with threshold weight and pass.

The 'MLDSACertSet' (independent per-validator ML-DSA certs + weighted quorum) IS
the existing WeightedQuorumCert (EvidenceWeightedSigSet), already bound to
validators.Root() via weighted-Merkle membership. P3Q now COMPOSES it: the
Direct path = rollup-root commitment over the marshalled WeightedQuorumCert,
then the audited validator-set-bound weighted-sig-set verify (membership + N
FIPS-204 verifies + threshold floor). No reinvented per-leaf verification, no
gap. Succinct STARK/Groth16 paths unchanged (audit-gated; classical caveat).

Also trim 4 unused speculative helpers (LaneFor + three IsPostQuantum methods) —
the suite registry is the single mapping authority.

Tests reworked to wrap a real fourMLDSA WeightedQuorumCert; sub-threshold and
corrupt-cert-set cases now prove the inner validator-set-bound verify runs.
2026-06-27 12:15:12 -07:00
zeekay beb84df10a quasar: prove the compact-certificate evidence layer
- STRICT_QUASAR (Beam ∧ Pulsar ∧ Corona) over one M verifies with real BLS
  aggregate + FIPS-204 ML-DSA + multi-node Ring-LWE threshold; missing Corona
  rejects (AND-mode).
- Policy table enforced: BLS_FAST Beam-only; HYBRID accepts Pulsar OR P3Q;
  RECOVERY accepts P3Q and REFUSES the Pulsar threshold mode; STRICT refuses
  the P3Q mode.
- COMPACTNESS: Beam aggregate size invariant under committee growth; compact
  checkpoint footprint is O(1) and 957x smaller than the naive O(N) 1000-raw-
  ML-DSA-cert object at N=1000 (3466 B vs 3.32 MB).
- Boring VerifyPulsar + suite dispatch safety: wrong-era / suite-mismatch /
  cross-lane suite / unknown suite / corrupt-sig / replayed-M each reject with
  a distinct typed error; exhaustive suite-registry lane invariant; no suite
  string reaches the wrong verifier.
- P3Q fallback: Direct path verifies a rollup root and rejects a tampered root,
  a corrupt leaf, and sub-threshold weight; STARK gated; Groth16 refused unless
  the policy opts into classical proof assumptions (then still backend-gated).
- PulsarKeyEra registry register/lookup/conflict; one-M property
  (QuasarFinalityMessage == consensusCertMessage).
2026-06-27 12:15:12 -07:00
zeekay c327ba40e3 quasar: compact-certificate evidence layer (owner-authoritative, O(1) for 1000+ validators)
Four typed compact evidence kinds, a PulsarKeyEra registry + deliberately
boring VerifyPulsar, the named policy table (BLS_FAST / HYBRID_PQ_CHECKPOINT /
STRICT_QUASAR / RECOVERY_MODE), and the P3Q ML-DSA-rollup fallback leg — all
layered on the existing decomplected ConsensusCert envelope (LegKind x
EvidenceMode orthogonal axes), reusing not rewriting.

- compact_evidence.go : EvidenceKind vocabulary (beam-bls / pulsar-threshold-
  mldsa / corona-ringtail / p3q-mldsa-rollup) + lane mapping + the suite-ID ->
  verifier dispatch registry. No suite string can reach the wrong verifier;
  suites carry the param set (ML-DSA-44/65/87).
- pulsar_key_era.go : PulsarKeyEra (one compact ML-DSA group key per validator-
  set era) + VerifyPulsar (boring: normal FIPS-204 verify under the era key) +
  the era registry. Production trust model (talus/ceremony/tee) is audit-only,
  orthogonal to verification.
- quasar_finality.go : the ONE canonical finality message M; consensusCertMessage
  now delegates to it, so all lanes provably sign byte-identical M.
- p3q_rollup.go / p3q_codec.go : the P3Q fallback as its own primitive (own
  mode/verifier/codec). Direct path re-verifies the raw MLDSACertSet (PQ-safe,
  O(N), challengeable); succinct STARK/Groth16 paths audit-gated; classical
  proof assumption enforced via policy opt-in.
- cert_policy_table.go : the four postures; the Pulsar-OR-P3Q is a MODE gate on
  one required leg KIND.
- consensus_cert.go : KeyEraID + StateRoot bound into M; EvidenceP3QRollup mode
  wired into dispatch.

Beam/Pulsar/Corona stay O(1) (one sig each); P3Q is fallback only. Raw
1000-cert-sets are never the normal finality object.
2026-06-27 12:15:12 -07:00
zeekay b0ffb5e8d2 quasar: parallel-PQ AND-mode dual-PQ finality (Pulsar ‖ Corona)
Add the canonical parallel post-quantum finality posture: every cert carries
a Pulsar (Module-LWE / FIPS-204 ML-DSA) leg AND a Corona (Ring-LWE, dealerless)
leg, in parallel, AND-mode (both required to finalize). Security is the safest
surviving leg: an adversary must break BOTH the Module-LWE leg and the Ring-LWE
leg in the same round (orthogonal hardness, conjunctive acceptance).

dualpq.go (production):
  - DualPQRequiredLegs(paramSet): the canonical {Pulsar, Corona} AND-mode
    required-leg set (matches config CertModeStrict+CertVariantStrict).
  - ComposeDualPQEvidence(...): pure-function composition of the two
    threshold-sig leg evidences from a real Corona threshold signature + a
    real Pulsar FIPS-204 signature over the same envelope message. Composition
    over inheritance — wraps each primitive's output, extends neither.

consensus_cert_dualpq_test.go (multi-node proof):
  - Corona leg: genuine multi-node two-round t-of-n threshold ceremony — N
    separate single-share Signers, Round1/Round2 over an explicit message bus,
    aggregated by one node from Round2 DATA (never shares).
  - Pulsar leg: real FIPS-204 ML-DSA-65 signature accepted byte-for-byte by
    the on-chain verifier (verifyPulsarLeg -> pulsarwire.VerifyBytes).
  - Proves: AND-mode (both legs required + verify), sub-quorum on EITHER leg
    fails finality, corrupt either leg fails, composer refuses a half cert.
  - Single-share invariant: compile-time witness that the only Corona signing
    constructor takes EXACTLY ONE *KeyShare; aggregator takes Round2 DATA.

Permissionless-safe today via the dealerless Corona leg in AND-mode; the
Pulsar leg's no-reconstruct t-of-n threshold production is documented as
blocked on a poly-vector secret-share type (ML-DSA s1 is non-linear in the
GF(257) seed-share) — see luxfi/pulsar BLOCKERS.md PULSAR-V12-PARALLEL-PQ.
No fakes: the Pulsar leg signature is a real FIPS-204 group-key signature,
not a claimed threshold ceremony.
2026-06-27 01:12:35 -07:00
zeekay 2af02cb260 security(quasar): CI guard vs trusted-dealer keygen + drop stale v0.1 pulsar comment
Add scripts/no-trusted-dealer-guard.sh: greps non-test .go in the consensus
tree for NewTrustedDealer / func GenerateKeys / GenerateDualKeys /
GenerateThresholdKeys / GenerateDualThresholdKeys and exits non-zero if any
land in production code. Wired as a prerequisite of `make test` and
`make security` so the regression that dropped the test-only relocation
(97b2dfe7c) on a merge cannot silently return.

Also delete the stale polaris.go comment "Produced by the
pulsar.ThresholdSigner ceremony" — that v0.1 reconstruct-then-sign Combine
path was ripped; the leg is filled by the corrected lattice kernel.
2026-06-26 20:48:33 -07:00
zeekay 94bd332781 security(quasar): relocate trusted-dealer keygen helpers to test-only — corona-genesis hardening
GenerateDualKeys / GenerateThresholdKeys / GenerateDualThresholdKeys sampled
the full secret in one process (BLS NewTrustedDealer + coronaThreshold.
GenerateKeys) — a trusted-dealer keygen sitting in non-test code (quasar.go).
Every real caller is a package-quasar _test.go (dual_threshold, cert_size,
triple_sign, cert_roundtrip, witness_fixtures, comprehensive); none is
production.

Production epoch/genesis keying already runs the dealerless Pedersen DKG via
keyera.Bootstrap (EpochManager.InitializeEpoch, epoch.go) — unchanged. Moved
the three helpers verbatim into keygen_testsupport_test.go (package quasar) so
NO non-test code in the consensus engine can route keying through a trusted
dealer. Pairs with the wave_signer deletion (5fe753c42): consensus now has zero
non-test reconstruct-then-sign and zero non-test trusted-dealer keygen.

Verified: GOWORK=off go build ./... exit 0; go vet clean; gofmt clean;
go test ./protocol/quasar/ (Dual/Triple/Cert/Witness) ok.

(cherry picked from commit 97b2dfe7ceac24404395c9c2fdb08bc91fc39572)
2026-06-26 20:42:43 -07:00
zeekay 981934f496 test(quasar): keygen deadline 10s→60s — coarse pathological guard, not an SLA (flaked under CI parallelism) 2026-06-26 19:43:50 -07:00
zeekay d5cf311822 security(quasar): delete dead wave_signer (RoundSigner) — H-1 dead-code rip
RoundSigner/RunRound in protocol/quasar/wave_signer.go had ZERO non-test
callers. It was a thin per-round adapter wrapping the pulsar v0.1
reconstruct-then-sign ceremony; consensus's real PQ-threshold signing
lane is corona (epoch.go), and the cert's pulsar leg is verify-only on
the live path. Deleting it removes consensus's only live invocation of
pulsar's reconstruct-then-sign (Combine) path.

- Delete wave_signer.go + wave_signer_test.go.
- cert_policy_verify_test.go / polaris_compose_test.go: the Pulsar leg
  in these cert-composition tests was produced by RoundSigner. Replace
  with a test-only ceremony helper (pulsar_leg_test.go) that runs the
  same canonical t-of-n ceremony directly over the DKG outputs the tests
  already build — the leg stays a real threshold signature (no stub),
  verifying under unmodified FIPS 204 ML-DSA via pulsar.VerifyCtx.
- Drop now-unused ids/prism/context imports from both tests.

Verified: GOWORK=off go build ./protocol/quasar green; cert-composition
+ policy tests pass (real ceremony exercised); full -short suite green.
2026-06-26 19:25:39 -07:00
zeekay 8fa57244ca quasar: consume corona v0.7.9 two-value Round1 (propagate the re-added error)
corona v0.7.9 (commit 7c102d2, a descendant of the old single-return
4d9334ee) re-added the error return on Signer.Round1 as kernel-boundary
hardening on SignRound1. consensus quasar still called it single-value
(with now-stale 'error return was removed upstream' comments), so node
v1.30.40 failed to compile: 'assignment mismatch: 1 variable but Round1
returns 2 values' at epoch.go:679/1052, grouped_threshold.go:510,
quasar.go:269. Thread the error through all four production sites
(enclosing funcs already return error) and the 11 quasar test sites.
Forward-only fix; corona v0.7.9 is the intended current API.
2026-06-23 09:06:39 -07:00
zeekay aa6ca9c32f consensus(chain): pin epoch reads to block P-chain height + export TrackOwnProposalForTest (b2)
The chain engine pins a block's weighted validator set to a P-CHAIN epoch
height: the set-root commitment, the 2/3-by-stake tally, AND per-voter pubkey
resolution all read the height-indexed validators.State at that ONE height
(MEDIUM-1/CRITICAL-1/RESIDUAL-B). The height is captured off the VM block at the
engine boundary (pChainHeightOf) into Block.pChainHeight, read back by
epochHeightLocked, and stamped into every VotePosition's set-root. When a block
exposes no PChainHeight the read falls back to height 0 (genesis set): non-empty,
identical on every node, <= current -> finality stays LIVE. The node-layer
wrapper (separate repo) delivers the REAL height so post-genesis staking-change
epochs track the LIVE set.

VerifyVote/Weight/TotalStake/ValidatorSetRoot now all take epochHeight so the
four reads share one height; the engine threads it from the block.

Adds exported TrackOwnProposalForTest: a cross-module test seam (the node b2 test
lives in another module) that inserts a verified own-proposal exactly as
buildBlocksLocked does and returns the bound position. It captures pChainHeightOf
off the supplied VM block and NEVER force-finalizes -> the real alpha-of-K cert
path is exercised end to end.

TDD: quorum_pchain_epoch_finality_test.go proves (A) a 2/3-stake epoch quorum
finalizes through the real source path, (B) the engine reads the set at the
P-chain epoch height not the value height, (C) a validator absent from the
current map but present at the epoch still has its vote verified (RESIDUAL-B),
(D) an empty epoch fails closed. Full engine/chain + quasar suites green
(CGO_ENABLED=0).
2026-06-22 22:16:21 -07:00
zeekay cf73ba3c48 consensus(chain): round-2/3 hardening — epoch-bind certs, SyncState monotonic guard, BFT msg, drop dead witness
MEDIUM (epoch binding): stake-weighted finality must be measured at the
cert-position epoch, not at verification time. Bind the active weighted
validator-set commitment into VotePosition.ValidatorSetRoot and fold it into
CanonicalVoteMessage + the wire codec, so a cert is cryptographically pinned to
the exact set it was certified under: re-presenting an epoch-E cert as
certifying under a different epoch's set fails clause-6 signature verification,
and a later stake change cannot retroactively flip an already-correct cert.
Mirrors quasar.ConsensusCert.ValidatorSetRoot. New optional ValidatorSetRootSource
(WithValidatorSetRoot / NetworkConfig.ValidatorSetRoot), stamped in
blockPositionLocked; nil source => ids.Empty => behavior identical to before
(backward-safe for fixed-set chains). QuorumCertVersion bumped 1->2 so a
mixed-version cert fails loudly (version + signature) rather than mis-parsing —
a deliberate forward-only break (the set upgrades together in one cascade).
TDD: TestEpochBinding_{CrossEpochCertRejected,StakeReadAtCertHeight,RoundTripPreservesRoot}
(proven to fail without the canonical-message binding).

INFO-2 (SyncState monotonic guard): ChainConsensus.SyncState bypasses
markFinalizedLocked by design (import is an out-of-band reconcile), so the
monotonic invariant is re-asserted explicitly — a backward import (height below
the already-finalized height) is refused (ErrSyncStateRegression) and leaves
finalized state untouched; an equal-height re-import must agree on the block;
forward + empty-reset still succeed. SyncState now returns error; the sole caller
(Transitive.SyncState) propagates it and does NOT partially mutate on refusal.
TDD: TestSyncState_{RefusesBackwardRegression,EqualHeightDifferentBlockRefused,
ForwardImportAdvances,EmptyResetAllowed} (proven to fail without the guard).

INFO-3 (BFT message): ErrKTooLowForMainnet/Testnet now state the real bound
(2*AlphaPreference-K >= floor((K-1)/3)+1 with f>=3 / f>=1) instead of a bare
"K>=11" magic number. Sentinel-based assertions unaffected.

INFO-1 (dead code): delete the VerkleWitness apparatus (witness.go + its tests +
the VerkleWitness funcs in comprehensive_test.go + the verifyIPAOpening test) —
zero non-test callers; verifyBLSAggregate was a deserialize-only (no-pairing)
forgery sink if ever revived. The live deserialization path that matters is
crypto/bls.SignatureFromBytes (fixed + tested in the crypto module);
TestVerifyBLSAggregate_C1_ActualDeserialization was proven green in CGO_ENABLED=0
against the crypto fix BEFORE this deletion removed it.
2026-06-22 18:49:19 -07:00
zooqueen 14619d99bc deps: bump PQ crypto to finalized tags + adapt to corona Round1 API
pulsar v1.1.1->v1.1.5, magnetar v1.2.0->v1.2.3, threshold v1.9.4->v1.9.9,
corona v0.7.6->v0.7.9 (indirect). The ConsensusCert envelope + all legs now
verify against the just-finalized PQ crypto. corona/threshold.Round1 added an
error return; adapted 4 production + 12 test call sites. quasar suite ok, gate PASS.
2026-06-21 21:05:34 -07:00
zooqueen d01dcfa1c1 feat(quasar): ConsensusCert envelope — policy-gated threshold certification
Canonical finality cert: one policy-gated envelope (VerifyConsensusCert) with
per-leg evidence modes — ThresholdSig (lattice O(1)), WeightedSigSet (independent
FIPS sigs + weighted-Merkle quorum; the only mode for SLH-DSA, never
seed-reconstructed), StarkCompressedSigSet (P3Q, same predicate, audit-gated),
ClassicalAggregate (BLS, never satisfies a PQ leg). Required legs are
policy-derived (CertPolicy.RequiredLegs), never cert bytes (Red H3 anti-downgrade);
full-tuple domain binding. WeightedQuorumCert foundation: policy-as-parameter,
message recomputed internally, strictly-increasing IDs, checked weight.

27 named envelope tests + F1-F5 + 6 CTO attacks PASS; verifier total (fuzz, zero
panics); a fuzz-found threshold-sig flags-byte malleability fixed. Fixed go.sum
[email protected] to canonical transparency-log hashes. Scrubbed ~54MB tracked binaries.
2026-06-21 20:39:11 -07:00
zeekay 6897c234ad consensus(quasar): verify-enforce QuasarCert AND via CertPolicy.RequiredLegs (Red H3)
The QuasarCert verifier made BLS mandatory and PQ legs optional/
cert-byte-driven: a CRQC that broke BLS-12-381 could ship a BLS-only
cert (omit every PQ leg) and verify true, while a legitimate pure-PQ
(no-BLS) cert was wrongly rejected. config.CertPolicy.RequiredLegs()
(the canonical leg-set authority) had zero production callers.

Wire it in:
- New cert_policy_verify.go: VerifyUnderPolicy(message, CertPolicy, CertKeys)
  is the canonical verifier. For every leg in RequiredLegs() it rejects
  unless the leg is BOTH present in the cert AND backed by a supplied key,
  BEFORE any signature math — leg presence is policy-driven, not
  attacker-controlled cert bytes. BLS is required iff the policy includes
  it (Variant=Hybrid); a Strict-variant policy yields a pure-PQ required
  set so a no-BLS cert verifies. Identity rollup is required under any PQ
  policy (downgrade-resistant), present-implies-verify otherwise.
- Decomplect leg crypto into verify{BLS,Corona,Magnetar,MLDSARollup}Leg
  helpers; VerifyWithRealKeys / VerifyWithRealKeysPolaris reimplemented on
  them with the BLS-mandatory bug fixed (BLS required iff key supplied;
  pure-PQ allowed; each PQ leg required iff its key supplied — an adversary
  cannot strip a leg the verifier was configured to check).
- Migrate callers to policy intent: engine/pq gains SetCertPolicy +
  policy-driven verify path; pkg/wire QuantumPolicy enforces requireRT
  (Corona key mandatory) when set.

Tests (real crypto, no stubs): leg-removal negatives for each required
leg reject; missing-key-for-required-leg rejects; pure-PQ cert verifies
under Strict-variant policy AND via VerifyWithRealKeysPolaris(nil BLS);
Red PoC (BLS-only cert + PQ keys configured) now rejected. Existing
ComposeAllThreeSchemes / cert roundtrip / CertBundle suites unchanged.

Pre-existing unrelated failure TestVerifyBLSAggregate_C1_ActualDeserialization
(witness_test.go) fails identically on origin — out of scope.
2026-06-19 12:48:06 -07:00
zeekay 52b8ef654b consensus(quasar): Z-lane rollup proof is strict-PQ STARK/FRI (P3Q), not Groth16
Rename the Z-Chain parallel-witness rollup proof system from
classical Groth16/bn254 (pairing-based, quantum-breakable) to the
post-quantum STARK/FRI path (P3Q: Plonky3 fork, cSHAKE256 Merkle over
Goldilocks, FRI; no KZG, no pairings, no trusted setup).

This is a comment/identifier rename only: QuasarCert.MLDSARollup is
opaque bytes and the consensus module has no proof-system dependency,
so the cert composition + ZWitnessProducer interface are unchanged.
The verifier and prover live in chains/zkvm.

- witness_producer.go: ZWitnessProducer doc + MLDSAGroth16->MLDSAStark;
  DisabledZWitnessProducer doc (STARK prover, AIR constraint count).
- types.go / polaris.go: MLDSARollup leg doc 'STARK/Groth16'->
  'strict-PQ STARK/FRI (P3Q)'; wire-layout comment.
- engine/pq/consensus.go: FinalityEvent.MLDSARollup doc.
- pkg/wire/candidate.go: PolicyQuantum/PolicyPZ/WitnessZ docs.

QuasarCert composition gate test (TestQuasarCert_ComposesAllThreeSchemes)
still passes. No wire-format change.
2026-06-19 12:23:02 -07:00
zeekay c2e93c3942 WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:33:52 -07:00
zeekay d58a1e0f6b quasar: wire Polaris cert profile composing pulsar + corona + magnetar
Audit found: the QuasarCert struct previously declared a Pulsar field
that was never serialized, and had no Magnetar slot at all — the cert
wire codec only carried BLS + Corona + MLDSARollup (three legs). The
papers/lux-quasar-composition profile registry §04 names three
profiles:

  Pulsar   — BLS ‖ Puls ‖ ZK            (minimum PQ posture)
  Aurora   — BLS ‖ Puls ‖ Cor ‖ ZK      (intra-lattice diversity)
  Polaris  — BLS ‖ Puls ‖ Cor ‖ Mag ‖ ZK (cross-family maximum)

The pre-change cert could not represent Polaris on the wire, and the
Pulsar field was structurally aspirational. This change makes the
"double lattice + hash-based" PQ defense-in-depth real:

* QuasarCert gains a Magnetar []byte slot and serializes all five
  legs (BLS + Corona + Pulsar + Magnetar + MLDSARollup) under a new
  scheme byte 0x05 (forward-only — no back-compat per "forward
  perfection" rule).

* polaris.go adds ComposePolaris(PolarisLegs) — pure-function cert
  composer that consumes already-completed signing outputs from each
  of the four primitives and emits one QuasarCert. Corona and Pulsar
  serialize via the canonical CORS / PULS wire codecs (luxfi/corona
  v0.7.6 and luxfi/pulsar v1.0.23). Magnetar serializes via
  EncodeMagnetarAggregate (Mode + N + signers + pubkeys + sigs,
  parallel-slices form matching magnetar.ValidatorAggregateCert).

* VerifyWithRealKeysPolaris extends VerifyWithRealKeys with two new
  legs: the Pulsar leg routes through pulsar.VerifyBytes (stateless
  PULG/PULS verifier) and the Magnetar leg routes through
  magnetar.VerifyAggregateCert with quorum = every-signer-verifies.

* corona_gob.go switched from a local ring-poly length-prefix encoding
  to the canonical CORS Signature.MarshalBinary wire codec, so the
  cert's Corona slot carries exactly what the corona/threshold v0.7.6
  verifier expects.

* go.mod adds direct requires for luxfi/magnetar v0.5.2 and
  luxfi/pulsar v1.0.23, and bumps luxfi/corona v0.7.5 → v0.7.6 to
  pick up the CORS wire codec.

Gate test TestQuasarCert_ComposesAllThreeSchemes constructs a real
(3, 2) Pulsar DKG + threshold-sign ceremony, a real Corona
trusted-dealer threshold sign, and three real Magnetar per-validator
standalone SLH-DSA signatures. It composes them into a QuasarCert,
round-trips through the 5-leg wire codec, verifies under
VerifyWithRealKeysPolaris, and tamper-checks each PQ leg. PASS in
~75s.

Production status today:
  Pulsar profile = wired (Pulsar field now serializes)
  Aurora profile = wired (Corona slot already carried CORS bytes;
                          now under the canonical v0.7.6 wire codec)
  Polaris profile = wired (this change)
2026-06-01 12:31:14 -07:00
zeekay 0142367b60 consensus: route corona + pulsar through threshold/protocols alias
Replaces every direct import of luxfi/corona/* and luxfi/pulsar/ref/go
in consensus with the canonical luxfi/threshold/protocols/{corona,pulsar}
alias surface. Pure import swap + go.mod bump; no behaviour change.

Files touched (16):

  bench/pq_modes_bench_test.go
  engine/pq/crypto.go
  engine/pq/engine.go
  pkg/wire/policies.go
  protocol/quasar/cert_size_compare_test.go
  protocol/quasar/corona_gob.go
  protocol/quasar/dual_threshold_test.go
  protocol/quasar/epoch.go
  protocol/quasar/epoch_test.go
  protocol/quasar/gpu_accel.go
  protocol/quasar/grouped_threshold.go
  protocol/quasar/quasar.go
  protocol/quasar/types.go
  protocol/quasar/wave_signer.go
  protocol/quasar/wave_signer_test.go
  protocol/quasar/witness.go
  protocol/quasar/witness_pulsar_test.go

Import path swaps:

  luxfi/corona/threshold        -> luxfi/threshold/protocols/corona
  luxfi/corona/keyera           -> luxfi/threshold/protocols/corona/keyera
  luxfi/corona/primitives       -> luxfi/threshold/protocols/corona/primitives
  luxfi/corona/reshare          -> luxfi/threshold/protocols/corona/reshare
  luxfi/corona/sign             -> luxfi/threshold/protocols/corona/sign
  luxfi/corona/gpu              -> luxfi/threshold/protocols/corona/gpu
  luxfi/pulsar/ref/go/pkg/pulsar -> luxfi/threshold/protocols/pulsar

The aliased package name "coronaThreshold" used by quasar / engine/pq /
pkg/wire identifiers is preserved at the call sites (it now refers to
the threshold/protocols/corona alias package). engine/pq/crypto.go is
the only file where the package was bare-imported as `threshold`; that
identifier was renamed to `corona` to match the new alias path.

go.mod:

  - luxfi/threshold bumped to a branch pseudo-version that carries the
    new alias subpackages; will pin to a stable tag once threshold's
    companion commit lands and a release is cut.
  - luxfi/corona and luxfi/pulsar drop to indirect (consensus now
    reaches them only via threshold/protocols).
  - luxfi/math v1.4.0 -> v1.4.1 and luxfi/metric v1.5.0 -> v1.5.7 are
    transitive minimum-version bumps pulled by the new threshold
    requirement; they are no-op for consensus call sites.

Comment-only references to "github.com/luxfi/corona/..." in bls.go,
round_digest.go, reshare_epoch.go, comprehensive_test.go,
engine_test.go, quasar_bench_test.go and config/pq_mode.go are
documentation pointers to where the underlying implementation lives;
they are accurate either way and are left unchanged (out of refactor
scope).

Layering shape after:

  consensus -> threshold/protocols/{corona,pulsar}  (stable alias)
                 |
                 v
              luxfi/corona, luxfi/pulsar  (primitive implementations)

Closes the 22-file layering violation flagged by the 2026-05-31
integration audit. Tests pass: ./engine/pq/..., ./pkg/wire/...,
./protocol/quasar/... (all key cases — TestEpochManager*,
TestRoundSigner*, TestVerifyPQFinality*, TestBLSThreshold*,
TestDualSigningFlow, TestCoronaCertSize* — green; the pre-existing
TestGroupedEpochManager_ParallelKeygen perf-bound flake on slow
machines was reproduced on unmodified main, so it is not a
regression).
2026-05-31 12:39:47 -07:00
zeekayandGitHub f02020578f chore(consensus): kill subnet — chain-only vocabulary (#10)
Comment + test-data renames in 4 files. No semantic change. Per the
canonical no-subnet rule.

protocol/quasar/core.go: 5 comment lines (subnets/chains → chains).
protocol/quasar/comprehensive_test.go: test data "New-Subnet" → "New-Chain".
protocol/quasar/dynamic_test.go: TestAutoRegisterNewSubnet → TestAutoRegisterNewChain.
bench/lux_vs_grpc_test.go: local var `subnet` → `chainID`.

Build clean. Renamed test passes (TestAutoRegisterNewChain).
2026-05-29 21:10:30 -07:00
zeekay 3e6b6479d5 fix(quasar): track corona keyera.Bootstrap 3-value return (*KeyEra, *BootstrapTranscript, error) 2026-05-22 20:10:11 -07:00
zeekay c6128b6122 quasar: route corona R-LWE through lattice GPU NTT dispatch
Add UseCoronaGPUAccelerator / DisableCoronaGPUAccelerator helpers
that flip the corona/gpu global flag. The flag arms the
lattice/ring.SetGPUDispatchers hook installed by lattice/gpu under
`cgo && gpu` build tags; non-GPU builds keep the existing CPU path.

Decomplecting note. Boot wiring stays on the consensus side
(operators decide whether to engage GPU NTT at startup); the actual
lattice ↔ corona ↔ GPU plumbing lives in corona/gpu and lattice/gpu.
Quasar is a thin facade over a single corona/gpu.UseAccelerator()
call.

corona pin bumped to v0.7.2 (in-tree replace pending CI proxy pickup,
matching the pattern documented in CROSS-REPO-VERSION-PIN.md).

Tests pass under CGO_ENABLED=0 and CGO_ENABLED=1 -tags gpu, including
the existing TestDualSigningFlow (Pulsar 2-round protocol) and new
TestUseCoronaGPUAccelerator / TestCoronaGPUBackendNonEmpty.
2026-05-21 13:41:56 -07:00
zeekay 8874f9a8bb quasar: rename ErrPulsarMVerifyFail → ErrPulsarVerifyFail
Decomplect metaphor naming: Pulsar (astronomy family with Quasar,
Corona, Magnetar) — no -M suffix. Go package was already 'pulsar';
only legacy error names + comments carried the stale 'PulsarM' /
'Pulsar-M' / 'pulsarm' identifiers.

Changes (witness.go + witness_pulsar_test.go):
- ErrPulsarMVerifyFail → ErrPulsarVerifyFail (4 occurrences)
- "Pulsar-M finality" comments → "Pulsar finality"
- "Pulsar-M-65 wire format" → "Pulsar-65 wire format"
- "lattice signature check via luxfi/pulsar-m" → "via luxfi/pulsar"
- "the underlying pulsarm error variants" → "the underlying pulsar error variants"

The formal academic "Pulsar-M" name in NIST MPTC submission docs and
proof artifacts (~/work/lux/proofs/pulsar-m/) remains as-is for
submission/publication consistency. This commit fixes only the
codebase-internal identifier where the -M suffix added no information.

Build verified: GOWORK=off go build ./protocol/quasar/ → exit 0
Tests pass: GOWORK=off go test -count=1 -short ./protocol/quasar/ → ok
2026-05-20 16:13:05 -07:00
zeekay bee18daf6a docs: canonical Quasar naming (drop "umbrella" / "composition" qualifiers; add supermassive black-hole framing) 2026-05-19 08:15:17 -07:00
zeekay b1fcac4013 quasar/doc: defer brand-level spec to luxfi/quasar umbrella; this package is the consensus-engine binding 2026-05-18 21:51:15 -07:00
zeekay edee0e9826 quasar: bump luxfi/pulsar v1.0.5 → v1.0.7 (CR-6/7/8 closure)
v1.0.7 closes CR-6 (long-term identity keys), CR-7 (ephemeral
session keys), CR-8 (KEM-wrapped DKG/reshare envelopes) on both
committee-size paths. NewThresholdSigner / NewDKGSession /
NewReshareSession now require IdentityKey + sessionKeys params.

protocol/quasar/wave_signer.go drives the in-process witness pattern
(one coordinator runs all T threshold signers). New private helper
witnessQuorumSessionKeys synthesizes per-party IdentityKeys and
derives pairwise SymmetricSession keys bound to (sessionID,
item || groupPK || committee[0]). Cryptographic derivation matches
the distributed-DKG path; the witness pattern's security model is
"single trusted coordinator holds all shares" by design.

protocol/quasar/wave_signer_test.go: generates per-committee
IdentityKeys + IdentityDirectory for the (3,2) DKG setup.

Verified: protocol/quasar tests pass (18.071s); full consensus
short suite green (config/core/engine/{chain,dag,pq,bft}/auth/
flare/focus/horizon/prism/quasar/wave/wave/fpc/test/{e2e,integration,
unit}/utils/codec all ok).
2026-05-18 18:52:29 -07:00
zeekay 2e945a212c consensus: PQ-GPU dispatch audit + CONSENSUS_PQ_MODE env (legacy LUX_ fallback)
PQ-GPU-AUDIT.md documents the per-primitive dispatch matrix:
- Pulsar: shipped via crypto/mldsa/gpu.go → accel.LatticeOps
- Corona: shipped transitively via luxfi/lattice/v6/ring NTT
- Comet: substrate exists in luxcpp/crypto/slhdsa/gpu/; lux-accel C API
  exposure pending (separate ticket)
- P3Q: shipped via accel.ZKOps BN254 MSM
- Quasar engine: composes the above; doc updated to reflect actual
  dispatch path (was 'aspirational')

Env var: PQModeFromEnv reads CONSENSUS_PQ_MODE first, LUX_CONSENSUS_PQ_MODE
fallback for rolling-validator stability.
2026-05-15 16:45:08 -07:00
zeekay 2fdec23582 quasar/config/wire: track pulsarm → pulsar rename
Consumer-side rename to follow the luxfi/pulsar package drop of the
-M suffix. All 25 touched files migrate the import path
github.com/luxfi/pulsar/ref/go/pkg/pulsarm → .../pulsar and the
in-file symbol references pulsarm.* → pulsar.* (plus the local
witness_pulsarm_test.go filename → witness_pulsar_test.go).

Build: go build ./... PASS.
Test: full ./... sweep — 28 packages PASS (protocol/quasar 15.9s,
engine 10.7s, config 0.9s, pkg/wire 1.5s).
2026-05-15 13:50:29 -07:00
zeekay 3250583bfc quasar: GroupedEpochManager batch-verifies via coronaThreshold.VerifyBatch
VerifyGroupedSignature previously verified each group signature
sequentially in a per-iteration `if coronaThreshold.Verify(...)` loop.
Replace with one call to coronaThreshold.VerifyBatch over the parallel
(groupKey, message, sig) triples — internally a GOMAXPROCS-bounded
worker pool, so N-group verification cost is now O(N / cores) instead
of O(N).

Also: keyera identifier rename caught by the same review pass:
  keyera.PulsarGroupID  → keyera.CoronaGroupID
  keyera.PulsarKeyEraID → keyera.CoronaKeyEraID
in epoch.go and grouped_threshold.go (the underlying luxfi/corona
keyera package renamed the types when corona became canonical R-LWE;
these were straggler references at the consumer.).

Build: go build ./protocol/quasar/... PASS.
Tests:
  TestGroupedEpochManager (7 subtests) PASS
  Full ./protocol/quasar/ sweep         PASS 15.591s
2026-05-15 01:49:23 -07:00
zeekay faf3f78756 gofmt: cleanup after Corona purge identifier renames 2026-05-12 14:53:30 -07:00
zeekay f6753f3b53 quasar: complete Corona purge — Corona (production R-LWE) + Nasua (academic upstream)
Followup to the QuasarCert field rename (commit d1c8a2cc). This sweeps
the remaining Corona identifiers and prose out of the consensus repo
entirely:

  - Identifiers (Go code):
    coronaThreshold   → coronaThreshold      (import alias)
    coronaGroupKey    → coronaGroupKey
    coronaSigners     → coronaSigners
    coronaShares      → coronaShares
    coronaGobEncode   → coronaGobEncode
    coronaGobDecode   → coronaGobDecode
    decodeCoronaSig   → decodeCoronaSig
    EncodeCoronaSig   → EncodeCoronaSig
    CoronaRound1{,Data,State} → CoronaRound1{,Data,State}
    CoronaGroupKey/Pub/Shares → Corona*

  - File rename:
    protocol/quasar/corona_gob.go → corona_gob.go

  - PQMode constants:
    PQModeCorona            → PQModeNasua
    SigSchemeCoronaAcademic → SigSchemeNasua
    "corona" / "corona-academic" string forms → "nasua"

  - Prose & comments throughout the repo: "Corona" → "Corona" when
    naming the production Ring-LWE library, "Nasua" when naming the
    historical academic upstream (luxfi/nasua, formerly luxfi/corona).

Canonical Pulsar family taxonomy after this rename:

  Nasua  — academic R-LWE 2-round threshold sig (Boschini et al,
           ePrint 2024/1113); trusted-dealer DKG; kept as historical
           reference fork only. NOT for an open public chain.
  Corona — production Ring-LWE threshold ML-DSA (Pedersen DKG over R_q
           + proactive resharing); `github.com/luxfi/corona`. The
           Ring-LWE leg of QuasarCert.
  Pulsar — production Module-LWE threshold ML-DSA (FIPS 204
           byte-equal); `github.com/luxfi/pulsar`. The Module-LWE
           leg of QuasarCert. Pure-PQ pair = Corona + Pulsar.

Wire-format unchanged. Tests: 28/28 packages green (-short suite,
quasar 29s, integration 58s, no regressions).
2026-05-12 08:58:51 -07:00
zeekay d1c8a2cc28 quasar: rename Corona→Corona, MLDSAProof→MLDSARollup, drop "triple"
The Quasar cert layers are the right cardinality of "Double Lattice +
Identity Rollup + optional Classical Fast-path", not "Triple Quantum":

  BLS-12-381  classical fast-path (pairing) — empty in pure-PQ
  Corona      Ring-LWE  threshold ML-DSA (luxfi/corona; production R-LWE)
  Pulsar      Module-LWE threshold ML-DSA (luxfi/pulsar; Class N1 byte-equal
                                            to FIPS 204)
  MLDSARollup per-validator ML-DSA-65 rolled into one STARK / Groth16 proof
                                            (Z-Chain → P3Q)

"Triple Quantum" misled because BLS is classical — at most one of three
legs was quantum in the historic BLS+Corona+ZK shape. The honest PQ
cardinality is **Double Lattice** (Ring + Module). Pure-PQ profiles
drop BLS entirely and rely on Corona + Pulsar.

Corona is the academic R-LWE construction with trusted-dealer DKG;
it stays out of any open public-chain path. Corona is the production
Ring-LWE library (Pedersen DKG over R_q + proactive resharing).

Surface (in protocol/quasar/types.go):
  QuasarCert.BLS         → unchanged (classical fast-path, optional)
  QuasarCert.Corona    → QuasarCert.Corona       (Ring-LWE threshold sig)
  QuasarCert.MLDSAProof  → QuasarCert.MLDSARollup  (succinct identity rollup)
  QuasarCert            now also has a Pulsar field (Module-LWE threshold sig,
                                                     populated when wired)

  QuasarSig.Corona     → QuasarSig.Corona  (also added Pulsar []byte)
  FinalityEvent.Corona / MLDSAProof → Corona / Pulsar / MLDSARollup

  New predicates:
    QuasarCert.IsDoubleLattice()       — Corona ∧ Pulsar (honest PQ count)
    QuasarCert.HasClassicalFastPath()  — len(BLS) > 0
    QuasarCert.HasIdentityRollup()     — len(MLDSARollup) > 0

The wire byte layout is unchanged (length-prefixed slots). Callers that
read the bytes don't notice. Source-level references that compiled
before the rename now compile against the new identifiers; bulk
rename swept the consensus repo (22 files, 219 insertions, 151
deletions) plus the engine/pq facing FinalityEvent.

Tests: GOWORK=off go test -short -count=1 ./... → all 28 packages green
(quasar suite 32s; integration 60s; no regressions).
2026-05-12 08:40:06 -07:00
zeekay 675ef93fa8 consensus: close BLOCKERS CR-4 + CR-10 + CR-11 (triple-mode envelope enforcement)
CR-4 — WitnessSet.MinPolicy wired at chain bootstrap
  protocol/quasar/witness_producer.go: NewWitnessSet(profile, p, q, z)
  refuses nil profile, derives MinPolicy from ChainSecurityProfile.
  strict/FIPS profiles pin PolicyQuantum + PQModeQuasar; permissive
  pins PolicyQuorum + PQModeBLS. Zero-value WitnessSet{} preserved
  for legacy callers (documented test-only).
  Closes silent-downgrade attack: strict-PQ chain can no longer
  emit a PolicyQuorum cert when Q or Z layers fail; it errors.

CR-10 — triple-mode QuasarCert enforcement at vote acceptance
  protocol/quasar/core.go: Quasar.addVoteLocked refuses single-layer
  per-validator sigs under strict-PQ profile; refuses if signer not in
  triple mode. New demandsTripleLocked helper.
  protocol/quasar/engine.go: Certifier.generateCert returns ErrPartial-
  TripleCert under strict-PQ when realCert path lacks Corona or no
  signer attached. processBlock gates QuasarCert.Verify(nil) at vote
  acceptance time, not just construction.

CR-11 — BFT engine adapter PQ envelope enforcement
  engine/bft/engine.go: new PQSigner / PQVerifier marker interfaces;
  NewEngineWithProfile(epoch, profile) refuses classical bft.Signer
  under strict-PQ / FIPS profile; refuses scheme mismatch between
  signer and verifier. Existing NewEngine(epoch) preserved as
  non-strict legacy path.
  New typed errors: ErrClassicalSignerUnderStrictPQ,
  ErrClassicalVerifierUnderStrictPQ, ErrPQSchemeMismatch.

Test coverage:
  +26 new tests across witness_constructor_test.go (9),
  triple_mode_gate_test.go (9), profile_gate_test.go (8)
  All pass under GOWORK=off go test ./...

Three critical findings closed in one commit. BLOCKERS CR-4, CR-10,
CR-11 marked [CLOSED] in BLOCKERS.md next pass.
2026-05-11 23:42:36 -07:00
zeekay 07ffec05a7 consensus: close BLOCKERS CR-12 + CR-13 (cross-policy replay + sampling bias)
CR-12 — bind effectivePolicy byte into ComputeRoundDigest transcript
  protocol/quasar/round_digest.go: new uint8 parameter, refused if zero,
  bound as a TupleHash part between verifierBytes and netBytes.
  Closes cross-policy replay: an adversary can no longer sign a Round R
  as PolicyPQ and strip the Q witness on retransmit, presenting the
  bare P witness as a PolicyQuorum cert under an identical digest.
  Tests updated: canonical inputs include effectivePolicy=1, FixedHash-
  Family mirror parts include the policy byte at the matching position.

CR-13 — rejection sampling closes modulo bias in three samplers
  protocol/photon/emitter.go cryptoRandInt
  protocol/prism/cut.go cryptoRandInt
  protocol/prism/stake_weighted_cut.go cryptoRandUint64
  Prior `% max` form was structurally biased for non-power-of-2 max;
  under nation-state grinding model (Pinkas-Reiter style) the bias was
  an exploit on committee sampling. Rejection sampling computes the
  largest multiple of max that fits in uint64 and rejects values above
  it. Expected iterations ~1-2; never more than 2 in expectation.

Tests pass on protocol/quasar, protocol/photon, protocol/prism.
BLOCKERS.md CR-12 and CR-13 marked closed.

Two of 13 critical findings down. Remaining 11 are in progress.
2026-05-11 22:54:56 -07:00