Files
HIPs/HIPs/hip-0079-q-chain-finality-blocks.md
zeekay ad5a010f79 Three words for what a proposal is, and a generator that refuses
The same fact lived in nine hand-kept places and they disagreed about 58
proposals. This makes the file the only authority and everything else a
projection of it.

ONE VOCABULARY, in vocabulary.json, read by the lint, the generator and the
doc site. Three words, each answering "can I rely on this?" and nothing else:

    Draft    nothing implements it yet, or its own text says the work is ahead
    Final    the thing it specifies exists in the code
    Living   governance and process: amended in place, never finished

Eight spellings folded in. `Active` was the one doing real damage: on four Meta
and Process HIPs it meant living, on 48 others it meant shipped, and one word
doing two jobs is why nothing agreed. `Review` and `Last Call` described a
queue rather than a document, and a proposal under review is still one you
cannot build against.

STATUS IS EARNED, not declared. Every Standards Track proposal was checked
against the estate: 235 have their implementation, and eight do not and say so.
HIP-0095 carries a twelve-week plan ending at "mainnet launch" and has no code.
HIP-0525 specifies an all-machine meeting while apps/meet refuses a machine a
seat. HIP-0078 names a prover at a "planned location" that does not exist.
HIP-0114 is the cog.* procedure set, and that procedure set returns nothing.
HIP-0003, HIP-0102, HIP-0305 and HIP-0901 likewise: looked, found nothing.

SIXTEEN TOMBSTONES DELETED rather than statused. A dead document kept alive in
the index is one concept wearing two names. Each successor exists and carries
the text, and the 90 references that pointed at a tombstone now point at it --
whole markdown links at once, so link text cannot outlive its target. Three of
the sixteen were pure compatibility: HIP-0028 specified a migration off a
Bitnami chart, HIP-0029 dual-engine MySQL, HIP-0030 a schema-deprecation
policy. We ship no backwards compatibility, so we do not specify it.

Nine links were already broken and had been since the corpus was renumbered:
short-form ./hip-5.md paths that never existed. Two named the wrong proposal --
one cited "HIP-1: Hanzo Multimodal Models" where multimodal is HIP-0003.

THE GENERATOR REFUSES more often than it writes, because a short table looks
exactly as authoritative as a complete one. Rows must equal files; a shrinking
index must say by how much; a section whose input is missing is an error, not
an omission; nothing is written until all of it validates. Its self-test breaks
the corpus eleven ways and requires each to refuse AND leave README byte-
identical -- a guard that reports after half-writing has protected nothing.
The lint's second implementation of the same comparison is gone.

The site rendered 52 proposals with a fallback grey badge and counted them
toward none of its three stat tiles, because docs/lib/source.ts declared a
status union by hand: it admitted a `Stagnant` no file has ever carried and
omitted the one 52 files did. Five components each kept their own copy of the
colours. All six now read vocabulary.json.
2026-08-28 18:05:41 -07:00

8.8 KiB

hip, title, type, category, status, author, requires, created
hip title type category status author requires created
0079 Q-Chain — Quasar Finality Block Standard Standards Track Infrastructure Final Hanzo AI HIP-0077, HIP-0078, HIP-0084 2026-05-10

HIP-0079: Q-Chain — Quasar Finality Block Standard

Abstract

Q-Chain is the compact finality-block log for the Quasar consensus engine. Each Q-Block carries the minimum information needed for finality: roots of the validator / DKG / identity state held in Z-Chain (HIP-0078) plus a single Pulsar-M-65 threshold signature (HIP-0084) that proves the current epoch's committee finalised the block. Q-Chain does not carry raw ML-DSA validator pubkeys, raw ML-DSA per-validator signatures, raw DKG transcript messages, or raw attestation blobs — those live in Z-Chain.

The result: a finality lane whose per-block size is bounded and small (a few hundred bytes plus one ~3.3 KB Pulsar-M-65 signature), independent of validator-set size. Adding validators makes Z-Chain heavier, not Q-Chain.

