hip-0079

HIP-79: Q-Chain — Quasar Finality Block Standard. Status Draft. Hanzo's own standard — read this before implementing against it.

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:

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).

  1. committee_root matches the latest accepted Z-Chain

committee_root.

  1. dkg_transcript_root matches the latest accepted Z-Chain

dkg_transcript_root.

  1. group_public_key_hash matches the latest accepted Z-Chain

group_public_key_hash.

  1. proof_system_id is NOT in the forbidden set

(IsForbiddenInPQMode() == false); strict-PQ networks require 0x10 STARK_FRI_SHA3_PQ.

  1. hash_suite_id == 0x01 SHA3_NIST.
  2. sig_scheme_id ∈ {0x52 Pulsar-M-65, 0x53 Pulsar-M-87} for

mainnet; testnet may also accept 0x51 Pulsar-M-44.

  1. signer_bitmap_or_weight_commitment satisfies the configured

threshold (43-of-64 default per HIP-0084).

  1. 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:

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

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.

(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.

flipping any one breaks signature verification.

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.

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.

  1. Quasar driver (consensus/protocol/quasar/) consumes Q-Blocks

from WitnessSet.Run outputs.

  1. Q-Chain VM at chains/quasarvm (planned location) implements

acceptance rule.

  1. Phase 2 of HIP-0078 ships, gating the proof_system_id = 0x10

requirement on Lux primary network.

References