Skip to content

Appendix D: Solutions for Chapter 34

This page collects solutions and editorial notes for the exercises in Chapter 34: STARKs and FRI revisited. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.

The fuller versions of these routines are in the starks package under solutions/ch34-starks. From a clone of the companion repository, pytest tests/ch34 runs its suite. Appendix C has the setup.

Use the pedagogical flat-depth model stipulated in the exercise. Per FRI query path, the cost is 1 trace opening (32 bytes) + 1 Merkle authentication path per FRI round. Each per-round authentication path is 20⋅32=64020 \cdot 32 = 640 bytes: the path is log⁡2N=20\log_2 N = 20 levels deep at N=220N = 2^{20}, and each level contributes one 256-bit sibling hash, which is 32 bytes. Total per path is 32+18⋅640=32+11,520=11,55232 + 18 \cdot 640 = 32 + 11{,}520 = 11{,}552 bytes.

Proof size S(μ)=μ⋅11,552S(\mu) = \mu \cdot 11{,}552 bytes. At μ=40\mu = 40: S=40⋅11,552=462,080S = 40 \cdot 11{,}552 = 462{,}080 bytes ≈451\approx 451 KB. Production STARKs do better than this estimate by shrinking the per-round path to the round’s actual domain depth (top-layer 20, then 19, 18, …), deduplicating sibling openings, and not including Merkle roots in the per-query cost.

N_bits = 20
hash_bytes = 32
trace_bytes = 32
r_fri = 18
path_bytes = N_bits * hash_bytes
per_query = trace_bytes + r_fri * path_bytes
mu = 40
print("path bytes:", path_bytes, "paths total:", r_fri * path_bytes)
print(per_query, mu * per_query, round(mu * per_query / 1024))
# ==> path bytes: 640 paths total: 11520
# ==> 11552 462080 451

The chapter’s DFMS20-shaped model rule, computed without the 2qbits2 q_{\text{bits}} shortcut: cbits≥2log⁡2(2q+1)+k/rFSc_{\text{bits}} \ge 2 \log_2(2q + 1) + k / r_{\text{FS}}. With q=280q = 2^{80}, k=128k = 128, rFS=8r_{\text{FS}} = 8:

cbits≥2log⁡2(281+1)+128/8c_{\text{bits}} \ge 2 \log_2(2^{81} + 1) + 128 / 8, and 2log⁡2(281+1)2 \log_2(2^{81} + 1) is just above 162162, so the bound sits just above 178.0178.0 and rounds up to 179179 bits per round. The approximation 2qbits+⌈k/rFS⌉=1762 q_{\text{bits}} + \lceil k / r_{\text{FS}} \rceil = 176 lands three bits short here, because the boundary falls on an integer.

A 256-bit field comfortably supplies 179 bits per challenge, so one field element per challenge is enough under the model. What the 179 bits do not establish is a deployment’s quantum soundness: the model drops the corollary’s additive challenge-space term and assumes an interactive error the protocol has to supply for itself (Ch 33).

Note where the width actually comes from in a FRI-based pipeline, because the obvious 256-bit candidates, the 254-bit BN254 and 255-bit BLS12-381 scalar fields, are the wrong ones. They are pairing-curve scalar fields, and in this stack they belong to an outer SNARK wrapper, not to the STARK. Chapter 34 Section 5.7 sets out the two patterns the STARK itself uses: a small base field plus an extension, or one large prime field. ethSTARK’s reference configuration follows the first, a base field of about 61 bits with Fp3\mathbb{F}_{p^3} at about 183 bits. Starknet’s Stone prover follows the second and runs FRI over the 252-bit Cairo field directly.

import math
q_bits, k, r_fs = 80, 128, 8
exact = 2 * math.log2(2 * 2 ** q_bits + 1) + k / r_fs
c_bits = math.ceil(exact) + (1 if math.ceil(exact) == exact else 0)
print(round(2 * math.log2(2 * 2 ** q_bits + 1), 1), k / r_fs, c_bits)
# ==> 162.0 16.0 179

