quasar

package
v1.31.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2026 License: BSD-3-Clause Imports: 5 Imported by: 0

Documentation

Overview

Package quasar is the node-side integration of the luxfi/consensus Quasar post-quantum finality-certificate layer.

luxd finalizes blocks on the classical Snow/Avalanche path (fast, every block). On top, at CHECKPOINTS (epoch boundaries — NOT every block), a sampled committee produces a QuasarCert over the finalized digest and validators VERIFY it. This package wires the VERIFY half: it consumes github.com/luxfi/consensus/protocol/quasar.VerifyConsensusCert as an OPTIONAL, FORWARD-DATED, DORMANT-BY-DEFAULT check in the block-accept path.

Safety contract

The forward-dated dormant activation is the whole reason this package has the shape it does:

  • Pre-activation (the default): VerifyAccepted is a pure no-op. Classical Snow finality is UNCHANGED. A nil *Gate, a zero Gate, or an unset activation height all mean "dormant" — zero behavior change.
  • Post-activation (owner sets Activation.Height to a real, forward-dated height): at every checkpoint height the gate REQUIRES a valid QuasarCert bound to the just-finalized block and FAILS CLOSED — a missing or invalid cert returns an error from Accept(), halting the chain rather than finalizing a checkpoint without post-quantum evidence.

Activation is therefore a deliberate switch the owner flips only AFTER the cert PRODUCER (the per-validator committee signer) is live and certs flow at the checkpoint cadence — otherwise every checkpoint would halt. See producer.go.

Default posture

HYBRID_PQ = Beam(BLS) ∧ Pulsar (standard FIPS-204 threshold ML-DSA), at checkpoint cadence. Per the measured policy-tier benchmarks, Pulsar verify (~140µs) is cheaper than BLS itself and the cert is compact (~27KB) — the right default production finality posture. STRICT_DUAL_PQ (∧ Corona) and the POLARIS tiers (∧ Magnetar) are configurable for stricter mainnet finality.

Index

Constants

View Source
const DefaultCheckpointInterval uint64 = 256

DefaultCheckpointInterval is the default checkpoint cadence in blocks. PQ certs ride epoch-boundary checkpoints, never every block (Magnetar sign is checkpoint-only; even the cheap Pulsar sign is checkpoint cadence). The owner overrides this to match the producer's cadence at activation time.

DefaultMode is the default Quasar posture: HYBRID_PQ (Beam ∧ Pulsar).

Variables

View Source
var (
	// ErrFinalityCertMissing — a checkpoint was finalized post-activation but no
	// QuasarCert is available for it (the producer has not delivered one).
	ErrFinalityCertMissing = errors.New("quasar: finality cert missing for checkpoint")

	// ErrFinalityCertMismatch — a cert exists but does not bind the finalized
	// block (chain/height/block/state mismatch). Anti-replay.
	ErrFinalityCertMismatch = errors.New("quasar: finality cert does not bind the finalized block")

	// ErrFinalityCertInvalid — the cert is bound correctly but failed consensus
	// verification (policy, validator-set root, or a leg signature).
	ErrFinalityCertInvalid = errors.New("quasar: finality cert failed verification")

	// ErrValidatorSetUnavailable — no committed validator set for the cert's
	// epoch (the verifier cannot resolve the per-leg verification keys).
	ErrValidatorSetUnavailable = errors.New("quasar: validator set unavailable for epoch")

	// ErrPolicyUnavailable — the gate has no configured policy.
	ErrPolicyUnavailable = errors.New("quasar: policy unavailable")

	// ErrPolicyMismatch — the cert's PolicyID is not the configured policy. A
	// cert cannot select its own (weaker) posture.
	ErrPolicyMismatch = errors.New("quasar: cert policy id does not match configured policy")

	// ErrGateMisconfigured — the gate is activated at a checkpoint but has no
	// cert store or validator provider. Fail closed rather than panic.
	ErrGateMisconfigured = errors.New("quasar: gate activated but missing store or validator provider")
)

Typed, fail-closed errors. Every one is returned (never swallowed) and, when surfaced from the accept hook post-activation, halts finalization rather than accepting a checkpoint without valid post-quantum evidence.