Specification

Q-Block structure

QBlock {
    version                      uint16
    network_id                   uint32
    chain_id                     uint32
    height                       uint64
    round_or_view                uint32
    parent_qblock_hash           [32]byte

    // State roots — Z-Chain anchors. Each MUST equal the latest
    // accepted EpochCommitment field for the current epoch.
    lux_state_root               [32]byte    // Lux primary state at this height
    zchain_state_root            [32]byte    // anchors EpochCommitment.zchain_state_root
    validator_set_root           [32]byte    // anchors EpochCommitment.validator_registry_root
    committee_root               [32]byte    // anchors EpochCommitment.committee_root
    dkg_transcript_root          [32]byte    // anchors EpochCommitment.dkg_transcript_root
    group_public_key_hash        [32]byte    // anchors EpochCommitment.group_public_key_hash

    // Payload anchors — block-specific data lives elsewhere.
    payload_root                 [32]byte    // application transactions
    da_root                      [32]byte    // data-availability commitment

    // Cert envelope.
    proof_system_id              ProofSystemID  // = 0x10 STARK_FRI_SHA3_PQ canonical (0x80/0x81 refused)
    hash_suite_id                HashSuiteID    // = 0x01 SHA3_NIST canonical
    sig_scheme_id                SigSchemeID    // = 0x52 Pulsar-M-65 default; 0x53 for high-value roots

    // Signature.
    signer_bitmap_or_weight_commitment  []byte  // who attested (committee bitmap or weight Merkle root)
    pulsar_m_threshold_signature        []byte  // single threshold sig over the canonical transcript
}

What Q-Chain MUST NOT carry

Hard exclusions:

  • Full validator ML-DSA-65 public keys (live in Z-Chain identity_root).
  • Per-validator ML-DSA-65 identity signatures (live in Z-Chain validator-registration proofs).
  • Full DKG ceremony messages (live in Z-Chain dkg_transcript_root).
  • Per-validator attestation blobs (live in Z-Chain).
  • Z-Chain proof bytes for non-epoch-boundary blocks (the cert references zchain_state_root by hash; the proof itself only ships at epoch boundaries).

A Q-Block that carries any of these violates the spec and MUST be rejected. The wire format does not have fields to hold them; this is an enforcement-by-construction guarantee.

Canonical transcript binding

The transcript Pulsar-M signs over for a Q-Block is computed as:

TranscriptHash = TupleHash256(
    [ "Q-Chain/v1",
      version,           // 2 bytes BE
      network_id,        // 4
      chain_id,          // 4
      height,            // 8
      round_or_view,     // 4
      parent_qblock_hash,
      lux_state_root,
      zchain_state_root,
      validator_set_root,
      committee_root,
      dkg_transcript_root,
      group_public_key_hash,
      payload_root,
      da_root,
      [proof_system_id],          // 1
      [hash_suite_id],             // 1
      [sig_scheme_id],             // 1
      signer_bitmap_or_weight_commitment ],
    customization = "PULSAR-M-Q-BLOCK-V1")

TupleHash256 per NIST SP 800-185 is used for unambiguous length-prefixed tuple hashing. Customisation tags pin the protocol version; bumping "v1" → "v2" is the only way to reuse the same transcript bytes for a breaking change. Hash family is HashSuiteID = 0x01 SHA3_NIST only — HashSuite-mismatched Q-Blocks are not produceable on Lux primary network.

Acceptance rule

