constraints

the AIR constraint format and encoding rules for zheng. each nox reduction pattern maps to a polynomial transition constraint over the Goldilocks field. SuperSpartan verifies these constraints via CCS and the sumcheck protocol.

constraint structure

a constraint is a polynomial equation C_p(t) that must equal zero at every trace row where pattern p fires:

C_p(t) = polynomial_expression(registers_at_row_t, registers_at_row_{t+1})

the combined constraint over all patterns:

C(t) = Σ_{p=0}^{16} selector_p(r0_t) × C_p(t)

where selector_p(r0_t) = 1 when r0_t = p, 0 otherwise. constructed via Lagrange interpolation over the 17 pattern values.

pattern constraint table

pattern name constraint degree count
0 axis navigation through subject tree 1 ~depth
1 quote r5_{t+1} = literal 1 1
2 compose chain: eval x, eval y, eval result on x's output 1 2
3 cons pair two sub-results 1 2
4 branch selector × (next − yes) + (1 − sel) × (next − no) = 0 2 2
5 add r5_{t+1} = r3_t + r4_t 1 1
6 sub r5_{t+1} = r3_t − r4_t 1 1
7 mul r5_{t+1} = r3_t × r4_t 2 1
8 inv r5_{t+1} × r3_t = 1 2 1
9 eq (r3_t − r4_t) × inv = flag, r5_{t+1} = 1 − flag 2 1
10 lt range decomposition into 64 bits 1 ~64
11 xor bit decomposition + XOR per bit 2 ~64
12 and bit decomposition + AND per bit 2 ~64
13 not bitwise complement 1 ~64
14 shl shift via multiplication by 2^n 2 ~64
15 hash Poseidon2 round: state_{t+1} = MDS × (state_t)^7 7 ~300
16 hint external constraint check (Layer 1 verification) varies varies

universal constraints

these hold at every row regardless of pattern:

focus accounting

r7_t = r6_t - cost(r0_t)

focus_after = focus_before − pattern cost. cost is fixed per pattern.

step index

r15_{t+1} = r15_t + 1

monotonically increasing step counter.

halting

when r14_t transitions to halted, all subsequent rows must be identity (padding):

if r14_t = HALTED: r_k_{t+1} = r_k_t  for all k

boundary constraints

boundary register row value
input r3, r4 0 program input operands
output r5 last active program output
status r14 last active HALTED
focus r7 last active 0 (or insufficient for next pattern)

boundary constraints are point evaluations of the trace polynomial at specific binary coordinates. WHIR proves these directly as evaluation claims.

CCS encoding

all constraints map to CCS instances. the key advantage: high-degree constraints (pattern 15, degree 7) cost only field operations in the SuperSpartan prover. no cryptographic cost increase over degree-1 constraints.

each pattern's constraint polynomial C_p(t) = 0 is decomposed into CCS form:

Σ_{i ∈ [q]} c_i · ∏_{j ∈ S_i} (M_j · z) = 0

where z is the witness vector (the trace row), M_j are sparse matrices that select registers from the trace at row t or t+1, S_i are index sets specifying which M_j outputs multiply together, and c_i are Goldilocks field coefficients.

register layout for z (row t concatenated with row t+1):

z = [r0_t, r1_t, ..., r15_t, r0_{t+1}, r1_{t+1}, ..., r15_{t+1}, 1]
     idx 0   idx 1      idx 15  idx 16     idx 17        idx 31    idx 32

index 32 holds the constant 1, used by M matrices that extract a fixed scalar.

CCS encoding per pattern

arithmetic patterns (5, 6, 7, 8)

these have the simplest CCS decomposition: one constraint, few matrices.

pattern 5: add

polynomial constraint:

C_5(t) = r5_{t+1} − r3_t − r4_t = 0