Transition constraints (interpreted as polynomials over the trace domain):

  • Algebraic: T1(x):=t(ω2x)−t(ωx)−t(x)−c(x)=0T_1(x) := t(\omega^2 x) - t(\omega x) - t(x) - c(x) = 0, where t(x)t(x) is the trace polynomial.
  • Boolean: T2(x):=c(x)(c(x)−1)=0T_2(x) := c(x) (c(x) - 1) = 0, where c(x)c(x) is the polynomial interpolating column cc over the trace domain.

Boundary constraints on cc are protocol-specific. If the application requires the secondary column to start at zero and end at zero (e.g. a free-input column with bracketing), the boundary constraints are c(ω0)=0c(\omega^0) = 0 and c(ωL−1)=0c(\omega^{L-1}) = 0. If the column is unconstrained at boundaries, no boundary constraints apply.

Composition-polynomial degree at trace length L=16L = 16: the trace polynomial has degree ≤L−1=15\le L - 1 = 15, so t(x)t(x) is degree 15 and c(x)c(x) is degree 15. T1T_1 is a linear combination of trace polynomials evaluated at x,ωx,ω2xx, \omega x, \omega^2 x, all degree 15. T2T_2 is degree 2⋅15=302 \cdot 15 = 30 (the bottleneck). The recurrence T1T_1 holds on rows 00 to L−3L - 3 only, so its divisor is the vanishing polynomial of those L−2L - 2 rows, (xL−1)/((x−ωL−2)(x−ωL−1))(x^L - 1) / ((x - \omega^{L-2})(x - \omega^{L-1})) of degree 1414, and its quotient has degree 15−14=115 - 14 = 1. Dividing T1T_1 by the full xL−1x^L - 1 would require the recurrence to wrap around from rows L−2L - 2 and L−1L - 1 back to rows 00 and 11, which the exercise does not impose and a Fibonacci trace does not satisfy. The Boolean constraint T2T_2 holds on every row, so its divisor is ∏i(x−ωi)=xL−1\prod_i (x - \omega^i) = x^L - 1 of degree 1616 and its quotient has degree 30−16=1430 - 16 = 14. The composition polynomial’s degree is the maximum, 1414, set by the Boolean constraint.

L = 16
deg_trace = L - 1 # each column interpolates to degree < L
deg_t1 = deg_trace # linear in t(x), t(omega x), t(omega^2 x), c(x)
deg_t2 = 2 * deg_trace # Boolean constraint c(x)(c(x) - 1): the bottleneck
deg_vanishing = L # x^L - 1: the divisor for T2, which holds on every row
print(deg_t1, deg_t2, max(deg_t1, deg_t2) - deg_vanishing)
# ==> 15 30 14

(i) CNFL exposure: L4 (Fiat-Shamir transform) carries the chapter’s CNFL exposure under its narrow model, and the exposure is an assurance gap rather than an attack mechanism. The published QROM results for FRI-based Fiat-Shamir (DFMS20 generically, Block et al. 2023 for FRI) are upper bounds on forgery probability that, as Chapter 34 Section 5.6 records, had not been instantiated at deployment parameters. Until an instantiation shows the deployed parameters, or changed ones, suffice, a legacy verifier still in service in 2040 accepts proofs whose post-quantum soundness has not been established at its parameters. Neither bound constructs an attack. L2 (Merkle binding) is a related but distinct future-verifier / hash-lifetime concern. A quantum collision-search attack on SHA-256 (BHT / CNPS budgets) does not depend on which transcripts were harvested. It does affect any verifier still trusting SHA-256 Merkle roots at the CNFL crossover.