Functions

This section is empty.

Types

type ActivationConfig added in v1.31.0

type ActivationConfig struct {
	// Height is the block height at and above which PQ-finality verification is
	// enforced at checkpoints. 0 == dormant (never).
	Height uint64
}

ActivationConfig is the forward-dated activation switch.

The zero value is DORMANT: Height == 0 means "never activate" and the gate is a no-op for every block. This mirrors the genesis upgrade discipline (a far-future / unset activation point cannot affect live finality).

Activation is by HEIGHT ONLY, deliberately. A block height is agreed by consensus, so every honest validator enforces PQ finality at exactly the SAME checkpoints — there is no node-local decision. (A wall-clock gate would split finalization across validators with skewed clocks: some halting on a missing cert while others finalize without one. Timestamp-based forward-dating is expressed by choosing the activation HEIGHT at the target time.)

type CertStore added in v1.31.0

type CertStore interface {
	Lookup(chainID uint32, height uint64, blockID [32]byte) (*qcert.ConsensusCert, bool)
}

CertStore resolves the QuasarCert that certifies a finalized block. In production it is filled by the cert gossip/ingest path (the producer follow-on, producer.go); the verify gate only READS from it. Keyed by the finalized position (chainID, height, blockID) so a cert can never be returned for the wrong block.

type Checkpoint added in v1.31.0

type Checkpoint struct {
	Epoch     uint64
	Height    uint64
	Round     uint32
	BlockID   [32]byte
	StateRoot [32]byte
}

Checkpoint is the finalized-block position the accept hook hands the gate. It is the binding the cert must match (anti-replay): a valid cert for a DIFFERENT block must never satisfy THIS checkpoint. The chain id is gate-level config, not a per-block field.

type Config added in v1.22.79

type Config struct {
	// ChainID is THIS chain's numeric identifier (the sovereign/EVM chain id),
	// bound into every cert and checked against it. A per-chain constant sourced
	// from chain config at gate construction — NOT pulled from a block, because
	// the proposervm layer carries the 32-byte validator-set id, not the numeric
	// chain id. Inert while dormant.
	ChainID uint32

	// Activation is the forward-dated dormant switch. Zero => dormant.
	Activation ActivationConfig

	// Mode is the Quasar evidence posture. Zero => DefaultMode (HYBRID_PQ).
	Mode qcert.QuasarEvidenceMode

	// MLDSAParam selects the ML-DSA parameter set for the Pulsar leg. 0 =>
	// ML-DSA-65 (the consensus default).
	MLDSAParam uint8

	// Threshold is the BFT quorum floor (minimum aggregate signer weight) every
	// leg's evidence must establish.
	Threshold uint64

	// CheckpointInterval is the checkpoint cadence in blocks. 0 =>
	// DefaultCheckpointInterval.
	CheckpointInterval uint64
}

Config is the node-surfaced PQ-finality configuration. Its zero value is dormant + HYBRID_PQ + default cadence.

type Gate added in v1.31.0

type Gate struct {
	// contains filtered or unexported fields
}

Gate enforces (or, dormant, ignores) PQ-finality at checkpoints. It is the single node-side seam between the classical accept path and the consensus Quasar verifier.

func NewGate added in v1.31.0

func NewGate(cfg Config, store CertStore, validators ValidatorSetProvider) *Gate

NewGate constructs a Gate. A Gate is meaningful even with a dormant Config: VerifyAccepted is a no-op until Activation.Height is set. store and validators are only consulted post-activation at checkpoints.

func (*Gate) Activated added in v1.31.0

func (g *Gate) Activated(height uint64) bool

Activated reports whether enforcement is live for a block at the given height and the current wall clock. Used by the producer-request site to decide whether a cert is needed at a checkpoint.

func (*Gate) IsCheckpoint added in v1.31.0

func (g *Gate) IsCheckpoint(height uint64) bool

IsCheckpoint reports whether the given height is a checkpoint under the gate's configured cadence. Exported so the producer-request site shares ONE cadence definition with the verify path (no second source of truth).

func (*Gate) MaybeProduce added in v1.31.0