CCS decomposition (q = 1 term, degree 1):

  • M_0: selects r5_{t+1} (row t+1, register 5 → z index 21)
  • M_1: selects r3_t (row t, register 3 → z index 3)
  • M_2: selects r4_t (row t, register 4 → z index 4)
  • S_0 = {0}, c_0 = 1 — extracts r5_{t+1}
  • S_1 = {1}, c_1 = −1 — subtracts r3_t
  • S_2 = {2}, c_2 = −1 — subtracts r4_t
1·(M_0·z) + (−1)·(M_1·z) + (−1)·(M_2·z) = 0

pattern 6: sub

polynomial constraint:

C_6(t) = r5_{t+1} − r3_t + r4_t = 0

CCS decomposition (q = 1 term, degree 1):

  • M_0: selects r5_{t+1} (z index 21)
  • M_1: selects r3_t (z index 3)
  • M_2: selects r4_t (z index 4)
  • S_0 = {0}, c_0 = 1
  • S_1 = {1}, c_1 = −1
  • S_2 = {2}, c_2 = 1
1·(M_0·z) + (−1)·(M_1·z) + 1·(M_2·z) = 0

pattern 7: mul

polynomial constraint:

C_7(t) = r5_{t+1} − r3_t × r4_t = 0

CCS decomposition (q = 2 terms, degree 2):

  • M_0: selects r5_{t+1} (z index 21)
  • M_1: selects r3_t (z index 3)
  • M_2: selects r4_t (z index 4)
  • S_0 = {0}, c_0 = 1 — linear term: r5_{t+1}
  • S_1 = {1, 2}, c_1 = −1 — degree-2 term: r3_t × r4_t
1·(M_0·z) + (−1)·(M_1·z ⊙ M_2·z) = 0

where ⊙ denotes Hadamard (entry-wise) product.

pattern 8: inv

polynomial constraint:

C_8(t) = r5_{t+1} × r3_t − 1 = 0

CCS decomposition (q = 2 terms, degree 2):

  • M_0: selects r5_{t+1} (z index 21)
  • M_1: selects r3_t (z index 3)
  • M_2: selects constant 1 (z index 32)
  • S_0 = {0, 1}, c_0 = 1 — degree-2 term: r5_{t+1} × r3_t
  • S_1 = {2}, c_1 = −1 — constant term: −1
1·(M_0·z ⊙ M_1·z) + (−1)·(M_2·z) = 0

logic patterns (9, 10, 11, 12, 13, 14)

these patterns require bit decomposition. auxiliary registers r8–r13 hold intermediate values (individual bits or partial sums). each bit position generates a sub-constraint, yielding ~64 constraints per pattern invocation.

pattern 9: eq

polynomial constraint (two sub-constraints):

C_9a(t) = (r3_t − r4_t) × r8_t − r9_t = 0      (r8 = inverse-or-zero, r9 = flag)
C_9b(t) = r5_{t+1} − (1 − r9_t) = 0             (result is 1 when equal)

CCS decomposition for C_9a (q = 2 terms, degree 2):

  • M_0: selects r3_t (z index 3)
  • M_1: selects r4_t (z index 4)
  • M_2: selects r8_t (z index 8) — auxiliary: conditional inverse
  • M_3: selects r9_t (z index 9) — auxiliary: equality flag
  • S_0 = {0, 2}, c_0 = 1 — r3_t × r8_t
  • S_1 = {1, 2}, c_1 = −1 — r4_t × r8_t
  • S_2 = {3}, c_2 = −1 — −r9_t

the prover sets r9_t = 0 when r3_t = r4_t, and r9_t = 1 otherwise, with r8_t the modular inverse of (r3_t − r4_t) when they differ.

CCS decomposition for C_9b (q = 3 terms, degree 1):

  • M_4: selects r5_{t+1} (z index 21)
  • M_5: selects constant 1 (z index 32)
  • M_6: selects r9_t (z index 9)
  • S_3 = {4}, c_3 = 1
  • S_4 = {5}, c_4 = −1
  • S_5 = {6}, c_5 = 1
C_9b: 1·(M_4·z) + (−1)·(M_5·z) + 1·(M_6·z) = 0

pattern 10: lt

polynomial constraint (range decomposition):

