Files
warp/quasar_cert.go
zeekay ffbfae06d3 feat(warp)!: typed finality-evidence model (Beam/Pulsar/Corona/P3Q) + Corona relabel
Decomplect the PQ finality lanes into a typed, impossible-to-confuse model where
KIND chooses the verifier and SUITE only chooses parameterization (fail-closed on
mismatch). Kills the prior conflation where the corona-backed lattice lane was
mislabeled "Pulse/Pulsar-SHA3".

Four strict evidence kinds — each its own verifier + suite + key registry:
  beam-bls                BLS12-381 aggregate (fast classical)
  pulsar-threshold-mldsa  threshold ML-DSA, verified as a STANDARD FIPS-204
                          ML-DSA signature under a group public key; the TALUS
                          dealerless machinery is entirely offline (VerifyPulsar
                          imports only mldsa65 — enforced by an import-hygiene test)
  corona-ringtail         Ringtail/Module-LWE lattice threshold (the corona kernel)
  p3q-mldsa-rollup        succinct rollup of independent ML-DSA-65 sigs; strict
                          ONLY when its proof system is PQ (classical SNARK gated)
Plus the raw mldsa-cert-set as a NON-strict availability/audit artifact.

Subject-agnostic verifiers take (subject, evidence, keyEra): one dispatch serves
the warp cross-chain digest D and the consensus finality subject
  M = keccak256("QUASAR_FINALITY_V1" || chainID || height || round || blockID ||
      stateRoot || signerSetID || keyEraID || generation || pChainHeight || policyID)
folding generation + pChainHeight + policyID so a signature binds its era, its
validator snapshot, and its tier (tier-downgrade replay fails closed).

Policy tiers (BLS_FAST / HYBRID_PQ_CHECKPOINT / STRICT_QUASAR / RECOVERY) select
which lanes are required — orthogonal to verification and to keys. PulsarKeyEra
and CoronaKeyEra are symmetric but DISTINCT registry types; P3Q/cert-set resolve a
per-validator SignerSetAuthority — no resolver can alias another's key material.
Offline signers (pulsard, coronad, P3Q prover) are interfaces only, never imported
by the verify path.

Wire unchanged: D, the Message c14n, messageDST, the ZAP magic and kind bytes are
byte-identical (forge Go<->Solidity equality holds; golden envelope SHA unchanged).
The corona lane DST is renamed pulseDST->coronaDST (no deployed corona sigs;
envelope KAT regenerated).

22 tests green: non-confusable dispatch, suite/kind fail-closed, Pulsar import
hygiene, subject-identity across lanes, policyID downgrade, key-era distinctness,
P3Q-classical-root strict gate.
2026-06-27 13:47:13 -07:00

112 lines
4.2 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// quasar_cert.go — the QuasarCert: the consensus FINALITY object.
//
// A QuasarCert carries TYPED finality evidence over the consensus subject M —
// NOT a thousand raw validator signatures. Each lane is one compact object:
//
// Beam one BLS aggregate over M (always present; the fast lane)
// Pulsar one threshold ML-DSA sig over M (optional; the PQ quorum lane)
// Corona one Ringtail threshold sig over M (optional; the PQ lattice lane)
// P3QRoot one succinct ML-DSA-65 rollup root (optional; recovery/compression)
// CertSet raw per-validator ML-DSA-65 certs (optional; AVAILABILITY only —
// never a strict finality root, but auditable + the P3Q input)
//
// The cert is a CARRIER, exactly like the warp Envelope: it computes the
// subject (M) and hands typed lane evidence to the same subject-agnostic
// verifiers. Admissibility under a finality tier is decided by AcceptQuasarCert
// (finality_tier.go); this file only models the value and enumerates it.
package warp
// QuasarCert is the consensus finality certificate for a single decided block.
// Subject is the QuasarFinalitySubject params; Subject.* and the lane evidence
// together are what a receiver verifies and admits.
type QuasarCert struct {
// Subject identifies the finalized decision. M = QuasarFinalitySubject(Subject)
// is the digest every present lane signs.
Subject QuasarFinalityParams
// Lanes. Beam is structurally always present (the BLS aggregate). The PQ
// lanes are optional; which combination is REQUIRED is a policy-tier
// decision (AcceptQuasarCert), not a structural one.
Beam BitSetSignature
Pulsar *PulsarEvidence
Corona *CoronaEvidence
P3QRoot *P3QRoot
// CertSet is the optional raw ML-DSA-65 availability artifact. It is the
// INPUT a P3Q rollup compresses and an accountable audit trail. It is
// NEVER admissible as a strict finality root (IsStrictFinalityKind ==
// false), so AcceptQuasarCert never counts it toward a required lane.
CertSet *CertSetEvidence
}
// SubjectBytes returns M, the 32-byte consensus finality digest every present
// lane is verified over.
func (c *QuasarCert) SubjectBytes() [32]byte {
return QuasarFinalitySubject(c.Subject)
}
// HasPulsar reports whether the cert carries a Pulsar threshold-ML-DSA lane.
func (c *QuasarCert) HasPulsar() bool { return c != nil && c.Pulsar != nil }
// HasCorona reports whether the cert carries a Corona Ringtail lane.
func (c *QuasarCert) HasCorona() bool { return c != nil && c.Corona != nil }
// HasP3Q reports whether the cert carries a P3Q rollup root.
func (c *QuasarCert) HasP3Q() bool { return c != nil && c.P3QRoot != nil }
// HasCertSet reports whether the cert carries a raw ML-DSA cert-set
// availability artifact.
func (c *QuasarCert) HasCertSet() bool { return c != nil && c.CertSet != nil }
// Evidence enumerates the typed finality-evidence lanes this cert carries,
// correct-by-construction (each lane paired with the suite its kind demands),
// mirroring Envelope.Evidence. The subject for these lanes is M
// (c.SubjectBytes()). Lanes appear only when present; the Beam is always
// present. The raw cert-set IS enumerated (it is recognized evidence), but
// IsStrictFinalityKind(EvidenceMLDSACertSet) is false so policy never admits it
// as a strict root.
func (c *QuasarCert) Evidence() []FinalityEvidence {
if c == nil {
return nil
}
out := make([]FinalityEvidence, 0, 5)
out = append(out, FinalityEvidence{
Kind: EvidenceBeamBLS,
Suite: SuiteBeamBLS12381,
Beam: &c.Beam,
})
if c.HasPulsar() {
out = append(out, FinalityEvidence{
Kind: EvidencePulsarThresholdMLDSA,
Suite: c.Pulsar.SuiteID,
Pulsar: c.Pulsar,
})
}
if c.HasCorona() {
out = append(out, FinalityEvidence{
Kind: EvidenceCoronaRingtail,
Suite: SuiteCoronaRingtailSHA3,
Corona: c.Corona,
})
}
if c.HasP3Q() {
out = append(out, FinalityEvidence{
Kind: EvidenceP3QMLDSARollup,
Suite: c.P3QRoot.SuiteID,
P3Q: c.P3QRoot,
})
}
if c.HasCertSet() {
out = append(out, FinalityEvidence{
Kind: EvidenceMLDSACertSet,
Suite: SuiteMLDSA65CertSetSHA3,
CertSet: c.CertSet,
})
}
return out
}