(ii) The model’s DFMS20-shaped multiplicative loss for rFS=20r_{\text{FS}} = 20: 2q+1=281+1≈2812q + 1 = 2^{81} + 1 \approx 2^{81}, so (2q+1)2rFS≈(281)2⋅20=23240(2q + 1)^{2 r_{\text{FS}}} \approx (2^{81})^{2 \cdot 20} = 2^{3240}. Combined with the per-round interactive soundness εrFS\varepsilon^{r_{\text{FS}}}, the chapter’s model carries this multiplicative factor. It is the model’s figure and not DFMS20’s corollary, which adds a challenge-space term the model drops (Ch 33).

(iii) 252-bit adequacy at 128-bit PQ under the simplified DFMS rule: Apply the per-round rule from E2 with rFS=20r_{\text{FS}} = 20. Then cbits≥2log⁡2(281+1)+128/20c_{\text{bits}} \ge 2 \log_2(2^{81} + 1) + 128 / 20, just above 162+6.4=168.4162 + 6.4 = 168.4, so 169169 bits per round. The rule sizes challenge entropy, and the 252-bit Cairo prime supplies log⁡2P=251.0\log_2 P = 251.0 bits of it, 82 bits above 169, so the challenge space is adequate at the per-round granularity under the chapter’s DFMS-shaped per-round model rule. This is not an end-to-end STARK security proof. The 25 grinding bits attenuate only the query-miss term (Section 5.5 of Chapter 34). The proximity-gap and bad-beta terms follow the BCIKS regime-dependent error from Section 5.1. A concrete deployment-parameter analysis (e.g. ethSTARK Documentation §7.1.1, classical-ROM provable soundness of the non-interactive argument, whose QROM counterpart (iv) records as missing) is the load-bearing argument, not the per-round cbitsc_{\text{bits}} model inequality.

(iv) The citation that supplies the QROM bound: (Block et al., 2023). Block et al. 2023 is a dedicated Fiat-Shamir analysis for FRI, and its QROM half comes from Chiesa, Manohar and Spooner’s lift of round-by-round soundness (Chiesa et al., 2019). Its asymptotic quantum bound is published for FRI that folds by two over a smooth multiplicative subgroup, under the paper’s parameter hypotheses. Whether it reaches deployed rates is open (Ch 34 Section 5.6). For a pipeline that folds by more than two, as the Stone root and Boojum do, the bound itself is an extension Ch 34 Section 5.6 records as unstated. Beyond those questions, what is missing is the composition: Section 5.6 records that no work in the literature had carried that bound through Merkle binding, hash-output width, grinding, batching, and recursion at the parameters of any pipeline Part VI reads.

(Don et al., 2020) is the reference whose arithmetic parts (ii) and (iii) use. Ch 34 Section 5.3 labels its DFMS20-shaped rule a stipulated model rather than the gating result: applying DFMS20 directly exceeds the published QROM loss by additional powers of qq, and the model also drops the corollary’s additive challenge-space term.

(v) What the transcripts contribute: nothing, for L4 soundness. Neither DFMS20’s bound nor Block et al.’s depends on how many honest transcripts the adversary holds, and no attack on Fiat-Shamir FRI published by 19 October 2026 consumes them. The 10,000 transcripts play no part in any answer above; CNFL, as Chapter 34 frames it, is a verifier-lifetime problem.

import math
q_bits, k, r_fs = 80, 128, 20
loss_exponent = (q_bits + 1) * 2 * r_fs # (2q+1)^{2 r_FS} with 2q+1 ~ 2^81
exact = 2 * math.log2(2 * 2 ** q_bits + 1) + k / r_fs
c_bits = math.ceil(exact) + (1 if math.ceil(exact) == exact else 0)
log2_p = math.log2(2 ** 251 + 17 * 2 ** 192 + 1) # challenge entropy, not storage width
print("loss exponent:", loss_exponent, "c_bits:", c_bits, "headroom:", math.floor(log2_p) - c_bits)
# ==> loss exponent: 3240 c_bits: 169 headroom: 82