the difference d = r3_t − r4_t is decomposed into 64 bits b_0 … b_63 stored across auxiliary registers (spread over multiple trace rows). two families of sub-constraints:

C_10_bit_k: b_k × (b_k − 1) = 0                 (each bit is boolean)
C_10_recompose: r3_t − r4_t − Σ_{k=0}^{63} b_k × 2^k = 0   (bits reconstruct the difference)

the result r5_{t+1} = b_63 (the sign bit, indicating r3 < r4 in unsigned interpretation).

CCS decomposition for each bit constraint (q = 2 terms, degree 2):

  • M_bit: selects b_k from an auxiliary register
  • M_const: selects constant 1 (z index 32)
  • S = {bit, bit}, c = 1 — b_k²
  • S = {bit}, c = −1 — −b_k

the recomposition constraint is degree 1 with 65 terms (one per bit plus the difference).

pattern 11: xor

polynomial constraint (per-bit XOR via arithmetic identity):

r3_t and r4_t are decomposed into bits a_k, b_k. for each bit position k:

C_11_bit_k: a_k + b_k − 2 × a_k × b_k − c_k = 0

where c_k is the k-th bit of the result. the result is recomposed: r5_{t+1} = Σ c_k × 2^k.

CCS decomposition per bit (q = 4 terms, degree 2):

  • M_a: selects a_k, M_b: selects b_k, M_c: selects c_k (from auxiliary registers)
  • S_0 = {a}, c_0 = 1 — a_k
  • S_1 = {b}, c_1 = 1 — b_k
  • S_2 = {a, b}, c_2 = −2 — −2 × a_k × b_k
  • S_3 = {c}, c_3 = −1 — −c_k

plus boolean constraints for a_k, b_k, c_k and recomposition constraints for both operands and the result. total: ~64 × 4 sub-constraints.

pattern 12: and

polynomial constraint (per-bit AND):

C_12_bit_k: a_k × b_k − c_k = 0

CCS decomposition per bit (q = 2 terms, degree 2):

  • M_a: selects a_k, M_b: selects b_k, M_c: selects c_k
  • S_0 = {a, b}, c_0 = 1 — a_k × b_k
  • S_1 = {c}, c_1 = −1 — −c_k

plus boolean and recomposition constraints (same structure as pattern 11).

pattern 13: not

polynomial constraint (per-bit complement):

C_13_bit_k: a_k + c_k − 1 = 0

CCS decomposition per bit (q = 3 terms, degree 1):

  • M_a: selects a_k, M_c: selects c_k, M_const: selects constant 1
  • S_0 = {a}, c_0 = 1
  • S_1 = {c}, c_1 = 1
  • S_2 = {const}, c_2 = −1

plus boolean and recomposition constraints. all degree 1.

pattern 14: shl

polynomial constraint:

C_14(t) = r5_{t+1} − r3_t × 2^{r4_t} = 0   (mod 2^64)

the shift amount r4_t is decomposed into bits to compute 2^{r4_t} via repeated squaring. auxiliary registers hold intermediate powers. the overflow is handled by range-checking the result to 64 bits.

CCS decomposition: a chain of degree-2 constraints for each bit of the shift amount:

C_14_pow_k: pow_{k+1} = pow_k × pow_k × b_k + pow_k × (1 − b_k)

simplified: pow_{k+1} = pow_k × (1 + (pow_k − 1) × b_k). each step is degree 2 when b_k is binary. the final constraint multiplies r3_t by the accumulated power and range-checks the result.

tree patterns (0, 1, 2, 3)

