transcript

the Fiat-Shamir transcript converts zheng's interactive proof into a non-interactive one. the prover maintains a running hemera hash of every message exchanged. each verifier challenge is derived by hashing the transcript so far. the verifier recomputes the same transcript and checks consistency.

construction

transcript state: H = hemera_init(DOMAIN_SEP)

DOMAIN_SEP = hemera(0x01 | "zheng-transcript-v1")

absorb(message):
  H = hemera_absorb(H, message)

squeeze(n_challenges):
  challenges = hemera_squeeze(H, n_challenges)
  H = hemera_absorb(H, challenges)
  return challenges

the transcript is a sponge: absorb prover messages, squeeze verifier challenges. hemera's sponge construction (Poseidon2 with 512-bit state, 256-bit capacity) provides 128-bit security against transcript manipulation.

domain separation

each proof phase uses a distinct domain separator to prevent cross-phase attacks:

phase separator purpose
commitment 0x02 | "commit" binds WHIR commitment to transcript
sumcheck round i 0x03 | i each round gets a unique challenge domain
evaluation 0x04 | "eval" separates evaluation point from constraint checks
WHIR opening 0x05 | "whir-open" prevents reuse of sumcheck challenges in PCS
recursive 0x06 | "recurse" inner proof transcripts isolated from outer

domain separators are absorbed before the corresponding message. this ensures that identical messages in different phases produce different challenges.

transcript format

a serialized proof contains the full sequence of prover messages. the verifier reconstructs the transcript by absorbing each message in order and checking that derived challenges match.

proof = [
  commitment: [u8; 64],           // WHIR commitment (hemera digest)
  sumcheck_polynomials: [         // one per round
    [GoldilocksElement; deg+1],   // coefficients of univariate gᵢ
  ],
  evaluation_value: GoldilocksElement,  // f(r) at sumcheck output point
  whir_opening: WHIRProof,        // WHIR evaluation proof
]

the verifier processes this sequentially: absorb commitment → squeeze sumcheck challenges → absorb each sumcheck polynomial → squeeze next challenge → ... → absorb evaluation → verify WHIR opening.

binary encoding

all multi-byte integers are little-endian. all field elements are in canonical form (value < p where p = 2^64 - 2^32 + 1).

GoldilocksElement

8 bytes. the canonical u64 representation in little-endian byte order. the value must satisfy 0 ≤ v < p. any encoding with v ≥ p is rejected by the verifier.

GoldilocksElement := u64_le(v)    // 8 bytes, v < 2^64 - 2^32 + 1

commitment

64 bytes. a hemera digest consists of 8 GoldilocksElements concatenated in order, each encoded as 8 bytes LE.

Commitment := GoldilocksElement[0] ∥ GoldilocksElement[1] ∥ ... ∥ GoldilocksElement[7]
           // 8 × 8 = 64 bytes

sumcheck polynomial (per round)

each round emits one univariate polynomial gᵢ of degree d. the encoding is:

SumcheckPoly :=
  degree: u8                          // 1 byte, value d
  coefficients: GoldilocksElement[d+1] // (d+1) × 8 bytes

coefficients are in ascending order: [c_0, c_1, ..., c_d] where gᵢ(X) = c_0 + c_1·X + c_2·X² + ... + c_d·X^d. each coefficient is an 8-byte LE u64 in canonical form.

total per round: 1 + 8·(d+1) bytes.

evaluation value

8 bytes. a single GoldilocksElement encoding f(r) at the sumcheck output point.

EvaluationValue := GoldilocksElement    // 8 bytes LE

WHIRProof

the WHIR opening proof encodes folding rounds followed by the final value:

WHIRProof :=
  for each folding round:
    round_commitment: Commitment        // 64 bytes
    query_count: u16_le                 // 2 bytes
    for each query (query_count times):
      position: u32_le                  // 4 bytes
      leaf_value: GoldilocksElement     // 8 bytes LE
      merkle_path: Commitment[depth]    // depth × 64 bytes
  final_value: GoldilocksElement        // 8 bytes LE

the number of folding rounds and the Merkle depth per round are determined by the WHIR parameters (see WHIR). the verifier knows these from the public configuration.

full proof wire format

Proof :=
  commitment: Commitment               // 64 bytes
  num_rounds: u16_le                   // 2 bytes
  sumcheck_polys: SumcheckPoly[num_rounds]
  evaluation: EvaluationValue          // 8 bytes
  whir_proof: WHIRProof

the prover writes fields in this exact order. the verifier reads them sequentially, absorbing each into the Fiat-Shamir transcript as it goes.

proof size calculation

for a circuit with n variables (2^n constraints):

at 100-bit security (typical parameters: n = 20, degree d = 3, 50 queries, 4 folding rounds, Merkle depth ~16):

  • commitment: 64 bytes
  • num_rounds: 2 bytes
  • sumcheck_polys: 20 × (1 + 4×8) = 20 × 33 = 660 bytes
  • evaluation: 8 bytes
  • whir_proof:
    • 4 rounds × (64 + 2 + 50 × (4 + 8 + 16×64)) = 4 × (66 + 50 × 1036) = 4 × 51866 = 207464 bytes
    • but with rate-halving per round, later rounds have fewer queries and shallower trees
    • effective total ~59 KiB
  • full proof ≈ 60 KiB

at 128-bit security (n = 20, degree d = 3, 83 queries, 4 folding rounds, Merkle depth ~16):

  • higher query count per round increases WHIR opening size proportionally
  • effective total ~157 KiB

these sizes match the design targets in WHIR and zheng.

properties

property value
hash function hemera (Poseidon2 over Goldilocks field)
state size 512 bits (8 field elements)
capacity 256 bits (4 field elements)
security 128-bit classical, 85+ bit post-quantum
challenge type native Goldilocks field elements (no truncation)
domain separation per-phase prefix absorb

soundness

if hemera behaves as a random oracle, the Fiat-Shamir transcript is as sound as the interactive protocol. the soundness error per sumcheck round is at most d/p, where d is the polynomial degree and p = 2^64 - 2^32 + 1. across k rounds, total soundness error ≤ kd/p — negligible for any practical parameters.

the critical property: challenges are native Goldilocks field elements. no reduction, no truncation, no modular bias. hemera outputs field elements directly.

see sumcheck for the protocol that generates transcript messages, WHIR for the opening proof format, hemera for the hash construction

Dimensions

transcript

Local Graph