The container: subsection 4.1, arithmetization, and Block 2 inside it. A multi-column trace stops being a list of field elements and becomes a list of columns, so a transition constraint has to name which columns its window spans, and the AIR container has to carry that. Block 2 declares TransitionConstraint as ("window", "evaluator", "name") and BoundaryConstraint as ("row", "expected", "name"), and evaluate_air iterates a flat trace. A multi-column version adds a column selector to both records, for example TransitionConstraint("window", "columns", "evaluator", "name") and a column field on BoundaryConstraint, and evaluate_air indexes trace[col][row] rather than trace[row]. That much is a change of container rather than of mathematics: the AIR is still a set of polynomial constraints that must vanish on the trace domain.

What the container change does not buy. Subsection 4.5 is where the extension stops being free, and an answer that stops at 4.1 has answered half the question. The toy prover sends one flat trace, and the verifier binds it to one committed word. For every query path, the opening at the path’s top-layer LDE point xx must equal t(x)t(x), where tt is the interpolation of the sent trace. One trace, one interpolation, one binding. Several columns have to be bound too, and this chapter has no machinery for it. So extending the container leaves three things unbuilt. The commitment has to bind every column, whether as one Merkle tree whose leaves carry a whole row or as a tree per column; a single root over rows binds several columns perfectly well, so the choice is a layout question and the requirement is that each column is opened at each query position. The prover and verifier wiring in 4.5 has to produce and check those openings. And the soundness accounting has to be redone, because the toy’s ((L−1)/N)μ((L - 1) / N)^{\mu} consistency bound covers one binding, and several bindings carry error terms of their own. A strong answer says this rather than asserting an unchanged budget.

The composition polynomial is a separate mechanism, and worth ruling out by name. Subsection 4.5 says outright that this chapter drops it, and a tempting wrong answer is that the dropped step is what would have collapsed the columns. It is not. The composition polynomial is a random linear combination of the constraint quotients, so what it aggregates is constraints, and a production pipeline commits the trace whatever its column count. The step that collapses several committed polynomials into the single codeword FRI runs on is a later random combination of their out-of-domain quotients, taken after both the trace and the composition polynomial have been committed: the DEEP consistency 4.5 names alongside it. Dropping the composition polynomial is why this chapter’s verifier checks the constraints against a sent trace rather than against a committed quotient. It is not what makes the container single-column.

Subsection 4.2 is the near-miss and worth ruling out explicitly. Multi-column traces do change what 4.2 does: each column interpolates to its own polynomial and gets its own low-degree extension, so the work multiplies by the column count. But interpolate_trace and extend_polynomial operate on one column at a time either way, and neither signature changes. Multiplying a call is not extending a type. Subsections 4.3 and 4.4 are the ones genuinely blind to the column count: FRI folds a codeword and the transcript absorbs roots, neither of which knows how the codeword was built.

One distinction the exercise turns on. How many columns a trace has is not how many variables its polynomials have. E3’s second column is interpolated as its own univariate polynomial of degree less than LL, exactly as the single Fibonacci column is, and a proof system that commits multilinear polynomials over a Boolean hypercube has changed the representation rather than the column count. Nothing in this chapter’s pipeline changes representation.

Block, A. R., Garreta, A., Katz, J., Thaler, J., Tiwari, P. R., & Zajac, M. (2023). Fiat-Shamir Security of FRI and Related SNARKs. Advances in Cryptology — ASIACRYPT 2023, 3–40. https://doi.org/10.1007/978-981-99-8724-5_1
Chiesa, A., Manohar, P., & Spooner, N. (2019). Succinct Arguments in the Quantum Random Oracle Model. Theory of Cryptography — TCC 2019, 1–29. https://doi.org/10.1007/978-3-030-36033-7_1
Don, J., Fehr, S., & Majenz, C. (2020). The Measure-and-Reprogram Technique 2.0: Multi-Round Fiat-Shamir and More. Advances in Cryptology — CRYPTO 2020. https://doi.org/10.1007/978-3-030-56877-1_21