func (g *Gate) MaybeProduce(ctx context.Context, producer Producer, cp Checkpoint) (*qcert.ConsensusCert, error)

MaybeProduce is the checkpoint producer-request site. It is nil-safe and activation-aware so the accept hook can call it unconditionally: a nil gate, dormant activation, a non-checkpoint height, or a nil producer all short- circuit to (nil, nil) — the verify-only default. When a producer IS wired and the checkpoint is live, it requests the cert; the caller gossips/stores it.

This keeps producer cadence and verify cadence on ONE definition (g.IsCheckpoint), so producer and verifier can never disagree on which heights carry certs.

func (*Gate) VerifyAccepted added in v1.31.0

func (g *Gate) VerifyAccepted(cp Checkpoint) error

VerifyAccepted is the accept-path hook and the SAFETY BOUNDARY.

  • g == nil OR dormant activation => returns nil immediately. This is the default and guarantees classical Snow finality is unchanged.
  • height below activation, or activation time not yet reached => nil.
  • not a checkpoint height => nil (certs ride checkpoints only).
  • checkpoint, activated => REQUIRE a valid cert bound to this block; FAIL CLOSED. A missing, mis-bound, or invalid cert is an error (the caller returns it from Accept, halting rather than finalizing without PQ evidence).

It is intentionally nil-safe so the proposervm hook can call vm.quasarGate.VerifyAccepted(...) unconditionally with a nil gate.

type MemCertStore added in v1.31.0

type MemCertStore struct {
	// contains filtered or unexported fields
}

MemCertStore is an in-memory CertStore keyed by (chainID, height, blockID). It is the ingest sink the cert-gossip handler writes into (Put) and the gate reads from (Lookup). Safe for concurrent use.

func NewMemCertStore added in v1.31.0

func NewMemCertStore() *MemCertStore

NewMemCertStore returns an empty in-memory cert store.

func (*MemCertStore) Lookup added in v1.31.0

func (m *MemCertStore) Lookup(chainID uint32, height uint64, blockID [32]byte) (*qcert.ConsensusCert, bool)

Lookup returns the cert for the finalized position, or (nil, false).

func (*MemCertStore) Put added in v1.31.0

func (m *MemCertStore) Put(cert *qcert.ConsensusCert)

Put indexes a cert by its own (ChainID, Height, BlockHash). The ingest path MUST verify a cert before Put (verify-before-store), exactly as the gossip layer verifies before re-gossip; the gate re-verifies at the checkpoint so a store poisoned by an unverified Put still cannot finalize an invalid cert.

type Producer added in v1.31.0

type Producer interface {
	Produce(ctx context.Context, subject Subject) (*qcert.ConsensusCert, error)
}

Producer is the committee cert-signing service contract (the per-validator "pulsard" committee). At a checkpoint, a producing validator calls Produce to obtain the QuasarCert over the finalized subject, then gossips it so peers can verify and store it (via a CertStore).

SCAFFOLDING — this is the seam, not the service. This milestone wires the VERIFY half (gate.go) and this interface. luxd ships with a nil Producer (verify-only): a node VERIFIES certs it receives but does not itself produce them. A nil Producer is the correct default — most of the rollout window is verify-only, and the producer is brought up before activation is forward-dated.

Implementation path for the follow-on:

  • github.com/luxfi/consensus/protocol/quasar already defines the producer-side abstractions: PWitnessProducer / QWitnessProducer / ZWitnessProducer + NewWitnessSet, and ComposeDualPQEvidence. The concrete committee signer implements Producer over those.
  • The signer needs the live Pulsar key share + nonce pool + offline preprocessing + one-round sign + verify-before-gossip + nonce-erase (the no-reconstruct hyperball signer), which lands with pulsar v1.7.1.
  • REQUIRED CONSENSUS EXPORT: the ConsensusCert envelope + per-leg payload ENCODERS are package-private in consensus v1.29.0 (only the verifiers are exported). An external producer — and any end-to-end "valid cert verifies through the gate" test — needs those encoders exported (a small, additive consensus change). The verify path here needs no such export: it consumes a fully-formed *ConsensusCert.

type StaticValidatorSetProvider added in v1.31.0