A full node accepts a Q-Block at height H iff every of the following holds:

  1. parent_qblock_hash matches the chain tip.
  2. zchain_state_root matches the latest accepted Z-Chain epoch root.
  3. validator_set_root matches the latest accepted Z-Chain validator_registry_root (per EpochCommitment).
  4. committee_root matches the latest accepted Z-Chain committee_root.
  5. dkg_transcript_root matches the latest accepted Z-Chain dkg_transcript_root.
  6. group_public_key_hash matches the latest accepted Z-Chain group_public_key_hash.
  7. proof_system_id is NOT in the forbidden set (IsForbiddenInPQMode() == false); strict-PQ networks require 0x10 STARK_FRI_SHA3_PQ.
  8. hash_suite_id == 0x01 SHA3_NIST.
  9. sig_scheme_id ∈ {0x52 Pulsar-M-65, 0x53 Pulsar-M-87} for mainnet; testnet may also accept 0x51 Pulsar-M-44.
  10. signer_bitmap_or_weight_commitment satisfies the configured threshold (43-of-64 default per HIP-0084).
  11. pulsar_m_threshold_signature verifies under unmodified FIPS 204 ML-DSA.Verify against the canonical transcript hash and the group public key derived from group_public_key_hash.

If any check fails, the block is rejected and a slashing-evidence record is produced per HIP-0084 §"Identifiable abort".

Production defaults

parameter value
version 0x0001
proof_system_id 0x10 (STARK_FRI_SHA3_PQ)
hash_suite_id 0x01 (SHA3_NIST)
sig_scheme_id 0x52 (Pulsar-M-65)
committee size 64
BFT fault bound f = 21
threshold 43-of-64
epoch length network-configurable; start 1 hour
DKG cadence once per epoch (HIP-0084)
online signing preprocessing-enabled
abort handling identifiable abort required

High-value roots (governance, bridges, slashing, archival) MAY upgrade sig_scheme_id to 0x53 Pulsar-M-87 for the duration of the operation. Block consumers MUST accept either.

What changes for Quasar mode

PQModeQuasar (consensus/config/pq_mode.go) maps to:

  • proof_system_id = 0x10 STARK_FRI_SHA3_PQ
  • hash_suite_id = 0x01 SHA3_NIST
  • sig_scheme_id = 0x52 Pulsar-M-65 (default) or 0x53 Pulsar-M-87 (high-value)

The legacy "Pulsar.R + Groth16/BN254 Z-Chain rollup" wire shape from the prior HIP-0077 is retired with the migration to Z-Chain v1 (HIP-0078 Phase 3 flag day).

Security considerations

  • No silent downgrade. A Q-Block that carries proof_system_id = 0x80 or 0x81 is refused before signature verification. The forbidden markers exist on the wire so a misconfigured operator's block is named, not silently accepted.
  • Single threshold sig per block. Multi-sig fallback paths (per-validator BLS + ML-DSA, classical/PQ hybrid) are NOT in this HIP. A future variant MAY add hybrid-cert support but it lives in a follow-up HIP, not here.
  • Root tampering. All root fields are bound into the transcript; flipping any one breaks signature verification.
  • Signer-bitmap forgery. Bitmap is bound into the transcript and validated against committee_root; an attacker who flips a bit must produce a valid Pulsar-M sig over the modified transcript, which reduces to forging Pulsar-M.
  • Replay across networks. network_id and chain_id are bound into the transcript; a Q-Block valid on Lux mainnet is not valid on a tenant network.

Adoption order

  1. Wire format frozen (this HIP).
  2. consensus/pkg/wire/qblock.go reference encoder/decoder.
  3. Test vectors at ~/work/lux/consensus/qblock-vectors/ covering acceptance, refusal-on-forbidden-proof-system, transcript binding.
  4. Quasar driver (consensus/protocol/quasar/) consumes Q-Blocks from WitnessSet.Run outputs.
  5. Q-Chain VM at chains/quasarvm (planned location) implements acceptance rule.
  6. Phase 2 of HIP-0078 ships, gating the proof_system_id = 0x10 requirement on Lux primary network.

References

  • HIP-0077 — Mesh Identity, Gossip & Payments (parent).
  • HIP-0078 — Z-Chain PQ Identity Rollup (root source).
  • HIP-0084 — Pulsar-M DKG (signing primitive).
  • FIPS 204 — ML-DSA verification target.
  • FIPS 202 + NIST SP 800-185 — TupleHash256 transcript family.