mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-04 20:03:41 +00:00
The operator caught what no check had ever tested: nobody had LOOKED at
the rendered pages. A ten-inspector visual audit of all 129 pages (every
page opened as an image) found 40 defects, including didactic
correctness bugs invisible to the text layer:
BAD, fixed:
- ch03 printed WRONG Lean operators: \lean{P /\ Q} lost its backslash
('P / Q') and \lean{P \/ Q} lost the operator entirely ('P Q') —
TeX ate them inside the non-verbatim macro. Now the unicode ∧/∨ the
book uses everywhere else.
- ch12: the doubling display overflowed its box border, slicing the
math; stacked on two lines.
- toolkit Card 6: the headroom-audit display was clipped by the page
edge; now an align* stack.
UGLY, fixed:
- title page: the 'pyramid motif' at 5% white opacity on near-black
rendered as smudge artifacts, plus a clipped ∀ glyph in the corner
— redrawn with solid mixed colors (no transparency), glyph removed;
the footer's mid-word paragraph gap was a \vspace landing inside
horizontal mode; fixed with \par
- ch06: both clock diagrams' wrap-around arrows ran counterclockwise,
retracing over earlier arcs — target position expressed as 12 (one
revolution) so the arc continues clockwise, landing on 0 (mod 12)
and 1 (mod 11) correctly
- ch04: two_mul'' printed as two_mul" (quote ligature)
- one-line orphaned box fragments and stranded solution headings
throughout: bigidea/tryit/pitfall/aha/checkpoint are now unbreakable
(none exceeds half a page), worked boxes announce '(continued)' after
a break, \solhead keeps four lines with \Needspace
- --all/--receipt flags printed as one merged dash: \ddash macro
- inline code no longer hyphen-breaks at underscores (codeguards)
- ch09's 2^{...} smudge, glossary margin overflow, ch08 orphaned
listing line, ch13 command-line layout, three >10pt overfulls
THE STRUCTURAL LESSON, encoded: the two worst clipping bugs had been
announced as 80pt/73pt overfull warnings in every build log and ignored.
check-book.sh now FAILS on any overfull box past 10pt — the machine was
telling us; now it is allowed to stop us.
132 pages; publication-history and README counts synced; every fixed
page re-rendered and verified by eye. Button: ALL GREEN (96 checks).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
387 lines
20 KiB
TeX
387 lines
20 KiB
TeX
\chapter{Verifying a Field: The Full Campaign}
|
|
\label{ch:field}
|
|
|
|
Partway through the campaign this chapter narrates, one proof step consumed
|
|
twelve gigabytes of memory and took the machine down --- not a slow test, not
|
|
a warning in a log: the computer proving these theorems crashed, mid-proof,
|
|
because of how a proof was written. This chapter climbs toward that wall,
|
|
hits it, quantifies exactly why it stands where it stands, and comes out the
|
|
other side with the discipline the rest of the book runs on. The crash is
|
|
the most instructive thing in the chapter. Keep it in view.
|
|
|
|
\section{The summit statement}
|
|
|
|
Every thread so far --- specs as types, tactics, automation, $\Fp$,
|
|
primality, extraction, denotation --- was preparation for one theorem. In
|
|
the companion repositories it is called the \emph{field implementation
|
|
certificate}, and (lightly paraphrased) it says:
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
theorem fieldImplementation :
|
|
-- p is prime, so ZMod p is genuinely a field
|
|
Nat.Prime p
|
|
-- and for all bounded limb arrays, every operation's
|
|
-- commuting square closes:
|
|
∧ (∀ a b, Bnd a → Bnd b → AddSquare a b)
|
|
∧ (∀ a b, Bnd a → Bnd b → SubSquare a b)
|
|
∧ (∀ a b, Bnd a → Bnd b → MulSquare a b)
|
|
∧ (∀ a, Bnd a → SquareSquare a)
|
|
∧ (∀ a, Bnd a → InvertSquare a) -- Fermat chain: a^(p-2)
|
|
∧ ... -- reduce, negate, encode
|
|
\end{lstlisting}
|
|
|
|
(Vocabulary re-armed, from Chapter~\ref{ch:denotation}: \lean{Bnd a} says
|
|
$a$'s limbs sit inside their envelopes; each \lean{...Square} says one
|
|
operation's commuting square closes --- run the extracted code, denote the
|
|
result, and you get the same field element as doing the mathematics.)
|
|
|
|
One theorem, kernel-checked, quantified over \emph{every} input the
|
|
representation admits: the extracted dalek field arithmetic implements
|
|
$\Fp$. This chapter is the story of the campaign that proves it --- told
|
|
honestly, including the two places where the terrain fought back, because
|
|
the failures teach more than the victories.
|
|
|
|
\section{Order of battle}
|
|
|
|
You do not prove such a conjunction by heroism; you prove it by sequencing.
|
|
The campaign order in the real projects, and the reason for each position:
|
|
|
|
\begin{enumerate}[leftmargin=1.6em]
|
|
\item \textbf{Bounds lemmas first} --- pure \lean{omega} facts about limb
|
|
sizes, no denotation at all. Cheap, and everything depends on them.
|
|
\item \textbf{Primality} (Chapter~\ref{ch:prime}) --- independent of the
|
|
code entirely; it dignifies \lean{ZMod p} into a field.
|
|
\item \textbf{add, sub, negate} --- linear operations; the commuting squares
|
|
close with \lean{omega} plus the denotation unfolds. Confidence builders.
|
|
\item \textbf{reduce} --- the $\times 19$ fold in isolation. Proving it
|
|
alone, before mul uses it, halves the hardest proof's size.
|
|
\item \textbf{mul, square} --- the boss fight of Chapter~\ref{ch:denotation}:
|
|
column sums, carries, folds. Squaring is mul with algebraic shortcuts ---
|
|
a separate code path in dalek, hence a separate theorem. No shortcuts
|
|
in the proof: the \emph{code's} shortcut is precisely what needs checking.
|
|
\item \textbf{invert} --- the 254-squaring Fermat chain, verified as a
|
|
\lean{calc} of exponent bookkeeping on top of \code{mul}/\code{square}
|
|
specs, ending at $a^{p-2}$; Fermat's little theorem (Mathlib's) closes
|
|
the square.
|
|
\end{enumerate}
|
|
|
|
Notice the shape: \emph{each layer consumes only the specs of the layer
|
|
below}, never reaching into implementations. By step 6 you are doing exponent
|
|
arithmetic, blissfully ignorant of carries. That is the compositionality that
|
|
Chapter~\ref{ch:denotation}'s two-clause specs (bounds + value) were designed
|
|
to buy.
|
|
|
|
\begin{worked}{why $16p$ --- auditing a design constant to the bit}
|
|
Step 3's subtraction hides the field layer's most quotable design
|
|
decision, and you can audit it completely. The problem: compute $a - b$
|
|
limb-wise in unsigned words, where limbs of $b$ may be as large as
|
|
$2^{54} - 1$ (the invariant's edge) while limbs of $a$ may be as small as
|
|
$0$. Unsigned subtraction would truncate (Chapter~\ref{ch:lean}); the fix
|
|
is to compute $a + (kp - b)$ for a suitable multiple $k$ of the prime ---
|
|
denotationally free, since $kp \equiv 0$ --- chosen so that every limb of
|
|
$kp - b$ stays positive. How large must $k$ be? The limb constants of
|
|
$kp$ scale the telescoped representation from Chapter~\ref{ch:denotation}:
|
|
\[
|
|
kP = \big(k(2^{51}-19),\; k(2^{51}-1),\; k(2^{51}-1),\; k(2^{51}-1),\;
|
|
k(2^{51}-1)\big),
|
|
\]
|
|
whose \emph{smallest} limb is $k(2^{51} - 19)$. Positivity of every limb
|
|
of $kp - b$ demands
|
|
\[
|
|
k\,(2^{51} - 19) \;\ge\; 2^{54} - 1 .
|
|
\]
|
|
Try the candidates, because powers of two are what the code wants:
|
|
\[
|
|
k = 8:\quad 8(2^{51}-19) = 2^{54} - 152 \;<\; 2^{54} - 1
|
|
\qquad\text{✗ fails, by 151;}
|
|
\]
|
|
\[
|
|
k = 16:\quad 16(2^{51}-19) = 2^{55} - 304 \;\ge\; 2^{54} - 1
|
|
\qquad\text{✓ with } 2^{54} - 303 \text{ to spare.}
|
|
\]
|
|
So $16$ is the \emph{least} power of two that works --- $8$ misses by a
|
|
margin of $151$, invisible to any test that doesn't drive a limb of $b$
|
|
within $151$ of the very top of its envelope. Two more checks complete
|
|
the audit: the result's limbs are bounded by
|
|
$2^{54} + 2^{55} < 2^{56} < 2^{64}$ (no overflow on the additive side ✓),
|
|
and the denotation shifts by exactly $16p \equiv 0$ ✓. In the shipped
|
|
proof this box is one lemma with three \lean{omega} obligations; in the
|
|
git history of more than one real crypto library, the analogous constant
|
|
is a patched CVE. Now you know how to tell which side of that line a
|
|
codebase is on.
|
|
\end{worked}
|
|
|
|
\begin{tryit}
|
|
Do not take the box's word for the margin of $151$ --- falsify the rejected
|
|
design yourself:
|
|
\begin{lstlisting}[language=Lean]
|
|
#eval (8 * (2^51 - 19) : Int) - (2^54 - 1) -- -151
|
|
#eval (16 * (2^51 - 19) : Int) - (2^54 - 1) -- 18014398509481681
|
|
\end{lstlisting}
|
|
The first line is the deficit a $k=8$ subtraction suffers when a limb of $b$
|
|
sits within $151$ of its envelope's top: in unsigned words that negative
|
|
number is a silent wrap-around --- and a random test hits the triggering
|
|
region with probability about $151/2^{54}$, which is to say never. The
|
|
second line is the shipped design's headroom. Two \lean{\#eval}s, one
|
|
CVE-class distinction.
|
|
\end{tryit}
|
|
|
|
\begin{worked}{verifying the inversion chain's bookkeeping --- all 265 steps}
|
|
Step 6 sounds heroic --- verify a hand-crafted chain of $254$ squarings
|
|
and $11$ multiplications computes $a^{p-2}$ --- until you see that the
|
|
whole verification is \emph{exponent arithmetic}, and the exponents obey
|
|
one identity you can check at every scale. The chain builds values
|
|
$a^{e}$ for a cascade of exponents $e$; squaring doubles $e$, multiplying
|
|
adds them. The dalek chain's opening moves:
|
|
\[
|
|
\begin{array}{lcl}
|
|
z_2 = a^2 & & e = 2\\
|
|
z_9 = (z_2)^{2^2} \cdot a & & e = 2\cdot 4 + 1 = 9\\
|
|
z_{11} = z_9 \cdot z_2 & & e = 9 + 2 = 11\\
|
|
z_{2^5-1} = (z_{11})^{2} \cdot z_9 & & e = 22 + 9 = 31 = 2^5 - 1 .
|
|
\end{array}
|
|
\]
|
|
From $31 = 2^5 - 1$ the chain climbs by a doubling ladder whose single
|
|
identity you should verify once symbolically:
|
|
\[
|
|
(2^k - 1)\cdot 2^k + (2^k - 1) \;=\; 2^{2k} - 1
|
|
\qquad\text{(``shift a block of $k$ ones up by $k$ and fill'').}
|
|
\]
|
|
Check it at $k=5$: $31 \cdot 32 + 31 = 992 + 31 = 1023 = 2^{10}-1$ ✓.
|
|
The ladder then assembles mixed blocks the same way:
|
|
$2^{10}-1 \to 2^{20}-1 \to 2^{40}-1$, then
|
|
$(2^{40}-1)2^{10} + (2^{10}-1) = 2^{50}-1$, then
|
|
$2^{100}-1 \to 2^{200}-1$, then
|
|
$(2^{200}-1)2^{50} + (2^{50}-1) = 2^{250}-1$. Final move: shift five and
|
|
add the stashed $z_{11}$:
|
|
\[
|
|
(2^{250}-1)\cdot 2^{5} + 11 \;=\; 2^{255} - 32 + 11 \;=\; 2^{255} - 21
|
|
\;=\; p - 2 . \qquad ✓
|
|
\]
|
|
Every line above is hand-checkable, and together they \emph{are} the
|
|
correctness of the inversion chain --- modulo only ``squaring squares and
|
|
multiplication adds exponents,'' which is the already-proven \code{mul}
|
|
and \code{square} specs feeding Fermat (Chapter~\ref{ch:modular}). Count
|
|
the cost while you are here. Squarings, stage by stage:
|
|
$1$ (to $z_2$) $+\,2$ (to $z_9$) $+\,1$ (to $z_{31}$) $+\,5+10+20$ (to
|
|
$2^{10},2^{20},2^{40}$) $+\,10$ (to $2^{50}$) $+\,50+100$ (to
|
|
$2^{100},2^{200}$) $+\,50$ (to $2^{250}$) $+\,5$ (final shift)
|
|
$= 254$. Multiplications: one per block-join --- count the ``$\cdot$''s
|
|
above --- exactly $11$. The advertised $254 + 11$ is not folklore; you
|
|
just audited it. That is the point of this box: ``verify 265 field
|
|
operations'' collapsed into ten lines of exponent algebra a patient
|
|
undergraduate audits over coffee.
|
|
\end{worked}
|
|
|
|
\section{Dispatches from the terrain}
|
|
|
|
\textbf{The wall that was really there.} This is the crash from the
|
|
chapter's first sentence, and here is exactly how it was earned: a
|
|
\code{mul}-scale goal, handed to a general decision procedure in one
|
|
monolithic call --- the natural move, the move you would make ---
|
|
generates an internal certificate with coefficients on the order of
|
|
$2^{256}$, and checking it can exhaust the proof checker's memory. During
|
|
the development of the Pasta field proofs that one call ate twelve
|
|
gigabytes and took the machine down (Chapter~\ref{ch:automation} told you
|
|
this story from the tactic side). The cure was never cleverness --- it was
|
|
\emph{decomposition}: isolate each carry step as its own small lemma with a
|
|
tiny context, prove the value identity with \lean{linear_combination}
|
|
(a tactic that checks a \emph{stated} linear certificate rather than
|
|
searching for one), and let the big theorem be an assembly of small
|
|
checked parts. The same discipline, plus hard memory caps on the checker
|
|
process, became house infrastructure.
|
|
|
|
\begin{worked}{the wall, quantified --- why the monolithic proof dies}
|
|
The memory catastrophe is not mystical; you can estimate it on one page,
|
|
and the estimate teaches the cure. When a decision procedure like
|
|
\lean{omega} proves a linear goal, it does not just say ``true'' --- it
|
|
emits a \emph{certificate}: a linear combination of the hypotheses with
|
|
explicit integer coefficients that the kernel then re-checks
|
|
(Chapter~\ref{ch:prime}'s find/check split, again). Now count what a
|
|
\emph{monolithic} field-multiplication goal hands it. The goal relates
|
|
quantities at wildly different scales: raw limbs ($\sim 2^{51}$), column
|
|
sums ($\sim 2^{111}$), folded values ($\sim 2^{116}$), and the final
|
|
positional stack, whose top weight is $2^{204}$ --- so eliminating
|
|
variables across the whole system produces certificate coefficients up
|
|
to the \emph{products} of these scales: numbers of order $2^{256}$ to
|
|
$2^{512}$, i.e.\ integers of $80$--$150$ decimal digits. Per
|
|
coefficient that is only tens of bytes --- but the elimination is
|
|
quadratic-ish in the hypothesis count: with $\sim 60$ hypotheses (five
|
|
limbs each for two inputs, nine columns, nine carries, plus bounds for
|
|
everything), the certificate and the kernel's intermediate terms
|
|
multiply into \emph{millions} of big-number nodes, each participating
|
|
in further products. Twelve gigabytes is not even surprising once you
|
|
draw the multiplication table of scales --- and this estimate, run
|
|
\emph{before} the tactic, is how the wall gets predicted rather than
|
|
hit.
|
|
|
|
The cure now reads directly off the arithmetic: cut the elimination
|
|
range. Prove each carry step as its \emph{own} lemma with only its own
|
|
few hypotheses in scope (coefficients stay near the step's own scale);
|
|
where a large linear identity is genuinely needed, state it yourself
|
|
and let \lean{linear_combination} \emph{check} your stated certificate
|
|
instead of searching for one (checking is cheap; finding at scale was
|
|
the memory bomb). The companion repos' rule --- no blanket automation
|
|
in fat contexts --- is this box, written as policy. And note the shape
|
|
of the lesson: it is the \emph{same} headroom discipline as the code's
|
|
(bound your intermediates, spend your budget consciously), applied one
|
|
level up, to the proof itself.
|
|
\end{worked}
|
|
|
|
\begin{aha}
|
|
There is a deep symmetry in that fix worth savoring: the \emph{proof} was
|
|
restructured exactly the way the \emph{code} was --- into small steps with
|
|
controlled intermediate size. Delayed carries in the implementation; small
|
|
lemmas in the verification. Bounded limbs; bounded contexts. Good proofs and
|
|
good fast code turn out to obey the same engineering aesthetics. This is not
|
|
a coincidence; both are fighting combinatorial growth with structure.
|
|
\end{aha}
|
|
|
|
\Needspace{4\baselineskip}
|
|
\noindent\textbf{Four forks, one method, real divergence.} The companion projects
|
|
verify not just upstream \code{curve25519-dalek} but three production forks
|
|
(Solana's, RISC~Zero's, Betrusted's) --- each against \emph{its own}
|
|
extraction. Worth it? The audit found the forks implement the same
|
|
constant-time conditional selection three different ways (a \code{subtle}
|
|
crate trait, a volatile-read \code{black_box}, a hand-rolled arithmetic
|
|
mask), and one fork reorders instructions in point doubling. All correct ---
|
|
\emph{provably}, now --- but the divergence is exactly the kind of thing
|
|
that silently breaks when someone ``harmonizes'' code during a rebase.
|
|
Per-fork verification is not pedantry; it is how you notice that ``the same
|
|
library'' isn't.
|
|
|
|
\begin{pitfall}
|
|
A verified fork is verified \emph{at a commit}. Change one line of
|
|
arithmetic and the certificate is stale --- that is a feature (the proof
|
|
\emph{should} break when the code changes), but it means verification is a
|
|
\emph{process wired into maintenance}, not a trophy. The companion repos
|
|
ship \code{check.sh} scripts that recompile and re-audit every shipped
|
|
proof from scratch (extraction replay is a separate pinned recipe,
|
|
\code{verification/extract.sh});
|
|
treat those as the project's pulse, not as CI decoration.
|
|
\end{pitfall}
|
|
|
|
\section{Reading a certificate like a professional}
|
|
|
|
Suppose a stranger hands you a repository claiming ``formally verified
|
|
field arithmetic.'' Chapter~\ref{ch:honesty} gives you the full audit
|
|
protocol, but the field-layer questions you can already ask are these:
|
|
|
|
\begin{itemize}[leftmargin=1.4em]
|
|
\item \textbf{What is denoted?} Find the denotation function. Does it map
|
|
the \emph{extracted} representation (good) or a hand-written lookalike
|
|
(Chapter~\ref{ch:rust}'s hole)?
|
|
\item \textbf{Is the square complete?} Value equation \emph{and} bounds
|
|
propagation \emph{and} the \lean{.ok} clause --- a spec that assumes
|
|
success proves nothing about overflow.
|
|
\item \textbf{Is the quantifier honest?} \lean{∀ a b} with bounds
|
|
hypotheses, or a handful of \lean{example}s on constants dressed up as
|
|
coverage?
|
|
\item \textbf{Does anything say \lean{sorry}?} One \lean{sorry} anywhere in
|
|
the dependency chain and the certificate is decorative. The checker will
|
|
tell you; ask it.
|
|
\end{itemize}
|
|
|
|
\begin{tryit}
|
|
Do the audit for real: open \code{dalek-ed25519-verified}, find the
|
|
denotation, pick the \code{sub} spec, and check the three clauses against
|
|
the list above. Then run the repo's \code{check.sh} and watch the whole
|
|
pyramid rebuild. Total time: one coffee. Skill acquired: permanent.
|
|
\end{tryit}
|
|
|
|
\section*{Exercises}
|
|
|
|
\exercise{Field subtraction in dalek computes $a - b$ as
|
|
$a + (16p - b)$ limb-wise (adding a multiple of $p$ keeps limbs positive ---
|
|
recall \lean{Nat} truncation!). State the commuting square for \code{sub}
|
|
including the exact multiple-of-$p$ fact you would need as a lemma, and
|
|
explain why $16p$ rather than $p$.}
|
|
|
|
\exercise{The Fermat inversion chain computes $a^{p-2}$ via 254 squarings
|
|
and 11 multiplications. Verify the exponent bookkeeping for the first three
|
|
steps of dalek's actual chain: $a^2$, $a^{9} = (a^2)^{2\cdot 2} \cdot a$,
|
|
$a^{11} = a^9 \cdot a^2$. (The full chain is exercise-by-induction: each
|
|
step's exponent is a sum of previous ones --- addition-chain arithmetic.)}
|
|
|
|
\exercise{(Discussion) Step 5 refuses to trust the squaring shortcut and
|
|
verifies \code{square} separately from \code{mul}. A colleague argues
|
|
``square is just \code{mul a a}, reuse the theorem.'' What, concretely,
|
|
would that argument miss about the code under verification?}
|
|
|
|
\section*{Solutions and pathways}
|
|
\solutionsintro
|
|
|
|
\solhead{10.1}
|
|
\pathway Assemble from parts you own: the two-clause spec shape
|
|
(Chapter~\ref{ch:denotation}), the multiple-of-$p$ freedom (the telescope
|
|
worked example), and the positivity audit (this chapter's $16p$ worked
|
|
example). The exercise is really asking you to \emph{compose} them into
|
|
one statement.
|
|
\answer The commuting square for \code{sub}, with the lemma named:
|
|
\[
|
|
\mathrm{Bnd54}(a) \to \mathrm{Bnd54}(b) \to
|
|
\exists c,\; \code{sub}\,a\,b = \mathtt{.ok}\ c \;\wedge\;
|
|
\mathrm{Bnd56}(c) \;\wedge\; \denote{c} = \denote{a} - \denote{b},
|
|
\]
|
|
resting on the lemma $\denote{16P} = 16p \equiv 0 \pmod p$ (the
|
|
telescope, scaled by $16$), which justifies
|
|
$\denote{a + (16P - b)} = \denote{a} + 0 - \denote{b}$. Why $16$ and not
|
|
$p$ itself (i.e.\ $k = 1$): positivity of every limb of $kP - b$ requires
|
|
the \emph{smallest} limb constant, $k(2^{51}-19)$, to dominate the
|
|
\emph{largest} allowed limb of $b$, $2^{54}-1$; $k = 1$ gives
|
|
$2^{51}-19 \ll 2^{54}$ --- hopeless --- and even $k = 8$ falls $151$
|
|
short, as the worked example computed. First power of two that clears
|
|
the bar: $16$.
|
|
|
|
\solhead{10.2}
|
|
\pathway Squaring doubles the exponent; multiplying adds. Track only
|
|
exponents and check each claimed identity by substitution --- the field
|
|
elements are irrelevant to the bookkeeping, which is the insight that
|
|
makes the whole verification tractable.
|
|
\answer $a^2$: exponent $2$ ✓ (one squaring).
|
|
$a^9 = (a^2)^{2\cdot 2} \cdot a$: squaring $z_2$ twice gives exponent
|
|
$2 \cdot 2 \cdot 2 = 8$; multiplying by $a$ adds $1$: exponent $9$ ✓
|
|
(two squarings, one multiplication).
|
|
$a^{11} = a^9 \cdot a^2$: $9 + 2 = 11$ ✓ (one multiplication).
|
|
The induction that carries the rest: each ladder stage claims
|
|
$(2^j - 1)\cdot 2^k + (2^k - 1) = 2^{j+k} - 1$ for the block sizes
|
|
$(j,k)$ it uses --- verify it once in general:
|
|
$(2^j - 1)2^k + 2^k - 1 = 2^{j+k} - 2^k + 2^k - 1 = 2^{j+k} - 1$ ✓ ---
|
|
and every remaining line of the chain is an instance. Addition-chain
|
|
verification in full: substitute, cancel, done.
|
|
|
|
\solhead{10.3}
|
|
\pathway Ask what \emph{differs between the two code paths}, not between
|
|
the two mathematical functions. The math of \code{square a} and
|
|
\code{mul a a} agree; the \emph{instructions} do not.
|
|
\answer Three concrete misses. (1) \code{square} is a \emph{different
|
|
routine}: it exploits symmetry ($a_i a_j$ appears twice for $i \neq j$,
|
|
so it computes $2 a_i a_j$ once), which changes the column expressions,
|
|
the constant factors, and therefore the \emph{bounds} --- the doubled
|
|
terms eat one extra headroom bit that \code{mul}'s proof never had to
|
|
account for. (2) A bug in that symmetry trick --- the classic one is a
|
|
missing factor of $2$ on exactly one cross term --- lives \emph{only} in
|
|
\code{square}'s code path; a theorem about \code{mul} quantifies over
|
|
\code{mul}'s instructions and says nothing about it. (3) Even the
|
|
\lean{.ok} clause differs: overflow behavior depends on the actual
|
|
intermediate expressions, not on the mathematical value. The one-line
|
|
summary for design reviews: \emph{we verify code paths, not functions ---
|
|
if the optimizer wrote a second path, the verifier owes a second
|
|
theorem}. (This is also exactly why the four forks each got their own
|
|
proofs: same math, four instruction streams.)
|
|
|
|
One last thing, so the summit does not feel abstract: the certificate this
|
|
chapter taught you to state is not a textbook artifact. It lives in four
|
|
public repositories --- one per fork --- where \code{check.sh} rebuilds it
|
|
from scratch on demand, and each of those repositories is attested as a
|
|
leaf of the live transparency log at \code{ltl.zkdefi.org}. When you audit
|
|
one with the four questions above, you are not doing an exercise; you are
|
|
doing the thing.
|
|
|
|
\begin{checkpoint}
|
|
You should now be able to: state the field implementation certificate and
|
|
every quantifier in it; recite the campaign order and justify why bounds
|
|
and \code{reduce} come early; tell the memory-wall story and its
|
|
decomposition moral; and audit a stranger's field-layer claim with four
|
|
pointed questions.
|
|
\end{checkpoint}
|