type StaticValidatorSetProvider struct{ Set qcert.ConsensusValidatorSet }

StaticValidatorSetProvider returns the same committed set for every (chain, epoch). It is the single-era / test provider; the production provider resolves per-epoch sets from the P-Chain validator manager + KeyEra registry.

func (StaticValidatorSetProvider) ValidatorSet added in v1.31.0

ValidatorSet implements ValidatorSetProvider.

type Subject added in v1.31.0

type Subject struct {
	ChainID   uint32
	Epoch     uint64
	Height    uint64
	Round     uint32
	BlockID   [32]byte
	StateRoot [32]byte
}

Subject is the finalized-block position a cert must certify — the producer's input at a checkpoint. Mirrors Checkpoint (the verify side) so producer and verifier bind the SAME tuple.

type ValidatorSet added in v1.31.0

type ValidatorSet struct {
	// contains filtered or unexported fields
}

ValidatorSet is a concrete ConsensusValidatorSet for one epoch: the committed weighted-validator-set root plus the per-leg verification keys the cert legs verify against (the classical BLS aggregate key for the Beam leg and the Pulsar ML-DSA threshold group key for the Pulsar leg — the HYBRID_PQ pair).

Production wiring (the activation seam): in production these fields are populated from the P-Chain-pinned validator set (Root, Epoch) and the active KeyEra group keys for the epoch. That population is era/rotation-coupled and lands with the producer + KeyEra-registry wiring (see producer.go). Corona (STRICT_DUAL_PQ) and Magnetar/P3Q (POLARIS / RECOVERY) group keys + the weighted-sig-set config are populated by that same follow-on; until then this set serves the HYBRID_PQ pair and reports "no key" for the other lanes.

func NewValidatorSet added in v1.31.0

func NewValidatorSet(root [48]byte, epoch uint64, blsAggKey, pulsarGroup []byte) *ValidatorSet

NewValidatorSet builds a committed validator set for one epoch from its root and the HYBRID_PQ verification keys.

func (*ValidatorSet) ClassicalAggregateKey added in v1.31.0

func (v *ValidatorSet) ClassicalAggregateKey(scheme qcert.ClassicalScheme) ([]byte, bool)

ClassicalAggregateKey returns the classical aggregate verification key for a scheme. Serves the BLS-12-381 Beam leg.

func (*ValidatorSet) Epoch added in v1.31.0

func (v *ValidatorSet) Epoch() uint64

Epoch returns the epoch this set was committed under.

func (*ValidatorSet) Root added in v1.31.0

func (v *ValidatorSet) Root() [48]byte

Root returns the 48-byte weighted-validator-set commitment.

func (*ValidatorSet) ThresholdGroupKey added in v1.31.0

func (v *ValidatorSet) ThresholdGroupKey(kind qcert.LegKind) (qcert.ThresholdGroupKey, bool)

ThresholdGroupKey returns the threshold-signature group public key for a leg kind. Serves the Pulsar (ML-DSA) lane; reports (zero, false) for the others until their group keys are wired by the follow-on.

func (*ValidatorSet) WeightedConfig added in v1.31.0

func (v *ValidatorSet) WeightedConfig() qcert.QuorumVerifierConfig

WeightedConfig returns the QuorumVerifierConfig for the WeightedSigSet evidence mode. HYBRID_PQ does not use weighted-sig-set legs; the zero config is correct here and is populated by the POLARIS / RECOVERY follow-on.

func (*ValidatorSet) WeightedEnvelope added in v1.31.0

func (v *ValidatorSet) WeightedEnvelope() qcert.QuorumMessageEnvelope

WeightedEnvelope returns the round-digest posture axes for the inner WeightedQuorumCert. Zero for HYBRID_PQ (no weighted-sig-set leg); populated by the POLARIS / RECOVERY follow-on.

type ValidatorSetProvider added in v1.31.0

type ValidatorSetProvider interface {
	ValidatorSet(chainID uint32, epoch uint64) (qcert.ConsensusValidatorSet, error)
}

ValidatorSetProvider resolves the committed validator set the verifier pins a cert against, for a (chain, epoch). Post-activation the gate calls this once per verified checkpoint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL