mirror of
https://github.com/luxfi/warp.git
synced 2026-10-30 15:13:04 +00:00
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.
96 lines
3.6 KiB
Go
96 lines
3.6 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// classification.go — Horizon-final envelope-SHAPE classifiers. The
|
|
// post-quantum lane structure is documented in LP-105 ("Lux stack lexicon",
|
|
// Proof-lane classification subsection) and
|
|
// proofs/definitions/finality-definitions.tex Definition ref:proof-lane and
|
|
// Remark ref:groth16-not-pq.
|
|
//
|
|
// Two envelope-shape predicates ship here:
|
|
//
|
|
// IsPQFinal(envelope) — true iff the envelope carries enough
|
|
// certificate-lane evidence to be Horizon-final under Prism. A
|
|
// Groth16 wrapper alone is NOT enough; a real Corona signature is required.
|
|
//
|
|
// HorizonFinalErr(envelope) — returns an error explaining WHY an
|
|
// envelope is not Horizon-final, or nil if it is.
|
|
//
|
|
// The proof-system PQ classifier IsPQRootOfTrust(provingSystem) is a
|
|
// subject-AGNOSTIC policy primitive and now lives in the warp ROOT package
|
|
// (warp.IsPQRootOfTrust) so both the warp envelope path AND the quasar
|
|
// consensus policy can reuse it without an import cycle. Use it from there.
|
|
//
|
|
// These helpers do NOT verify signatures; they classify the envelope
|
|
// SHAPE. Use VerifyWithOptions / VerifyPQLanes to check the bytes.
|
|
|
|
package pulsar
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/luxfi/warp"
|
|
)
|
|
|
|
// ErrNotHorizonFinal is returned when an envelope cannot be lifted into
|
|
// a Horizon-final certificate. It typically means the envelope is
|
|
// missing one or more of the Beam / ML-DSA / Corona lanes that Prism
|
|
// requires (Definition ref:prism in finality-definitions.tex).
|
|
var ErrNotHorizonFinal = errors.New("warp pulsar: envelope is not Horizon-final")
|
|
|
|
// IsPQFinal reports whether the envelope carries enough evidence to
|
|
// be Horizon-final under Prism. The required shape, per
|
|
// proofs/definitions/finality-definitions.tex Definition
|
|
// ref:horizon-cert, is:
|
|
//
|
|
// - A v1 Beam (BLS aggregate over the v1 Message). Always present;
|
|
// the v1 Message field is non-nil.
|
|
// - An ML-DSA cert set (either raw FIPS-204 signatures or a
|
|
// compressed Groth16 rollup of those signatures). The shape is
|
|
// the same on the wire.
|
|
// - A Corona Ringtail signature — a real Module-LWE lattice threshold
|
|
// signature over the envelope transcript. This is the PQ root of
|
|
// trust; without it the envelope is not Horizon-final, regardless of
|
|
// how impressive the wrapper proof attached to the ML-DSA lane is.
|
|
//
|
|
// In particular: an envelope carrying a Groth16 rollup of ML-DSA
|
|
// verification but no Corona signature is NOT Horizon-final. The Groth16
|
|
// wrapper is a classical compatibility / compression / privacy adapter
|
|
// (see warp.IsPQRootOfTrust); it does not contribute PQ liveness on its
|
|
// own.
|
|
func IsPQFinal(env *warp.Envelope) bool {
|
|
if env == nil {
|
|
return false
|
|
}
|
|
if !env.HasMLDSACertSet() {
|
|
return false
|
|
}
|
|
if !env.HasCorona() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// HorizonFinalErr returns nil when env is Horizon-final-shaped, or
|
|
// ErrNotHorizonFinal wrapping a structured reason string when it is
|
|
// not. Use this in receivers that want to distinguish between
|
|
// "envelope shape OK, signature failed" (returned by VerifyV2) and
|
|
// "envelope shape inadmissible".
|
|
func HorizonFinalErr(env *warp.Envelope) error {
|
|
if env == nil {
|
|
return ErrNotHorizonFinal
|
|
}
|
|
missing := make([]string, 0, 3)
|
|
if !env.HasMLDSACertSet() {
|
|
missing = append(missing, "MLDSACertSet")
|
|
}
|
|
if !env.HasCorona() {
|
|
missing = append(missing, "CoronaRingtail")
|
|
}
|
|
if len(missing) > 0 {
|
|
return errors.New("warp pulsar: envelope missing required lanes: " + strings.Join(missing, ", "))
|
|
}
|
|
return nil
|
|
}
|