these patterns navigate the subject tree (a binary tree representing the program's data). operand hashes in r1, r2 anchor Merkle-path verification.

pattern 0: axis

polynomial constraint:

C_0(t) = r1_{t+1} − select(r3_t, r1_t, direction_bit) = 0

axis navigates one level in the subject tree. the direction bit (extracted from r3_t, the axis number) determines whether to descend left or right. the constraint enforces that r1_{t+1} (the new operand hash) equals the child hash consistent with the Merkle path:

C_0_step: r1_{t+1} − (direction × r8_t + (1 − direction) × r9_t) = 0

where r8_t and r9_t hold the left and right child hashes provided by the prover (verified against the parent hash via a separate Poseidon2 check). the direction bit extraction uses one degree-2 constraint per depth level.

CCS decomposition per depth step (q = 3 terms, degree 2):

  • M_0: selects r1_{t+1} (z index 17)
  • M_1: selects direction bit from auxiliary register
  • M_2: selects r8_t (left child hash, z index 8)
  • M_3: selects r9_t (right child hash, z index 9)
  • M_4: selects constant 1 (z index 32)
  • S_0 = {0}, c_0 = 1 — r1_{t+1}
  • S_1 = {1, 2}, c_1 = −1 — direction × left_child
  • S_2 = {1}, c_2 = 1; combined with S_3 = {3}, c_3 = −1 and S_4 = {4, 3} for the (1 − direction) × right_child term

each depth level of the axis also requires a Poseidon2 hash constraint to verify the parent-child relationship, reusing pattern 15's constraint structure.

pattern 1: quote

polynomial constraint:

C_1(t) = r5_{t+1} − r3_t = 0

the simplest pattern: the result is the literal operand itself.

CCS decomposition (q = 2 terms, degree 1):

  • M_0: selects r5_{t+1} (z index 21)
  • M_1: selects r3_t (z index 3)
  • S_0 = {0}, c_0 = 1
  • S_1 = {1}, c_1 = −1
1·(M_0·z) + (−1)·(M_1·z) = 0

pattern 2: compose

polynomial constraint:

compose chains two evaluations: first evaluate sub-expression x, then evaluate sub-expression y using x's output as input. the constraint enforces correct threading of intermediate results:

C_2a(t) = r3_{t_mid} − r5_{t_x} = 0     (y's input = x's output)
C_2b(t) = r5_{t+1} − r5_{t_y} = 0       (compose result = y's output)

these are degree-1 constraints linking trace rows at three time points (t, t_mid, t_end). the CCS encoding uses M matrices that select registers from the appropriate rows, with the row offsets baked into the matrix structure.

CCS decomposition for each sub-constraint: same shape as pattern 1 (two matrices, degree 1, two terms).

pattern 3: cons

polynomial constraint:

cons pairs two sub-results into a new tree node:

C_3(t) = r1_{t+1} − poseidon2(r5_{t_left}, r5_{t_right}) = 0

the result hash is the Poseidon2 hash of the left and right sub-results. this decomposes into a hash constraint (reusing pattern 15's structure) plus a linking constraint that threads sub-expression outputs:

C_3_link_left:  input_left  − r5_{t_left}  = 0
C_3_link_right: input_right − r5_{t_right} = 0
C_3_hash:       r1_{t+1} − hash_output = 0

CCS decomposition: the linking constraints are degree 1 (two terms each). the hash constraint follows pattern 15's encoding.

control patterns (4, 16)

pattern 4: branch

polynomial constraint:

C_4(t) = r8_t × (r5_{t+1} − r3_t) + (1 − r8_t) × (r5_{t+1} − r4_t) = 0

where r8_t is the selector (derived from the condition evaluation): r8_t = 1 selects r3_t (the "yes" branch), r8_t = 0 selects r4_t (the "no" branch).

expanding: r5_{t+1} − r8_t × r3_t − r4_t + r8_t × r4_t = 0

CCS decomposition (q = 4 terms, degree 2):

  • M_0: selects r5_{t+1} (z index 21)
  • M_1: selects r8_t (z index 8) — selector
  • M_2: selects r3_t (z index 3) — yes branch
  • M_3: selects r4_t (z index 4) — no branch
  • S_0 = {0}, c_0 = 1 — r5_{t+1}
  • S_1 = {1, 2}, c_1 = −1 — −selector × yes
  • S_2 = {3}, c_2 = −1 — −no
  • S_3 = {1, 3}, c_3 = 1 — +selector × no
1·(M_0·z) + (−1)·(M_1·z ⊙ M_2·z) + (−1)·(M_3·z) + 1·(M_1·z ⊙ M_3·z) = 0

an additional boolean constraint enforces r8_t ∈ {0, 1}:

C_4_bool: r8_t × (r8_t − 1) = 0

pattern 16: hint

polynomial constraint:

hint injects externally computed values into the trace. the constraint structure varies by hint type. the prover supplies a value in r5_{t+1} along with a proof that the external verifier (Layer 1) accepted it. the minimal constraint:

C_16(t) = r14_{t+1} − r14_t = 0   (status unchanged through hint)

additional constraints depend on the hint category (e.g., Layer 1 state root verification, oracle value attestation). these are encoded as separate CCS instances composed with the main trace via the SuperSpartan folding mechanism.

CCS decomposition for the base constraint (q = 2 terms, degree 1):

  • M_0: selects r14_{t+1} (z index 30)
  • M_1: selects r14_t (z index 14)
  • S_0 = {0}, c_0 = 1
  • S_1 = {1}, c_1 = −1

hash pattern (15)

pattern 15: hash (Poseidon2 round)

polynomial constraint:

Poseidon2 operates on a state of width w (typically 8 field elements). each round applies: S-box (x → x^7), then MDS matrix multiplication, then round constant addition. the state elements are spread across registers r3–r5 and auxiliary registers r8–r13 (six state elements per trace row, requiring two rows per full state).

for a single S-box + MDS step on one state element s:

C_15_sbox: r8_t − (s_t)^7 = 0

since (s_t)^7 = s_t × s_t × s_t × s_t × s_t × s_t × s_t, the prover decomposes this into intermediate squares:

C_15_sq1: r9_t − s_t × s_t = 0                    (s^2)
C_15_sq2: r10_t − r9_t × r9_t = 0                  (s^4)
C_15_cube: r11_t − r10_t × r9_t = 0                (s^6)
C_15_final: r8_t − r11_t × s_t = 0                 (s^7)

each sub-constraint is degree 2. this decomposes the degree-7 S-box into four degree-2 constraints per state element.

CCS decomposition for C_15_sq1 (q = 2 terms, degree 2):

  • M_0: selects r9_t (z index 9)
  • M_1: selects s_t (whichever register holds the state element)
  • S_0 = {0}, c_0 = 1
  • S_1 = {1, 1}, c_1 = −1 — s_t × s_t (same matrix used twice in the product set)

the remaining sub-constraints follow the same shape: one degree-2 product term and one degree-1 extraction term.

the MDS matrix multiplication is a linear operation, encoded as a single degree-1 constraint per output element:

C_15_mds_j: state_j_{t+1} − Σ_{k} MDS[j][k] × sbox_output_k = 0

CCS decomposition for MDS (q = w+1 terms, degree 1):

  • one M matrix per sbox output element, with the MDS coefficient baked into c
  • S_k = {k}, c_k = MDS[j][k] for each input k
  • S_{w} = {out_j}, c_{w} = −1 for the output element

round constants are added by adjusting the c coefficient of the constant-1 wire (z index 32).

total per Poseidon2 round: 4 × w degree-2 constraints (S-box decomposition) + w degree-1 constraints (MDS). for w = 8: 32 + 8 = 40 constraints per round. a full Poseidon2 permutation with 8 rounds uses ~320 constraints.

constraint budget by proof type

proof type dominant patterns total constraints
identity (preimage) 15 (one hash) ~300
anonymous cyberlink 15, 4, 9 ~13,000
delivery (per hop) 15, 7, 4 ~60,000
private transfer (BBG) 15, 7, 9 ~50,000
recursive verification (jets) 15, 5, 7, 4 ~70,000
recursive verification (no jets) 15 dominates ~600,000

see SuperSpartan for the IOP that verifies constraints, sumcheck for the reduction mechanism, nox for pattern definitions, transcript for Fiat-Shamir challenge derivation

Dimensions

constraints

Local Graph