diff --git a/ArkLib.lean b/ArkLib.lean index 6b64cfc6ba..766d21b7c2 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -3,32 +3,32 @@ import ArkLib.Commitments.Functional.Basic import ArkLib.Commitments.Functional.Hachi import ArkLib.Commitments.Functional.Hachi.Commitment import ArkLib.Commitments.Functional.Hachi.Composition -import ArkLib.Commitments.Functional.Hachi.Escape import ArkLib.Commitments.Functional.Hachi.EvalSplit -import ArkLib.Commitments.Functional.Hachi.Gadget import ArkLib.Commitments.Functional.Hachi.Gadget.Basic +import ArkLib.Commitments.Functional.Hachi.Gadget.Core import ArkLib.Commitments.Functional.Hachi.Gadget.Norms -import ArkLib.Commitments.Functional.Hachi.InnerOuter import ArkLib.Commitments.Functional.Hachi.InnerOuter.Arithmetic +import ArkLib.Commitments.Functional.Hachi.InnerOuter.Basic import ArkLib.Commitments.Functional.Hachi.InnerOuter.Correctness import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security -import ArkLib.Commitments.Functional.Hachi.QuadEval +import ArkLib.Commitments.Functional.Hachi.QuadEval.Basic import ArkLib.Commitments.Functional.Hachi.QuadEval.Bridge import ArkLib.Commitments.Functional.Hachi.QuadEval.Gadgets import ArkLib.Commitments.Functional.Hachi.QuadEval.Reduction import ArkLib.Commitments.Functional.Hachi.QuadEval.Soundness +import ArkLib.Commitments.Functional.Hachi.Recursion.Basic import ArkLib.Commitments.Functional.Hachi.Recursion.PartialEval import ArkLib.Commitments.Functional.Hachi.Recursion.TraceHandoff import ArkLib.Commitments.Functional.Hachi.Recursion.ZBatchBridge -import ArkLib.Commitments.Functional.Hachi.RingSwitch +import ArkLib.Commitments.Functional.Hachi.RingSwitch.Basic import ArkLib.Commitments.Functional.Hachi.RingSwitch.Reduction import ArkLib.Commitments.Functional.Hachi.RingSwitch.Rlin -import ArkLib.Commitments.Functional.Hachi.Sumcheck +import ArkLib.Commitments.Functional.Hachi.Sumcheck.Basic import ArkLib.Commitments.Functional.Hachi.Sumcheck.Bridge import ArkLib.Commitments.Functional.Hachi.Sumcheck.FinalEval import ArkLib.Commitments.Functional.Hachi.Sumcheck.Rounds -import ArkLib.Commitments.Functional.Hachi.ZeroCheck +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Basic import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction @@ -223,7 +223,6 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound import ArkLib.OracleReduction.Security.Implications import ArkLib.OracleReduction.Security.Rewinding diff --git a/ArkLib/Commitments/Functional/Hachi.lean b/ArkLib/Commitments/Functional/Hachi.lean index 907103bb49..1612a76472 100644 --- a/ArkLib/Commitments/Functional/Hachi.lean +++ b/ArkLib/Commitments/Functional/Hachi.lean @@ -24,11 +24,11 @@ subprotocols and the completeness layer — the honest-prover `opening` (`hachi. ## Folder structure -The folder `Hachi/` is organized by paper section, each subfolder carrying an umbrella `.lean` -re-export next to it (as this file does for the whole folder): +The folder `Hachi/` is organized by paper section. Each subfolder carries a `Basic.lean` +umbrella re-export inside the folder (as this file does for the whole Hachi development): * `Gadget/` (§2.1) — the base-`b` Ajtai gadget matrix `G` and its digit-decomposition inverse - `G⁻¹` (`Basic`), with centered `ℓ∞` / `ℓ₂²` norm bounds for both directions (`Norms`). + `G⁻¹` (`Core`), with centered `ℓ∞` / `ℓ₂²` norm bounds for both directions (`Norms`). * `EvalSplit.lean` (§4, Eq. (12)) — multilinear evaluation as the vector–matrix–vector product `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))`; kept top-level because the future §3 packing head reuses it over the subfield. @@ -38,10 +38,15 @@ re-export next to it (as this file does for the whole folder): * `QuadEval/` (§4.2, Figure 3) — the quadratic polynomial-evaluation reduction: gadget algebra (`Gadgets`), protocol data and relations (`Reduction`), Hachi Lemma 8 coordinate-wise special soundness (`Soundness`), and the zero-round polynomial-level bridge (`Bridge`). -* `Composition.lean` — the CWSS composition home: the finished core - `evalChain = bridgePackage ▷ quadEvalPackage` with its certificate - `eval_coordinateWiseSpecialSound`; every further subprotocol lands as one more `CWSSPackage` - `▷`-appended there. +* `RingSwitch/`, `ZeroCheck/`, and `Sumcheck/` (§4.3) — the lift, corrected zero-check, and + guarded sumcheck stages of the opening chain. +* `Recursion/` (§4.5) — the partial-evaluation, packing, and trace-handoff adapters that close + one iteration at the next ring's plain `QuadEval.relIn` relation. +* `Composition.lean` — the CWSS composition home: `evalChain = bridgePackage ▷ + quadEvalPackage`, followed by the opening subprotocols. Every package exposes the ordinary + `relIn` / `relOut` flow; the cryptographic failure modes of extraction (`QuadEval`'s Module-SIS + break, the weak-binding collisions of Figures 4–6) are **escape events** on the transcript tree, + entering each certificate as a disjunct of its conclusion. * `Commitment.lean` — Hachi as a `Commitment.Scheme`: the multilinear eval-oracle interface and the honest `keygen` / `commit` (the opening `Proof` is a documented `sorry` pending the remaining subprotocols). diff --git a/ArkLib/Commitments/Functional/Hachi/Commitment.lean b/ArkLib/Commitments/Functional/Hachi/Commitment.lean index 3a62c9c6d1..941e06e3ad 100644 --- a/ArkLib/Commitments/Functional/Hachi/Commitment.lean +++ b/ArkLib/Commitments/Functional/Hachi/Commitment.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.QuadEval +import ArkLib.Commitments.Functional.Hachi.QuadEval.Basic import ArkLib.Commitments.Functional.Basic /-! @@ -20,7 +20,7 @@ the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q`, Hachi §2.1/§4.1), and t The eval-oracle interface and the honest committer operations are real; the opening `Proof` is deferred (`sorry`, see the `TODO`). The coordinate-wise-special-sound (CWSS) composition the finished opening will run over lives in the sibling `Composition.lean` -(`evalChain` / `eval_coordinateWiseSpecialSound`). +(`evalChain` / `eval_coordinateWiseSpecialSoundWith`). ## Main definitions diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 778f77184c..db1dff140a 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.QuadEval +import ArkLib.Commitments.Functional.Hachi.QuadEval.Basic import ArkLib.Commitments.Functional.Hachi.Sumcheck.FinalEval import ArkLib.Commitments.Functional.Hachi.Recursion.TraceHandoff import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition @@ -16,13 +16,16 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded This is the designated home of the growing n-ary composition of the subprotocols of Hachi [NOZ26], a lattice-based multilinear polynomial commitment scheme. Each subprotocol is formalized in its own -file and exported as a `CWSSPackage` (pure verifier) or `GCWSSPackage` (guarded verifier — may -`failure` at runtime), bundling the verifier with its proof of coordinate-wise special soundness -(CWSS), the knowledge-soundness notion under which a witness is extracted from a suitably -structured tree of accepting transcripts. This file only **imports those packages and chains -them**: pure links with `▷` (`CWSSPackage.append`, seams discharged by `rfl`), guarded links with -`▷ᵍ` (`GCWSSPackage.append`, the B4 skeleton in -`OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean`). The composed chain's +file and exported as a CWSS *package* in the weakest of the four kinds it honestly lives in — +`CWSSPackage`, `GCWSSPackage` (guarded verifier: may `failure` at runtime), `EscapeCWSSPackage` +(extraction may exhibit a cryptographic escape), `EscapeGCWSSPackage` (both) — bundling the verifier +with its proof of coordinate-wise special soundness (CWSS), the knowledge-soundness notion under +which a witness is extracted from a suitably structured tree of accepting transcripts. This file +only **imports those packages and chains them** with the universal append `▷`, which dispatches on +the factors' package kinds and lifts each to the join automatically (both lifts are lossless). Only +the ordinary relation seam has to match — escape events compose without a seam. The guarded +composition theorem lives in +`OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean`. The composed chain's `isCWSS` field is the CWSS certificate for the whole reduction. (Hachi as a `Commitment.Scheme` — the honest committer `keygen`/`commit` and the `hachi` functional commitment — lives in the sibling `Commitment.lean`.) @@ -30,84 +33,105 @@ sibling `Commitment.lean`.) ## The three layers of this file 1. **`evalChain`** (sorry-free, finished): the polynomial-level bridge ▷ `QuadEval` - (§4.2 / Figure 3 / Lemma 8). -2. **`openCore`** (skeleton, pure links): the escape-threaded front `evalChainE` extended by the + (§4.2 / Figure 3 / Lemma 8) — an escape-aware package whose event is `QuadEval`'s. +2. **`openCore`** (skeleton, pure links): the escape-aware `evalChain` extended by the §4.3 stages up to the sumcheck bridge — R^lin adapter (F2) ▷ HMZ25 lift (Figure 4 / Lemma 9) ▷ batching bridge (Eqs. (22)–(23)) ▷ zero-check (Figure 5 / **corrected** Lemma 10) ▷ sumcheck bridge. -3. **`openingChain`** (skeleton, guarded tail): `openCore` ▷ᵍ the paired sumcheck loop - (Figure 6 / Lemma 11, `m₀` guarded rounds) ▷ᵍ final evaluation (Figure 7 tail) ▷ᵍ the §4.5 - recursion adapters (partial evaluations ▷ᵍ `Z`-packing bridge ▷ᵍ trace handoff), landing on +3. **`openingChain`** (skeleton, guarded tail): the pure `openCore` ▷ the paired sumcheck loop + (Figure 6 / Lemma 11, `m₀` guarded rounds) ▷ final evaluation (Figure 7 tail) ▷ the + §4.5 recursion adapters (pure partial evaluations ▷ pure `Z`-packing bridge ▷ guarded trace + handoff), landing on the **next iteration's** `QuadEval` input relation over the next ring `Φ'` — the recursion - loop's closing seam. + loop's closing seam. The universal `▷` lifts each pure factor into the escape-guarded world + automatically (`CoordinateWiseSpecialSoundness/Escape.lean`, package-lattice section); no + explicit `.toGuarded` calls. ## The composed verifier chain, seam by seam Top-to-bottom is one opening iteration of the ArkLib Hachi commitment, whose committed data is an `Rq`-valued multilinear polynomial. The opening starts with the Figure 3 path, not with §3: the §3 packing head (extension-field claims into `Rq`-claims, via the generalized `RingSwitching` packing -phase — see `HACHI_RING_SWITCHING_PLAN.md`, Phases B–E) is a separate track that wraps *external* -extension-field claims in front of `relPolyEval(E)`; the §4.5 adapters below close the recursion -*internally*. Witnesses in rows 3–11 are `· ⊕ E`: escape threading (`Set.withEscape`) gives every -seam a home for the `w̃`-commitment's weak-binding break (design G1; `E` abstract, escape set -`K.esc`). +phase) is a separate track that wraps *external* extension-field claims in front of `relPolyEval`; +the §4.5 adapters below close the recursion *internally*. + +Every row's relations are the ordinary protocol relations. The cryptographic failure modes of +extraction live in the rows' **escape events** (`ChallengeTree.EscapeEvent`), which enter each +certificate as a disjunct of its *conclusion*, and compose along the chain by +`ChallengeTree.EscapeEvent.append` — so factors tracking breaks of different assumptions need only +match their relation seam. ```text # | link (file) | rounds: wire | relIn → relOut | CWSS, k ---+----------------------------+----------------------+---------------------------+--------------- - 1 | bridge (QuadEval/Bridge) | 0 | relPolyEvalE → relInE | any (0 chals) - 2 | QuadEval (QuadEval/*) | msg v; c ∈ C^{2^r} | relInE → relOutE (Eq. 20) | ℓ=2^r, k=2 (L8) - 3 | R^lin (RingSwitch/Rlin) | 0 | relOutE → relRlinE | any - 4 | lift (…/Reduction) | msg t; α ∈ F | relRlinE → relLiftE | ℓ=1, k=2d (L9) - 5 | batch (ZeroCheck/Batch) | 0 | relLiftE → relBatchedE | any - 6 | zero-check (…/Reduction) | (ρ₀,ρ_α) ∈ F² | relBatchedE → relZeroChkE | ℓ=2, k=D (L10*) - 7 | sc bridge (Sumcheck/Bridge)| 0 | relZeroChkE → roundRelE 0 | any - 8 | rounds ×m₀ (…/Rounds) | (g-pair; aᵢ)ᵢ | roundRelE 0 → roundRelE m₀| ℓ=1, k=2b+1 + 1 | bridge (QuadEval/Bridge) | 0 | relPolyEval → relIn | any (0 chals) + 2 | QuadEval (QuadEval/*) | msg v; c ∈ C^{2^r} | relIn → relOut (Eq. 20) | ℓ=2^r, k=2 (L8) + 3 | R^lin (RingSwitch/Rlin) | 0 | relOut → relRlin | any + 4 | lift (…/Reduction) | msg t; α ∈ F | relRlin → relLift | ℓ=1, k=2d (L9) + 5 | batch (ZeroCheck/Batch) | 0 | relLift → relBatched | any + 6 | zero-check (…/Reduction) | (ρ₀,ρ_α) ∈ F² | relBatched → relZeroCheck | ℓ=2, k=D (L10*) + 7 | sc bridge (Sumcheck/Bridge)| 0 | relZeroCheck → roundRel 0 | any + 8 | rounds ×m₀ (…/Rounds) | (g-pair; aᵢ)ᵢ | roundRel 0 → roundRel m₀ | ℓ=1, k=2b+1 | — GUARDED: gᵢ(0)+gᵢ(1)=z | | | (L11)/round - 9 | final eval (…/FinalEval) | msg y′ ∈ F | roundRelE m₀ → relWEvalE | any — GUARDED -10 | partials (Recursion/PartialEval)| msg (yᵢ)_{i≠0} | relWEvalE → relPartialE | any (pure) -11 | Z-pack (…/ZBatchBridge) | 0 | relPartialE → relHatEvalE | any — ⚠ GAP -12 | handoff (…/TraceHandoff) | msg p ∈ R′q | relHatEvalE → relInE(Φ′) | any — GUARDED + 9 | final eval (…/FinalEval) | msg y′ ∈ F | roundRel m₀ → relWEvalClaim | any — GUARDED +10 | partials (Recursion/PartialEval)| msg (yᵢ)_{i≠0} | relWEvalClaim → relPartialEval | any (pure) +11 | Z-pack (…/ZBatchBridge) | 0 | relPartialEval → relHatEval | any — ⚠ GAP +12 | handoff (…/TraceHandoff) | msg p ∈ R′q | relHatEval → relIn(Φ′) | any — GUARDED | | | = next iteration's row 2 | ``` +**Which rows carry an escape event.** Row 2 carries `QuadEval`'s Module-SIS(B/D) break of the fixed +key (`quadEvalEscLocal`); rows 4, 6 and 8 carry the weak-binding collision of the `w̃`-commitment +(`LiftCom.Collision`, via `liftEscLocal` / `zeroCheckEsc` / `roundEsc`). Those four are +`EscapeCWSSPackage`/`EscapeGCWSSPackage`s; every other row is escape-free +(`CWSSPackage`/`GCWSSPackage`) and enters the chain at the never-firing event through the universal +`▷`'s lossless lift. + - Rows 1–7 have **pure** verifiers: every check constrains either retained statement data or the never-sent witness, so it lives in the output relation (the `QuadEval` precedent). Rows 8, 9, 12 are **guarded** (design D6): their runtime check reads data the next statement type drops (the previous sumcheck target; the final targets; the packed claim value) — exactly the paper's - runtime checks — and compose via `▷ᵍ`, whose composition theorem (B4) is the one sorried piece - of *generic* machinery. + runtime checks — and compose through the guarded append, whose composition theorem (B4) is the + one sorried piece of *generic* machinery. - Row 6 implements the **corrected Lemma 10**: the paper's uniform-vector star extraction is not provable (axis-cross counterexample); the challenge is a pair of scalar **Kronecker seeds** with the batching points derived on the curves `κ_m(ρ) = (ρ, ρ², ρ⁴, …)`, giving genuine - `(ℓ, k) = (2, D)` CWSS at `D = max 2^{m₀} 2^{m₁}`. See `HACHI_LEMMA10_GAP.md`. This is the one - place the formalization deliberately changes the paper's protocol. + `(ℓ, k) = (2, D)` CWSS at `D = max 2^{m₀} 2^{m₁}`. The counterexample and the repair are spelled + out in `ZeroCheck/Reduction.lean`. This is the one place the formalization deliberately changes + the paper's protocol. - Row 11 isolates the **§4.5/§3.2 partial-evaluation gap** found while auditing this skeleton: the packed claim of Eq. (26) pins only one `F`-linear functional of the per-slice values, so the paper's step is (apparently) not knowledge-sound as stated; the bridge's pull-back sorry is expected to be unprovable until a repair (batching challenge / generic §3.1 packing) is - adopted. See `HACHI_RECURSION_GAP.md`. All other sorries in the chain are honest skeleton work. -- Row 12 lands on `relInE Φ'` — the escape-threaded `QuadEval` input relation at the **next** - ring: iteration `i+1` re-enters at `quadEvalPackageE Φ'` directly (its bases are `eq`-tensor + adopted; the analysis and the candidate repairs are in `Recursion/ZBatchBridge.lean`. All other + sorries in the chain are honest skeleton work. +- Row 12 lands on plain `relIn Φ'`. Iteration + `i+1` re-enters at `quadEvalPackage Φ'` directly (its bases are `eq`-tensor packings, not monomial bases, so the polynomial-level bridge of row 1 is head-only). Asymptotic termination (§4.4: reveal the final polynomial once small) and the concrete §4.5 Greyhound/LaBRADOR cutoff are future zero-round tails at that seam. ## Sorry inventory of the composed chain (provenance of the certificate) -*Generic machinery* (B4): `Verifier.IsGuarded.append`, -`Verifier.append_coordinateWiseSpecialSound_of_guardedLeft` (`Guarded.lean`); -`coordinateWiseSpecialSound_of_mkWitness_scalar` (`ScalarRound.lean`, consumed only by future -proofs). *Escape threading* (F2.0): `quadEval_coordinateWiseSpecialSound_withEscape`. -*Per-link math*: the F2 index bookkeeping (`rlinStmt`/`unstack`/`mem_relOutE_of_relRlinE`), -Lemma 9 (`lift_coordinateWiseSpecialSound`), the F5 encodings (`Constraints.lean`), the -un-batching (`mem_relLiftE_of_relBatchedE`), corrected Lemma 10 -(`zeroCheck_coordinateWiseSpecialSound`), the sum-to-point bridge, Lemma 11 -(`round_coordinateWiseSpecialSound`), F8 (`finalEval_coordinateWiseSpecialSound` + the -`finalCheck` encoding), G2 (`partialEval_coordinateWiseSpecialSound` + its encoding defs), G3 -(`handoff_coordinateWiseSpecialSound` + `traceCheck`/`toNextQuadEvalStatement`/`hatEval`). +*Generic machinery* (B4): `Verifier.IsGuarded.append` and +`Verifier.append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft` (`Guarded.lean`; the latter is +the fundamental B4.3 obligation, stated escape-threaded at explicit guard data — the plain guarded +append is *proven* from it at the never-firing events); the two scalar-round assemblies +`coordinateWiseSpecialSoundWith(Escape)_of_mkWitness_scalar` (`ScalarRound.lean`, consumed only by +future proofs — its readers, shape recovery, extractor and escape event are proven). The escape +layer (`TranscriptTree/Basic.lean`, `CWSS/{Basic,Composition}.lean`, `Escape.lean`) with its append +theorem, the single-round escape assembly and `quadEval_coordinateWiseSpecialSoundWithEscape` are +proven (`sorryAx`-free). Each sorried row carries its extraction *algorithm* as an explicitly +sorried `Extractor.TreeBased`. +*Per-link math*: the F2 index bookkeeping (`rlinStmt`/`unstack`/`mem_relOut_of_relRlin`), +Lemma 9 (`lift_coordinateWiseSpecialSoundWith`), the F5 encodings (`Constraints.lean`), the +un-batching (`mem_relLift_of_relBatched`), corrected Lemma 10 +(`zeroCheck_coordinateWiseSpecialSoundWith`), the sum-to-point bridge, Lemma 11 +(`round_coordinateWiseSpecialSoundWith`), F8 (`finalEval_coordinateWiseSpecialSoundWith` + the +`finalCheck` encoding), G2 (`partialEval_coordinateWiseSpecialSoundWith` + its encoding defs), G3 +(`handoff_coordinateWiseSpecialSoundWith` + `traceCheck`/`toNextQuadEvalStatement`/`hatEval`). Every sorried encoding def carries an in-situ `**Sorried**` docstring naming its milestone. -*Flagged as an open gap (not merely unproven)*: `mem_relPartialEvalE_of_relHatEvalE` (row 11). +*Flagged as an open gap (not merely unproven)*: `mem_relPartialEval_of_relHatEval` (row 11). ## References @@ -132,49 +156,57 @@ variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} variable {σ : Type} -/-- **The composed evaluation reduction** (Hachi [NOZ26, §4.2, Figure 3], `Rq`-level): the bridge -package chained with the `QuadEval` package via the `CWSSPackage` operator `▷`. -Both packages are defined next to their CWSS theorems in the component files (`bridgePackage` in -`QuadEval/Bridge`, `quadEvalPackage` in `QuadEval/Soundness`); here they are only imported and -composed. The seam is definitional — the bridge's `relOut` *is* `QuadEval`'s `relIn` — so `▷` -discharges it by `rfl`. The chain's `isCWSS` field is `eval_coordinateWiseSpecialSound`. This is -the finished, sorry-free core; the escape-threaded variant `evalChainE` -(`Escape.lean`) is its drop-in for the extended opening chain below. -/ -def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - CWSSPackage init impl +/-- The polynomial bridge followed by `QuadEval`, along a single relation seam. The bridge is +escape-free and `QuadEval` escape-aware, so the universal `▷` lifts the bridge at the never-firing +event and the composed event fires exactly when `QuadEval`'s own event fires on the suffix tree. -/ +noncomputable def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) : + EscapeCWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) - (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows × + CarrierCom 𝓜(q, α) dRows × + (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) + innerDigits zDigits) ((!p[] : ProtocolSpec 0) ++ₚ pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := - bridgePackage 𝓜(q, α) init impl (b : ZMod q) + bridgePackage (oSpec := oSpec) 𝓜(q, α) init impl pp (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) ▷ - quadEvalPackage init impl hq5 hκ hτ - -/-- **Hachi evaluation reduction — coordinate-wise special soundness (Hachi [NOZ26, §4.2, -Figure 3], `Rq`-level), `sorry`-free.** This is the certificate carried by `evalChain`: the composed -verifier (bridge ⧺ `QuadEval`) is CWSS for `ofIsEmpty.append foldStructure`, reducing the -polynomial-level `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or MSIS(D)) to Hachi -Eq. (20) (`relOut`), pinned to `𝓜(q, α)` with the [LS18] hypotheses of -`quadEval_coordinateWiseSpecialSound`. -/ -theorem eval_coordinateWiseSpecialSound (init : ProbComp σ) + quadEvalPackage init impl hq5 hκ hτ pp + +/-- **Hachi evaluation reduction — escape-threaded coordinate-wise special soundness (Hachi +[NOZ26, §4.2, Figure 3], `Rq`-level), `sorry`-free, at the chain's named extractor.** The endpoint +relations are `relPolyEval` and `relOut`, and the extractor is the composed algorithm +`(evalChain …).extractor` (the bridge's pull-back run on the prefix tree of `QuadEval`'s Lemma 8 +extractor). The reduction's Module-SIS(B/D) failure mode is the certificate's escape disjunct +`(evalChain …).esc`, which by `ChallengeTree.EscapeEvent.append` reduces to `QuadEval`'s own event +on the suffix tree at the bridge's verdict. + +Pinned to `𝓜(q, α)` with the [LS18] hypotheses of +`quadEval_coordinateWiseSpecialSoundWithEscape`. -/ +theorem eval_coordinateWiseSpecialSoundWithEscape (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} - (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - ((bridgeVerifier (oSpec := oSpec) (innerRows := innerRows) (messageDigits := messageDigits) - (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r) - 𝓜(q, α)).append - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α))).coordinateWiseSpecialSound init impl + (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) : + Verifier.coordinateWiseSpecialSoundWithEscape init impl (CWSSStructure.ofIsEmpty.append (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r))) - (relPolyEval 𝓜(q, α) (b : ZMod q) + ((evalChain (b := b) (γ := γ) init impl hq5 hκ hτ pp).esc) + (relPolyEval 𝓜(q, α) pp (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := - (evalChain init impl hq5 hκ hτ).isCWSS + (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + ((bridgeVerifier (oSpec := oSpec) (innerRows := innerRows) (messageDigits := messageDigits) + (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r) + 𝓜(q, α)).append + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α))) + ((evalChain (b := b) (γ := γ) init impl hq5 hκ hτ pp).extractor) := + (evalChain (b := b) (γ := γ) init impl hq5 hκ hτ pp).isCWSS end Composition @@ -224,22 +256,26 @@ same workaround as `roundsSpecSampleable`). Requires a sampler for the fold chal (h₂ := ProtocolSpec.instSampleableTypeChallengeEmpty))))) /-- **The pure prefix of one Hachi opening iteration** (rows 1–7 of the chain table): the -escape-threaded evaluation front (`evalChainE` = bridge ▷ `QuadEval`, both widened by the escape -budget `E`) extended by the §4.3 stages with pure verifiers — the `R^lin` adapter, the HMZ25 -lift, the batching bridge, the (corrected-Lemma-10) zero-check, and the sumcheck bridge. Every -seam is definitional (`rfl`). The result reduces the polynomial-level `relPolyEvalE` to the -round-`0` sumcheck seam `roundRelE 0`. -/ +escape-aware evaluation front (`evalChain` = bridge ▷ `QuadEval`) extended by the §4.3 stages +with pure verifiers — the `R^lin` adapter, the HMZ25 lift, the batching bridge, the +(corrected-Lemma-10) zero-check, and the sumcheck bridge. Every relation seam is definitional +(`rfl`). The public result reduces `relPolyEval` to the round-`0` `roundRel`; the composite's escape +event is the `EscapeEvent.append`-nesting of the honest factor events (`QuadEval`'s Module-SIS break +in row 2, the lift's weak-binding collision in row 4, the zero-check's in row 6), each on its own +subtree. -/ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ ρBound m₀ m₁ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) (liftShort 𝓜(q, α) γ ρBound)) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) : - CWSSPackage init impl + EscapeCWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) (RoundStatement 𝓜(q, α) K.TCom F n₀ μ₀ 0) - (LiftedWitness 𝓜(q, α) μ₀ n₀ ⊕ E) + (LiftedWitness 𝓜(q, α) μ₀ n₀) (openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F) := haveI : ∀ i, SampleableType ((((!p[] : ProtocolSpec 0) ++ₚ @@ -247,21 +283,25 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := ProtocolSpec.instSampleableTypeChallengeEmpty) (h₂ := CoordinateWise.SingleRound.instSampleableTypeChallengePSpec) - evalChainE (b := b) (γ := γ) init impl hq5 hκ hτ K.esc ▷ - rlinPackage (zDigits := zDigits) 𝓜(q, α) init impl (b : ZMod q) ω γ K.esc ▷ + evalChain (b := b) (γ := γ) init impl hq5 hκ hτ pp ▷ + rlinPackage (zDigits := zDigits) 𝓜(q, α) init impl pp (b : ZMod q) ω γ ▷ liftPackage 𝓜(q, α) γ ρBound K φF init impl hd ▷ batchPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b hq2 hb ▷ zeroCheckPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b ▷ sumcheckBridgePackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b /-- **One full Hachi opening iteration** (rows 1–12 of the chain table): the pure prefix -`openCore` composed — through the guarded append `▷ᵍ` — with the guarded tail: the `m₀` paired -sumcheck rounds (Lemma 11, guarded on the round checks), the final-evaluation step (guarded on -the target checks), and the §4.5 recursion adapters (the pure partial-evaluation head, the ⚠ -`Z`-packing bridge of `HACHI_RECURSION_GAP.md`, and the guarded trace handoff). The chain lands -on `relInE Φ'` — the escape-threaded `QuadEval` input relation at the next ring `Φ'` — closing -the recursion loop: iteration `i+1` is this chain re-instantiated at `Φ'` (entering at -`quadEvalPackageE`, without row 1). +`openCore` composed with the guarded tail: the `m₀` paired sumcheck rounds (Lemma 11, guarded on +the round checks), the final-evaluation step (guarded on the target checks), and the §4.5 +recursion adapters (the pure partial-evaluation head, the ⚠ `Z`-packing bridge with the open +row-11 soundness question, and the guarded trace handoff). Pure factors (`openCore`, +`partialEvalPackage`, `zBatchPackage`) stay pure escape packages and are lifted into the +escape-guarded world by the mixed appends behind the universal `▷` (the two head seams, whose +relation identifications are the named `roundsChain_relIn`/`roundsChain_relOut` lemmas rather than +`rfl`, use `EscapeCWSSPackage.appendEscapeGuarded` / `EscapeGCWSSPackage.appendGuarded` +explicitly). The chain lands on the plain `relIn Φ'` relation — closing the recursion loop: +iteration `i+1` is this chain re-instantiated at `Φ'` (entering at `quadEvalPackage`, without +row 1). The certificate `openingChain.isCWSS` is the one-iteration CWSS statement; its provenance (which links are finished, skeleton-sorried, or gap-flagged) is inventoried in the module header. The @@ -270,7 +310,9 @@ variables. -/ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ ρBound m₁ mLow κ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) (liftShort 𝓜(q, α) γ ρBound)) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) (zpow : Fin (2 ^ κ) → F) @@ -280,12 +322,12 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : - GCWSSPackage init impl + EscapeGCWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') - (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits' ⊕ E) + (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits') ((openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F ++ₚ roundsSpec F b (mLow + κ) ++ₚ pSpecFinalEval F) ++ₚ (pSpecPartialEval F κ ++ₚ ((!p[] : ProtocolSpec 0) ++ₚ pSpecHandoff Φ'))) := @@ -300,27 +342,34 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta roundsSpec F b (mLow + κ)) ++ₚ pSpecFinalEval F).Challenge i) := ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := i₁) (h₂ := instSampleableTypeChallengePSpecFinalEval) - (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hq2 hb).toGuarded.append + (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K pp φF hd hq2 + hb).appendEscapeGuarded (roundsChain 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF (mLow + κ)) (roundsChain_relIn 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF - (mLow + κ)).symm).append + (mLow + κ)).symm).appendGuarded (finalEvalPackage 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF) - (roundsChain_relOut 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF (mLow + κ))) ▷ᵍ - (partialEvalPackage 𝓜(q, α) mLow κ γ ρBound b init impl K φF).toGuarded ▷ᵍ - (zBatchPackage 𝓜(q, α) mLow κ γ ρBound init impl zpow K φF).toGuarded ▷ᵍ + (roundsChain_relOut 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF + (mLow + κ))) ▷ + partialEvalPackage 𝓜(q, α) mLow κ γ ρBound b init impl K φF ▷ + zBatchPackage 𝓜(q, α) mLow κ γ ρBound init impl zpow K φF ▷ handoffPackage 𝓜(q, α) Φ' mLow κ γ ρBound init impl zpow K φF pp' reinterpretCom base' βSq' γ' κ' -/-- **Hachi one-iteration opening — coordinate-wise special soundness (skeleton certificate).** -The composed verifier of rows 1–12 is CWSS, reducing the polynomial-level `relPolyEvalE` (over -the current ring `𝓜(q, α)`) to the next iteration's `relInE` (over `Φ'`). The proof term is -just `openingChain.isCWSS`; its assumptions are exactly the sorried links inventoried in the -module header (in particular the ⚠ row-11 gap and the B4 guarded-append machinery). -/ -theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) +/-- **Hachi one-iteration opening — escape-threaded coordinate-wise special soundness (skeleton +certificate), at the chain's named extractor.** The composed verifier of rows 1–12 is CWSS over the +endpoint relations `relPolyEval` (over the current ring `𝓜(q, α)`) and `relIn` (over `Φ'`), at the +composed extraction algorithm `(openingChain …).extractor`, with the composed escape event +`(openingChain …).esc` as the certificate's disjunct — the `EscapeEvent.append`-nesting of the +honest per-row events (rows 2, 4, 6, 8), each on its own subtree. The proof term is just +`openingChain.isCWSS`; its assumptions are exactly the sorried links inventoried in the module +header (in particular the ⚠ row-11 gap and the B4 guarded-append machinery). -/ +theorem hachi_iteration_coordinateWiseSpecialSoundWithEscape (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ ρBound m₁ mLow κ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) (liftShort 𝓜(q, α) γ ρBound)) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) (zpow : Fin (2 ^ κ) → F) @@ -330,16 +379,20 @@ theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : - ((openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ - hτ K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' - γ' κ')).verifier.coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWithEscape init impl (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ hτ - K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').struct - (relPolyEvalE 𝓜(q, α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) K.esc) - (relInE Φ' base' βSq' γ' κ' K.esc) := + K pp φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').struct + (openingChain (b := b) (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl + hq5 hκ hτ K pp φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').esc + (relPolyEval 𝓜(q, α) pp (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (relIn Φ' pp' base' βSq' γ' κ') + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ + hτ K pp φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').verifier + (openingChain (b := b) (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl + hq5 hκ hτ K pp φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').extractor := (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ hτ - K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').isCWSS + K pp φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').isCWSS end OpeningChain @@ -347,17 +400,17 @@ end OpeningChain * **§3 packing head (external extension-field claims).** The paper's headline multilinear-over-`F_{q^k}` interface (§3.1/§3.2) wraps an extension-field evaluation claim in - front of `relPolyEvalE`; it is planned as an instance of the generalized `RingSwitching` - packing phase (`HACHI_RING_SWITCHING_PLAN.md`, Phases B–E), not as a Hachi-local head. + front of `relPolyEval`; it is planned as an instance of the generalized `RingSwitching` + packing phase, not as a Hachi-local head. * **Recursion termination.** At the row-12 seam: the §4.4 asymptotic base case (reveal the final small polynomial — a `SendWitness`-style tail) and the §4.5 concrete cutoff (switch to Greyhound/LaBRADOR, i.e. the JL projection route) are future zero-round/one-message tails. * **Discharging the skeleton.** The sorry inventory in the module header, in dependency order: B4 (`Guarded.lean`) → F2.0/F2 → F3/F4 → F5 → F6 → F7 → F8 → G2/G3 — plus a repair decision for - the row-11 gap (`HACHI_RECURSION_GAP.md`) and the Phase-G `LiftCom` instantiation (the - inner-outer commitment without re-decomposition, its collision escape via - `outputToModuleSIS_valid_of_verified`, and the ring-dimension reinterpretation used by row 12). + the row-11 gap and the `LiftCom` instantiation (the inner-outer commitment without + re-decomposition, its collision escape via `outputToModuleSIS_valid_of_verified`, and the + ring-dimension reinterpretation used by row 12). * **Knowledge-error accounting** (FMN24 Lemma 4), `Commitment.extractability`, and Fiat–Shamir - remain out of scope (design D12/R6). -/ + remain out of scope. -/ end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Escape.lean b/ArkLib/Commitments/Functional/Hachi/Escape.lean deleted file mode 100644 index 8d82089465..0000000000 --- a/ArkLib/Commitments/Functional/Hachi/Escape.lean +++ /dev/null @@ -1,193 +0,0 @@ -/- -Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann --/ -import ArkLib.Commitments.Functional.Hachi.QuadEval.Bridge -import ArkLib.Commitments.Functional.Hachi.QuadEval.Soundness -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape - -/-! - # Escape-threaded Hachi front (`evalChainE`) — skeleton (sumcheck-track milestone F2.0) - - The §4.3 opening chain (`RingSwitch/`, `ZeroCheck/`, `Sumcheck/`) introduces a **new - commitment** — Figure 4's - `t = Com(w̃)` — whose binding break is a fresh extraction escape (Hachi [NOZ26] Remark 2: - weak binding, a Module-SIS solution via Lemma 7). Composed CWSS extraction feeds every - downstream extractor's output into the *previous* seam relation, so this escape must flow - backwards through **all** upstream seams — including the finished bridge/`QuadEval` chain, - whose relations (`relPolyEval`, `relIn`, `relOut`) have no home for it. - - This file threads a single abstract escape budget `E` (with escape set `esc : Set E`, - statement-independent — design decision G1) through the finished front via `Set.withEscape`: - - * `relPolyEvalE`, `relInE`, `relOutE` — the widened relations (witnesses `· ⊕ E`); - * `bridgePackageE` — the widened polynomial-level bridge, **sorry-free** (the escape branch of - the pull-back is the identity; the real branch is the finished `mem_relPolyEval_of_relIn`); - * `quadEval_coordinateWiseSpecialSound_withEscape` — the widened Lemma 8 (**sorried**: re-run - the finished extraction with an escape-pass-through witness assembler `buildWitnessE`; if any - branch response is `.inr e`, output `.inr e`; otherwise strip the `Sum.inl`s and apply the - finished `buildWitness_mem_relIn` verbatim — no edits to done proofs); - * `quadEvalPackageE` and the composed widened front `evalChainE = bridgePackageE ▷ - quadEvalPackageE`, the drop-in replacement of `evalChain` that the §4.3 chain composes onto. - - At `E := Empty`, `esc := ∅` the widened relations degenerate to the originals - (`Set.withEscape_empty_iff`), so nothing is lost. - - ## References - - * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear - Polynomial Commitments over Extension Fields*][NOZ26] --/ - -namespace ArkLib.Lattices.Ajtai.InnerOuter - -open CompPoly ArkLib.Lattices.CyclotomicModulus -open WeakBinding -open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound - -/-- A left-inhabited sum is inhabited — `Nonempty (Wit ⊕ E)` for the escape-threaded witness -types, from the existing witness `Nonempty` instances. -/ -instance {A E : Type} [Nonempty A] : Nonempty (A ⊕ E) := ⟨.inl (Classical.arbitrary A)⟩ - -section ThreadedRelations - -variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] - (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} -variable {E : Type} - -/-- Escape-threaded `relPolyEval` (the chain-head relation): a real polynomial-level witness, or -an escape from further down the chain. -/ -def relPolyEvalE (base : ZMod q) (βSq γ κ : ℕ) (esc : Set E) : - Set (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r × - (QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E)) := - (relPolyEval Φ base βSq γ κ).withEscape esc - -/-- Escape-threaded `QuadEval.relIn` (Lemma 8's extraction disjunction, widened). -/ -def relInE (base : ZMod q) (βSq γ κ : ℕ) (esc : Set E) : - Set (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - (QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E)) := - (relIn Φ base βSq γ κ).withEscape esc - -/-- Escape-threaded `QuadEval.relOut` (Hachi Eq. (20) + range checks, widened): the §4.3 chain's -input seam. -/ -def relOutE (base : ZMod q) (ω γ : ℕ) (esc : Set E) : - Set ((QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) × - (QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits ⊕ E)) := - (relOut (zDigits := zDigits) Φ base ω γ).withEscape esc - -omit [NeZero q] in -/-- **Escape-threaded pull-back** for the polynomial-level bridge (the `hRel` of -`bridgePackageE`): the real branch is the finished `mem_relPolyEval_of_relIn`; the escape branch -passes through (escapes are statement-independent). Sorry-free. -/ -theorem mem_relPolyEvalE_of_relInE (base : ZMod q) (βSq γ κ : ℕ) (esc : Set E) - (s : PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) - (w : QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) - (h : (toQuadEvalStatement Φ s, w) ∈ relInE Φ base βSq γ κ esc) : - (s, w) ∈ relPolyEvalE Φ base βSq γ κ esc := by - cases w with - | inl w' => exact mem_relPolyEval_of_relIn Φ base βSq γ κ s w' h - | inr e => exact h - -end ThreadedRelations - -section ThreadedPackages - -variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} -variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} -variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} {E : Type} - -/-- **The escape-threaded polynomial-level bridge as a `CWSSPackage`** (widened -`bridgePackage`), sorry-free: the same zero-round `ReduceClaim` verifier, with the widened -relations and the escape-pass-through pull-back `mem_relPolyEvalE_of_relInE`. -/ -def bridgePackageE (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (base : ZMod q) (βSq γ κ : ℕ) (esc : Set E) : - CWSSPackage init impl - (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) - (!p[] : ProtocolSpec 0) where - verifier := bridgeVerifier (oSpec := oSpec) 𝓜(q, α) - struct := CWSSStructure.ofIsEmpty - relIn := relPolyEvalE 𝓜(q, α) base βSq γ κ esc - relOut := relInE 𝓜(q, α) base βSq γ κ esc - isPure := ⟨fun stmt _ => toQuadEvalStatement 𝓜(q, α) stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relPolyEvalE 𝓜(q, α) base βSq γ κ esc) - (relOut := relInE 𝓜(q, α) base βSq γ κ esc) - (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (mem_relPolyEvalE_of_relInE 𝓜(q, α) base βSq γ κ esc) - -/-- **Escape-threaded Hachi Lemma 8 (skeleton).** The `QuadEval` fold verifier is CWSS for the -*widened* relations `relInE`/`relOutE`. - -**Sorried (F2.0).** Proof plan: `coordinateWiseSpecialSound_of_mkWitness` with the widened -assembler `buildWitnessE` — if some branch response is `.inr e` (pick the least such branch), -output `.inr e` (its `relOutE`-membership is exactly `e ∈ esc`, which is `relInE`'s `.inr` -case); otherwise all responses are `.inl`, strip them and apply the finished -`buildWitness_mem_relIn` verbatim. No edits to the finished sorry-free proofs. -/ -theorem quadEval_coordinateWiseSpecialSound_withEscape - (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) (esc : Set E) : - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) - (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (m := m) - (r := r)).coordinateWiseSpecialSound init impl - (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) - (C := ShortChallenge 𝓜(q, α) ω) (r := r)) - (relInE 𝓜(q, α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) esc) - (relOutE (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ esc) := by - sorry - -/-- **Escape-threaded `QuadEval` package** (widened `quadEvalPackage`): the same two-round fold -verifier and `foldStructure`, with the widened relations; the certificate is the sorried -`quadEval_coordinateWiseSpecialSound_withEscape`. -/ -def quadEvalPackageE (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) (esc : Set E) : - CWSSPackage init impl - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) - (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits ⊕ E) - (pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where - verifier := verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) - struct := - foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) - (r := r) - relIn := relInE 𝓜(q, α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) esc - relOut := relOutE (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ esc - isPure := ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ - isCWSS := quadEval_coordinateWiseSpecialSound_withEscape init impl hq5 hκ hτ esc - -/-- **The escape-threaded evaluation front** `bridgePackageE ▷ quadEvalPackageE`: the widened -drop-in for `evalChain`, from `relPolyEvalE` to `relOutE` (Eq. (20) + ranges, widened). The -§4.3 opening chain (`RingSwitch/` onwards) composes onto this front's `relOutE` seam. -/ -def evalChainE (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) (esc : Set E) : - CWSSPackage init impl - (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) - (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits ⊕ E) - ((!p[] : ProtocolSpec 0) ++ₚ - pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := - bridgePackageE init impl (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) esc ▷ - quadEvalPackageE init impl hq5 hκ hτ esc - -end ThreadedPackages - -end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget.lean b/ArkLib/Commitments/Functional/Hachi/Gadget.lean deleted file mode 100644 index c8052e0d8a..0000000000 --- a/ArkLib/Commitments/Functional/Hachi/Gadget.lean +++ /dev/null @@ -1,37 +0,0 @@ -/- -Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann --/ -import ArkLib.Commitments.Functional.Hachi.Gadget.Basic -import ArkLib.Commitments.Functional.Hachi.Gadget.Norms - -/-! -# Ajtai Gadget Matrices - -Umbrella for `Hachi/Gadget/`: the base-`b` Ajtai gadget matrix -`G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic ring `Rq Φ` and its norm-reducing -inverse `G⁻¹`, the base-`b` digit decomposition of Hachi [NOZ26, §2.1]. The gadget trades one -ring element for `digits` elements with small coefficients: `G⁻¹` shortens, `G` recombines. It is -the shortness workhorse of the Greyhound [NS24] / Hachi [NOZ26] inner-outer commitment — honest -commitments consist of gadget digits (hence are short), and the verifier's checks recombine them -through `G`. - -## Folder structure - -* `Gadget/Basic.lean` — the algebra: the gadget matrix `G` (`gadgetMatrix` / `gadgetMul`), lawful - decompositions (`IsLawfulGadgetDecomposition`, i.e. `G · G⁻¹(x) = x`), the abstract - per-coefficient digit map (`DigitDecomposition`) with its concrete `ZMod q` instance - (`zmodDigitDecomposition`), and the induced gadget inverse `gadgetDecompose` with its - lawfulness proof. -* `Gadget/Norms.lean` — the analysis: centered `ℓ∞` / `ℓ₂²` shortness of the honest - decomposition `G⁻¹(x)` (feeding perfect correctness in `InnerOuter/Correctness.lean`), and - controlled norm growth of the recomposition `G·ẑ` for any range-checked `ẑ` (feeding Lemma 8 - in `QuadEval/Soundness.lean`). - -## References - -* [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] -* [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear - Polynomial Commitments over Extension Fields*][NOZ26] --/ diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean index 07368a1528..e3e47f860d 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean @@ -3,44 +3,31 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Data.Lattices.ModuleSIS -import Mathlib.Data.Nat.Digits.Lemmas -import Mathlib.Data.ZMod.Basic -import Mathlib.Algebra.Field.ZMod +import ArkLib.Commitments.Functional.Hachi.Gadget.Core +import ArkLib.Commitments.Functional.Hachi.Gadget.Norms /-! # Ajtai Gadget Matrices -The base-`b` gadget matrix `G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic -ring `Rq Φ`, mapping `rows * digits` ring elements to `rows` ring elements, used by the -inner-outer (Greyhound [NS24] / Hachi [NOZ26]) commitment. Gadget entries are *ring -constants* `C(bᵉ)` embedded into `Rq Φ`. `IsLawfulGadgetDecomposition` records when a -decomposition is inverted by gadget multiplication (`G · G⁻¹(x) = x`). - -The norm-reducing inverse `G⁻¹` is the genuine **base-`b` digit decomposition** of the Hachi -paper ([NOZ26], §2.1): each coefficient of a ring element is written in base `b`, and digit `e` -of each coefficient is placed in the `bᵉ`-slot of its block. Trading one ring element for -`digits` elements with small digit coefficients is what keeps honest Ajtai openings short -(the norm bounds live in `Gadget.Norms`). The decomposition is captured abstractly by -`DigitDecomposition` (a per-coefficient digit map satisfying the base-`b` reconstruction law) -and realized concretely over `ZMod q` by `zmodDigitDecomposition`. - -## Main definitions - -* `gadgetMatrix`, `gadgetMul`: the gadget matrix `G` and multiplication by it. -* `IsLawfulGadgetDecomposition`: a decomposition is lawful when `G · G⁻¹(x) = x` for all `x`. -* `DigitDecomposition`: abstract base-`b` digit map on the coefficient ring, with the - reconstruction law `∑ₑ bᵉ · digit c e = c`. -* `zmodDigitDecomposition`: the concrete digits of the canonical representative over `ZMod q`, - valid when `1 < b` and `q ≤ b ^ digits`. -* `gadgetDecompose`: the gadget inverse `G⁻¹` induced by a `DigitDecomposition`. - -## Main results - -* `gadgetMul_apply`: row `i` of the gadget product is the base-weighted sum of the `digits` - slots of block `i`. -* `gadgetDecompose_lawful`: `gadgetDecompose` is a lawful gadget decomposition, so the - inner-outer correctness theorem instantiates with this genuine base-`b` decomposition. +Umbrella for `Hachi/Gadget/`: the base-`b` Ajtai gadget matrix +`G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic ring `Rq Φ` and its norm-reducing +inverse `G⁻¹`, the base-`b` digit decomposition of Hachi [NOZ26, §2.1]. The gadget trades one +ring element for `digits` elements with small coefficients: `G⁻¹` shortens, `G` recombines. It is +the shortness workhorse of the Greyhound [NS24] / Hachi [NOZ26] inner-outer commitment — honest +commitments consist of gadget digits (hence are short), and the verifier's checks recombine them +through `G`. + +## Folder structure + +* `Gadget/Core.lean` — the algebra: the gadget matrix `G` (`gadgetMatrix` / `gadgetMul`), lawful + decompositions (`IsLawfulGadgetDecomposition`, i.e. `G · G⁻¹(x) = x`), the abstract + per-coefficient digit map (`DigitDecomposition`) with its concrete `ZMod q` instance + (`zmodDigitDecomposition`), and the induced gadget inverse `gadgetDecompose` with its + lawfulness proof. +* `Gadget/Norms.lean` — the analysis: centered `ℓ∞` / `ℓ₂²` shortness of the honest + decomposition `G⁻¹(x)` (feeding perfect correctness in `InnerOuter/Correctness.lean`), and + controlled norm growth of the recomposition `G·ẑ` for any range-checked `ẑ` (feeding Lemma 8 + in `QuadEval/Soundness.lean`). ## References @@ -48,206 +35,3 @@ and realized concretely over `ZMod q` by `zmodDigitDecomposition`. * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over Extension Fields*][NOZ26] -/ - -open CompPoly ArkLib.Lattices ArkLib.Lattices.CyclotomicModulus - -namespace ArkLib.Lattices.Ajtai - -/-! ## Base-`b` reconstruction of `Nat.ofDigits` as a finite sum -/ - -/-- `Nat.ofDigits` as the finite sum of digit-weighted powers over the length of the list. -/ -private theorem ofDigits_eq_sum_range {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) : - Nat.ofDigits β L = ∑ i ∈ Finset.range L.length, (L.getD i 0 : α) * β ^ i := by - induction L with - | nil => simp [Nat.ofDigits] - | cons h t ih => - rw [show Nat.ofDigits β (h :: t) = (h : α) + β * Nat.ofDigits β t from rfl, ih, - List.length_cons, Finset.sum_range_succ', Finset.mul_sum] - simp only [List.getD_cons_succ, List.getD_cons_zero, pow_zero, mul_one, pow_succ] - rw [add_comm] - congr 1 - apply Finset.sum_congr rfl - intro i _ - ring - -/-- `Nat.ofDigits` as a finite sum over any range `D` at least the list length (the extra -high-order digits are zero). -/ -private theorem ofDigits_eq_sum_range_of_len_le {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) - {D : ℕ} (hLD : L.length ≤ D) : - Nat.ofDigits β L = ∑ i ∈ Finset.range D, (L.getD i 0 : α) * β ^ i := by - rw [ofDigits_eq_sum_range β L] - apply Finset.sum_subset (fun x hx => - Finset.mem_range.mpr (lt_of_lt_of_le (Finset.mem_range.mp hx) hLD)) - intro i _ hi - rw [Finset.mem_range, not_lt] at hi - rw [List.getD_eq_default _ _ hi, Nat.cast_zero, zero_mul] - -/-! ## Abstract digit decompositions of the coefficient ring -/ - -section Digit - -variable {R : Type*} [CommSemiring R] - -/-- A base-`base` digit decomposition of the coefficient ring `R`: for each coefficient `c`, -`digit c e` is the `e`-th base-`base` digit, and the `digits` digits reconstruct `c` via -`∑ₑ baseᵉ · digit c e = c`. This is the per-coefficient data behind the Hachi gadget inverse -`G⁻¹`. -/ -structure DigitDecomposition (base : R) (digits : Nat) where - /-- The `e`-th base-`base` digit of a coefficient. -/ - digit : R → Fin digits → R - /-- The digits reconstruct the coefficient: `∑ₑ baseᵉ · digit c e = c`. -/ - reconstruct : ∀ c : R, ∑ e : Fin digits, base ^ (e : ℕ) * digit c e = c - -end Digit - -/-! ## The concrete base-`b` digit decomposition over `ZMod q` -/ - -section ZModDigit - -variable {q : ℕ} [NeZero q] - -/-- The genuine base-`b` (binary, for `b = 2`) digit decomposition over `ZMod q`: digit `e` -of a coefficient `c` is the `e`-th base-`b` digit of its canonical representative `c.val`. -Reconstruction holds whenever `1 < b` and `q ≤ b ^ digits` (so every residue fits in -`digits` base-`b` digits). This is the coefficient-level Hachi `G⁻¹`. -/ -def zmodDigitDecomposition (b digits : ℕ) (hb : 1 < b) (hq : q ≤ b ^ digits) : - DigitDecomposition (R := ZMod q) (b : ZMod q) digits where - digit c e := ((Nat.digits b c.val).getD (e : ℕ) 0 : ZMod q) - reconstruct c := by - set L := Nat.digits b c.val with hL - have hlen : L.length ≤ digits := - (Nat.digits_length_le_iff hb c.val).mpr (lt_of_lt_of_le (ZMod.val_lt c) hq) - calc ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * ((L.getD (e : ℕ) 0 : ZMod q)) - = ∑ e : Fin digits, ((L.getD (e : ℕ) 0 : ZMod q)) * (b : ZMod q) ^ (e : ℕ) := by - apply Finset.sum_congr rfl; intro e _; ring - _ = ∑ i ∈ Finset.range digits, ((L.getD i 0 : ZMod q)) * (b : ZMod q) ^ i := - Fin.sum_univ_eq_sum_range (fun i => (L.getD i 0 : ZMod q) * (b : ZMod q) ^ i) digits - _ = Nat.ofDigits (b : ZMod q) L := (ofDigits_eq_sum_range_of_len_le (b : ZMod q) L hlen).symm - _ = ((Nat.ofDigits b L : ℕ) : ZMod q) := (Nat.coe_ofDigits (ZMod q) b L).symm - _ = ((c.val : ℕ) : ZMod q) := by rw [hL, Nat.ofDigits_digits] - _ = c := ZMod.natCast_zmod_val c - -end ZModDigit - -/-! ## The gadget matrix over `Rq Φ` -/ - -variable {R : Type} [Field R] [BEq R] [LawfulBEq R] [DecidableEq R] - (Φ : CyclotomicModulus R) [IsCyclotomic Φ] - -/-- Entry of the base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`: -column `j` of row `i` is `base^(j % digits)` when `j / digits = i`, else `0`. -/ -def gadgetEntry (base : R) {rows digits : Nat} (i : Fin rows) (j : Fin (rows * digits)) : Rq Φ := - if j.val / digits = i.val then Rq.constRq Φ (base ^ (j.val % digits)) else 0 - -/-- The base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`. -/ -def gadgetMatrix (base : R) (rows digits : Nat) : PolyMatrix (Rq Φ) rows (rows * digits) := - fun i j => gadgetEntry Φ base i j - -/-- Apply the gadget matrix to a decomposed vector. -/ -def gadgetMul (base : R) {rows digits : Nat} (v : PolyVec (Rq Φ) (rows * digits)) : - PolyVec (Rq Φ) rows := - gadgetMatrix Φ base rows digits *ᵥ v - -/-- A gadget decomposition is lawful when gadget multiplication reconstructs its input. -/ -def IsLawfulGadgetDecomposition (base : R) {rows digits : Nat} - (decompose : PolyVec (Rq Φ) rows → PolyVec (Rq Φ) (rows * digits)) : Prop := - ∀ x, gadgetMul Φ base (decompose x) = x - -/-! ## The gadget product as a block digit-sum -/ - -omit [DecidableEq R] in -/-- The gadget entry at the flattened index `finProdFinEquiv (i', e)` is `constRq (base^e)` -on the diagonal block and `0` elsewhere. -/ -theorem gadgetEntry_finProdFinEquiv (base : R) {rows digits : Nat} (hd : 0 < digits) - (i i' : Fin rows) (e : Fin digits) : - gadgetEntry Φ base i (finProdFinEquiv (i', e)) - = if i' = i then Rq.constRq Φ (base ^ (e : ℕ)) else 0 := by - unfold gadgetEntry - have hval : (finProdFinEquiv (i', e)).val = e.val + digits * i'.val := rfl - have hdiv : (finProdFinEquiv (i', e)).val / digits = i'.val := by - rw [hval, Nat.add_mul_div_left _ _ hd, Nat.div_eq_of_lt e.isLt, zero_add] - have hmod : (finProdFinEquiv (i', e)).val % digits = e.val := by - rw [hval, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt e.isLt] - rw [hdiv, hmod] - simp only [Fin.ext_iff] - -omit [DecidableEq R] in -/-- The gadget product, evaluated at row `i`, is the base-weighted sum of the `digits` -slots of block `i`. -/ -theorem gadgetMul_apply (base : R) {rows digits : Nat} (hd : 0 < digits) - (v : PolyVec (Rq Φ) (rows * digits)) (i : Fin rows) : - gadgetMul Φ base v i - = ∑ e : Fin digits, Rq.constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by - rw [gadgetMul, matVecMul_apply, dot_eq_sum] - simp only [gadgetMatrix] - rw [← Equiv.sum_comp finProdFinEquiv (fun j => gadgetEntry Φ base i j * v j), - Fintype.sum_prod_type] - rw [Finset.sum_eq_single i] - · apply Finset.sum_congr rfl - intro e _ - rw [gadgetEntry_finProdFinEquiv Φ base hd i i e, if_pos rfl] - · intro i' _ hne - apply Finset.sum_eq_zero - intro e _ - rw [gadgetEntry_finProdFinEquiv Φ base hd i i' e, if_neg hne, zero_mul] - · intro h - exact absurd (Finset.mem_univ i) h - -/-! ## The base-`b` gadget decomposition and its lawfulness - -`gadgetDecompose dd` is the Hachi gadget inverse `G⁻¹` built from a `DigitDecomposition dd`: -block `i`'s slot `e` is the ring element whose `k`-th coefficient is the `e`-th base-`b` -digit of the `k`-th coefficient of `x i`. By the reconstruction law of `dd`, gadget -multiplication recovers `x` (`gadgetDecompose_lawful`), so the inner-outer correctness -theorem instantiates with this genuine binary decomposition. -/ - -variable {base : R} - -/-- The base-`b` gadget decomposition (Hachi `G⁻¹`) induced by a `DigitDecomposition`. -/ -def gadgetDecompose {rows digits : Nat} (dd : DigitDecomposition base digits) - (x : PolyVec (Rq Φ) rows) : PolyVec (Rq Φ) (rows * digits) := - fun j => Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k => dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2) - -/-- Value of `gadgetDecompose` at the flattened index `finProdFinEquiv (i, e)`. -/ -theorem gadgetDecompose_apply {rows digits : Nat} (dd : DigitDecomposition base digits) - (x : PolyVec (Rq Φ) rows) (i : Fin rows) (e : Fin digits) : - gadgetDecompose Φ dd x (finProdFinEquiv (i, e)) - = Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k => dd.digit ((x i).1.coeff k) e) := by - unfold gadgetDecompose - simp only [Equiv.symm_apply_apply] - -/-- The base-`b` gadget decomposition is a lawful gadget decomposition. -/ -theorem gadgetDecompose_lawful {rows digits : Nat} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) - (dd : DigitDecomposition base digits) : - IsLawfulGadgetDecomposition Φ base (gadgetDecompose Φ dd (rows := rows)) := by - intro x - funext i - rw [gadgetMul_apply Φ base hd] - simp_rw [gadgetDecompose_apply Φ dd x i] - apply Subtype.ext - rw [CompPoly.CPolynomial.eq_iff_coeff] - intro k - have hsum : (∑ e : Fin digits, - Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k - = ∑ e : Fin digits, - (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k := by - rw [← Rq.coeffHom_apply Φ k, map_sum] - simp only [Rq.coeffHom_apply] - have hterm : ∀ e : Fin digits, - (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k - = base ^ (e : ℕ) * (if k < Φ.φ.natDegree then dd.digit ((x i).1.coeff k) e else 0) := by - intro e - rw [Rq.constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (Rq.phi_natDegree_le_degree Φ)] - rw [hsum] - simp_rw [hterm] - by_cases hk : k < Φ.φ.natDegree - · simp only [if_pos hk] - exact dd.reconstruct ((x i).1.coeff k) - · simp only [if_neg hk, mul_zero, Finset.sum_const_zero] - exact (Rq.coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm - -end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean new file mode 100644 index 0000000000..3e9cdbad9d --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean @@ -0,0 +1,253 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.Data.Lattices.ModuleSIS +import Mathlib.Data.Nat.Digits.Lemmas +import Mathlib.Data.ZMod.Basic +import Mathlib.Algebra.Field.ZMod + +/-! +# Ajtai Gadget Matrix Core + +The base-`b` gadget matrix `G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic +ring `Rq Φ`, mapping `rows * digits` ring elements to `rows` ring elements, used by the +inner-outer (Greyhound [NS24] / Hachi [NOZ26]) commitment. Gadget entries are *ring +constants* `C(bᵉ)` embedded into `Rq Φ`. `IsLawfulGadgetDecomposition` records when a +decomposition is inverted by gadget multiplication (`G · G⁻¹(x) = x`). + +The norm-reducing inverse `G⁻¹` is the genuine **base-`b` digit decomposition** of the Hachi +paper ([NOZ26], §2.1): each coefficient of a ring element is written in base `b`, and digit `e` +of each coefficient is placed in the `bᵉ`-slot of its block. Trading one ring element for +`digits` elements with small digit coefficients is what keeps honest Ajtai openings short +(the norm bounds live in `Gadget.Norms`). The decomposition is captured abstractly by +`DigitDecomposition` (a per-coefficient digit map satisfying the base-`b` reconstruction law) +and realized concretely over `ZMod q` by `zmodDigitDecomposition`. + +## Main definitions + +* `gadgetMatrix`, `gadgetMul`: the gadget matrix `G` and multiplication by it. +* `IsLawfulGadgetDecomposition`: a decomposition is lawful when `G · G⁻¹(x) = x` for all `x`. +* `DigitDecomposition`: abstract base-`b` digit map on the coefficient ring, with the + reconstruction law `∑ₑ bᵉ · digit c e = c`. +* `zmodDigitDecomposition`: the concrete digits of the canonical representative over `ZMod q`, + valid when `1 < b` and `q ≤ b ^ digits`. +* `gadgetDecompose`: the gadget inverse `G⁻¹` induced by a `DigitDecomposition`. + +## Main results + +* `gadgetMul_apply`: row `i` of the gadget product is the base-weighted sum of the `digits` + slots of block `i`. +* `gadgetDecompose_lawful`: `gadgetDecompose` is a lawful gadget decomposition, so the + inner-outer correctness theorem instantiates with this genuine base-`b` decomposition. + +## References + +* [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] +* [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +open CompPoly ArkLib.Lattices ArkLib.Lattices.CyclotomicModulus + +namespace ArkLib.Lattices.Ajtai + +/-! ## Base-`b` reconstruction of `Nat.ofDigits` as a finite sum -/ + +/-- `Nat.ofDigits` as the finite sum of digit-weighted powers over the length of the list. -/ +private theorem ofDigits_eq_sum_range {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) : + Nat.ofDigits β L = ∑ i ∈ Finset.range L.length, (L.getD i 0 : α) * β ^ i := by + induction L with + | nil => simp [Nat.ofDigits] + | cons h t ih => + rw [show Nat.ofDigits β (h :: t) = (h : α) + β * Nat.ofDigits β t from rfl, ih, + List.length_cons, Finset.sum_range_succ', Finset.mul_sum] + simp only [List.getD_cons_succ, List.getD_cons_zero, pow_zero, mul_one, pow_succ] + rw [add_comm] + congr 1 + apply Finset.sum_congr rfl + intro i _ + ring + +/-- `Nat.ofDigits` as a finite sum over any range `D` at least the list length (the extra +high-order digits are zero). -/ +private theorem ofDigits_eq_sum_range_of_len_le {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) + {D : ℕ} (hLD : L.length ≤ D) : + Nat.ofDigits β L = ∑ i ∈ Finset.range D, (L.getD i 0 : α) * β ^ i := by + rw [ofDigits_eq_sum_range β L] + apply Finset.sum_subset (fun x hx => + Finset.mem_range.mpr (lt_of_lt_of_le (Finset.mem_range.mp hx) hLD)) + intro i _ hi + rw [Finset.mem_range, not_lt] at hi + rw [List.getD_eq_default _ _ hi, Nat.cast_zero, zero_mul] + +/-! ## Abstract digit decompositions of the coefficient ring -/ + +section Digit + +variable {R : Type*} [CommSemiring R] + +/-- A base-`base` digit decomposition of the coefficient ring `R`: for each coefficient `c`, +`digit c e` is the `e`-th base-`base` digit, and the `digits` digits reconstruct `c` via +`∑ₑ baseᵉ · digit c e = c`. This is the per-coefficient data behind the Hachi gadget inverse +`G⁻¹`. -/ +structure DigitDecomposition (base : R) (digits : Nat) where + /-- The `e`-th base-`base` digit of a coefficient. -/ + digit : R → Fin digits → R + /-- The digits reconstruct the coefficient: `∑ₑ baseᵉ · digit c e = c`. -/ + reconstruct : ∀ c : R, ∑ e : Fin digits, base ^ (e : ℕ) * digit c e = c + +end Digit + +/-! ## The concrete base-`b` digit decomposition over `ZMod q` -/ + +section ZModDigit + +variable {q : ℕ} [NeZero q] + +/-- The genuine base-`b` (binary, for `b = 2`) digit decomposition over `ZMod q`: digit `e` +of a coefficient `c` is the `e`-th base-`b` digit of its canonical representative `c.val`. +Reconstruction holds whenever `1 < b` and `q ≤ b ^ digits` (so every residue fits in +`digits` base-`b` digits). This is the coefficient-level Hachi `G⁻¹`. -/ +def zmodDigitDecomposition (b digits : ℕ) (hb : 1 < b) (hq : q ≤ b ^ digits) : + DigitDecomposition (R := ZMod q) (b : ZMod q) digits where + digit c e := ((Nat.digits b c.val).getD (e : ℕ) 0 : ZMod q) + reconstruct c := by + set L := Nat.digits b c.val with hL + have hlen : L.length ≤ digits := + (Nat.digits_length_le_iff hb c.val).mpr (lt_of_lt_of_le (ZMod.val_lt c) hq) + calc ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * ((L.getD (e : ℕ) 0 : ZMod q)) + = ∑ e : Fin digits, ((L.getD (e : ℕ) 0 : ZMod q)) * (b : ZMod q) ^ (e : ℕ) := by + apply Finset.sum_congr rfl; intro e _; ring + _ = ∑ i ∈ Finset.range digits, ((L.getD i 0 : ZMod q)) * (b : ZMod q) ^ i := + Fin.sum_univ_eq_sum_range (fun i => (L.getD i 0 : ZMod q) * (b : ZMod q) ^ i) digits + _ = Nat.ofDigits (b : ZMod q) L := (ofDigits_eq_sum_range_of_len_le (b : ZMod q) L hlen).symm + _ = ((Nat.ofDigits b L : ℕ) : ZMod q) := (Nat.coe_ofDigits (ZMod q) b L).symm + _ = ((c.val : ℕ) : ZMod q) := by rw [hL, Nat.ofDigits_digits] + _ = c := ZMod.natCast_zmod_val c + +end ZModDigit + +/-! ## The gadget matrix over `Rq Φ` -/ + +variable {R : Type} [Field R] [BEq R] [LawfulBEq R] [DecidableEq R] + (Φ : CyclotomicModulus R) [IsCyclotomic Φ] + +/-- Entry of the base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`: +column `j` of row `i` is `base^(j % digits)` when `j / digits = i`, else `0`. -/ +def gadgetEntry (base : R) {rows digits : Nat} (i : Fin rows) (j : Fin (rows * digits)) : Rq Φ := + if j.val / digits = i.val then Rq.constRq Φ (base ^ (j.val % digits)) else 0 + +/-- The base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`. -/ +def gadgetMatrix (base : R) (rows digits : Nat) : PolyMatrix (Rq Φ) rows (rows * digits) := + fun i j => gadgetEntry Φ base i j + +/-- Apply the gadget matrix to a decomposed vector. -/ +def gadgetMul (base : R) {rows digits : Nat} (v : PolyVec (Rq Φ) (rows * digits)) : + PolyVec (Rq Φ) rows := + gadgetMatrix Φ base rows digits *ᵥ v + +/-- A gadget decomposition is lawful when gadget multiplication reconstructs its input. -/ +def IsLawfulGadgetDecomposition (base : R) {rows digits : Nat} + (decompose : PolyVec (Rq Φ) rows → PolyVec (Rq Φ) (rows * digits)) : Prop := + ∀ x, gadgetMul Φ base (decompose x) = x + +/-! ## The gadget product as a block digit-sum -/ + +omit [DecidableEq R] in +/-- The gadget entry at the flattened index `finProdFinEquiv (i', e)` is `constRq (base^e)` +on the diagonal block and `0` elsewhere. -/ +theorem gadgetEntry_finProdFinEquiv (base : R) {rows digits : Nat} (hd : 0 < digits) + (i i' : Fin rows) (e : Fin digits) : + gadgetEntry Φ base i (finProdFinEquiv (i', e)) + = if i' = i then Rq.constRq Φ (base ^ (e : ℕ)) else 0 := by + unfold gadgetEntry + have hval : (finProdFinEquiv (i', e)).val = e.val + digits * i'.val := rfl + have hdiv : (finProdFinEquiv (i', e)).val / digits = i'.val := by + rw [hval, Nat.add_mul_div_left _ _ hd, Nat.div_eq_of_lt e.isLt, zero_add] + have hmod : (finProdFinEquiv (i', e)).val % digits = e.val := by + rw [hval, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt e.isLt] + rw [hdiv, hmod] + simp only [Fin.ext_iff] + +omit [DecidableEq R] in +/-- The gadget product, evaluated at row `i`, is the base-weighted sum of the `digits` +slots of block `i`. -/ +theorem gadgetMul_apply (base : R) {rows digits : Nat} (hd : 0 < digits) + (v : PolyVec (Rq Φ) (rows * digits)) (i : Fin rows) : + gadgetMul Φ base v i + = ∑ e : Fin digits, Rq.constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by + rw [gadgetMul, matVecMul_apply, dot_eq_sum] + simp only [gadgetMatrix] + rw [← Equiv.sum_comp finProdFinEquiv (fun j => gadgetEntry Φ base i j * v j), + Fintype.sum_prod_type] + rw [Finset.sum_eq_single i] + · apply Finset.sum_congr rfl + intro e _ + rw [gadgetEntry_finProdFinEquiv Φ base hd i i e, if_pos rfl] + · intro i' _ hne + apply Finset.sum_eq_zero + intro e _ + rw [gadgetEntry_finProdFinEquiv Φ base hd i i' e, if_neg hne, zero_mul] + · intro h + exact absurd (Finset.mem_univ i) h + +/-! ## The base-`b` gadget decomposition and its lawfulness + +`gadgetDecompose dd` is the Hachi gadget inverse `G⁻¹` built from a `DigitDecomposition dd`: +block `i`'s slot `e` is the ring element whose `k`-th coefficient is the `e`-th base-`b` +digit of the `k`-th coefficient of `x i`. By the reconstruction law of `dd`, gadget +multiplication recovers `x` (`gadgetDecompose_lawful`), so the inner-outer correctness +theorem instantiates with this genuine binary decomposition. -/ + +variable {base : R} + +/-- The base-`b` gadget decomposition (Hachi `G⁻¹`) induced by a `DigitDecomposition`. -/ +def gadgetDecompose {rows digits : Nat} (dd : DigitDecomposition base digits) + (x : PolyVec (Rq Φ) rows) : PolyVec (Rq Φ) (rows * digits) := + fun j => Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k => dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2) + +/-- Value of `gadgetDecompose` at the flattened index `finProdFinEquiv (i, e)`. -/ +theorem gadgetDecompose_apply {rows digits : Nat} (dd : DigitDecomposition base digits) + (x : PolyVec (Rq Φ) rows) (i : Fin rows) (e : Fin digits) : + gadgetDecompose Φ dd x (finProdFinEquiv (i, e)) + = Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k => dd.digit ((x i).1.coeff k) e) := by + unfold gadgetDecompose + simp only [Equiv.symm_apply_apply] + +/-- The base-`b` gadget decomposition is a lawful gadget decomposition. -/ +theorem gadgetDecompose_lawful {rows digits : Nat} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) + (dd : DigitDecomposition base digits) : + IsLawfulGadgetDecomposition Φ base (gadgetDecompose Φ dd (rows := rows)) := by + intro x + funext i + rw [gadgetMul_apply Φ base hd] + simp_rw [gadgetDecompose_apply Φ dd x i] + apply Subtype.ext + rw [CompPoly.CPolynomial.eq_iff_coeff] + intro k + have hsum : (∑ e : Fin digits, + Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k + = ∑ e : Fin digits, + (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k := by + rw [← Rq.coeffHom_apply Φ k, map_sum] + simp only [Rq.coeffHom_apply] + have hterm : ∀ e : Fin digits, + (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k + = base ^ (e : ℕ) * (if k < Φ.φ.natDegree then dd.digit ((x i).1.coeff k) e else 0) := by + intro e + rw [Rq.constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (Rq.phi_natDegree_le_degree Φ)] + rw [hsum] + simp_rw [hterm] + by_cases hk : k < Φ.φ.natDegree + · simp only [if_pos hk] + exact dd.reconstruct ((x i).1.coeff k) + · simp only [if_neg hk, mul_zero, Finset.sum_const_zero] + exact (Rq.coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm + +end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean index 5910dea588..94a938d75d 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.Gadget.Basic +import ArkLib.Commitments.Functional.Hachi.Gadget.Core import ArkLib.Data.Lattices.CyclotomicRing.NormBounds /-! diff --git a/ArkLib/Commitments/Functional/Hachi/InnerOuter.lean b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Basic.lean similarity index 95% rename from ArkLib/Commitments/Functional/Hachi/InnerOuter.lean rename to ArkLib/Commitments/Functional/Hachi/InnerOuter/Basic.lean index eb74827bf8..2c7607bc4f 100644 --- a/ArkLib/Commitments/Functional/Hachi/InnerOuter.lean +++ b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Basic.lean @@ -9,7 +9,7 @@ import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security /-! # Inner-Outer Ajtai Commitment -Umbrella for `Hachi/InnerOuter/`: the Greyhound [NS24] / Hachi [NOZ26] two-layer Ajtai +Umbrella module for `Hachi/InnerOuter/`: the Greyhound [NS24] / Hachi [NOZ26] two-layer Ajtai commitment over the cyclotomic ring `Rq Φ` (Hachi [NOZ26, §4.1]). Each message block is gadget-decomposed and inner-committed under the matrix `A`; the inner commitments are gadget-decomposed again, flattened, and outer-committed under `B`. The scheme's opening notion diff --git a/ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean index 218344cfee..1ea8bb28d1 100644 --- a/ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean +++ b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ import ArkLib.Commitments.Ordinary.Ajtai.Simple.Scheme -import ArkLib.Commitments.Functional.Hachi.Gadget.Basic +import ArkLib.Commitments.Functional.Hachi.Gadget.Core import ArkLib.Data.Lattices.CyclotomicRing.NormBounds import VCVio diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Basic.lean similarity index 73% rename from ArkLib/Commitments/Functional/Hachi/QuadEval.lean rename to ArkLib/Commitments/Functional/Hachi/QuadEval/Basic.lean index 5b1ca8a082..8021f0e818 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Basic.lean @@ -9,7 +9,7 @@ import ArkLib.Commitments.Functional.Hachi.QuadEval.Bridge /-! # Hachi Polynomial-Evaluation Reduction `QuadEval` -Umbrella for `Hachi/QuadEval/`: Hachi's [NOZ26, §4.2] polynomial-evaluation reduction +Umbrella module for `Hachi/QuadEval/`: Hachi's [NOZ26, §4.2] polynomial-evaluation reduction (Figure 3, "Polynomial Evaluation as Quadratic Equation" — hence the name `QuadEval`). The reduction proves an evaluation claim `f(x) = y` on an inner-outer-committed multilinear polynomial by rewriting the evaluation as the quadratic form `bᵀ M a` (Eq. (12)) and folding the @@ -24,12 +24,16 @@ inner-outer lift of Greyhound's [NS24, §3.1] folding protocol. `z`, and the `tensorG` / `tensorG1` challenge combinations with the coordinate-isolation lemmas at the heart of the Lemma 8 extraction. * `QuadEval/Reduction.lean` — the two-round protocol data: the statement/response/witness types, - the challenge space `ShortChallenge`, the relations `relIn` (weak opening ∨ Module-SIS(B) ∨ - Module-SIS(D)) and `relOut` (Eq. (20) + the range checks), and the pure pass-through - `verifier` with the honest `prover` skeleton. -* `QuadEval/Soundness.lean` — **Hachi Lemma 8**: the subtract-and-divide extractor - (`buildWitness`) and the coordinate-wise special soundness - `quadEval_coordinateWiseSpecialSound`, bundled as the composable `quadEvalPackage`; also the + the challenge space `ShortChallenge`, the ordinary relations `relIn` (an eval-consistent weak + opening) and `relOut` (Eq. (20) + the range checks) over the fixed commitment key `pp`, the + `QuadEvalSISBreak`/`quadEvalSISSet` break vocabulary for the Module-SIS(B/D) extraction outcomes + (validated against the same fixed `pp` — the key is a parameter, never statement data), + and the pure pass-through `verifier` with the honest `prover` skeleton. +* `QuadEval/Soundness.lean` — **Hachi Lemma 8**: the subtract-and-divide extraction + (`buildWitness`, split into the plain assembler `quadEvalMkWitness` and the escape event + `quadEvalEscLocal`) and the escape-threaded coordinate-wise special soundness + `quadEval_coordinateWiseSpecialSoundWithEscape` at the **plain** relations, bundled as the + composable `quadEvalPackage`; also the reduction's derived norm constants `B_z` / `βSq`. The soundness is genuinely `sorry`-free — axiom-clean (`#print axioms` gives only `propext` / `Classical.choice` / `Quot.sound`), and its one deep input, Lyubashevsky–Seiler short-element invertibility `isUnit_of_l1Norm_le`, is itself diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Bridge.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Bridge.lean index 7891abf17b..7be9bc4338 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Bridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Bridge.lean @@ -6,8 +6,7 @@ Authors: Tobias Rothmann import ArkLib.Commitments.Functional.Hachi.QuadEval.Reduction import ArkLib.Commitments.Functional.Hachi.EvalSplit import ArkLib.ProofSystem.Component.ReduceClaim -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape /-! # Polynomial-level bridge into Hachi's `QuadEval` reduction @@ -24,32 +23,34 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChalleng Eq. (12) bases to be the monomial tensor bases `mb(xl)` / `mb(xh)` of the low/high halves of the evaluation point (`toQuadEvalStatement`), realized as the `ReduceClaim` reduction. Because `ReduceClaim`'s verifier is pure with no challenge rounds, its coordinate-wise special soundness - (CWSS; `ReduceClaim.verifier_coordinateWiseSpecialSound`) collapses to the transcript-level + (CWSS; `ReduceClaim.verifier_coordinateWiseSpecialSoundWith`) collapses to the transcript-level pull-back `mem_relPolyEval_of_relIn`, so the bridge is CWSS for **any** `D` - (`bridge_coordinateWiseSpecialSound`). + (`bridge_coordinateWiseSpecialSoundWith`). The result is a polynomial-level input relation `relPolyEval` (a weak `VerifiedOpening` whose - *extracted polynomial* evaluates to `y` at `xl ++ xh`, or a Module-SIS solution for `B`/`D`) that + *extracted polynomial* evaluates to `y` at `xl ++ xh`) that `QuadEval`'s two-round reduction refines to Hachi Eq. (20). `Composition.lean` chains the bridge before `QuadEval` (`evalChain = bridgePackage ▷ quadEvalPackage`), yielding the sorry-free - `eval_coordinateWiseSpecialSound`. + `eval_coordinateWiseSpecialSoundWith`. ## Main definitions - * `PolyEvalStatement`: the polynomial-level statement — public parameters `(A, B, D)`, outer - commitment `u`, split evaluation point `(xl, xh)`, and claimed value `y`. + * `PolyEvalStatement`: the polynomial-level statement — outer commitment `u`, split evaluation + point `(xl, xh)`, and claimed value `y` (the key `(A, B, D)` is the relations' `pp` + parameter, not statement data). * `toQuadEvalStatement`: the reinterpretation, with `bvec := mb(xl)` and `avec := mb(xh)`. * `bridgeVerifier`: the zero-round `ReduceClaim` verifier realizing it. * `extractedPoly`: the polynomial read back from a weak opening's Eq. (15) derived-message matrix via `Hachi.toPolynomial` (round-trip: `toMatrix_extractedPoly`). * `relPolyEval`: the polynomial-level input relation described above. - * `bridgePackage`: the bridge as a composable `CWSSPackage` (empty challenge structure). + * `bridgePackage`: the bridge as a composable, escape-free `CWSSPackage`. ## Main results * `mem_relPolyEval_of_relIn`: `QuadEval`'s `relIn` at `toQuadEvalStatement Φ s` pulls back to `relPolyEval` at `s`, via `splitForm_monomialBasis_eq_eval`. - * `bridge_coordinateWiseSpecialSound`: the bridge is CWSS for any `D`. All proofs in this file + * `bridge_coordinateWiseSpecialSoundWith`: the bridge is CWSS for any `D`, at the named + `ReduceClaim.treeExtractor`. All proofs in this file are sorry-free. ## Faithfulness note (Eq. (12) convention) @@ -92,14 +93,13 @@ variable {innerRows messageDigits outerRows innerDigits dRows m r : Nat} variable {ι : Type} {oSpec : OracleSpec ι} /-- Input statement of the composed Hachi evaluation protocol at the polynomial level (Hachi -§4.2/Figure 3, `Rq`-level): the public parameters `(A, B, D)`, the outer commitment `u`, the -evaluation point *split as a pair* `(xl, xh)` (low/first `r` variables and high/last `m` variables — -storing the split avoids `take`/`drop` casts; `xl ++ xh` recovers the paper's point), and the -claimed evaluation `y = f(xl ++ xh)`. -/ +§4.2/Figure 3, `Rq`-level): the outer commitment `u`, the evaluation point *split as a pair* +`(xl, xh)` (low/first `r` variables and high/last `m` variables — storing the split avoids +`take`/`drop` casts; `xl ++ xh` recovers the paper's point), and the claimed evaluation +`y = f(xl ++ xh)`. As at the `QuadEval` level, the public parameters `(A, B, D)` are the fixed +commitment key — a parameter of the relations, not statement data. -/ structure PolyEvalStatement (Φ : CyclotomicModulus R) (innerRows messageDigits outerRows innerDigits dRows m r : Nat) where - /-- Public matrices `(A, B, D)`. -/ - pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows /-- The outer commitment `u`. -/ u : Commitment Φ outerRows /-- The outer/low point half `x₁ … x_r` (the first `r` variables; the matrix-row / `b` split). -/ @@ -117,7 +117,6 @@ the last `m` variables, indexing columns). `.get : Fin (2^·) → Rq Φ` is defi def toQuadEvalStatement (s : PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows where - pp := s.pp u := s.u avec := (CMlPolynomial.monomialBasis s.xh).get bvec := (CMlPolynomial.monomialBasis s.xl).get @@ -125,7 +124,7 @@ def toQuadEvalStatement /-- The zero-round **bridge verifier**: a `ReduceClaim` head that reinterprets the polynomial-level statement as a `QuadEvalStatement` via `toQuadEvalStatement`. Pure with no challenge rounds, so its -CWSS holds for any `D` (`bridge_coordinateWiseSpecialSound`). -/ +CWSS holds for any `D` (`bridge_coordinateWiseSpecialSoundWith`). -/ def bridgeVerifier : Verifier oSpec (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) @@ -161,68 +160,76 @@ omit [NeZero q] in simp only [extractedPoly, Hachi.toMatrix_toPolynomial] /-- **`relPolyEval` — the polynomial-level input relation** of the composed Hachi evaluation -protocol: either a weak `VerifiedOpening` for `u` whose *extracted polynomial* evaluates to `y` at -`xl ++ xh`, or a Module-SIS solution for the outer matrix `B`, or one for the short-commitment -matrix `D`. It pulls back `QuadEval`'s `relIn` (whose opening disjunct is the matrix-level -`evalConsistency`) through `toQuadEvalStatement`; the opening disjunct is the interface into a -`CMlPolynomial`-level functional commitment. -/ -def relPolyEval (base : ZMod q) (βSq γ κ : ℕ) : +protocol: a weak `VerifiedOpening` for `u` under the fixed key `pp` whose *extracted polynomial* +evaluates to `y` at `xl ++ xh`. It pulls back `QuadEval`'s ordinary `relIn` (whose second +conjunct is the matrix-level `evalConsistency`) through `toQuadEvalStatement`; this is the +interface into a `CMlPolynomial`-level functional commitment. -/ +def relPolyEval + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (βSq γ κ : ℕ) : Set (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r × QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := - { p | match p with - | (s, .opening o) => - VerifiedOpening Φ base βSq γ κ s.pp.toPublicParams s.u o ∧ - CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y - | (s, .msisB z) => ModuleSIS.relation Φ (outerShort Φ γ) s.pp.outerMatrix z = true - | (s, .msisD z) => ModuleSIS.relation Φ (dShort Φ γ) s.pp.dMatrix z = true } + { p | + VerifiedOpening Φ base βSq γ κ pp.toPublicParams p.1.u p.2 ∧ + CMlPolynomial.eval (extractedPoly Φ base p.2) (p.1.xl ++ p.1.xh) = p.1.y } omit [NeZero q] in /-- **Pull-back lemma** (the `hRel` for the bridge's CWSS): a `QuadEvalWitness` accepted by `QuadEval`'s `relIn` at the reinterpreted statement `toQuadEvalStatement Φ s` is accepted by -`relPolyEval` at the polynomial-level statement `s`. The MSIS disjuncts are preserved verbatim -(`toQuadEvalStatement` keeps `pp`); the opening disjunct converts the matrix-level `evalConsistency` +`relPolyEval` at the polynomial-level statement `s`. The proof converts the matrix-level +`evalConsistency` (`splitForm (derivedMsgMatrix …) (mb xl) (mb xh) = y`) to the `CMlPolynomial.eval` claim via `Hachi.splitForm_monomialBasis_eq_eval`. -/ -theorem mem_relPolyEval_of_relIn (base : ZMod q) (βSq γ κ : ℕ) +theorem mem_relPolyEval_of_relIn + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (βSq γ κ : ℕ) (s : PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) (w : QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) - (h : (toQuadEvalStatement Φ s, w) ∈ relIn Φ base βSq γ κ) : - (s, w) ∈ relPolyEval Φ base βSq γ κ := by - cases w with - | opening o => - obtain ⟨hvo, hec⟩ := h - refine ⟨hvo, ?_⟩ - change CMlPolynomial.eval (Hachi.toPolynomial (derivedMsgMatrix Φ base o)) (s.xl ++ s.xh) - = s.y - rw [← Hachi.splitForm_monomialBasis_eq_eval (derivedMsgMatrix Φ base o) s.xl s.xh] - exact hec - | msisB z => exact h - | msisD z => exact h + (h : (toQuadEvalStatement Φ s, w) ∈ relIn Φ pp base βSq γ κ) : + (s, w) ∈ relPolyEval Φ pp base βSq γ κ := by + obtain ⟨hvo, hec⟩ := h + refine ⟨hvo, ?_⟩ + change CMlPolynomial.eval (Hachi.toPolynomial (derivedMsgMatrix Φ base w)) (s.xl ++ s.xh) + = s.y + rw [← Hachi.splitForm_monomialBasis_eq_eval (derivedMsgMatrix Φ base w) s.xl s.xh] + exact hec omit [NeZero q] in -/-- **CWSS of the bridge.** The zero-round `ReduceClaim` head is coordinate-wise special sound for -any `D`: with no challenge rounds, CWSS collapses (via the no-challenge bridge) to the -transcript-level pull-back `mem_relPolyEval_of_relIn`, reducing `QuadEval`'s `relIn` to the -polynomial-level `relPolyEval`. The witness type is unchanged (`QuadEvalWitness`), so the witness -pull-back is the identity. -/ -theorem bridge_coordinateWiseSpecialSound {σ : Type} +/-- **CWSS of the bridge, named form.** The zero-round `ReduceClaim` head is coordinate-wise +special sound for any `D`, at the named `ReduceClaim.treeExtractor`: with no challenge rounds, +CWSS collapses (via the no-challenge bridge) to the transcript-level pull-back +`mem_relPolyEval_of_relIn`, reducing `QuadEval`'s `relIn` to the polynomial-level `relPolyEval`. +The witness type is unchanged (`QuadEvalWitness`), so the witness pull-back is the identity. -/ +theorem bridge_coordinateWiseSpecialSoundWith {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (D : CWSSStructure (!p[] : ProtocolSpec 0)) (base : ZMod q) (βSq γ κ : ℕ) : - (bridgeVerifier (oSpec := oSpec) Φ (innerRows := innerRows) (messageDigits := messageDigits) - (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) - (r := r)).coordinateWiseSpecialSound init impl D - (relPolyEval Φ base βSq γ κ) (relIn Φ base βSq γ κ) := - ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relPolyEval Φ base βSq γ κ) (relOut := relIn Φ base βSq γ κ) + (D : CWSSStructure (!p[] : ProtocolSpec 0)) + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (βSq γ κ : ℕ) : + Verifier.coordinateWiseSpecialSoundWith init impl D + (relPolyEval Φ pp base βSq γ κ) (relIn Φ pp base βSq γ κ) + (bridgeVerifier (oSpec := oSpec) Φ (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)) + (ReduceClaim.treeExtractor (mapStmt := toQuadEvalStatement Φ) + (relIn Φ pp base βSq γ κ) (fun _ w => w) D) := + ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := relPolyEval Φ pp base βSq γ κ) (relOut := relIn Φ pp base βSq γ κ) (mapWitInv := fun _ w => w) (D := D) - (mem_relPolyEval_of_relIn Φ base βSq γ κ) - -/-- **The polynomial-level bridge as a `CWSSPackage`** (Hachi [NOZ26, §4.2]): the zero-round -`ReduceClaim` head `bridgeVerifier` bundled with the empty challenge structure (`ofIsEmpty`) and its -CWSS certificate `bridge_coordinateWiseSpecialSound`, ready to be `▷`-composed before `QuadEval`. -Its `relOut` is `QuadEval`'s input relation `relIn`, so the seam with `quadEvalPackage` is `rfl`. -/ -def bridgePackage {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (base : ZMod q) (βSq γ κ : ℕ) : + (mem_relPolyEval_of_relIn Φ pp base βSq γ κ) + +/-- **The polynomial-level bridge as a (plain) `CWSSPackage`** (Hachi [NOZ26, §4.2]): the +zero-round `ReduceClaim` head `bridgeVerifier` bundled with the empty challenge structure +(`ofIsEmpty`) and its named CWSS certificate `bridge_coordinateWiseSpecialSoundWith`, ready to be +`▷`-composed before `QuadEval`. Its public `relOut` is `QuadEval`'s ordinary input relation +`relIn`. + +The bridge is a statement *reinterpretation* with no cryptographic content, so it carries no escape +event; composing it before the escape-aware `quadEvalPackage` costs nothing, the universal `▷` +lifting it at the never-firing event. -/ +noncomputable def bridgePackage {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (βSq γ κ : ℕ) : CWSSPackage init impl (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) (QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) @@ -231,10 +238,13 @@ def bridgePackage {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (Stat (!p[] : ProtocolSpec 0) where verifier := bridgeVerifier (oSpec := oSpec) Φ struct := CWSSStructure.ofIsEmpty - relIn := relPolyEval Φ base βSq γ κ - relOut := relIn Φ base βSq γ κ + relIn := relPolyEval Φ pp base βSq γ κ + relOut := relIn Φ pp base βSq γ κ isPure := ⟨fun stmt _ => toQuadEvalStatement Φ stmt, fun _ _ => rfl⟩ - isCWSS := bridge_coordinateWiseSpecialSound Φ init impl CWSSStructure.ofIsEmpty base βSq γ κ + extractor := ReduceClaim.treeExtractor (mapStmt := toQuadEvalStatement Φ) + (relIn Φ pp base βSq γ κ) (fun _ w => w) CWSSStructure.ofIsEmpty + isCWSS := bridge_coordinateWiseSpecialSoundWith Φ init impl CWSSStructure.ofIsEmpty pp base + βSq γ κ end ZModDefs diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Gadgets.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Gadgets.lean index 72d2043d41..3d4a799fa2 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Gadgets.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Gadgets.lean @@ -18,7 +18,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic provides the definitions those equations are stated in and the subtraction/isolation identities the extraction step relies on; the protocol itself lives in `QuadEval/Reduction.lean`. - Throughout, `G` is the base-`b` gadget matrix `I ⊗ [1, b, …]` of `Gadget/Basic.lean` and `G⁻¹` + Throughout, `G` is the base-`b` gadget matrix `I ⊗ [1, b, …]` of `Gadget/Core.lean` and `G⁻¹` its digit decomposition, with `G *ᵥ G⁻¹(x) = x`. ## Main definitions diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean index 1699dfb5ef..b1b246496d 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean @@ -22,7 +22,12 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRoun (`QuadEvalStatement`, `QuadEvalResponse`, `QuadEvalWitness`), the short-challenge space (`ShortChallenge`, shortness carried by the subtype), the relations (`derivedMsgMatrix`, `evalConsistency` = Eq. (15), `relOut` = Eq. (20) + the `S_b` range checks, `relIn` = weak - opening ∨ Module-SIS(B) ∨ Module-SIS(D), `dShort`), and the protocol (the two-round + opening, `dShort`). The commitment key `(A, B, D)` is the *parameter* `pp` of the relations and + of the Module-SIS **break vocabulary** (`QuadEvalSISBreak` / `quadEvalSISSet`), never statement + data — breaks are checkable against the fixed key alone, which ties them to the actual key rather + than an adversary-chosen matrix. The reduction's escape *event* over that vocabulary, + `quadEvalEscLocal`, lives in `QuadEval/Soundness.lean`. The file closes with the protocol (the + two-round `pSpec ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin 2ʳ → C]⟩` of `CoordinateWise.SingleRound`, the pure pass-through `verifier`, and the honest `prover` skeleton). Round 0 (P→V) sends the short commitment `v = D ŵ`; round 1 (V→P) is the challenge vector; the triple `(ŵ, t̂, ẑ)` is @@ -62,18 +67,22 @@ variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows `v = D ŵ` lives in the `D`-row space. -/ abbrev CarrierCom (Φ : CyclotomicModulus R) (dRows : Nat) := Simple.Commitment Φ dRows -/-- Input statement of Hachi's polynomial-evaluation reduction (Hachi §4.2, Figure 3): the public -parameters `(A, B, D)`, the outer commitment `u`, the two evaluation basis vectors -`a ∈ Rq^{2^m}` (`avec`) and `b ∈ Rq^{2^r}` (`bvec`) of Eq. (12), and the claimed evaluation -`y = u_eval`. +/-- Input statement of Hachi's polynomial-evaluation reduction (Hachi §4.2, Figure 3): the outer +commitment `u`, the two evaluation basis vectors `a ∈ Rq^{2^m}` (`avec`) and `b ∈ Rq^{2^r}` +(`bvec`) of Eq. (12), and the claimed evaluation `y = u_eval`. -The dimension parameters (`messageRows`, `blocks`, …) are left generic on the structure; the +The public parameters `(A, B, D)` are **not** statement data: the commitment key is fixed once +for the whole reduction (honestly, sampled by `keygen`) and enters the relations as the +parameter `pp`. Keeping the key out of the adversary-chosen statement is what ties the parallel +Module-SIS break vocabulary (`quadEvalSISSet`) to the *actual* key — a statement-carried key would +let breaks be validated against an adversary-chosen matrix. + +The dimension parameters (`messageRows`, `blocks`, …) are left generic on the structure (the +full profile of the reduction instance, shared with the key's `PublicParamsD` type); the relations and protocol below specialize `messageRows := 2^m` and `blocks := 2^r` (the paper's Figure 3 shape), matching the genericity of the other reduction structures. -/ structure QuadEvalStatement (Φ : CyclotomicModulus R) (innerRows messageRows messageDigits outerRows blocks innerDigits dRows : Nat) where - /-- Public matrices `(A, B, D)`. -/ - pp : Hachi.PublicParamsD Φ innerRows messageRows messageDigits outerRows blocks innerDigits dRows /-- The outer commitment `u`. -/ u : Commitment Φ outerRows /-- The inner evaluation basis `aᵀ = (x_{r+1}^{j₁} ⋯ x_l^{j_m})_j ∈ Rq^{2^m}` (Eq. 12). -/ @@ -100,20 +109,32 @@ instance : Nonempty (QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits) := ⟨⟨fun _ => 0, fun _ _ => 0, fun _ => 0⟩⟩ -/-- The extracted (input-side) witness of Hachi Lemma 8: either a weak `Opening` for `u`, or a -Module-SIS solution for the outer matrix `B`, or one for the short-commitment matrix `D`. -/ -inductive QuadEvalWitness (Φ : CyclotomicModulus R) - (innerRows messageRows messageDigits blocks innerDigits : Nat) where - /-- A weak opening `(sᵢ, t̂ᵢ, c̄ᵢ)ᵢ` for the outer commitment `u`. -/ - | opening (o : Opening Φ innerRows messageRows messageDigits blocks innerDigits) - /-- A Module-SIS solution for the outer matrix `B`. -/ - | msisB (z : ModuleSIS.Solution Φ (blocks * (innerRows * innerDigits))) - /-- A Module-SIS solution for the short-commitment matrix `D`. -/ - | msisD (z : ModuleSIS.Solution Φ (blocks * messageDigits)) - -/-- `QuadEvalWitness` is inhabited (a trivial `msisB` witness). -/ +/-- The input-side witness of `QuadEval`: a genuine weak opening for `u`. + +Module-SIS breakages found by Hachi Lemma 8 are deliberately not constructors of this type: they are +reported by the escape event `quadEvalEscLocal` (`QuadEval/Soundness.lean`), a predicate on the +observable `(statement, transcript tree)`. -/ +abbrev QuadEvalWitness (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits blocks innerDigits : Nat) := + Opening Φ innerRows messageRows messageDigits blocks innerDigits + +/-- `QuadEvalWitness` is inhabited by the all-zero opening. The value need not satisfy `relIn`; +the instance is used only as the total fallback of generic extractors outside accepting trees. -/ instance : Nonempty (QuadEvalWitness Φ innerRows messageRows messageDigits blocks innerDigits) := - ⟨.msisB (fun _ => 0)⟩ + ⟨{ message := fun _ _ => 0, innerDecomp := fun _ _ => 0, challenge := fun _ => 0 }⟩ + +/-- A concrete Module-SIS break produced by the three-case extractor of Hachi Lemma 8: a candidate +kernel vector for the outer commitment matrix `B` or the carrier commitment matrix `D`. The break +carries only the solution; validity (`quadEvalSISSet`) is checked against the *fixed* commitment key +`pp` — a parameter of the reduction, not statement data — so it is a break of the actual key. +(Carrying the matrix inside the break instead would let it be validated against an arbitrary — e.g. +zero — matrix, making the set free.) -/ +inductive QuadEvalSISBreak (Φ : CyclotomicModulus R) + (innerRows messageDigits outerRows blocks innerDigits dRows : Nat) where + /-- A short nonzero kernel vector for the outer commitment matrix `B`. -/ + | msisB (solution : ModuleSIS.Solution Φ (blocks * (innerRows * innerDigits))) + /-- A short nonzero kernel vector for the carrier commitment matrix `D`. -/ + | msisD (solution : ModuleSIS.Solution Φ (blocks * messageDigits)) end Defs @@ -184,6 +205,25 @@ def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec ( def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool := fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ) +/-- The set of **valid** Module-SIS breaks that `QuadEval`'s extraction may exhibit, **relative to +the fixed commitment key `pp`**: solutions are checked against `pp`'s `B` matrix (a divergent inner +decomposition) or its `D` matrix (a divergent carrier decomposition). Checkability against the key +alone — never against statement data — is what makes this a genuine hardness target: an element is a +Module-SIS break of the *actual* key ([NOZ26] Remark 2 / Lemma 7). + +This set is the target of the reduction's escape event `quadEvalEscLocal` +(`QuadEval/Soundness.lean`), which fires on `(stmt, tree)` exactly when the tree's own +`relOut`-responses make `buildWitness` return one of its elements. -/ +def quadEvalSISSet + (pp : Hachi.PublicParamsD Φ innerRows messageRows messageDigits outerRows blocks innerDigits + dRows) (γ : ℕ) : + Set (QuadEvalSISBreak Φ innerRows messageDigits outerRows blocks innerDigits dRows) := + { e | match e with + | .msisB solution => + ModuleSIS.relation Φ (outerShort Φ γ) pp.outerMatrix solution = true + | .msisD solution => + ModuleSIS.relation Φ (dShort Φ γ) pp.dMatrix solution = true } + /-- **`relOut` — Hachi Eq. (20) (rows c1–c5 verbatim) plus a symmetric-`ℓ∞`-ball model of the `S_b` range checks (c6)** on `((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: @@ -207,8 +247,10 @@ reduction's intended output relation and the one downstream Hachi code should ci challenge-norm checks appear (the challenge TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `Gadget/Norms.lean`) — both exactly as in the -paper. -/ -def relOut (base : ZMod q) (ω γ : ℕ) : +paper. `pp` is the fixed commitment key `(A, B, D)` the c1/c2/c5 rows check against. -/ +def relOut + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω γ : ℕ) : Set ((QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) × QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) := @@ -218,9 +260,9 @@ def relOut (base : ZMod q) (ω γ : ℕ) : let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec -- c1: `D ŵ = v` - Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ + Simple.commit Φ pp.dMatrix resp.carrierDec = v ∧ -- c2: `B (flatten t̂) = u` - Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ + Simple.commit Φ pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ -- c3: `bᵀ (G_{2^r} ŵ) = y` dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ -- c4: `(cᵀ ⊗ G₁) ŵ = aᵀ (G_{2^m} z)` @@ -228,7 +270,7 @@ def relOut (base : ZMod q) (ω γ : ℕ) : dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ -- c5: `(cᵀ ⊗ G_{n_A}) t̂ = A z` Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = - stmt.pp.innerMatrix *ᵥ z ∧ + pp.innerMatrix *ᵥ z ∧ -- c6: the `S_b` range checks (as `ℓ∞` balls) vecLInftyNorm Φ resp.carrierDec ≤ γ ∧ vecLInftyNorm Φ (PolyVec.flattenBlocks resp.innerDec) ≤ γ ∧ @@ -274,7 +316,9 @@ theorem vecLInftyNorm_le_of_vecInSb {β γ cols : ℕ} (hγ : β / 2 ≤ γ) c6 range checks are the paper's exact `S_b` box membership (`vecInSb`, Hachi [NOZ26] §2.1) instead of the symmetric `ℓ∞` ball. This is the relation the Hachi verifier actually checks; rows c1–c5 mirror `relOut` verbatim (only c6 differs). -/ -def paperRelOut (base : ZMod q) (ω b : ℕ) : +def paperRelOut + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω b : ℕ) : Set ((QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) × QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) := @@ -284,13 +328,13 @@ def paperRelOut (base : ZMod q) (ω b : ℕ) : let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec -- c1–c5: the linear system, identical to `relOut` - Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ - Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ + Simple.commit Φ pp.dMatrix resp.carrierDec = v ∧ + Simple.commit Φ pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ Hachi.tensorG1 Φ base messageDigits c resp.carrierDec = dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = - stmt.pp.innerMatrix *ᵥ z ∧ + pp.innerMatrix *ᵥ z ∧ -- c6: the paper's exact `S_b` box (Eq. (20)'s `(ŵ, t̂, ẑ) ∈ S_b`) vecInSb Φ b resp.carrierDec ∧ vecInSb Φ b (PolyVec.flattenBlocks resp.innerDec) ∧ @@ -302,35 +346,30 @@ relOut`). Every transcript the Figure 3 verifier accepts (Eq. (20), `(ŵ, t̂, by ArkLib's generalized `relOut` at any range `γ ≥ ⌊b/2⌋`: rows c1–c5 pass through verbatim, and each `S_b` box check (`vecInSb b`) implies the ball check `vecLInftyNorm ≤ γ` via `vecLInftyNorm_le_of_vecInSb`. In particular at the paper's own `γ := b` (`b ≥ ⌊b/2⌋`) this shows -the Lemma 8 CWSS theorem (`quadEval_coordinateWiseSpecialSound`) covers the paper's verifier. -/ -theorem paperRelOut_subset_relOut (base : ZMod q) (ω : ℕ) {b γ : ℕ} (hγ : b / 2 ≤ γ) : - paperRelOut Φ (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (zDigits := zDigits) (m := m) (r := r) - base ω b - ⊆ relOut Φ (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (zDigits := zDigits) (m := m) (r := r) - base ω γ := by +the Lemma 8 CWSS theorem (`quadEval_coordinateWiseSpecialSoundWithEscape`) covers the paper's +verifier. -/ +theorem paperRelOut_subset_relOut + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω : ℕ) {b γ : ℕ} (hγ : b / 2 ≤ γ) : + paperRelOut (zDigits := zDigits) Φ pp base ω b + ⊆ relOut (zDigits := zDigits) Φ pp base ω γ := by rintro ⟨⟨stmt, v, chals⟩, resp⟩ ⟨h1, h2, h3, h4, h5, hb1, hb2, hb3⟩ exact ⟨h1, h2, h3, h4, h5, vecLInftyNorm_le_of_vecInSb Φ hγ hb1, vecLInftyNorm_le_of_vecInSb Φ hγ hb2, vecLInftyNorm_le_of_vecInSb Φ hγ hb3⟩ -/-- **`relIn` — Hachi Lemma 8's extraction disjunction**: a weak `VerifiedOpening` for `u` that is -also eval-consistent (Eq. 15), or a Module-SIS solution for `B`, or one for `D`. The `.opening` -disjunct is the interface into `outputToModuleSIS_valid_of_verified` for the downstream -cross-run knowledge-soundness step. -/ -def relIn (base : ZMod q) (βSq γ κ : ℕ) : +/-- **`relIn` — the ordinary input relation of `QuadEval`**: a weak `VerifiedOpening` for `u` under +the fixed key `pp` that is also eval-consistent (Eq. 15). Module-SIS outcomes are not witnesses of +this relation; they are reported by the escape event `quadEvalEscLocal` +(`QuadEval/Soundness.lean`). -/ +def relIn + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (βSq γ κ : ℕ) : Set (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := - { p | match p with - | (stmt, .opening o) => - VerifiedOpening Φ base βSq γ κ stmt.pp.toPublicParams stmt.u o ∧ - evalConsistency Φ base stmt.avec stmt.bvec stmt.y o - | (stmt, .msisB z) => - ModuleSIS.relation Φ (outerShort Φ γ) stmt.pp.outerMatrix z = true - | (stmt, .msisD z) => - ModuleSIS.relation Φ (dShort Φ γ) stmt.pp.dMatrix z = true } + { p | VerifiedOpening Φ base βSq γ κ pp.toPublicParams p.1.u p.2 ∧ + evalConsistency Φ base p.1.avec p.1.bvec p.1.y p.2 } /-! ## The protocol: pure pass-through verifier and honest prover -/ diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean index 741ff4f34b..4c558c33c3 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean @@ -5,8 +5,8 @@ Authors: Tobias Rothmann -/ import ArkLib.Commitments.Functional.Hachi.QuadEval.Reduction import ArkLib.Commitments.Functional.Hachi.Gadget.Norms +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! # Hachi polynomial-evaluation reduction (`QuadEval`) — coordinate-wise special soundness @@ -17,8 +17,8 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package transcripts whose challenge vectors form a star in `SS(C, 2ʳ, 2)` — a central branch plus, for each coordinate `j`, a sibling branch differing from it exactly at `j` — the tree extractor either reconstructs a valid weak `InnerOuter.Opening` by subtract-and-divide (subtract the - sibling's response from the central one, then divide by the invertible challenge difference), - or outputs a Module-SIS solution for `B` or `D`. The file is `sorry`-free. + central branch's response from the sibling's, then divide by the invertible challenge + difference), or outputs a Module-SIS solution for `B` or `D`. The file is `sorry`-free. ## Main definitions @@ -28,11 +28,14 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package `VerifiedBlock.scaled_short`. * `extractedOpening` — the subtract-and-divide weak opening assembled from a star of accepting branches (total, no `IsUnit`/star hypotheses; correctness lives in the lemmas below). - * `buildWitness` — Lemma 8's three-case witness assembler: a divergent inner decomposition `t̂` - gives a `B`-kernel MSIS solution, a divergent carrier decomposition `ŵ` a `D`-kernel one, - and otherwise the star yields `extractedOpening`. - * `quadEvalPackage` — the reduction's `verifier` bundled with its CWSS certificate as a - composable `CWSSPackage`, ready to be `▷`-composed after the polynomial-level bridge. + * `buildWitness` — Lemma 8's three-case extraction result: divergent inner/carrier + decompositions give `B`/`D`-kernel Module-SIS breaks; otherwise the star yields + `extractedOpening`. Its sum is protocol-local data, split into: + * `quadEvalMkWitness` — the plain (`WitIn`-valued) witness assembler fed to the generic + single-round assembly, and + * `quadEvalEscLocal` — the reduction's **escape event** in local per-star form: "the tree's own + responses make extraction land in a Module-SIS break valid for the fixed key `pp`". + * `quadEvalPackage` — the reduction as an `EscapeCWSSPackage`. ## Main results @@ -43,11 +46,12 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package invertibility of short elements. * `verifiedOpening_of_star`, `evalConsistency_of_relOut_star` — case (C): the extracted opening is a `VerifiedOpening` at `βSq`/`γ`/`2ω` and satisfies Eq. (15) eval-consistency. - * `buildWitness_mem_relIn` — every case of `buildWitness` lands in `relIn`; the single math - lemma to which the whole of Lemma 8 reduces. - * `quadEval_coordinateWiseSpecialSound` — **Hachi Lemma 8**: assembled from - `buildWitness_mem_relIn` by the generic `coordinateWiseSpecialSound_of_mkWitness` - (`SingleRound.lean`), which discharges every tree/extractor/guard obligation. + * `buildWitness_break_or_mem_relIn` — every local result is either a concrete break in the + key-tied `quadEvalSISSet` of the fixed `pp`, or an opening in the plain `relIn`. + * `quadEval_coordinateWiseSpecialSoundWithEscape` — **the advertised Lemma 8 statement**, and the + one the composed chain consumes: a *named-extractor*, escape-threaded CWSS certificate at + `relIn`/`relOut`, whose escape disjunct is the tight, key-tied event + `SingleRound.escEvent relOut quadEvalEscLocal`. Feeds `quadEvalPackage`'s certificate. Mirroring `InnerOuter/Security.lean`, the extraction lemmas carry the Lyubashevsky–Seiler [LS18] hypotheses `q ≡ 5 (mod 8)`, `(2ω)² < q` (only there does challenge invertibility @@ -121,7 +125,7 @@ theorem evalConsistency_of_star (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) /-- **Hachi Lemma 8, cases (A)/(B), two-transcript step**: two `γ`-short openings of the same commitment under `M` differ by an `ℓ∞`-short kernel vector — a Module-SIS solution for `M` at the bound `subLInftyNormBound γ = 2·γ`. Instantiated at `M = B` with `outerShort` (case (A)) -and at `M = D` with `dShort` (case (B)) in `buildWitness_mem_relIn`. -/ +and at `M = D` with `dShort` (case (B)) in `buildWitness_break_or_mem_relIn`. -/ theorem msis_of_commit_eq {rows cols γ : ℕ} (M : Simple.PublicParams Φ rows cols) {u : Simple.Commitment Φ rows} {x₁ x₂ : PolyVec (Rq Φ) cols} @@ -195,24 +199,82 @@ argument of the generic extractor `E`: opening `extractedOpening`. ("Two branches differ" is equivalent to "some branch differs from the central one": if two -branches disagree, at least one of them disagrees with the central branch.) Fully defined; -that each case lands in `relIn` is `buildWitness_mem_relIn`. -/ +branches disagree, at least one of them disagrees with the central branch.) The sum is +**protocol-local data**: the left summand is the ordinary opening witness and the right summand a +concrete Module-SIS break of the fixed key `pp` (`quadEvalSISSet Φ pp γ`). The two summands are +separated below into the plain extractor `quadEvalMkWitness` and the escape event +`quadEvalEscLocal`; correctness is `buildWitness_break_or_mem_relIn`. -/ noncomputable def buildWitness (base : ZMod q) - (_stmt : - QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) - (_v : CarrierCom Φ dRows) (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : - QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ + QuadEvalSISBreak Φ innerRows messageDigits outerRows (2 ^ r) innerDigits dRows := if hB : ∃ j, PolyVec.flattenBlocks (resp j).innerDec ≠ PolyVec.flattenBlocks (resp (central fam)).innerDec then - .msisB (PolyVec.flattenBlocks (resp hB.choose).innerDec - - PolyVec.flattenBlocks (resp (central fam)).innerDec) + .inr (.msisB + (PolyVec.flattenBlocks (resp hB.choose).innerDec - + PolyVec.flattenBlocks (resp (central fam)).innerDec)) else if hD : ∃ j, (resp j).carrierDec ≠ (resp (central fam)).carrierDec then - .msisD ((resp hD.choose).carrierDec - (resp (central fam)).carrierDec) + .inr (.msisD + ((resp hD.choose).carrierDec - (resp (central fam)).carrierDec)) else - .opening (extractedOpening Φ base fam resp) + .inl (extractedOpening Φ base fam resp) + +/-- **The reduction's escape event, in local (per-star) form** — the `escLocal` argument of +`SingleRound.escEvent`: at a shared message `v` and a star-shaped challenge family with per-branch +responses, the three-case extraction lands in its Module-SIS branch with a break that is **valid for +the fixed key `pp`**. + +Against the escape-event contract (`ChallengeTree.EscapeEvent`): the conjunct +`br ∈ quadEvalSISSet Φ pp γ` alone says `br` is a short nonzero kernel vector of `pp`'s `B` or `D` +matrix — a Module-SIS solution for the *actual, statement-independent* key ([NOZ26] Remark 2 / +Lemma 7) — and it says so at every `(stmt, v, fam, resp)`, including ones no honest execution +produces. The event reads only the message, the challenge family and the responses, which the +ambient `SingleRound.escEvent` pins to the tree's own data. The `buildWitness … = Sum.inr br` is +what makes it *tight*: it fires only where extraction genuinely cannot return an opening. -/ +def quadEvalEscLocal (base : ZMod q) + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (γ : ℕ) : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → + CarrierCom Φ dRows → + (Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) → + (Fin (2 ^ r + 1) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) → Prop := + fun _ _ fam resp => + ∃ br ∈ quadEvalSISSet Φ pp γ, buildWitness Φ base fam resp = Sum.inr br + +/-- **The reduction's plain witness assembler** — the `mkWitness` argument of +`SingleRound.coordinateWiseSpecialSoundWithEscape_of_mkWitness`: `buildWitness`'s opening branch, +with the (total) `extractedOpening` as the fallback on its Module-SIS branch. The fallback is +irrelevant to soundness: on exactly those inputs `quadEvalEscLocal` fires, so the certificate's left +disjunct carries the conclusion. -/ +noncomputable def quadEvalMkWitness (base : ZMod q) + (_stmt : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (_v : CarrierCom Φ dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := + (buildWitness (outerRows := outerRows) (dRows := dRows) Φ base fam resp).elim id + (fun _ => extractedOpening Φ base fam resp) + +omit [NeZero q] in +/-- On `buildWitness`'s opening branch, `quadEvalMkWitness` *is* that opening. -/ +theorem quadEvalMkWitness_of_inl (base : ZMod q) + (stmt : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (v : CarrierCom Φ dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + {w : QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits} + (hw : buildWitness (outerRows := outerRows) (dRows := dRows) Φ base fam resp = Sum.inl w) : + quadEvalMkWitness Φ base stmt v fam resp = w := by + unfold quadEvalMkWitness + rw [hw] + rfl omit [NeZero q] [IsCyclotomic Φ] in /-- Coordinate difference transfers from the `ShortChallenge` subtype to the underlying ring @@ -262,19 +324,21 @@ At a star-shaped family of `2^r + 1` `relOut`-accepting branches sharing the car subtype challenges (`ShortChallenge.l1Norm_val_sub_le`). -/ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) (v : CarrierCom 𝓜(q, α) dRows) (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) pp (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) (ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec) (_hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : VerifiedOpening 𝓜(q, α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) - stmt.pp.toPublicParams stmt.u + pp.toPublicParams stmt.u (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp) := by -- `outer_eq` / `outer_short` are c2 / c6t of the central branch (the extracted `innerDecomp` -- IS the central `t̂`, so the `γ` bound applies verbatim — no `2γ` slack). @@ -312,7 +376,7 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω have hchain : ((fam (sib fam i) i).val - (fam (central fam) i).val) •ᵥ (gadgetMatrix 𝓜(q, α) (b : ZMod q) innerRows innerDigits *ᵥ (resp (central fam)).innerDec i) - = stmt.pp.innerMatrix *ᵥ + = pp.innerMatrix *ᵥ (Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (sib fam i)).zDec - Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ @@ -320,7 +384,7 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω rw [matVecMul_sub, ← hc5s, ← hc5e, ← Hachi.tensorG_sub_challenge, Hachi.tensorG_coord_diff 𝓜(q, α) (b : ZMod q) innerRows innerDigits hcoord] simp only [extractedOpening] - exact inner_eq_of_chain 𝓜(q, α) stmt.pp.innerMatrix + exact inner_eq_of_chain 𝓜(q, α) pp.innerMatrix ((resp (central fam)).innerDec i) _ ((fam (sib fam i) i).val - (fam (central fam) i).val) hunit hchain @@ -329,13 +393,15 @@ the shared-`ŵ` c3 row plus the coordinate-isolated, unit-divided c4 rows discha `w`/`c3`/`c4` hypotheses of `evalConsistency_of_star` at the shared recomposed carrier `w := G_{2^r} *ᵥ ŵ`. -/ theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) (v : CarrierCom 𝓜(q, α) dRows) (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) pp (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) (hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : evalConsistency 𝓜(q, α) (b : ZMod q) stmt.avec stmt.bvec stmt.y @@ -379,53 +445,73 @@ theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : rw [matVecMul_scalarVecMul, dot_scalarVecMul, hchain, ← mul_assoc, Ring.inverse_mul_cancel _ hunit, one_mul] -/-- **The witness assembler is correct** — the `hmk` input to the generic assembly -`coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), and the mathematical content of -Hachi Lemma 8's three-case split: at every star-shaped family of `relOut`-accepting branches, -`buildWitness` lands in `relIn`. -/ -theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) +/-- **The local extractor is correct** — the mathematical content of Hachi Lemma 8's three-case +split. At every star-shaped family of `relOut`-accepting branches, `buildWitness` either returns a +concrete break of the fixed key `pp` (an element of `quadEvalSISSet`) or an opening lying in +`relIn`. + +The first disjunct is exactly the escape event `quadEvalEscLocal`; the second gives the plain +assembler `quadEvalMkWitness` its `relIn`-membership (via `quadEvalMkWitness_of_inl`). Together they +feed `SingleRound.coordinateWiseSpecialSoundWithEscape_of_mkWitness`. -/ +theorem buildWitness_break_or_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} + (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) (v : CarrierCom 𝓜(q, α) dRows) (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) pp (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) : - (stmt, buildWitness 𝓜(q, α) (b : ZMod q) stmt v fam resp) ∈ - relIn 𝓜(q, α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) := by - unfold buildWitness + (∃ br ∈ quadEvalSISSet 𝓜(q, α) pp γ, + buildWitness (outerRows := outerRows) (dRows := dRows) 𝓜(q, α) (b : ZMod q) fam resp + = Sum.inr br) ∨ + ∃ w, buildWitness (outerRows := outerRows) (dRows := dRows) 𝓜(q, α) (b : ZMod q) fam resp + = Sum.inl w ∧ + (stmt, w) ∈ relIn 𝓜(q, α) pp (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) := by by_cases hB : ∃ j, PolyVec.flattenBlocks (resp j).innerDec ≠ PolyVec.flattenBlocks (resp (central fam)).innerDec · -- Case (A): some branch's inner decomposition differs → `B`-kernel MSIS solution -- (both branches' c2 commit to the shared `stmt.u`). - rw [dif_pos hB] - obtain ⟨-, hu₁, -, -, -, -, hγ₁, -⟩ := hrel hB.choose - obtain ⟨-, hu₂, -, -, -, -, hγ₂, -⟩ := hrel (central fam) - exact msis_of_commit_eq 𝓜(q, α) stmt.pp.outerMatrix hu₁ hu₂ hγ₁ hγ₂ hB.choose_spec + refine Or.inl ⟨.msisB (PolyVec.flattenBlocks (resp hB.choose).innerDec - + PolyVec.flattenBlocks (resp (central fam)).innerDec), ?_, ?_⟩ + · obtain ⟨-, hu₁, -, -, -, -, hγ₁, -⟩ := hrel hB.choose + obtain ⟨-, hu₂, -, -, -, -, hγ₂, -⟩ := hrel (central fam) + exact msis_of_commit_eq 𝓜(q, α) pp.outerMatrix hu₁ hu₂ hγ₁ hγ₂ hB.choose_spec + · unfold buildWitness + rw [dif_pos hB] · by_cases hD : ∃ j, (resp j).carrierDec ≠ (resp (central fam)).carrierDec · -- Case (B): shared `t̂` but some carrier decomposition differs → `D`-kernel MSIS solution -- (the shared round-0 message `v` is what makes both branches commit to the same `v`). - rw [dif_neg hB, dif_pos hD] - obtain ⟨hv₁, -, -, -, -, hγ₁, -, -⟩ := hrel hD.choose - obtain ⟨hv₂, -, -, -, -, hγ₂, -, -⟩ := hrel (central fam) - exact msis_of_commit_eq 𝓜(q, α) stmt.pp.dMatrix hv₁ hv₂ hγ₁ hγ₂ hD.choose_spec + refine Or.inl ⟨.msisD ((resp hD.choose).carrierDec - + (resp (central fam)).carrierDec), ?_, ?_⟩ + · obtain ⟨hv₁, -, -, -, -, hγ₁, -, -⟩ := hrel hD.choose + obtain ⟨hv₂, -, -, -, -, hγ₂, -, -⟩ := hrel (central fam) + exact msis_of_commit_eq 𝓜(q, α) pp.dMatrix hv₁ hv₂ hγ₁ hγ₂ hD.choose_spec + · unfold buildWitness + rw [dif_neg hB, dif_pos hD] · -- Case (C): shared `t̂` and `ŵ` → the subtract-and-divide weak opening. - rw [dif_neg hB, dif_neg hD] - push Not at hB hD - have ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec := - fun j => funext fun i => PolyVec.block_eq_of_flattenBlocks_eq (hB j) i - exact ⟨verifiedOpening_of_star hq5 hκ hτ stmt v fam resp hrel hstar ht hD, - evalConsistency_of_relOut_star hq5 hκ stmt v fam resp hrel hstar hD⟩ + refine Or.inr ⟨extractedOpening 𝓜(q, α) (b : ZMod q) fam resp, ?_, ?_⟩ + · unfold buildWitness + rw [dif_neg hB, dif_neg hD] + · push Not at hB hD + have ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec := + fun j => funext fun i => PolyVec.block_eq_of_flattenBlocks_eq (hB j) i + exact ⟨verifiedOpening_of_star hq5 hκ hτ pp stmt v fam resp hrel hstar ht hD, + evalConsistency_of_relOut_star hq5 hκ pp stmt v fam resp hrel hstar hD⟩ /-- **Hachi Lemma 8 (CWSS of Hachi's polynomial-evaluation reduction, Figure 3; originally -Greyhound's [NS24, §3.1] folding protocol).** The reduction's verifier is coordinate-wise -special sound for the `(ℓ, k) = (2^r, 2)` structure, with `relOut` = Eq. (20) rows + the -symmetric-ball `S_b` model (`QuadEval/Reduction.lean`) and `relIn` = weak opening (eval-consistent) -∨ MSIS(B) ∨ MSIS(D), at the derived constants `βSq = quadEvalBetaSq γ b zDigits (deg φ) m -messageDigits` and `κ = 2ω`. +Greyhound's [NS24, §3.1] folding protocol), escape-threaded.** `relIn` contains only an +eval-consistent weak opening, `relOut` Eq. (20) plus its range checks, and the extractor is a plain +tree-based extractor. The Module-SIS(B/D) outcomes of the three-case extraction appear as the +certificate's *escape disjunct*: the event `SingleRound.escEvent relOut quadEvalEscLocal`, which +fires on a `(statement, tree)` pair exactly when that tree's own `relOut`-responses make the +extraction land in a genuine Module-SIS break of the fixed key `pp` (honesty argument: +`quadEvalEscLocal`). **Paper parameter mapping (an intentional generalization).** The theorem is stated over ArkLib's generalized relation and exposes `(βSq, γ, κ)` as free parameters: `βSq = quadEvalBetaSq γ b …` is @@ -434,66 +520,85 @@ symmetric-ball range bound of c6, and `κ = 2ω`. Hachi Lemma 8 fixes the specif `(β̄, ω̄, γ̄) = (2·bᵗ, 2ω, b)`. Instantiating `γ := b` matches `γ̄ = b` and `ω̄ = 2ω` exactly, but **not** `β̄`: ArkLib's `VerifiedOpening` records a squared-`ℓ₂` bound `βSq` on the scaled blocks (not the paper's `ℓ₂`/`ℓ∞` value `2·bᵗ`), a deliberate modeling choice (see `quadEvalZL2SqBound`). -That `γ := b` instantiation is the named corollary `quadEval_coordinateWiseSpecialSound_paperParams` -below. The paper's exact `S_b`-box output relation is `QuadEval/Reduction.paperRelOut`, with the -`paperRelOut ⊆ relOut` containment proved as `QuadEval/Reduction.paperRelOut_subset_relOut`. - -Assembled by `coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), which discharges -every tree/extractor/guard obligation generically; the whole of Hachi Lemma 8 thereby reduces -to the single math lemma `buildWitness_mem_relIn`. -/ -theorem quadEval_coordinateWiseSpecialSound {ι : Type} {oSpec : OracleSpec ι} {σ : Type} +That `γ := b` instantiation is the named corollary +`quadEval_coordinateWiseSpecialSoundWithEscape_paperParams` below. The paper's exact `S_b`-box +output relation is `QuadEval/Reduction.paperRelOut`, with the `paperRelOut ⊆ relOut` containment +proved as `QuadEval/Reduction.paperRelOut_subset_relOut`. + +Assembled by `SingleRound.coordinateWiseSpecialSoundWithEscape_of_mkWitness`, which discharges +every tree/extractor/guard obligation generically; the protocol-specific content is +`buildWitness_break_or_mem_relIn`. -/ +theorem quadEval_coordinateWiseSpecialSoundWithEscape + {ι : Type} {oSpec : OracleSpec ι} {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) - (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (m := m) - (r := r)).coordinateWiseSpecialSound init impl + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) : + Verifier.coordinateWiseSpecialSoundWithEscape init impl (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r)) - (relIn 𝓜(q, α) (b : ZMod q) + (escEvent (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + (quadEvalEscLocal (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) pp γ)) + (relIn 𝓜(q, α) pp (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := - coordinateWiseSpecialSound_of_mkWitness init impl _ (fun _ _ => rfl) _ _ - (buildWitness 𝓜(q, α) (b : ZMod q)) - (fun stmtIn v fam resp hbranch hstar => - buildWitness_mem_relIn hq5 hκ hτ stmtIn v fam resp hbranch hstar) + (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)) + (treeExtractor (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + (quadEvalMkWitness (outerRows := outerRows) 𝓜(q, α) (b : ZMod q))) := by + refine coordinateWiseSpecialSoundWithEscape_of_mkWitness init impl _ (fun _ _ => rfl) _ _ _ _ + (fun stmtIn v fam resp hbranch hstar => ?_) + rcases buildWitness_break_or_mem_relIn hq5 hκ hτ pp stmtIn v fam resp hbranch hstar with + hbad | ⟨w, hw, hmem⟩ + · exact Or.inl hbad + · refine Or.inr ?_ + rw [quadEvalMkWitness_of_inl 𝓜(q, α) (b : ZMod q) stmtIn v fam resp hw] + exact hmem /-- **Paper-parameter instantiation of Hachi Lemma 8** — the named bridge to the paper's -weak-opening contract. This is `quadEval_coordinateWiseSpecialSound` specialized to the paper's -range `γ := b`. Two of the paper's three Lemma 8 bounds match **exactly**: `γ̄ = b` and `ω̄ = 2ω`. -The third does **not**: the paper's `β̄ = 2·bᵗ` (an `ℓ₂`/`ℓ∞` bound on `‖c̄ᵢsᵢ‖`) is replaced by -ArkLib's `βSq = quadEvalBetaSq b b zDigits (deg φ) m messageDigits`, a *squared-`ℓ₂`* bound on the -scaled blocks carrying extra `2ᵐ·δ·(deg φ)` dimensional factors — a deliberate `VerifiedOpening` -modeling choice, not a paper-faithful value (see `quadEvalZL2SqBound`). Later binding code should -cite this entry point; the general-`γ` theorem above is the intentional ArkLib generalization, and -`QuadEval/Reduction.paperRelOut_subset_relOut` proves the `paper ⊆ code` containment on the output -relation (for `b / 2 ≤ γ`, in particular `γ := b`). -/ -theorem quadEval_coordinateWiseSpecialSound_paperParams {ι : Type} {oSpec : OracleSpec ι} {σ : Type} +weak-opening contract. This is `quadEval_coordinateWiseSpecialSoundWithEscape` specialized to the +paper's range `γ := b`. Two of the paper's three Lemma 8 bounds match **exactly**: `γ̄ = b` and +`ω̄ = 2ω`. The third does **not**: the paper's `β̄ = 2·bᵗ` (an `ℓ₂`/`ℓ∞` bound on `‖c̄ᵢsᵢ‖`) is +replaced by ArkLib's `βSq = quadEvalBetaSq b b zDigits (deg φ) m messageDigits`, a +*squared-`ℓ₂`* bound on the scaled blocks carrying extra `2ᵐ·δ·(deg φ)` dimensional factors — a +deliberate `VerifiedOpening` modeling choice, not a paper-faithful value (see +`quadEvalZL2SqBound`). Later binding code should cite this entry point; the general-`γ` theorem +above is the intentional ArkLib generalization, and +`QuadEval/Reduction.paperRelOut_subset_relOut` proves the `paper ⊆ code` containment on the +output relation (for `b / 2 ≤ γ`, in particular `γ := b`). -/ +theorem quadEval_coordinateWiseSpecialSoundWithEscape_paperParams + {ι : Type} {oSpec : OracleSpec ι} {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) - (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (m := m) - (r := r)).coordinateWiseSpecialSound init impl + (hq5 : q % 8 = 5) {b ω : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) : + Verifier.coordinateWiseSpecialSoundWithEscape init impl (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r)) - (relIn 𝓜(q, α) (b : ZMod q) + (escEvent (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω b) + (quadEvalEscLocal (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) pp b)) + (relIn 𝓜(q, α) pp (b : ZMod q) (quadEvalBetaSq b b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) b (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω b) := - quadEval_coordinateWiseSpecialSound (γ := b) init impl hq5 hκ hτ - --- An `OracleVerifier` wrapper is deliberately not included: it needs an `OracleInterface` --- instance for `Simple.Commitment` (a query-model design decision that does not exist in the --- repo yet) and the still-sorried oracle-level append theorem. The plain-`Verifier` statement --- above is the right interface for Lemma 8; the oracle wrapper belongs to the composition step. - -/-- **`QuadEval` as a `CWSSPackage`** (Hachi [NOZ26, §4.2, Figure 3]; Lemma 8): the two-round fold -`verifier` bundled with its `foldStructure` CWSS certificate `quadEval_coordinateWiseSpecialSound`, -ready to be `▷`-composed after the polynomial-level bridge. -/ -def quadEvalPackage {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω b) + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)) + (treeExtractor (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω b) + (quadEvalMkWitness (outerRows := outerRows) 𝓜(q, α) (b : ZMod q))) := + quadEval_coordinateWiseSpecialSoundWithEscape (γ := b) init impl hq5 hκ hτ pp + +/-- **The escape-aware `QuadEval` package.** `relIn` is the ordinary opening relation, `relOut` the +Eq.-(20) response relation, and `extractor` the actual Lemma 8 extraction algorithm +(`quadEvalMkWitness`), exposed by composed chains via `.extractor`. Its one escape-specific field is +the `esc` event, firing exactly on trees whose responses yield a genuine break of the fixed key +`pp`. -/ +noncomputable def quadEvalPackage {ι : Type} {oSpec : OracleSpec ι} {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - CWSSPackage init impl + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) + innerDigits dRows) : + EscapeCWSSPackage init impl (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) @@ -504,12 +609,22 @@ def quadEvalPackage {ι : Type} {oSpec : OracleSpec ι} {σ : Type} (pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where verifier := verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) struct := - foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r) - relIn := relIn 𝓜(q, α) (b : ZMod q) + foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r) + relIn := relIn 𝓜(q, α) pp (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) - relOut := relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ + relOut := relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ + esc := escEvent (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + (quadEvalEscLocal (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) pp γ) isPure := ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ - isCWSS := quadEval_coordinateWiseSpecialSound init impl hq5 hκ hτ + extractor := treeExtractor (relOut (zDigits := zDigits) 𝓜(q, α) pp (b : ZMod q) ω γ) + (quadEvalMkWitness (outerRows := outerRows) 𝓜(q, α) (b : ZMod q)) + isCWSS := quadEval_coordinateWiseSpecialSoundWithEscape init impl hq5 hκ hτ pp + +-- An `OracleVerifier` wrapper is deliberately not included: it needs an `OracleInterface` +-- instance for `Simple.Commitment` (a query-model design decision that does not exist in the +-- repo yet) and the still-sorried oracle-level append theorem. The plain-`Verifier` statement +-- above is the right interface for Lemma 8; the oracle wrapper belongs to the composition step. end Pinned diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/Basic.lean new file mode 100644 index 0000000000..90ab0c734c --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/Basic.lean @@ -0,0 +1,25 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.Commitments.Functional.Hachi.Recursion.TraceHandoff + +/-! +# Hachi Recursion Adapters + +Umbrella module for `Hachi/Recursion/`: the adapters that close one Hachi opening iteration and +hand its evaluation claim to the next ring. + +## Folder structure + +* `Recursion/PartialEval.lean` — transforms the final multilinear-evaluation claim into the + collection of partial evaluations used by the recursion step. +* `Recursion/ZBatchBridge.lean` — packs those partial evaluations into `relHatEval`. +* `Recursion/TraceHandoff.lean` — performs the guarded trace handoff into the next iteration's + plain `QuadEval.relIn` relation. All three adapters reshape or re-read claims rather than + introducing a new commitment, so all three are escape-free. + +This umbrella re-exports the folder (`TraceHandoff` transitively imports `ZBatchBridge` and +`PartialEval`). The full guarded chain is composed in the sibling `Composition.lean`. +-/ diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean index 2d03bce824..ccdc82ee51 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean @@ -29,7 +29,7 @@ import ArkLib.Commitments.Functional.Hachi.Sumcheck.FinalEval `mle[w̃](a₀ ++ a₁) = y′` follows by the mle splitting identity (`wTableMleEval_split`), with zero soundness error. (The *next* seam — collapsing the per-`i` claims into the single `Z`-packed claim of Eq. (26) — is where the paper's §4.5/§3.2 argument has an apparent gap; - see `Recursion/ZBatchBridge.lean` and `HACHI_RECURSION_GAP.md`.) + see `Recursion/ZBatchBridge.lean`.) **Sorried**: the encoding defs (`partialEvalAt`, `deriveFamily`, `wTableMleEval_split`) and the CWSS theorem. @@ -74,7 +74,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (mLow κ : ℕ) (bound ρBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -145,59 +145,73 @@ def partialEvalProver {TCom : Type} `t` and *every* partial evaluation in the derived family is well-formed. This seam is the sound stopping point of the §4.5 peeling; collapsing it into the single `Z`-packed claim is the `Recursion/ZBatchBridge.lean` step (⚠ see there). -/ -def relPartialEval (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relPartialEval (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : Set (PartialEvalStatement K.TCom F mLow κ × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.t ∧ ∀ i, partialEvalAt Φ mLow κ φF p.2 p.1.pointLow i = p.1.partials i} -/-- Escape-threaded per-`i` partial-evaluation relation. -/ -def relPartialEvalE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) : - Set (PartialEvalStatement K.TCom F mLow κ × (LiftedWitness Φ μ n ⊕ E)) := - (relPartialEval Φ mLow κ bound ρBound K φF).withEscape K.esc - variable [SampleableType F] -/-- **CWSS of the partial-evaluation head (skeleton, G2) — a sound, zero-error seam.** +/-- **The partial-evaluation extraction algorithm (skeleton, G2).** + +**Sorried** — this def is the milestone's *algorithm* (the transcript-level pull-back of the proof +plan on `partialEval_coordinateWiseSpecialSoundWith`). -/ +noncomputable def partialEvalExtractor + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) : + Extractor.TreeBased (WEvalStatement K.TCom F (mLow + κ)) (LiftedWitness Φ μ n) + (pSpecPartialEval F κ) + (CWSSStructure.toShape (CWSSStructure.ofIsEmpty + (pSpec := pSpecPartialEval F κ))).arity := + sorry + +/-- **CWSS of the partial-evaluation head (skeleton, G2) — a sound, zero-error seam, at the +named `partialEvalExtractor`** (the named form is deliberate — see +`Verifier.treeSpecialSoundWith`; filling G2 means filling the extractor and this specification +about it). **Sorried.** Proof plan: no challenge round, so CWSS collapses to a transcript-level pull-back -(the no-challenge bridge; the verifier is pure): from a `relPartialEvalE` witness at the derived +(the no-challenge bridge; the verifier is pure): from a `relPartialEval` witness at the derived statement, the mle splitting identity `wTableMleEval_split` plus the derivation construction (`deriveFamily` makes Eq. (24)'s display equation true by fiat, and the per-`i` claims pin every -`partials i` to the true partial) yield `mle[w̃](a₀ ++ a₁) = y′`, i.e. `relWEvalClaimE` -membership; escapes pass through. -/ -theorem partialEval_coordinateWiseSpecialSound +`partials i` to the true partial) yield `mle[w̃](a₀ ++ a₁) = y′`, i.e. `relWEvalClaim` +membership. -/ +theorem partialEval_coordinateWiseSpecialSoundWith (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : - (partialEvalVerifier (oSpec := oSpec) mLow κ (TCom := K.TCom) - (F := F)).coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWith init impl CWSSStructure.ofIsEmpty - (relWEvalClaimE Φ (mLow + κ) bound ρBound b K φF) - (relPartialEvalE Φ mLow κ bound ρBound K φF) := by + (relWEvalClaim Φ (mLow + κ) bound ρBound b K φF) + (relPartialEval Φ mLow κ bound ρBound K φF) + (partialEvalVerifier (oSpec := oSpec) mLow κ (TCom := K.TCom) (F := F)) + (partialEvalExtractor Φ mLow κ bound ρBound K φF) := by sorry -/-- **The partial-evaluation head as a `CWSSPackage`** (Hachi §4.5, Eq. (24)): the pure +/-- **The partial-evaluation head as a (plain) `CWSSPackage`** (Hachi §4.5, Eq. (24)): the pure one-message derive-`y₀` head with the empty challenge structure, reducing the evaluation claim -`relWEvalClaimE` to the per-`i` claims `relPartialEvalE`. -/ -def partialEvalPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +`relWEvalClaim` to the per-`i` claims `relPartialEval`. A sound, zero-error reshaping, hence +escape-free. -/ +noncomputable def partialEvalPackage (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : CWSSPackage init impl - (WEvalStatement K.TCom F (mLow + κ)) (LiftedWitness Φ μ n ⊕ E) - (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n ⊕ E) + (WEvalStatement K.TCom F (mLow + κ)) (LiftedWitness Φ μ n) + (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n) (pSpecPartialEval F κ) where verifier := partialEvalVerifier (oSpec := oSpec) mLow κ (TCom := K.TCom) (F := F) struct := CWSSStructure.ofIsEmpty - relIn := relWEvalClaimE Φ (mLow + κ) bound ρBound b K φF - relOut := relPartialEvalE Φ mLow κ bound ρBound K φF + relIn := relWEvalClaim Φ (mLow + κ) bound ρBound b K φF + relOut := relPartialEval Φ mLow κ bound ρBound K φF isPure := ⟨fun stmt tr => ⟨stmt.t, fun j => stmt.point (Fin.castAdd κ j), fun j => stmt.point (Fin.natAdd mLow j), deriveFamily κ stmt.value (fun j => stmt.point (Fin.natAdd mLow j)) (tr 0)⟩, fun _ _ => rfl⟩ - isCWSS := partialEval_coordinateWiseSpecialSound Φ mLow κ bound ρBound b init impl K φF + extractor := partialEvalExtractor Φ mLow κ bound ρBound K φF + isCWSS := partialEval_coordinateWiseSpecialSoundWith Φ mLow κ bound ρBound b init impl K φF end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean index 629e01e159..477f3959ff 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean @@ -39,9 +39,10 @@ import ArkLib.Commitments.Functional.Hachi.Recursion.ZBatchBridge Extraction (sorried): a next-iteration witness at the mapped statement is a weak opening of the reinterpreted `t` that is eval-consistent for the `eq`-tensor bases with value `p` - (`QuadEval`'s `relInE` at `Φ'`), or an MSIS/escape. Pulling the opening back through the + (`QuadEval`'s plain `relIn` at `Φ'`). The package carries any ambient escape separately. + Pulling the opening back through the `ψ`/`Z`-packing bijection yields an opening `w̃` of `t`; Theorem 2 turns the eval-consistency - plus the **guard's** trace equation into `hatEval w̃ a₀ = value` — exactly `relHatEvalE`. + plus the **guard's** trace equation into `hatEval w̃ a₀ = value` — exactly `relHatEval`. (The extracted table entries are subfield-valued with small `Eq. (7)`-basis coordinates; the `Zq`-entry reading is recovered through the same bijection. The *semantic* content of this seam — unlike the `Z`-packing bridge before it — is pinned exactly by the trace: no slack.) @@ -66,7 +67,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (mLow κ : ℕ) (bound ρBound : ℕ) variable {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -87,14 +88,13 @@ def traceCheck {TCom : Type} (φF : ZMod q →+* F) (stmt : HatEvalStatement TCom F mLow) (p : Rq Φ') : Bool := sorry -/-- The next-iteration statement (Eq. (27) as a `QuadEval` claim over `Φ'`): public parameters -`pp'`, the **reinterpreted** commitment `reinterpretCom stmt.t`, the `eq`-tensor bases derived -from the low point (σ₋₁-twisted, design D5), and the evaluation `p`. **Sorried (G3)** — the -`e`/`f` packing (`psi` on the `eq`-tensor halves) and the split bookkeeping +/-- The next-iteration statement (Eq. (27) as a `QuadEval` claim over `Φ'`): the +**reinterpreted** commitment `reinterpretCom stmt.t`, the `eq`-tensor bases derived +from the low point (σ₋₁-twisted, design D5), and the evaluation `p`. (The next iteration's key +`pp'` is not statement data; it enters only the next iteration's relations.) **Sorried (G3)** — +the `e`/`f` packing (`psi` on the `eq`-tensor halves) and the split bookkeeping `mLow = m' + r' + (α' − κ)`. -/ def toNextQuadEvalStatement {TCom : Type} (φF : ZMod q →+* F) - (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') - innerDigits' dRows') (reinterpretCom : TCom → Commitment Φ' outerRows') (stmt : HatEvalStatement TCom F mLow) (p : Rq Φ') : QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' @@ -104,8 +104,6 @@ def toNextQuadEvalStatement {TCom : Type} (φF : ZMod q →+* F) /-- The trace-handoff verifier (Hachi §4.5, Eqs. (27)–(28)): **guarded** on the trace check, outputting the next iteration's `QuadEvalStatement` over `Φ'`. -/ def handoffVerifier {TCom : Type} (φF : ZMod q →+* F) - (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') - innerDigits' dRows') (reinterpretCom : TCom → Commitment Φ' outerRows') : Verifier oSpec (HatEvalStatement TCom F mLow) (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' @@ -113,26 +111,24 @@ def handoffVerifier {TCom : Type} (φF : ZMod q →+* F) (pSpecHandoff Φ') where verify := fun stmt tr => if traceCheck Φ' mLow φF stmt (tr 0) then - pure (toNextQuadEvalStatement Φ' mLow φF pp' reinterpretCom stmt (tr 0)) + pure (toNextQuadEvalStatement Φ' mLow φF reinterpretCom stmt (tr 0)) else failure omit [NeZero q] [IsCyclotomic Φ] [IsCyclotomic Φ'] in /-- The trace-handoff verifier is guarded — definitionally, by `traceCheck`. -/ theorem handoffVerifier_isGuarded {TCom : Type} (φF : ZMod q →+* F) - (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') - innerDigits' dRows') (reinterpretCom : TCom → Commitment Φ' outerRows') : - (handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' reinterpretCom).IsGuarded := + (handoffVerifier (oSpec := oSpec) Φ' mLow + (innerRows' := innerRows') (messageDigits' := messageDigits') (innerDigits' := innerDigits') + (dRows' := dRows') (m' := m') (r' := r') φF reinterpretCom).IsGuarded := ⟨fun stmt tr => traceCheck Φ' mLow φF stmt (tr 0), - fun stmt tr => toNextQuadEvalStatement Φ' mLow φF pp' reinterpretCom stmt (tr 0), + fun stmt tr => toNextQuadEvalStatement Φ' mLow φF reinterpretCom stmt (tr 0), fun _ _ => rfl⟩ /-- The honest trace-handoff prover skeleton: sends `p` (the parameter `computeP`, honestly Eq. (27)'s `eᵀ(σ₋₁(ψ(f))ᵀ ⊗ I)ψ(ŵ)`), and carries the witness forward as the next iteration's opening data (the parameter `computeWit` — the ψ-packed re-reading of `w̃`). -/ def handoffProver {TCom WitOut : Type} (φF : ZMod q →+* F) - (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') - innerDigits' dRows') (reinterpretCom : TCom → Commitment Φ' outerRows') (computeP : HatEvalStatement TCom F mLow → LiftedWitness Φ μ n → Rq Φ') (computeWit : LiftedWitness Φ μ n → WitOut) : @@ -149,67 +145,87 @@ def handoffProver {TCom WitOut : Type} (φF : ZMod q →+* F) receiveChallenge | ⟨0, h⟩ => nomatch h output := fun ⟨stmt, wit⟩ => - pure (toNextQuadEvalStatement Φ' mLow φF pp' reinterpretCom stmt (computeP stmt wit), + pure (toNextQuadEvalStatement Φ' mLow φF reinterpretCom stmt (computeP stmt wit), computeWit wit) variable [SampleableType F] -/-- **CWSS of the trace handoff (skeleton, G3) — closing the recursion loop.** +/-- **The trace-handoff extraction algorithm (skeleton, G3).** + +**Sorried** — this def is the milestone's *algorithm* (the transcript-level pull-back of the proof +plan on `handoff_coordinateWiseSpecialSoundWith`). -/ +noncomputable def handoffExtractor + (zpow : Fin (2 ^ κ) → F) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) + (reinterpretCom : K.TCom → Commitment Φ' outerRows') : + Extractor.TreeBased (HatEvalStatement K.TCom F mLow) (LiftedWitness Φ μ n) + (pSpecHandoff Φ') + (CWSSStructure.toShape (CWSSStructure.ofIsEmpty + (pSpec := pSpecHandoff Φ'))).arity := + sorry + +/-- **CWSS of the trace handoff (skeleton, G3) — closing the recursion loop, at the named +`handoffExtractor`** (the named form is deliberate — see `Verifier.treeSpecialSoundWith`; +filling G3 means filling the extractor and this specification about it). **Sorried.** Proof plan: no challenge round, so CWSS collapses to a transcript-level pull-back (the probability-phrased no-challenge bridge tolerates the guard): acceptance forces -`traceCheck = true`; a next-iteration `relInE`-witness at the mapped statement is a weak opening -of `reinterpretCom t` that is eval-consistent for the `eq`-tensor bases with value `p` (or an -MSIS/escape — pass through, absorbing the next iteration's own MSIS disjuncts into the witness -shape). Pull the opening back through the commitment reinterpretation and the `ψ`/`Z`-packing +`traceCheck = true`; a next-iteration `relIn` witness at the mapped statement is a weak opening +of `reinterpretCom t` that is eval-consistent for the `eq`-tensor bases with value `p`. Pull the +opening back through the commitment reinterpretation and the `ψ`/`Z`-packing bijection (`psi_bijective`) to an opening `w̃` of `t`; Theorem 2 (`traceH_psi_mul_conj`) turns -eval-consistency plus the guard's trace equation into `hatEval w̃ a₀ = value` — `relHatEvalE` +eval-consistency plus the guard's trace equation into `hatEval w̃ a₀ = value` — `relHatEval` membership. Norm bookkeeping through `ψ` is Lemma 6 (`cInfNorm_psi_le`, gate G1); the reinterpretation identity `Com_d(w̃) = Com'_{d′}(ψ(ŵ))` is the Phase-G `LiftCom` instantiation obligation. -/ -theorem handoff_coordinateWiseSpecialSound +theorem handoff_coordinateWiseSpecialSoundWith (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : - (handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' - reinterpretCom).coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWith init impl CWSSStructure.ofIsEmpty - (relHatEvalE Φ mLow κ bound ρBound zpow K φF) - (relInE Φ' base' βSq' γ' κ' K.esc) := by + (relHatEval Φ mLow κ bound ρBound zpow K φF) + (relIn Φ' pp' base' βSq' γ' κ') + (handoffVerifier (oSpec := oSpec) Φ' mLow φF reinterpretCom) + (handoffExtractor Φ Φ' mLow κ bound ρBound zpow K φF reinterpretCom) := by sorry -/-- **The trace handoff as a guarded package** (`GCWSSPackage`; Hachi §4.5, Eqs. (27)–(28)): -the guarded one-message verifier with the empty challenge structure, reducing the `Z`-packed -claim `relHatEvalE` to the **next iteration's** escape-threaded `QuadEval` input relation -`relInE` over `Φ'` — the recursion loop's closing seam (the next iteration re-enters at -`quadEvalPackageE Φ'`, bypassing the polynomial-level bridge: the bases are `eq`-tensor -packings, not monomial bases of a point). -/ -def handoffPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) +/-- **The trace handoff as a guarded `GCWSSPackage`** (Hachi §4.5, Eqs. (27)–(28)): the guarded +one-message verifier with the empty challenge structure, reducing the `Z`-packed claim `relHatEval` +to the **next iteration's** `QuadEval` input relation `relIn` over `Φ'` — the recursion loop's +closing seam (the next iteration re-enters at `quadEvalPackage Φ'`, bypassing the polynomial-level +bridge: the bases are `eq`-tensor packings, not monomial bases of a point). + +The handoff *re-reads* the existing commitment through `ψ` rather than introducing a new one, hence +carries no escape event. -/ +noncomputable def handoffPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : GCWSSPackage init impl - (HatEvalStatement K.TCom F mLow) (LiftedWitness Φ μ n ⊕ E) + (HatEvalStatement K.TCom F mLow) (LiftedWitness Φ μ n) (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') - (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits' ⊕ E) + (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits') (pSpecHandoff Φ') where - verifier := handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' reinterpretCom + verifier := handoffVerifier (oSpec := oSpec) Φ' mLow φF reinterpretCom struct := CWSSStructure.ofIsEmpty - relIn := relHatEvalE Φ mLow κ bound ρBound zpow K φF - relOut := relInE Φ' base' βSq' γ' κ' K.esc - isGuarded := handoffVerifier_isGuarded Φ' mLow φF pp' reinterpretCom - isCWSS := handoff_coordinateWiseSpecialSound Φ Φ' mLow κ bound ρBound init impl zpow K φF pp' - reinterpretCom base' βSq' γ' κ' + relIn := relHatEval Φ mLow κ bound ρBound zpow K φF + relOut := relIn Φ' pp' base' βSq' γ' κ' + isGuarded := handoffVerifier_isGuarded Φ' mLow φF reinterpretCom + extractor := handoffExtractor Φ Φ' mLow κ bound ρBound zpow K φF reinterpretCom + isCWSS := handoff_coordinateWiseSpecialSoundWith Φ Φ' mLow κ bound ρBound init impl zpow K + φF pp' reinterpretCom base' βSq' γ' κ' end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean index 51a3569c2d..dd932adeb1 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean @@ -11,19 +11,19 @@ import ArkLib.Commitments.Functional.Hachi.Recursion.PartialEval Zero-round bridge collapsing the per-`i` partial-evaluation claims into the **single `Z`-packed claim** of Hachi Eq. (26): - * `relIn = relPartialEvalE` — `∀ i ∈ {0,1}^κ: partialEvalAt w̃ a₀ i = yᵢ` + * `relIn = relPartialEval` — `∀ i ∈ {0,1}^κ: partialEvalAt w̃ a₀ i = yᵢ` (`Recursion/PartialEval.lean`); - * `relOut = relHatEvalE` — `hatEval w̃ a₀ = ∑ᵢ yᵢ·Z^{⟨i⟩}`, where + * `relOut = relHatEval` — `hatEval w̃ a₀ = ∑ᵢ yᵢ·Z^{⟨i⟩}`, where `hatEval w̃ a₀ := ∑ⱼ ŵⱼ·eq(j, a₀)` with `ŵⱼ := ∑ᵢ w̃_{j‖i}·Z^{⟨i⟩}` (Eq. (25)); the statement map computes the public right-hand side `∑ᵢ yᵢ·zpow i`. The completeness direction is trivial (substitute the per-`i` claims). **The extraction direction — the paper's implicit "equivalence" claim below Eq. (26) — appears to be FALSE**, - and this bridge's sorried pull-back `mem_relPartialEvalE_of_relHatEvalE` is recorded as an + and this bridge's sorried pull-back `mem_relPartialEval_of_relHatEval` is recorded as an **open soundness question**, deliberately isolated in this one zero-round seam (mirroring how the Lemma 10 gap is isolated in the zero-check). - ## ⚠ The gap (see `HACHI_RECURSION_GAP.md` for the full analysis) + ## ⚠ The gap The packed claim pins only the single `F`-linear combination `∑ᵢ Z^{⟨i⟩}·(pᵢ − yᵢ) = 0` of the per-`i` defects `pᵢ − yᵢ := partialEvalAt w̃ a₀ i − yᵢ ∈ F`. Since the defects are @@ -33,10 +33,10 @@ import ArkLib.Commitments.Functional.Hachi.Recursion.PartialEval the packed right-hand side invariant while shifting the reconstructed evaluation `y₀ + a·y₁ = mle[w̃](a₀, a) + δ(Z − a)` — for `a ≠ Z` every target value is reachable, so the §4.5 recursion step (and §3.2's generic form) is not knowledge-sound as stated. Candidate - repairs (recorded in the gap note; all deviate from the paper): a batching challenge round - over the peeled index (Kronecker-seeded, DP24-relocation style), or replacing the peeling with - the generic §3.1 packing (`F_{q^k}`-coefficient reading, paper Fig. 2 row 1, at the cost of - `κ` extra variables and a sparser commitment reinterpretation). + repairs (all deviate from the paper): a batching challenge round over the peeled index + (Kronecker-seeded, DP24-relocation style), or replacing the peeling with the generic §3.1 + packing (`F_{q^k}`-coefficient reading, paper Fig. 2 row 1, at the cost of `κ` extra variables + and a sparser commitment reinterpretation). Until a repair is adopted, this bridge is the faithful rendering of the paper's step, and its sorry is expected to be **unprovable as stated** — kept so the composed chain records exactly @@ -67,7 +67,7 @@ section Bridge variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (mLow κ : ℕ) (bound ρBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -83,63 +83,59 @@ def hatEval (φF : ZMod q →+* F) (zpow : Fin (2 ^ κ) → F) (w : LiftedWitnes evaluates to the packed public value at the low point half. This is the claim the trace handoff (`Recursion/TraceHandoff.lean`) converts into the next iteration's `Rq`-statement. -/ def relHatEval (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : Set (HatEvalStatement K.TCom F mLow × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.t ∧ hatEval Φ mLow κ φF zpow p.2 p.1.pointLow = p.1.value} -/-- Escape-threaded `Z`-packed claim relation. -/ -def relHatEvalE (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) : - Set (HatEvalStatement K.TCom F mLow × (LiftedWitness Φ μ n ⊕ E)) := - (relHatEval Φ mLow κ bound ρBound zpow K φF).withEscape K.esc - /-- The bridge's statement map: forget the peeled point half and pack the partial evaluations into the public right-hand side `∑ᵢ yᵢ·zpow i` of Eq. (26). -/ def toHatEvalStatement {TCom : Type} (zpow : Fin (2 ^ κ) → F) (s : PartialEvalStatement TCom F mLow κ) : HatEvalStatement TCom F mLow := ⟨s.t, s.pointLow, ∑ i, s.partials i * zpow i⟩ -/-- ⚠ **The un-packing pull-back — open soundness question (`HACHI_RECURSION_GAP.md`).** As the -paper's step below Eq. (26) implicitly requires, the packed claim should imply the per-`i` -claims. **This statement is expected to be unprovable**: the packed claim constrains only one -`F`-linear combination of the per-`i` defects, which have a nontrivial kernel for `κ ≥ 1` (see +/-- ⚠ **The un-packing pull-back — open soundness question.** As the paper's step below Eq. (26) +implicitly requires, the packed claim should imply the per-`i` claims. **This statement is +expected to be unprovable**: the packed claim constrains only one `F`-linear combination of the +per-`i` defects, which have a nontrivial kernel for `κ ≥ 1` (see the module docstring for the explicit `κ = 1` cheat). The sorry is kept — deliberately isolated in this zero-round seam — until a repair (batching challenge / generic §3.1 packing) is adopted; any repair changes this bridge's *protocol content*, not the surrounding seams. -/ -theorem mem_relPartialEvalE_of_relHatEvalE (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +theorem mem_relPartialEval_of_relHatEval (zpow : Fin (2 ^ κ) → F) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) - (s : PartialEvalStatement K.TCom F mLow κ) (w : LiftedWitness Φ μ n ⊕ E) - (h : (toHatEvalStatement mLow κ zpow s, w) ∈ relHatEvalE Φ mLow κ bound ρBound zpow K φF) : - (s, w) ∈ relPartialEvalE Φ mLow κ bound ρBound K φF := by + (s : PartialEvalStatement K.TCom F mLow κ) (w : LiftedWitness Φ μ n) + (h : (toHatEvalStatement mLow κ zpow s, w) ∈ relHatEval Φ mLow κ bound ρBound zpow K φF) : + (s, w) ∈ relPartialEval Φ mLow κ bound ρBound K φF := by sorry -/-- **The `Z`-packing bridge as a `CWSSPackage`** (Hachi §4.5, Eqs. (25)–(26)): zero-round -`ReduceClaim` at `mapStmt := toHatEvalStatement`, reducing `relPartialEvalE` to `relHatEvalE`. +/-- **The `Z`-packing bridge as a (plain) `CWSSPackage`** (Hachi §4.5, Eqs. (25)–(26)): zero-round +`ReduceClaim` at `mapStmt := toHatEvalStatement`, reducing `relPartialEval` to `relHatEval`. A pure +statement repacking, hence escape-free. ⚠ Its certificate rests on the sorried — and expectedly unprovable as stated — un-packing pull-back; see the module docstring. -/ -def zBatchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) +noncomputable def zBatchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : CWSSPackage init impl - (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n ⊕ E) - (HatEvalStatement K.TCom F mLow) (LiftedWitness Φ μ n ⊕ E) + (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n) + (HatEvalStatement K.TCom F mLow) (LiftedWitness Φ μ n) (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec (toHatEvalStatement mLow κ zpow) struct := CWSSStructure.ofIsEmpty - relIn := relPartialEvalE Φ mLow κ bound ρBound K φF - relOut := relHatEvalE Φ mLow κ bound ρBound zpow K φF + relIn := relPartialEval Φ mLow κ bound ρBound K φF + relOut := relHatEval Φ mLow κ bound ρBound zpow K φF isPure := ⟨fun stmt _ => toHatEvalStatement mLow κ zpow stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relPartialEvalE Φ mLow κ bound ρBound K φF) - (relOut := relHatEvalE Φ mLow κ bound ρBound zpow K φF) + extractor := ReduceClaim.treeExtractor (mapStmt := toHatEvalStatement mLow κ zpow) + (relHatEval Φ mLow κ bound ρBound zpow K φF) (fun _ w => w) CWSSStructure.ofIsEmpty + isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := relPartialEval Φ mLow κ bound ρBound K φF) + (relOut := relHatEval Φ mLow κ bound ρBound zpow K φF) (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (mem_relPartialEvalE_of_relHatEvalE Φ mLow κ bound ρBound zpow K φF) + (fun s w h => mem_relPartialEval_of_relHatEval Φ mLow κ bound ρBound zpow K φF s w h) end Bridge diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean similarity index 63% rename from ArkLib/Commitments/Functional/Hachi/RingSwitch.lean rename to ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean index b24d17d02a..76a69b5cf3 100644 --- a/ArkLib/Commitments/Functional/Hachi/RingSwitch.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean @@ -8,7 +8,8 @@ import ArkLib.Commitments.Functional.Hachi.RingSwitch.Reduction /-! # Hachi Ring-Switching Lift (Figure 4 / Lemma 9) -Umbrella for `Hachi/RingSwitch/`: the entry of Hachi's [NOZ26, §4.3] sumcheck-based opening — the +Umbrella module for `Hachi/RingSwitch/`: the entry of Hachi's [NOZ26, §4.3] sumcheck-based +opening — the Huang–Mao–Zhang [HMZ25] ring-switching lift. Following [HMZ25], `M z = y` over the cyclotomic ring `Rq` holds **iff** there is a quotient `r` with `M z = y + (Xᵈ + 1)·r` over `Zq[X]`; the prover commits to the lifted witness `(z, r)` and both sides evaluate the lifted rows at a random @@ -19,17 +20,19 @@ reduction — also a ring-switching idea — lives under `ArkLib/ProofSystem/Rin ## Folder structure * `RingSwitch/Rlin.lean` — the zero-round **entry adapter**: reinterprets `QuadEval`'s Eq. (20) - output (the escape-threaded `relOutE` of the sibling `Escape.lean`) as the unstructured linear - relation `R^lin` (`relRlinE`), the input the lift addresses. Statement reshaping only - (`ReduceClaim`), so it is CWSS for any structure; the sorried pieces are the block-matrix - assembly/unstacking and the block-row equivalence pull-back. + output (`relOut`) as the unstructured linear relation `R^lin` (`relRlin`), the input the lift + addresses. Statement reshaping only (`ReduceClaim`), so it is CWSS for any structure and needs no + escape event — a plain `CWSSPackage`; the sorried pieces are the block-matrix assembly/unstacking + and the block-row equivalence pull-back. * `RingSwitch/Reduction.lean` — **Hachi Figure 4 / Lemma 9**: the two-round lift (commit `t := Com(w̃)`; sample `α ← F`; evaluate the lifted rows at `α`), the abstract weak-binding - commitment `LiftCom`, the output relation `relLift`, and the plain-special-sound CWSS theorem - `lift_coordinateWiseSpecialSound` at `k = 2d` (**sorried**: Lemma 9's interpolation extraction). + commitment `LiftCom` with its short-collision set `LiftCom.Collision`, the output relation + `relLift`, the weak-binding escape event `liftEscLocal`, and the plain-special-sound + escape-threaded CWSS theorem `lift_coordinateWiseSpecialSoundWithEscape` at `k = 2d` + (**sorried**: Lemma 9's interpolation extraction). -This umbrella re-exports the folder (`Reduction` transitively imports `Rlin`). Its output relation -`relLiftE` is the input of the batching bridge in `ZeroCheck/`; the chain is composed in +This umbrella re-exports the folder (`Reduction` transitively imports `Rlin`). The plain +`relLift` is the input of the batching bridge in `ZeroCheck/`; the chain is composed in `Composition.lean`. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean index 6db2a68ea0..ebb45311e2 100644 --- a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean @@ -33,26 +33,28 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRoun short. **CWSS at `k = 2d`** (`scalarStructure`, plain special soundness): each row's defect polynomial `∑ⱼ Mᵢⱼ·zⱼ − yᵢ − (X^d+1)·ρᵢ` has degree `≤ 2d − 1`, so `2d` accepting branches at pairwise-distinct `α` either exhibit two distinct short openings of `t` — the weak-binding - escape (`LiftCom.collision_mem`; [NOZ26] Remark 2 / Lemma 7), threaded through `K.esc` — or - share one opening whose row defects have `2d` roots, hence vanish identically: `M z = y` over - `Rq` plus the range bound, i.e. `relRlinE` membership. + **escape event** `liftEscLocal` (`LiftCom.Collision`; [NOZ26] Remark 2 / Lemma 7) — or share one + opening whose row defects have `2d` roots, hence vanish identically: `M z = y` over `Rq` plus the + range bound, i.e. `relRlin` membership. ## The abstract commitment `LiftCom` and the norm bookkeeping The commitment is abstract (design G2: the key is a *parameter*, not a statement field; Lemma 9 - needs only binding). Weak binding is **norm-conditioned**, so `LiftCom` is parameterized by a - shortness predicate `Short` and its collision axiom requires both openings short; this chain - instantiates `Short := liftShort bound ρBound` at the *global* norm parameters. `relLift` - therefore carries (i) `liftShort bound ρBound w̃` — feeding both the collision axiom and, (ii) - via the public sanity conjunct `bound ≤ s.bound`, the statement-level `R^lin` bound of the - extraction target (assembled statements have `s.bound = γ = bound`, so completeness is - unaffected). The concrete instantiation — the inner-outer commitment *without initial - decomposition* ([NOZ26] §4.5), collision discharged by `outputToModuleSIS_valid_of_verified` — - and the commitment reinterpretation at the next ring dimension used by the recursion handoff - (`Recursion/TraceHandoff.lean`) are Phase-G deliverables. - - **Sorried**: the CWSS theorem `lift_coordinateWiseSpecialSound` (Lemma 9's interpolation - extraction; consumes the F3 quotient-lift algebra and the F4.1 scalar-round assembly). + needs only binding), so `LiftCom` carries nothing but `{TCom, com}` over its shortness index. + Weak binding is **norm-conditioned**, hence that index: this chain instantiates + `Short := liftShort bound ρBound` at the *global* norm parameters, and the short-collision set + `LiftCom.Collision` — the target of the escape event `liftEscLocal` — reads it. `relLift` + therefore carries (i) `liftShort bound ρBound w̃` — feeding + both the collision argument and, (ii) via the public sanity conjunct `bound ≤ s.bound`, the + statement-level `R^lin` bound of the extraction target (assembled statements have + `s.bound = γ = bound`, so completeness is unaffected). The concrete instantiation — the + inner-outer commitment *without initial decomposition* ([NOZ26] §4.5), collision discharged by + `outputToModuleSIS_valid_of_verified` — and the commitment reinterpretation at the next ring + dimension used by the recursion handoff (`Recursion/TraceHandoff.lean`) are Phase-G deliverables. + + **Sorried**: the extraction algorithm `liftExtractor` and the CWSS theorem + `lift_coordinateWiseSpecialSoundWithEscape` (Lemma 9's interpolation extraction; consumes the F3 + quotient-lift algebra and the F4.1 scalar-round assembly). ## References @@ -69,7 +71,7 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.ScalarRoun variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} +variable {n μ : ℕ} /-- **The lifted witness** (Hachi Eq. (21), polynomial form): the `R^lin` witness `z` together with the per-row quotient polynomials `ρᵢ` of the `Zq[X]`-lift, with their structural degree @@ -92,29 +94,44 @@ def RhoShort (ρBound : ℕ) (ρ : Fin n → Polynomial (ZMod q)) : Prop := ∀ i k, ((ρ i).coeff k).valMinAbs.natAbs ≤ ρBound /-- The combined shortness predicate of the lifted witness — the norm side of `relLift`, and the -`Short` parameter of the abstract commitment `LiftCom` (weak binding is norm-conditioned, +`Short` index of the abstract commitment `LiftCom` (weak binding is norm-conditioned, [NOZ26] Lemma 7). -/ def liftShort (bound ρBound : ℕ) (w : LiftedWitness Φ μ n) : Prop := vecLInftyNorm Φ w.z ≤ bound ∧ RhoShort ρBound w.ρ /-- **Abstract binding commitment** for the lifted witness (design G2: abstract in F4; -instantiated by the §4.5 inner-outer commitment without initial decomposition in Phase G). -`collision_mem` is the weak-binding axiom: two distinct *short* openings of the same commitment -yield a valid escape (concretely, a Module-SIS solution via [NOZ26] Lemma 7 / -`outputToModuleSIS_valid_of_verified`). The shortness conditioning is load-bearing: Ajtai-style -commitments are only binding on short openings. -/ -structure LiftCom (W E : Type) (Short : W → Prop) where +instantiated by the §4.5 inner-outer commitment without initial decomposition in Phase G). Lemma 9 +needs nothing but the commitment map itself: weak binding enters as the *escape event* +`liftEscLocal` below — "this transcript tree exhibits two distinct short openings of the shared `t`" +— which is a break of the fixed key by [NOZ26] Lemma 7 / +`outputToModuleSIS_valid_of_verified`. Since Ajtai-style commitments are only binding on short +openings, the structure is indexed by the shortness predicate `Short` that its short-collision set +`LiftCom.Collision` reads. -/ +structure LiftCom (W : Type) (Short : W → Prop) where /-- The commitment space (the wire type of Figure 4's first message). -/ TCom : Type /-- The (deterministic) commitment function. -/ com : W → TCom - /-- The escape set: valid cryptographic break artifacts (statement-independent, design G1). -/ - esc : Set E - /-- The escape produced from a commitment collision. -/ - escOfCollision : W → W → E - /-- Weak binding: a collision of two distinct short openings is a valid escape. -/ - collision_mem : ∀ w w', w ≠ w' → com w = com w' → Short w → Short w' → - escOfCollision w w' ∈ esc + +namespace LiftCom + +variable {W : Type} {Short : W → Prop} + +/-- The **short-collision set** of the commitment: pairs of distinct `Short` openings that collide. +By weak binding ([NOZ26] Lemma 7) an element of this set is a Module-SIS solution for the fixed key, +so it is the hardness target the lift's escape event points at; taking `Short` from the structure's +own index keeps an event from being stated at a mismatched shortness regime. Note this set is +nonempty for every compressing commitment, which is why exhibiting a member has to be an *event on +the transcript tree* (`liftEscLocal`) rather than an extractor output. -/ +def Collision (K : LiftCom W Short) : Set (W × W) := + {p | p.1 ≠ p.2 ∧ K.com p.1 = K.com p.2 ∧ Short p.1 ∧ Short p.2} + +/-- Membership in the short-collision set, unfolded. -/ +theorem mem_Collision (K : LiftCom W Short) (w w' : W) : + (w, w') ∈ K.Collision ↔ + w ≠ w' ∧ K.com w = K.com w' ∧ Short w ∧ Short w' := Iff.rfl + +end LiftCom variable {F : Type} [Field F] (bound ρBound : ℕ) @@ -142,7 +159,7 @@ i.e. `∑ⱼ Mᵢⱼ(α)·zⱼ(α) = yᵢ(α) + (α^d + 1)·ρᵢ(α)`; and `w̃ Lemma 9's extraction. The final conjunct `bound ≤ s.bound` is the public sanity condition tying the global norm parameter to the statement's declared `R^lin` bound (see the module docstring). -/ -def relLift (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relLift (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : Set (LiftStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := {p | @@ -153,17 +170,10 @@ def relLift (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) liftShort Φ bound ρBound p.2 ∧ bound ≤ p.1.1.bound} -/-- Escape-threaded lift relation — the seam consumed by the batching bridge -(`ZeroCheck/Batch.lean`). -/ -def relLiftE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) : - Set (LiftStatement Φ K.TCom F n μ × (LiftedWitness Φ μ n ⊕ E)) := - (relLift Φ bound ρBound K φF).withEscape K.esc - section Protocol variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} -variable (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +variable (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) /-- The lift's verifier (Hachi Figure 4): a **pure pass-through** extending the statement by the @@ -200,46 +210,82 @@ def liftProver (WitIn : Type) variable [SampleableType F] -/-- **Hachi Lemma 9 (skeleton): CWSS of the HMZ25 lift at `k = 2d`.** +/-- **The lift's escape event, in local (per-family) form** — the `escLocal` argument of +`ScalarRound.escEventScalar`: at a shared commitment `t` and `k` branch witnesses, two branches +carry **distinct short openings of `t`**. + +Against the escape-event contract (`ChallengeTree.EscapeEvent`): the event exhibits a member of +`K.Collision`, i.e. a commitment collision on two short openings — a Module-SIS solution for the +fixed key by [NOZ26] Lemma 7 (`outputToModuleSIS_valid_of_verified`) — and does so unconditionally. +(`relLift`'s own `liftShort` conjunct would already give shortness on an accepting tree; the event +repeats it so the break needs no acceptance hypothesis.) It reads only the round-0 message `t` and +the branch witnesses, which the ambient `ScalarRound.escEventScalar` pins to the tree's own data +and to `relLift`. -/ +def liftEscLocal {k : ℕ} : + RlinStatement Φ n μ → K.TCom → (Fin k → F) → (Fin k → LiftedWitness Φ μ n) → Prop := + fun _ t _ resp => + ∃ j j', (resp j, resp j') ∈ K.Collision ∧ K.com (resp j) = t + +/-- **The Lemma 9 extraction algorithm (skeleton, F4.4).** + +**Sorried** — this def is the milestone's *algorithm*: `ScalarRound.treeExtractorScalar` at the +interpolation `mkWitness` of the plan on `lift_coordinateWiseSpecialSoundWithEscape`. -/ +noncomputable def liftExtractor (hd : 0 < Φ.φ.natDegree) (φF : ZMod q →+* F) : + Extractor.TreeBased (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ) + (pSpecScalar K.TCom F) + (CWSSStructure.toShape (scalarStructure (2 * Φ.φ.natDegree) (by omega))).arity := + sorry + +/-- **Hachi Lemma 9 (skeleton): escape-threaded CWSS of the HMZ25 lift at `k = 2d`, at the named +`liftExtractor`.** The relations are `relRlin` / `relLift`; the weak-binding failure mode is the +escape disjunct `ScalarRound.escEventScalar … liftEscLocal`. **Sorried (F4.4).** Extraction plan, case-faithful to the paper: -* if some branch's `relLiftE`-witness is an escape `.inr e`, pass it through; -* if two branches carry distinct openings `w ≠ w'` of the shared `t`, both are short - (`relLift`'s `liftShort` conjunct), so `K.collision_mem` yields the weak-binding escape; +* if two branches carry distinct openings `w ≠ w'` of the shared `t`, both short (`relLift`'s + `liftShort` conjunct), then `liftEscLocal` fires — take the left disjunct; * otherwise all `2d` branches share one `w̃`; for each row `i` the defect polynomial `rowSum − yᵢ.rep − φ·ρᵢ` (degree `≤ 2d − 2 < 2d` by `w̃.hρ` and representative degree bounds) vanishes at the `2d` pairwise-distinct challenges (`scalarStructure`'s injective family), hence is zero (F3 interpolation kernel); the `Zq[X]`-identities descend to `M z = y` over `Rq` (F3 quotient-witness lemma), and `liftShort` + `bound ≤ s.bound` give the `R^lin` norm conjunct — - `.inl w̃.z` lands in `relRlinE`. + `w̃.z` lands in `relRlin`. -Assembled via `coordinateWiseSpecialSound_of_mkWitness_scalar` (F4.1); `2 ≤ 2d` from -`hd : 0 < d`. No field-size hypothesis is needed for CWSS itself (an injective `2d`-family in `F` -is the tree's obligation; only knowledge-error accounting, out of scope, needs `2d ≤ |F|`). -/ -theorem lift_coordinateWiseSpecialSound +Assembled via `ScalarRound.coordinateWiseSpecialSoundWithEscape_of_mkWitness_scalar` (F4.1); +`2 ≤ 2d` from `hd : 0 < d`. No field-size hypothesis is needed for CWSS itself (an injective +`2d`-family in `F` is the tree's obligation; only knowledge-error accounting, out of scope, needs +`2d ≤ |F|`). -/ +theorem lift_coordinateWiseSpecialSoundWithEscape (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hd : 0 < Φ.φ.natDegree) : - (liftVerifier (oSpec := oSpec) Φ bound ρBound K).coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWithEscape init impl (scalarStructure (2 * Φ.φ.natDegree) (by omega)) - (relRlinE Φ (n := n) (μ := μ) K.esc) - (relLiftE Φ bound ρBound K φF) := by + (ScalarRound.escEventScalar (by omega) (relLift Φ bound ρBound K φF) + (liftEscLocal Φ bound ρBound K)) + (relRlin Φ (n := n) (μ := μ)) + (relLift Φ bound ρBound K φF) + (liftVerifier (oSpec := oSpec) Φ bound ρBound K) + (liftExtractor Φ bound ρBound K hd φF) := by sorry -/-- **The HMZ25 lift as a `CWSSPackage`** (Hachi [NOZ26] Figure 4 / Lemma 9): the two-round +/-- **The HMZ25 lift as an `EscapeCWSSPackage`** (Hachi [NOZ26] Figure 4 / Lemma 9): the two-round commit-then-challenge verifier with the plain-special-soundness structure at `k = 2d`, reducing -`relRlinE` to `relLiftE`. The certificate is the sorried `lift_coordinateWiseSpecialSound`. -/ -def liftPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) +`relRlin` to `relLift`. Its one escape-specific field is the weak-binding event `liftEscLocal`, +lifted to the tree by `ScalarRound.escEventScalar`. -/ +noncomputable def liftPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hd : 0 < Φ.φ.natDegree) : - CWSSPackage init impl - (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E) - (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + EscapeCWSSPackage init impl + (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ) + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) (pSpecScalar K.TCom F) where verifier := liftVerifier (oSpec := oSpec) Φ bound ρBound K struct := scalarStructure (2 * Φ.φ.natDegree) (by omega) - relIn := relRlinE Φ (n := n) (μ := μ) K.esc - relOut := relLiftE Φ bound ρBound K φF + relIn := relRlin Φ + relOut := relLift Φ bound ρBound K φF + esc := ScalarRound.escEventScalar (by omega) (relLift Φ bound ρBound K φF) + (liftEscLocal Φ bound ρBound K) isPure := ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ - isCWSS := lift_coordinateWiseSpecialSound Φ bound ρBound K φF init impl hd + extractor := liftExtractor Φ bound ρBound K hd φF + isCWSS := lift_coordinateWiseSpecialSoundWithEscape Φ bound ρBound K φF init impl hd end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean index cd9e30ac80..916d7925d3 100644 --- a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean @@ -3,7 +3,9 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.Escape +import ArkLib.Commitments.Functional.Hachi.QuadEval.Reduction +import ArkLib.ProofSystem.Component.ReduceClaim +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape /-! # Eq. (20) → `R^lin` adapter — skeleton (Hachi §4.3 entry; sumcheck-track milestone F2) @@ -29,13 +31,12 @@ import ArkLib.Commitments.Functional.Hachi.Escape `ReduceClaim` bridge realizing that reading. It is **statement reshaping only** — no soundness error, CWSS for any structure, pure verifier — assembled sorry-free from `ReduceClaim`; the **sorried** pieces are the assembly/unstacking functions (`rlinStmt`, `unstack`) and the - block-row equivalence pull-back (`mem_relOutE_of_relRlinE`) — pure index bookkeeping + block-row equivalence pull-back (`mem_relOut_of_relRlin`) — pure index bookkeeping (milestone F2.1/F2.2: `stackRows`/`pasteCols`/`finAppend` helpers plus the `tensorG`/`tensorG1`-as-matrix-rows rewriting lemmas over `QuadEval/Gadgets.lean`). - Seam discipline (design decision G6): this file's `relIn` **is** `relOutE` (the - escape-threaded Eq. (20) relation from `Escape.lean`), and its `relOut` - `relRlinE` is definitionally the next link's (`RingSwitch/Reduction.lean`) `relIn`. + Seam discipline (design decision G6): the package's public `relIn` **is** the Eq. (20) `relOut`, + and its public `relOut` is the next link's `relRlin`. ## References @@ -52,7 +53,7 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} -variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} {E : Type} +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- Column count `μ` of the Eq. (20) block system: the stacked witness `ζ = ŵ ++ (flatten t̂ ++ ẑ)`. Associativity fixed once, here (F2.2 convention pin). -/ @@ -79,20 +80,17 @@ structure RlinStatement (Φ : CyclotomicModulus (ZMod q)) (n μ : ℕ) where def relRlin {n μ : ℕ} : Set (RlinStatement Φ n μ × PolyVec (Rq Φ) μ) := {p | p.1.M *ᵥ p.2 = p.1.yvec ∧ vecLInftyNorm Φ p.2 ≤ p.1.bound} -/-- Escape-threaded `R^lin` relation — the §4.3 chain's second seam. -/ -def relRlinE {n μ : ℕ} (esc : Set E) : - Set (RlinStatement Φ n μ × (PolyVec (Rq Φ) μ ⊕ E)) := - (relRlin Φ).withEscape esc - /-- **Statement assembly** (the bridge's `mapStmt`): build the Eq. (20) block matrix and -right-hand side from `QuadEval`'s output statement `(stmt, v, c)` — rows c1–c5 as in the module -docstring, from `stmt.pp.dMatrix`/`stmt.pp.outerMatrix`/`stmt.pp.innerMatrix`, the bases +right-hand side from the fixed key `pp` and `QuadEval`'s output statement `(stmt, v, c)` — rows +c1–c5 as in the module docstring, from `pp.dMatrix`/`pp.outerMatrix`/`pp.innerMatrix`, the bases `stmt.bvec`/`stmt.avec`, the carrier commitment `v`, the challenges `c`, and the gadget matrices `gadgetMatrix`/`jMatrix` (`QuadEval/Gadgets.lean`); right-hand side `(v, u, y, 0, 0)`; `bound := γ`. **Sorried (F2.2)**: needs the `stackRows`/`pasteCols` block-matrix helpers (F2.1). -/ -def rlinStmt (base : ZMod q) (ω γ : ℕ) +def rlinStmt + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω γ : ℕ) (X : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) : @@ -113,46 +111,52 @@ def unstack /-- **Block-row equivalence pull-back** (the bridge's `hRel`; the substance of F2): an `R^lin` witness at the assembled statement `rlinStmt base ω γ X` un-stacks to an Eq. (20)-valid `QuadEvalResponse` at `X` — c1–c5 are the five block rows of `M ζ = y`, c6 is the norm conjunct -split along the stacking. Escapes pass through. +split along the stacking. **Sorried (F2.2)**: `matVecMul`-over-`stackRows`/`pasteCols` splits `M ζ = yvec` into the five component equations; c3/c4 via `dot`-associativity and a `tensorG1`-as-row lemma; c5 via a `tensorG`-as-matrix lemma plus `matVecMul` composition for `A·J`; the norm conjunct by a `vecLInftyNorm`-over-append lemma (`max ≤ γ ↔` three `≤ γ`). -/ -theorem mem_relOutE_of_relRlinE (base : ZMod q) (ω γ : ℕ) (esc : Set E) +theorem mem_relOut_of_relRlin + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω γ : ℕ) (X : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) - (w : PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r) ⊕ E) - (h : (rlinStmt (zDigits := zDigits) Φ base ω γ X, w) ∈ relRlinE Φ esc) : - (X, w.map (unstack Φ) id) ∈ relOutE (zDigits := zDigits) Φ base ω γ esc := by + (w : PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r)) + (h : (rlinStmt (zDigits := zDigits) Φ pp base ω γ X, w) ∈ relRlin Φ) : + (X, unstack Φ w) ∈ relOut (zDigits := zDigits) Φ pp base ω γ := by sorry -/-- **The `R^lin` adapter as a `CWSSPackage`** (Hachi [NOZ26] §4.3 entry): the zero-round -`ReduceClaim` head `rlinStmt` with the empty challenge structure, reducing the escape-threaded -Eq. (20) relation `relOutE` to `relRlinE`. Assembled sorry-free from -`ReduceClaim.verifier_coordinateWiseSpecialSound`; all remaining work lives in the sorried -`rlinStmt`/`unstack`/`mem_relOutE_of_relRlinE`. -/ -def rlinPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (base : ZMod q) (ω γ : ℕ) (esc : Set E) : +/-- **The `R^lin` adapter as a (plain) `CWSSPackage`** (Hachi [NOZ26] §4.3 entry): the zero-round +`ReduceClaim` head `rlinStmt` with the empty challenge structure, reducing `relOut` to `relRlin`. +Pure statement reshaping with no cryptographic content, hence escape-free. Assembled from +`ReduceClaim.verifier_coordinateWiseSpecialSoundWith`; all remaining work lives in the sorried +`rlinStmt`/`unstack`/`mem_relOut_of_relRlin`. -/ +noncomputable def rlinPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) (base : ZMod q) (ω γ : ℕ) : CWSSPackage init impl (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) - (QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits ⊕ E) + (QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) (RlinStatement Φ (rlinRows innerRows outerRows dRows) (rlinCols innerRows messageDigits innerDigits zDigits m r)) - (PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r) ⊕ E) + (PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r)) (!p[] : ProtocolSpec 0) where - verifier := ReduceClaim.verifier oSpec (rlinStmt (zDigits := zDigits) Φ base ω γ) + verifier := ReduceClaim.verifier oSpec (rlinStmt (zDigits := zDigits) Φ pp base ω γ) struct := CWSSStructure.ofIsEmpty - relIn := relOutE (zDigits := zDigits) Φ base ω γ esc - relOut := relRlinE Φ esc - isPure := ⟨fun stmt _ => rlinStmt (zDigits := zDigits) Φ base ω γ stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relOutE (zDigits := zDigits) Φ base ω γ esc) - (relOut := relRlinE Φ esc) - (mapWitInv := fun _ w => w.map (unstack Φ) id) (D := CWSSStructure.ofIsEmpty) - (mem_relOutE_of_relRlinE Φ base ω γ esc) + relIn := relOut (zDigits := zDigits) Φ pp base ω γ + relOut := relRlin Φ + isPure := ⟨fun stmt _ => rlinStmt (zDigits := zDigits) Φ pp base ω γ stmt, fun _ _ => rfl⟩ + extractor := ReduceClaim.treeExtractor + (mapStmt := rlinStmt (zDigits := zDigits) Φ pp base ω γ) + (relRlin Φ) (fun _ w => unstack Φ w) CWSSStructure.ofIsEmpty + isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := relOut (zDigits := zDigits) Φ pp base ω γ) + (relOut := relRlin Φ) + (mapWitInv := fun _ w => unstack Φ w) (D := CWSSStructure.ofIsEmpty) + (fun X w h => mem_relOut_of_relRlin Φ pp base ω γ X w h) end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Basic.lean similarity index 87% rename from ArkLib/Commitments/Functional/Hachi/Sumcheck.lean rename to ArkLib/Commitments/Functional/Hachi/Sumcheck/Basic.lean index bd12af36ee..be87f43497 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Basic.lean @@ -8,7 +8,8 @@ import ArkLib.Commitments.Functional.Hachi.Sumcheck.FinalEval /-! # Hachi Sumcheck Loop (Figure 6 / Lemma 11 + Figure 7 tail) -Umbrella for `Hachi/Sumcheck/`: the sumcheck loop that finishes Hachi's [NOZ26, §4.3] opening. It +Umbrella module for `Hachi/Sumcheck/`: the sumcheck loop that finishes Hachi's [NOZ26, §4.3] +opening. It reduces the zero-check's point-evaluation claims `H₀(τ₀) = 0 ∧ H_α(τ_α) = 0` to hypercube-sum claims, runs `m₀` sumcheck rounds down to a single evaluation of the committed table `w̃`, and closes with the final-evaluation tail that hands the resulting evaluation claim to the §4.5 @@ -37,15 +38,15 @@ record-then-bridge convention reconciled with the structured round's `![...]` RB protocols" step. Pure reshaping through the batching identities. * `Sumcheck/Rounds.lean` — **Hachi Figure 6 / Lemma 11**: the `m₀`-round paired sumcheck loop (each round sends the univariate pair `(gᵢ⁽⁰⁾, gᵢ⁽ᵅ⁾)` under a shared challenge `aᵢ`), with - **guarded** round verifiers (`gᵢ(0)+gᵢ(1) = targetᵢ₋₁`), composed by recursion over the guarded - append `▷ᵍ`. CWSS theorem `round_coordinateWiseSpecialSound` (**sorried**). + **guarded** round verifiers (`gᵢ(0)+gᵢ(1) = targetᵢ₋₁`), composed by recursion over the binary + guarded append. CWSS theorem `round_coordinateWiseSpecialSoundWith` (**sorried**). * `Sumcheck/FinalEval.lean` — **Hachi Figure 7 tail**: the closing step — the prover sends the claimed evaluation `y′ = w̃(a)`, the guarded verifier checks the two final sumcheck targets, and the output is the evaluation claim `mle[w̃](a) = y′` consumed by the `Recursion/` adapters. -This umbrella re-exports the folder (`FinalEval` transitively imports `Rounds` and `Bridge`). Its -output relation `relWEvalE` is the input of the §4.5 recursion; the full chain (guarded tail -included) is composed in `Composition.lean`. +This umbrella re-exports the folder (`FinalEval` transitively imports `Rounds` and `Bridge`). +The plain output relation `relWEvalClaim` is the input of the §4.5 recursion; the full chain, +including its guarded tail, is composed in `Composition.lean`. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean index dfe2b52cf6..8e5da30236 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean @@ -11,9 +11,9 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction Zero-round bridge from the zero-check's *point-evaluation* claims to the *initial sumcheck* claims consumed by the round loop ([NOZ26] §4.3, "finish the proof using sumcheck protocols"): - * `relIn = relZeroCheckE` — `H₀^{w̃}(τ₀) = 0 ∧ H_α^{w̃}(τ_α) = 0` at the derived Kronecker + * `relIn = relZeroCheck` — `H₀^{w̃}(τ₀) = 0 ∧ H_α^{w̃}(τ_α) = 0` at the derived Kronecker points; - * `relOut = roundRelE 0` — `∑_{x ∈ {0,1}^{m₀}} F_{0,τ₀}(x) = 0` and + * `relOut = roundRel 0` — `∑_{x ∈ {0,1}^{m₀}} F_{0,τ₀}(x) = 0` and `∑_{x ∈ {0,1}^{m₀}} F_{α,τ_α}(x) = a`, where the initial linear target `a := zcTargetAlpha = ∑ᵢ eq̃(τ_α, i)·ŷᵢ(α)` is computed by the verifier from the statement alone. @@ -37,7 +37,7 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -49,39 +49,41 @@ noncomputable def toRoundStatement {TCom : Type} (φF : ZMod q →+* F) /-- **Sum-to-point pull-back** (the bridge's `hRel`): the initial hypercube-sum claims at the installed targets imply the zero-check's point-evaluation claims, through the batching -identities `∑ F_{0,τ₀} = H₀(τ₀)` and `∑ F_{α,τ_α} = H_α(τ_α) + zcTargetAlpha`. Escapes pass -through; the bound-sanity conjunct is shared verbatim. +identities `∑ F_{0,τ₀} = H₀(τ₀)` and `∑ F_{α,τ_α} = H_α(τ_α) + zcTargetAlpha`. The bound-sanity +conjunct is shared verbatim. **Sorried** (a corollary of the sorried F5 identities `sum_sumcheckPolyZero` / `sum_sumcheckPolyAlpha`, plus `challenges`-uniqueness `Fin 0 → F`). -/ -theorem mem_relZeroCheckE_of_roundRelE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +theorem mem_relZeroCheck_of_roundRel + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) - (s : ZeroCheckStatement Φ K.TCom F n μ) (w : LiftedWitness Φ μ n ⊕ E) - (h : (toRoundStatement Φ m₁ φF s, w) ∈ roundRelE Φ m₀ m₁ bound ρBound K φF b 0) : - (s, w) ∈ relZeroCheckE Φ m₀ m₁ bound ρBound K φF b := by + (s : ZeroCheckStatement Φ K.TCom F n μ) (w : LiftedWitness Φ μ n) + (h : (toRoundStatement Φ m₁ φF s, w) ∈ roundRel Φ m₀ m₁ bound ρBound K φF b 0) : + (s, w) ∈ relZeroCheck Φ m₀ m₁ bound ρBound K φF b := by sorry -/-- **The sumcheck bridge as a `CWSSPackage`**: zero-round `ReduceClaim` at -`mapStmt := toRoundStatement`, reducing `relZeroCheckE` to the round-`0` seam `roundRelE 0` -with no soundness error. -/ +/-- **The sumcheck bridge as a (plain) `CWSSPackage`**: zero-round `ReduceClaim` at +`mapStmt := toRoundStatement`, reducing `relZeroCheck` to the round-`0` `roundRel` with no soundness +error, hence escape-free. -/ noncomputable def sumcheckBridgePackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : CWSSPackage init impl - (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) - (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n ⊕ E) + (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) + (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n) (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec (toRoundStatement Φ m₁ φF) struct := CWSSStructure.ofIsEmpty - relIn := relZeroCheckE Φ m₀ m₁ bound ρBound K φF b - relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b 0 + relIn := relZeroCheck Φ m₀ m₁ bound ρBound K φF b + relOut := roundRel Φ m₀ m₁ bound ρBound K φF b 0 isPure := ⟨fun stmt _ => toRoundStatement Φ m₁ φF stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) - (relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b 0) + extractor := ReduceClaim.treeExtractor (mapStmt := toRoundStatement Φ m₁ φF) + (roundRel Φ m₀ m₁ bound ρBound K φF b 0) (fun _ w => w) CWSSStructure.ofIsEmpty + isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := relZeroCheck Φ m₀ m₁ bound ρBound K φF b) + (relOut := roundRel Φ m₀ m₁ bound ρBound K φF b 0) (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (mem_relZeroCheckE_of_roundRelE Φ m₀ m₁ bound ρBound K φF b) + (fun s w h => mem_relZeroCheck_of_roundRel Φ m₀ m₁ bound ρBound K φF b s w h) end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean index b1b41506ec..33b77001c1 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean @@ -24,8 +24,8 @@ import ArkLib.Commitments.Functional.Hachi.Sumcheck.Rounds `a`, and the claimed value `y′` — the recursion currency (`mle[w̃](a) = y′` for the committed `w̃`), consumed by the `Recursion/` adapters. - Extraction (sorried): from a `relWEvalClaimE`-witness (an opening `w̃` of `t` with - `mle[w̃](a) = y′`, or an escape) and the **guard facts** (available from acceptance on a + Extraction (sorried): from a `relWEvalClaim`-witness (an opening `w̃` of `t` with + `mle[w̃](a) = y′`) and the **guard facts** (available from acceptance on a guarded verifier), the two final-round point-evaluation claims of `roundRel m₀` follow by computing `F_{0,τ₀}(a)` and `F_{α,τ_α}(a)` through `mle[w̃](a) = y′` — the evaluation factorizations of the (sorried F5) sumcheck polynomials. @@ -69,7 +69,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -121,57 +121,69 @@ def finalEvalProver {TCom : Type} /-- **The evaluation-claim relation** — the §4.3 chain's final seam and the recursion's input: `w̃` opens `t` and its table's multilinear extension evaluates to the claimed value at the point. -/ -def relWEvalClaim (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relWEvalClaim (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : Set (WEvalStatement K.TCom F m₀ × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.t ∧ wTableMleEval Φ m₀ φF b p.2 p.1.point = p.1.value} -/-- Escape-threaded evaluation-claim relation. -/ -def relWEvalClaimE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) : - Set (WEvalStatement K.TCom F m₀ × (LiftedWitness Φ μ n ⊕ E)) := - (relWEvalClaim Φ m₀ bound ρBound b K φF).withEscape K.esc - variable [SampleableType F] -/-- **CWSS of the final-evaluation step (skeleton, F8).** +/-- **The final-evaluation extraction algorithm (skeleton, F8).** + +**Sorried** — this def is the milestone's *algorithm* (the transcript-level pull-back of the proof +plan on `finalEval_coordinateWiseSpecialSoundWith`). -/ +noncomputable def finalEvalExtractor + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) : + Extractor.TreeBased (RoundStatement Φ K.TCom F n μ m₀) (LiftedWitness Φ μ n) + (pSpecFinalEval F) + (CWSSStructure.toShape (CWSSStructure.ofIsEmpty + (pSpec := pSpecFinalEval F))).arity := + sorry + +/-- **CWSS of the final-evaluation step (skeleton, F8), at the named `finalEvalExtractor`** +(the named form is deliberate — see `Verifier.treeSpecialSoundWith`; filling F8 means filling +the extractor and this specification about it). **Sorried.** Proof plan: the protocol has no challenge round, so CWSS collapses (via the probability-phrased no-challenge bridge, which already tolerates rejecting verifiers) to a transcript-level extraction: acceptance forces `finalCheck = true` (the guarded rejection -lemma, B4.1) and yields a `relWEvalClaimE`-witness; on the real branch, evaluate the two -sumcheck polynomials at the point through `mle[w̃](a) = y′` and the guard's target equations to -recover `roundRel m₀`'s point claims (the round-`m₀` `hypercubeSum` is the plain evaluation); -the bound-sanity conjunct is re-supplied by the guard; escapes pass through. -/ -theorem finalEval_coordinateWiseSpecialSound +lemma, B4.1) and yields a `relWEvalClaim`-witness; evaluate the two sumcheck polynomials at the +point through `mle[w̃](a) = y′` and the guard's target equations to recover `roundRel m₀`'s point +claims (the round-`m₀` `hypercubeSum` is the plain evaluation); the bound-sanity conjunct is +re-supplied by the guard. -/ +theorem finalEval_coordinateWiseSpecialSoundWith (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : - (finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (TCom := K.TCom) - φF).coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWith init impl CWSSStructure.ofIsEmpty - (roundRelE Φ m₀ m₁ bound ρBound K φF b m₀) - (relWEvalClaimE Φ m₀ bound ρBound b K φF) := by + (roundRel Φ m₀ m₁ bound ρBound K φF b m₀) + (relWEvalClaim Φ m₀ bound ρBound b K φF) + (finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (TCom := K.TCom) φF) + (finalEvalExtractor Φ m₀ bound ρBound K φF) := by sorry -/-- **The final-evaluation step as a guarded package** (`GCWSSPackage`): the guarded one-message -verifier with the empty challenge structure, reducing the round-`m₀` seam to the evaluation -claim `relWEvalClaimE`. Certificate: the sorried `finalEval_coordinateWiseSpecialSound`. -/ -def finalEvalPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- **The final-evaluation step as a guarded `GCWSSPackage`**: the guarded one-message verifier with +the empty challenge structure, reducing the round-`m₀` seam to the evaluation claim `relWEvalClaim`. +A guarded *re-reading* of the final targets, hence escape-free. Certificate: the sorried +`finalEval_coordinateWiseSpecialSoundWith`. -/ +noncomputable def finalEvalPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : GCWSSPackage init impl - (RoundStatement Φ K.TCom F n μ m₀) (LiftedWitness Φ μ n ⊕ E) - (WEvalStatement K.TCom F m₀) (LiftedWitness Φ μ n ⊕ E) + (RoundStatement Φ K.TCom F n μ m₀) (LiftedWitness Φ μ n) + (WEvalStatement K.TCom F m₀) (LiftedWitness Φ μ n) (pSpecFinalEval F) where verifier := finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (TCom := K.TCom) φF struct := CWSSStructure.ofIsEmpty - relIn := roundRelE Φ m₀ m₁ bound ρBound K φF b m₀ - relOut := relWEvalClaimE Φ m₀ bound ρBound b K φF + relIn := roundRel Φ m₀ m₁ bound ρBound K φF b m₀ + relOut := relWEvalClaim Φ m₀ bound ρBound b K φF isGuarded := finalEvalVerifier_isGuarded Φ m₀ m₁ bound b φF - isCWSS := finalEval_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound b init impl K φF + extractor := finalEvalExtractor Φ m₀ bound ρBound K φF + isCWSS := finalEval_coordinateWiseSpecialSoundWith Φ m₀ m₁ bound ρBound b init impl K φF end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean index cc181915c8..2cb5fd23bd 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean @@ -31,18 +31,23 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded ## Per-round soundness (Lemma 11) and the loop Per-round CWSS at `k = max (2b) 2 + 1` (plain special soundness, `scalarStructure`): the - branches of a tree node share the message pair; either two branch witnesses differ (binding - escape via `K.collision_mem`) or the shared `w̃` makes + branches of a tree node share the message pair; either two branch witnesses differ (the + weak-binding **escape event** `roundEsc`, pointing at `LiftCom.Collision`) or the shared `w̃` + makes `T ↦ ∑_{x} H(prefix, T, x) − g_i(T)` a degree-`≤ deg` polynomial with `deg + 1` distinct roots, hence zero; evaluating at `0, 1` and summing, the **guard's** `g_i(0) + g_i(1) = target_{i−1}` recovers the previous round's claim. (The guard fact is available to the round's own extraction: acceptance probability `1` on a guarded verifier forces `check = true`.) - The loop is composed by **recursion over the binary guarded append `▷ᵍ`** - (`roundsChain count = roundsChain (count−1) ▷ᵍ roundPackage (count−1)`, base = the identity + The loop is composed by **recursion over the binary guarded append** + (`roundsChain count = roundsChain (count−1) ▷ roundPackage (count−1)`, base = the identity package), so the only composition machinery it consumes is `Guarded.lean`'s B4 skeleton. - **Sorried**: the per-round CWSS theorem `round_coordinateWiseSpecialSound` (Lemma 11). +The loop's recursion pins the relation seams (`roundsChain_relIn` / `roundsChain_relOut`); the + composed escape event is assembled by `ChallengeTree.EscapeEvent.append`. + + **Sorried**: the per-round extraction algorithm `roundExtractor` and the CWSS theorem + `round_coordinateWiseSpecialSoundWithEscape` (Lemma 11). ## References @@ -88,7 +93,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] [DecidableEq F] +variable {n μ : ℕ} {F : Type} [Field F] [DecidableEq F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -150,13 +155,59 @@ def roundProver {TCom : Type} (i : ℕ) variable [SampleableType F] +/-- Validity `2 ≤ k` of the round's soundness parameter `k = max (2b) 2 + 1`, named once so that the +extractor, the escape event and the certificate below are pinned to the *same* structure (and hence +the same arity). -/ +theorem round_two_le_k : 2 ≤ max (roundDegZero b) roundDegAlpha + 1 := by + have := Nat.le_max_right (roundDegZero b) roundDegAlpha + unfold roundDegAlpha at *; omega + +/-- **The Lemma 11 per-round escape event**: the tree's own message pair and challenge family admit +per-branch `roundRel (i+1)`-responses — at the branch's *guard output* statement, since the round +verifier replaces the targets rather than extending the statement, hence the `…OfValid` form — among +which two are **distinct short openings of the shared commitment** `stmt.zc.t`, i.e. a member of +`LiftCom.Collision` and so a Module-SIS break of the fixed key by [NOZ26] Lemma 7. + +Against the escape-event contract (`ChallengeTree.EscapeEvent`): the collision conjunct is an +unconditional break at every `(statement, tree)`, and the event reads only the statement and the +tree (via `ScalarRound`'s readers), with responses pinned to the **output** relation, which keeps +it tight. It does mention the guard's output map, which is plain data — the same map the composed +`ChallengeTree.EscapeEvent.append` uses. -/ +def roundEsc + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) (i : ℕ) : + ChallengeTree.EscapeEvent (RoundStatement Φ K.TCom F n μ i) (pSpecScalar (RoundMsg F b) F) + (CWSSStructure.toShape + (scalarStructure (max (roundDegZero b) roundDegAlpha + 1) (round_two_le_k b))).arity := + ScalarRound.escEventScalarOfValid (round_two_le_k b) + (fun stmt g fam j w => + (⟨stmt.zc, Fin.snoc stmt.challenges (fam j), g.1.1.eval (fam j), g.2.1.eval (fam j)⟩, w) ∈ + roundRel Φ m₀ m₁ bound ρBound K φF b (i + 1)) + (fun _ _ _ resp => ∃ j j', (resp j, resp j') ∈ K.Collision) + +/-- **The Lemma 11 per-round extraction algorithm (skeleton, F7).** + +**Sorried** — this def is the milestone's *algorithm* (the case split of the extraction plan on +`round_coordinateWiseSpecialSoundWithEscape`, ultimately the guarded scalar-round engine's +`ScalarRound.treeExtractorScalar`). -/ +noncomputable def roundExtractor + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) (i : ℕ) : + Extractor.TreeBased (RoundStatement Φ K.TCom F n μ i) (LiftedWitness Φ μ n) + (pSpecScalar (RoundMsg F b) F) + (CWSSStructure.toShape + (scalarStructure (max (roundDegZero b) roundDegAlpha + 1) (round_two_le_k b))).arity := + sorry + /-- **Hachi Lemma 11 (skeleton): per-round CWSS of the paired sumcheck round at -`k = max (2b) 2 + 1`.** +`k = max (2b) 2 + 1`, at the named `roundExtractor`** (the named form is deliberate — see +`Verifier.treeSpecialSoundWith`; filling F7 means filling the extractor and this specification +about it). **Sorried (F7).** Extraction plan (Lemma 11, case-faithful): the `k` accepting branches of a tree node share the message pair `(g^{(0)}, g^{(α)})` and carry pairwise-distinct challenges -(`scalarStructure`'s injective family); escapes and differing openings pass through resp. hit -`K.collision_mem`; otherwise the shared `w̃` makes both defect polynomials +(`scalarStructure`'s injective family); if two branch openings differ, `roundEsc` fires (take the +left disjunct); otherwise the shared `w̃` makes both defect polynomials `T ↦ hypercubeSum H (i+1) (snoc prefix T) − g(T)` (degrees `≤ 2b` resp. `≤ 2`) vanish at `k` distinct points, hence identically; evaluating at `0, 1`, summing, and using the **guard fact** `roundCheck = true` (available from acceptance on a guarded verifier) recovers the round-`i` @@ -168,71 +219,79 @@ round (`ArkLib/ProofSystem/Sumcheck/Structured`) rather than the bespoke `roundV its CWSS discharged by the (to-be-built, wire-format-generic / guarded) analog of the scalar-round engine applied to `Structured.roundOracleVerifier`, with the round relations read off `Structured.sumcheckConsistencyProp` / `computeRoundPoly`. The verifier wiring is left `sorry` for -now pending that reconciliation (see the `Sumcheck.lean` umbrella). -/ -theorem round_coordinateWiseSpecialSound +now pending that reconciliation (see the `Sumcheck/Basic.lean` umbrella). -/ +theorem round_coordinateWiseSpecialSoundWithEscape (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (i : ℕ) : - (roundVerifier (oSpec := oSpec) Φ b (TCom := K.TCom) - i).coordinateWiseSpecialSound init impl - (scalarStructure (max (roundDegZero b) roundDegAlpha + 1) - (by have := Nat.le_max_right (roundDegZero b) roundDegAlpha - unfold roundDegAlpha at *; omega)) - (roundRelE Φ m₀ m₁ bound ρBound K φF b i) - (roundRelE Φ m₀ m₁ bound ρBound K φF b (i + 1)) := by + Verifier.coordinateWiseSpecialSoundWithEscape init impl + (scalarStructure (max (roundDegZero b) roundDegAlpha + 1) (round_two_le_k b)) + (roundEsc Φ m₀ m₁ bound ρBound b K φF i) + (roundRel Φ m₀ m₁ bound ρBound K φF b i) + (roundRel Φ m₀ m₁ bound ρBound K φF b (i + 1)) + (roundVerifier (oSpec := oSpec) Φ b (TCom := K.TCom) i) + (roundExtractor Φ bound ρBound b K φF i) := by sorry -/-- The `i`-th paired sumcheck round as a **guarded** package (`GCWSSPackage`): the guarded -round verifier with the `k = max (2b) 2 + 1` plain-special-soundness structure, reducing the -round-`i` seam to the round-`(i+1)` seam. Certificate: the sorried -`round_coordinateWiseSpecialSound` (Lemma 11). -/ -def roundPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- The `i`-th paired sumcheck round as a guarded `EscapeGCWSSPackage`: the guarded round verifier +with the `k = max (2b) 2 + 1` plain-special-soundness structure, reducing the round-`i` seam to the +round-`(i+1)` seam, with the weak-binding event `roundEsc` as its one escape-specific field. +Certificate: the sorried `round_coordinateWiseSpecialSoundWithEscape` (Lemma 11). -/ +noncomputable def roundPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (i : ℕ) : - GCWSSPackage init impl - (RoundStatement Φ K.TCom F n μ i) (LiftedWitness Φ μ n ⊕ E) - (RoundStatement Φ K.TCom F n μ (i + 1)) (LiftedWitness Φ μ n ⊕ E) + EscapeGCWSSPackage init impl + (RoundStatement Φ K.TCom F n μ i) (LiftedWitness Φ μ n) + (RoundStatement Φ K.TCom F n μ (i + 1)) (LiftedWitness Φ μ n) (pSpecScalar (RoundMsg F b) F) where verifier := roundVerifier (oSpec := oSpec) Φ b (TCom := K.TCom) i - struct := scalarStructure (max (roundDegZero b) roundDegAlpha + 1) - (by have := Nat.le_max_right (roundDegZero b) roundDegAlpha - unfold roundDegAlpha at *; omega) - relIn := roundRelE Φ m₀ m₁ bound ρBound K φF b i - relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b (i + 1) + struct := scalarStructure (max (roundDegZero b) roundDegAlpha + 1) (round_two_le_k b) + relIn := roundRel Φ m₀ m₁ bound ρBound K φF b i + relOut := roundRel Φ m₀ m₁ bound ρBound K φF b (i + 1) + esc := roundEsc Φ m₀ m₁ bound ρBound b K φF i isGuarded := roundVerifier_isGuarded Φ b i - isCWSS := round_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound b init impl K φF i + extractor := roundExtractor Φ bound ρBound b K φF i + isCWSS := round_coordinateWiseSpecialSoundWithEscape Φ m₀ m₁ bound ρBound b init impl K φF i /-- The empty round loop has no challenges. -/ instance : IsEmpty (roundsSpec F b 0).ChallengeIdx := ⟨fun i => Fin.elim0 i.1⟩ /-- **The composed sumcheck loop, with its seam invariant** (Hachi Figure 7's round phase): -`count` paired rounds chained by recursion over the binary guarded append `▷ᵍ` (base case: the +`count` paired rounds chained by recursion over the binary guarded append (base case: the zero-round identity package), together with the proofs that the composite's `relIn`/`relOut` are the round-`0`/round-`count` seam relations — the recursion's seams are definitional only -*per instance*, not for an open `count`, so the invariant must ride along. -/ +*per instance*, not for an open `count`, so the invariant must ride along. + +Only the relation seams need pinning; the composite's event is whatever the recursion built — a +nested disjunction of the per-round `roundEsc`s, each at its own subtree. -/ noncomputable def roundsChainAux (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) : (count : ℕ) → - { P : GCWSSPackage init impl - (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n ⊕ E) - (RoundStatement Φ K.TCom F n μ count) (LiftedWitness Φ μ n ⊕ E) + { P : EscapeGCWSSPackage init impl + (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n) + (RoundStatement Φ K.TCom F n μ count) (LiftedWitness Φ μ n) (roundsSpec F b count) // - P.relIn = roundRelE Φ m₀ m₁ bound ρBound K φF b 0 ∧ - P.relOut = roundRelE Φ m₀ m₁ bound ρBound K φF b count } + P.relIn = roundRel Φ m₀ m₁ bound ρBound K φF b 0 ∧ + P.relOut = roundRel Φ m₀ m₁ bound ρBound K φF b count } | 0 => - ⟨CWSSPackage.toGuarded + ⟨EscapeCWSSPackage.toGuarded { verifier := ReduceClaim.verifier oSpec id struct := CWSSStructure.ofIsEmpty - relIn := roundRelE Φ m₀ m₁ bound ρBound K φF b 0 - relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b 0 + relIn := roundRel Φ m₀ m₁ bound ρBound K φF b 0 + relOut := roundRel Φ m₀ m₁ bound ρBound K φF b 0 + esc := fun _ _ => False isPure := ⟨fun stmt _ => stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := roundRelE Φ m₀ m₁ bound ρBound K φF b 0) - (relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b 0) - (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (fun _ _ h => h) }, + extractor := ReduceClaim.treeExtractor (mapStmt := id) + (roundRel Φ m₀ m₁ bound ρBound K φF b 0) (fun _ w => w) + CWSSStructure.ofIsEmpty + isCWSS := Verifier.coordinateWiseSpecialSoundWith.withEscape init impl _ + (ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := roundRel Φ m₀ m₁ bound ρBound K φF b 0) + (relOut := roundRel Φ m₀ m₁ bound ρBound K φF b 0) + (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) + (fun _ _ h => h)) }, rfl, rfl⟩ | count + 1 => let prev := roundsChainAux init impl K φF count @@ -243,30 +302,30 @@ noncomputable def roundsChainAux (init : ProbComp σ) (installed by the sumcheck bridge) to the round-`count` seam (consumed by the final-evaluation step). Instantiated at `count := m₀` in the composition. -/ noncomputable def roundsChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (count : ℕ) : - GCWSSPackage init impl - (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n ⊕ E) - (RoundStatement Φ K.TCom F n μ count) (LiftedWitness Φ μ n ⊕ E) + EscapeGCWSSPackage init impl + (RoundStatement Φ K.TCom F n μ 0) (LiftedWitness Φ μ n) + (RoundStatement Φ K.TCom F n μ count) (LiftedWitness Φ μ n) (roundsSpec F b count) := (roundsChainAux Φ m₀ m₁ bound ρBound b init impl K φF count).1 /-- The loop's input seam is the round-`0` relation (the seam pin for composing after the sumcheck bridge). -/ theorem roundsChain_relIn (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (count : ℕ) : (roundsChain Φ m₀ m₁ bound ρBound b init impl K φF count).relIn = - roundRelE Φ m₀ m₁ bound ρBound K φF b 0 := + roundRel Φ m₀ m₁ bound ρBound K φF b 0 := (roundsChainAux Φ m₀ m₁ bound ρBound b init impl K φF count).2.1 /-- The loop's output seam is the round-`count` relation (the seam pin for composing with the final-evaluation step). -/ theorem roundsChain_relOut (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (count : ℕ) : (roundsChain Φ m₀ m₁ bound ρBound b init impl K φF count).relOut = - roundRelE Φ m₀ m₁ bound ρBound K φF b count := + roundRel Φ m₀ m₁ bound ρBound K φF b count := (roundsChainAux Φ m₀ m₁ bound ρBound b init impl K φF count).2.2 end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Basic.lean similarity index 85% rename from ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean rename to ArkLib/Commitments/Functional/Hachi/ZeroCheck/Basic.lean index e293809517..fc5803229f 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Basic.lean @@ -8,7 +8,8 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction /-! # Hachi Zero-Check (Figure 5 / corrected Lemma 10) -Umbrella for `Hachi/ZeroCheck/`: the batched-constraint encoding (Hachi [NOZ26] Eqs. (21)–(23)) +Umbrella module for `Hachi/ZeroCheck/`: the batched-constraint encoding (Hachi [NOZ26] +Eqs. (21)–(23)) and the zero-check subprotocol that reduces the two polynomial identities `H₀ ≡ 0 ∧ H_α ≡ 0` — the range constraints and the `α`-evaluated linear constraints, both `eq̃`-batched — to their evaluations at random points. @@ -19,7 +20,8 @@ Hachi's Lemma 10 (uniform-vector-challenge extraction) is **not provable as stat coordinate-wise star certifies only axis-cross vanishing, and for `m ≥ 2` that does not imply `H ≡ 0`. `ZeroCheck/Reduction.lean` implements the adopted repair — two scalar **Kronecker seeds** `(ρ₀, ρ_α)`, with the evaluation points derived on the curves `κ_m(ρ) = (ρ, ρ², ρ⁴, …)`, where -univariate root counting is information-complete. Full analysis: `HACHI_LEMMA10_GAP.md`. +univariate root counting is information-complete. The counterexample and the repair are spelled +out in that file's module docstring. ## Folder structure @@ -34,12 +36,12 @@ univariate root counting is information-complete. Full analysis: `HACHI_LEMMA10_ (`relBatched`, Eqs. (22)–(23)). Statement reshaping only. * `ZeroCheck/Reduction.lean` — **Hachi Figure 5 / corrected Lemma 10**: one challenge round carrying the seed pair `(ρ₀, ρ_α) ∈ F²`, reducing the identities to point evaluations at the - derived Kronecker points, with the CWSS theorem `zeroCheck_coordinateWiseSpecialSound` at + derived Kronecker points, with the CWSS theorem `zeroCheck_coordinateWiseSpecialSoundWith` at `k = D` (**sorried**). This umbrella re-exports the folder (`Reduction` transitively imports `Batch` and `Constraints`). -Its output relation `relZeroCheckE` is the input of the sumcheck bridge in `Sumcheck/`; the chain -is composed in `Composition.lean`. +Its plain output relation `relZeroCheck` is the input of the sumcheck bridge in `Sumcheck/`; +the chain is composed in `Composition.lean`. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean index baa10c5633..ed2ddd3d11 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -11,9 +11,9 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints Zero-round bridge between the lift's per-row/per-entry residual claims and the **batched polynomial-identity** form the zero-check tests: - * `relIn = relLiftE` — opening `w̃` of `t`, per-row `α`-evaluated constraints, entrywise + * `relIn = relLift` — opening `w̃` of `t`, per-row `α`-evaluated constraints, entrywise ranges (`RingSwitch/Reduction.lean`); - * `relOut = relBatchedE` — opening `w̃` of `t`, `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` as + * `relOut = relBatched` — opening `w̃` of `t`, `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` as `MvPolynomial` identities (Eqs. (22)–(23), `ZeroCheck/Constraints.lean`). The statement is **unchanged** (`ReduceClaim` at `mapStmt := id`, witness maps `id`): only the @@ -22,7 +22,7 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints * **completeness direction** (not needed for CWSS): per-row + ranges ⇒ every `eq̃`-basis coefficient of `H_α`/`H₀` vanishes ⇒ both identities; - * **extraction direction** (the sorried pull-back `mem_relLiftE_of_relBatchedE`): + * **extraction direction** (the sorried pull-back `mem_relLift_of_relBatched`): `H_α ≡ 0` ⇒ per-row constraints, by non-degeneracy of the `eq̃` basis (evaluation at the Boolean points is the identity matrix); `H₀ ≡ 0` ⇒ per-entry range membership, since each entry is a root of the `2b − 1`-factor range product over the *field* `F` (needs @@ -42,14 +42,14 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- **The batched relation** (Hachi Eqs. (22)–(23) as polynomial identities): `w̃` opens `t`, the range polynomial `H₀^{w̃}` and the linear-constraint polynomial `H_α^{w̃}` are identically zero, and the public bound-sanity conjunct is retained. This is the zero-check's `relIn`. -/ -def relBatched (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relBatched (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : Set (LiftStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := {p | @@ -58,14 +58,8 @@ def relBatched (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBoun hAlpha Φ m₁ φF b p.1.1 p.1.2.2 p.2 = 0 ∧ bound ≤ p.1.1.bound} -/-- Escape-threaded batched relation — the zero-check's seam. -/ -def relBatchedE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) (b : ℕ) : - Set (LiftStatement Φ K.TCom F n μ × (LiftedWitness Φ μ n ⊕ E)) := - (relBatched Φ m₀ m₁ bound ρBound K φF b).withEscape K.esc - /-- **Un-batching pull-back** (the bridge's `hRel`): the batched identities imply the lift's -per-row and range claims. Escapes pass through. +per-row and range claims. **Sorried.** Proof plan: `H_α ≡ 0` ⇒ all `eq̃`-basis coefficients vanish (basis non-degeneracy: `eq̃(i', i) = δ_{i,i'}` on Boolean points) ⇒ the per-row `evalAt`-equations of `relLift` @@ -73,34 +67,36 @@ per-row and range claims. Escapes pass through. of `X·∏_{j=1}^{b−1}(X − j)(X + j)` over the field `F` ⇒ (with `hq : 2 * b ≤ q + 1` reading roots as centered representatives and `hb : b - 1 ≤ bound`, `hρ`-side analogously through the digit recomposition) `liftShort bound ρBound w̃`; the bound-sanity conjunct is shared verbatim. -/ -theorem mem_relLiftE_of_relBatchedE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +theorem mem_relLift_of_relBatched + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) (hq : 2 * b ≤ q + 1) (hb : b - 1 ≤ bound) - (X : LiftStatement Φ K.TCom F n μ) (w : LiftedWitness Φ μ n ⊕ E) - (h : (X, w) ∈ relBatchedE Φ m₀ m₁ bound ρBound K φF b) : - (X, w) ∈ relLiftE Φ bound ρBound K φF := by + (X : LiftStatement Φ K.TCom F n μ) (w : LiftedWitness Φ μ n) + (h : (X, w) ∈ relBatched Φ m₀ m₁ bound ρBound K φF b) : + (X, w) ∈ relLift Φ bound ρBound K φF := by sorry -/-- **The batching bridge as a `CWSSPackage`**: zero-round `ReduceClaim` at `mapStmt := id`, -reducing `relLiftE` to `relBatchedE` with no soundness error (the whole content is the sorried -un-batching pull-back). -/ -def batchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- **The batching bridge as a (plain) `CWSSPackage`**: zero-round `ReduceClaim` at `mapStmt := id`, +reducing `relLift` to `relBatched` (the content is the sorried un-batching pull-back). A pure +re-reading of the claims, hence escape-free. -/ +noncomputable def batchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) (hq : 2 * b ≤ q + 1) (hb : b - 1 ≤ bound) : CWSSPackage init impl - (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) - (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec id struct := CWSSStructure.ofIsEmpty - relIn := relLiftE Φ bound ρBound K φF - relOut := relBatchedE Φ m₀ m₁ bound ρBound K φF b + relIn := relLift Φ bound ρBound K φF + relOut := relBatched Φ m₀ m₁ bound ρBound K φF b isPure := ⟨fun stmt _ => stmt, fun _ _ => rfl⟩ - isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSound - (relIn := relLiftE Φ bound ρBound K φF) - (relOut := relBatchedE Φ m₀ m₁ bound ρBound K φF b) + extractor := ReduceClaim.treeExtractor (mapStmt := id) + (relBatched Φ m₀ m₁ bound ρBound K φF b) (fun _ w => w) CWSSStructure.ofIsEmpty + isCWSS := ReduceClaim.verifier_coordinateWiseSpecialSoundWith + (relIn := relLift Φ bound ρBound K φF) + (relOut := relBatched Φ m₀ m₁ bound ρBound K φF b) (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) (fun stmtIn witOut h => - mem_relLiftE_of_relBatchedE Φ m₀ m₁ bound ρBound K φF b hq hb stmtIn witOut h) + mem_relLift_of_relBatched Φ m₀ m₁ bound ρBound K φF b hq hb stmtIn witOut h) end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean index afcf94aed9..690c1f8513 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -42,7 +42,7 @@ import Mathlib.Algebra.MvPolynomial.Basic off against the printed product — everything downstream is degree-parametric (`roundDegZero`/`roundDegAlpha`), so the pin is a one-line change if a convention shifts. - ## The Kronecker point (Lemma 10 repair, `HACHI_LEMMA10_GAP.md`) + ## The Kronecker point ([NOZ26, Lemma 10] repair) `kroneckerPoint m ρ = (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})`: the pullback of an `m`-variate multilinear polynomial along this curve is univariate of degree `< 2^m` and the pullback is **injective** @@ -66,13 +66,13 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (m₀ m₁ : ℕ) /-! ## The Kronecker curve (real definitions) -/ /-- **The Kronecker point** `κ_m(ρ) := (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})` — the corrected Lemma 10's -challenge-derivation curve (`HACHI_LEMMA10_GAP.md` §3.K). Computable by repeated squaring. -/ +challenge-derivation curve. Computable by repeated squaring. -/ def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F := fun j => ρ ^ (2 ^ (j : ℕ)) @@ -136,7 +136,7 @@ theorem hAlpha_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement combinator (degree 2), `F_{0,τ₀}` via the range combinator `∏ⱼ (X − j)` of degree `2b` (the `SumcheckMultiplierParam` docstring anticipates this Hachi case) — and the round consistency (`hypercubeSum` / `roundRel`) via `Sumcheck.Structured.sumcheckConsistencyProp` over -`SumcheckDomain.boolDomain`. See the `Sumcheck.lean` umbrella. -/ +`SumcheckDomain.boolDomain`. See the `Sumcheck/Basic.lean` umbrella. -/ /-- **`F_{0,τ₀}`** (the range sumcheck summand, [NOZ26] §4.3 "finish the proof using sumcheck"): `F_{0,τ₀}(x) := eq̃(τ₀, x)·w̃(x)·∏_{j=1}^{b−1}(w̃(x) − j)(w̃(x) + j)·1_{table}(x)`, where `w̃` is @@ -185,9 +185,8 @@ theorem sum_sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatemen /-! ## Statement types of the zero-check and sumcheck stages -/ /-- The zero-check's output statement: the lift statement extended by the two **Kronecker -seeds** `(ρ₀, ρ_α)` of the corrected Lemma 10 (`HACHI_LEMMA10_GAP.md` §3.K.2: the challenge is -the seed pair; the batching points `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)` are derived -deterministically). -/ +seeds** `(ρ₀, ρ_α)` of the corrected Lemma 10 (the challenge is the seed pair; the batching +points `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)` are derived deterministically). -/ structure ZeroCheckStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) where /-- The `R^lin` statement (carrying the public `M`, `yvec`, `bound`). -/ rlin : RlinStatement Φ n μ @@ -223,7 +222,7 @@ equal the current targets. Round `0` (full sums) is produced by the sumcheck bri `bound ≤ rlin.bound` threads the global norm parameter back to the `R^lin` statement bound (it originates in `relLift`, is preserved by every intermediate extraction since the statement components are shared, and is re-supplied at the final-evaluation step by its runtime guard). -/ -def roundRel (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def roundRel (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) (i : ℕ) : Set (RoundStatement Φ K.TCom F n μ i × LiftedWitness Φ μ n) := {p | @@ -235,10 +234,4 @@ def roundRel (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound) p.2) i p.1.challenges = p.1.targetα ∧ bound ≤ p.1.zc.rlin.bound} -/-- Escape-threaded per-round seam relation. -/ -def roundRelE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) - (φF : ZMod q →+* F) (b : ℕ) (i : ℕ) : - Set (RoundStatement Φ K.TCom F n μ i × (LiftedWitness Φ μ n ⊕ E)) := - (roundRel Φ m₀ m₁ bound ρBound K φF b i).withEscape K.esc - end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean index c8574942b3..678a030a57 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -20,9 +20,9 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch *axis cross* through the star's center, and for `m ≥ 2` cross-vanishing does not imply `H ≡ 0` — `H(t₁,t₂) = (t₁−a)(t₂−b)` vanishes on every axis line through `(a,b)` yet is nonzero, and an adversary can realize exactly this shape against the paper's own range check - with a single out-of-range entry. No choice of the paper's parameter `D` helps. Full analysis, - protocol-level counterexample, and the repair space: [`HACHI_LEMMA10_GAP.md`](../../../../../ - HACHI_LEMMA10_GAP.md) (plan risk R7). + with a single out-of-range entry. No choice of the paper's parameter `D` helps: uniform vector + challenges cannot certify more than axis-cross vanishing, so [NOZ26, Lemma 10] is unprovable + without changing the challenge *distribution* — which is what the repair below does. **Adopted repair (one round, Kronecker curve):** sample two independent scalar **seeds** `(ρ₀, ρ_α) ← F²` and derive the evaluation points on the Kronecker curves @@ -50,8 +50,9 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch node (one flat three-coordinate star over `(α, ρ₀, ρ_α)` would recreate the missing-corners problem for the mixed `(α, ρ_α)`-dependence of `H_α`). - **Sorried**: the CWSS theorem `zeroCheck_coordinateWiseSpecialSound` (the corrected Lemma 10; - Kronecker injectivity + univariate root counting + the weak-binding escape). + **Sorried**: the extraction algorithm `zeroCheckExtractor` and the CWSS theorem + `zeroCheck_coordinateWiseSpecialSoundWithEscape` (the corrected Lemma 10; Kronecker injectivity + + univariate root counting + the weak-binding escape event `zeroCheckEsc`). ## References @@ -92,11 +93,53 @@ def zeroCheckStructure (F : Type) (m₀ m₁ : ℕ) : CWSSStructure (pSpecZeroCh arity := fun _ => 2 * (max 2 (max (2 ^ m₀) (2 ^ m₁)) - 1) + 1 arity_eq := rfl +/-! ### Reading the seed family off a tree + +The zero-check has a *single* challenge round and no prover message, so every full challenge tree is +one `chalNode` over leaves. The reader below is index-generic in the same way as +`CoordinateWise.SingleRound`'s (a naive `match` on a `ChallengeTree … 0` fails with "dependent +elimination failed"), but needs no `Fin.cast` bridge: `zeroCheckStructure`'s arity is already +`2D − 1` by `rfl`. The reader is what makes the escape event below +`(statement, tree)`-determined. -/ + +section SeedReader + +variable {F : Type} {arity : (pSpecZeroCheck F).ChallengeIdx → ℕ} + +/-- Index-generic reader: peel the round-0 `chalNode`'s sibling-seed family off a tree at any +index `a`, together with a proof `a = 0`. -/ +def seedsAux : {a : Fin 2} → ChallengeTree (pSpecZeroCheck F) arity a → a = (0 : Fin 2) → + (Fin (arity ⟨0, rfl⟩) → (pSpecZeroCheck F).Challenge ⟨0, rfl⟩) + | _, .leaf, ha => by simp at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simp at this ⊢) + exact absurd h Direction.noConfusion + | _, .chalNode m _ chals _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simp at this ⊢) + exact chals + +/-- Read the sibling family of Kronecker seed pairs off a full tree. -/ +def readSeeds (tree : ChallengeTree (pSpecZeroCheck F) arity 0) : + Fin (arity ⟨0, rfl⟩) → (pSpecZeroCheck F).Challenge ⟨0, rfl⟩ := + seedsAux tree rfl + +/-- The star tree of the zero-check: one challenge node carrying the seed family, leaves below. -/ +def tree1 (seeds : Fin (arity ⟨0, rfl⟩) → (pSpecZeroCheck F).Challenge ⟨0, rfl⟩) : + ChallengeTree (pSpecZeroCheck F) arity 0 := + .chalNode 0 rfl seeds (fun _ => .leaf) + +/-- The reader computes on the star tree. -/ +@[simp] theorem readSeeds_tree1 + (seeds : Fin (arity ⟨0, rfl⟩) → (pSpecZeroCheck F).Challenge ⟨0, rfl⟩) : + readSeeds (tree1 seeds) = seeds := rfl + +end SeedReader + section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -variable {n μ : ℕ} {E : Type} {F : Type} [Field F] +variable {n μ : ℕ} {F : Type} [Field F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -129,7 +172,7 @@ def zeroCheckProver {TCom : Type} : /-- **The zero-check's output relation** (corrected Figure 5 residual claims): `w̃` opens `t`, and both batched constraint polynomials vanish **at the derived Kronecker points** `τ₀ = κ_{m₀}(ρ₀)`, `τ_α = κ_{m₁}(ρ_α)`. -/ -def relZeroCheck (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relZeroCheck (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : Set (ZeroCheckStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := {p | @@ -139,57 +182,90 @@ def relZeroCheck (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBo (hAlpha Φ m₁ φF b p.1.rlin p.1.α p.2) = 0 ∧ bound ≤ p.1.rlin.bound} -/-- Escape-threaded zero-check relation — the sumcheck bridge's seam. -/ -def relZeroCheckE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- **The zero-check's escape event** (corrected Lemma 10's weak-binding case): the tree's own seed +family admits per-branch `relZeroCheck`-responses among which two are **distinct short openings of +the statement's commitment `t`** — a member of `LiftCom.Collision`, hence a Module-SIS break of the +fixed key by [NOZ26] Lemma 7. (Both openings automatically open `t`: `relZeroCheck`'s first conjunct +pins `K.com w = t` and every branch's output statement carries the same `t`.) + +Against the escape-event contract (`ChallengeTree.EscapeEvent`): the collision conjunct is an +unconditional break at *every* `(statement, tree)`, and the event is determined by the statement and +the tree's seeds (read by `readSeeds`) together with responses pinned to the **output** relation. +That pinning is what keeps it tight — it cannot fire on trees where all branches share one opening, +which is exactly where extraction succeeds. -/ +def zeroCheckEsc (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : - Set (ZeroCheckStatement Φ K.TCom F n μ × (LiftedWitness Φ μ n ⊕ E)) := - (relZeroCheck Φ m₀ m₁ bound ρBound K φF b).withEscape K.esc + ChallengeTree.EscapeEvent (LiftStatement Φ K.TCom F n μ) (pSpecZeroCheck F) + (CWSSStructure.toShape (zeroCheckStructure F m₀ m₁)).arity := + fun stmt tree => + ∃ resp : Fin ((zeroCheckStructure F m₀ m₁).arity ⟨0, rfl⟩) → LiftedWitness Φ μ n, + (∀ j, (⟨stmt.1, stmt.2.1, stmt.2.2, + (readSeeds tree j).1, (readSeeds tree j).2⟩, resp j) ∈ + relZeroCheck Φ m₀ m₁ bound ρBound K φF b) ∧ + ∃ j j', (resp j, resp j') ∈ K.Collision variable [SampleableType F] -/-- **Corrected Hachi Lemma 10 (skeleton): one-round Kronecker-seed CWSS of the zero-check.** - -**Sorried (F6).** Extraction plan (`HACHI_LEMMA10_GAP.md` §3.K.3): an `SS(F, 2, D)` star of -`2D − 1` accepting branches has `D` pairwise-distinct `ρ₀`-values on its first arm (the second -coordinate held at the center) and `D` pairwise-distinct `ρ_α`-values on its second arm. If two -branch witnesses are escapes or distinct openings of `t`, pass through resp. invoke -`K.collision_mem` (all openings are... short by the downstream range extraction — precisely, the -collision escape here reuses the same weak-binding route as Lemma 9's). Otherwise all branches -share one `w̃`: the univariate pullback `K₀(T) := H₀^{w̃}(κ_{m₀}(T))` has degree `< 2^{m₀} ≤ D` -(multilinearity `hZero_degreeOf_le` + `LinearMvExtension.powAlgHom` degree bound) and `D` -distinct roots on the first arm, hence `K₀ = 0`; **Kronecker injectivity** of the pullback on -multilinear polynomials (the still-missing `powAlgHom_injective_on_multilinear`) gives -`H₀^{w̃} ≡ 0`. The second arm gives `H_α^{w̃} ≡ 0` identically. The axis-cross counterexample -of the gap file cannot survive: its pullback is a nonzero univariate of degree `< 2^{m₀}`. -/ -theorem zeroCheck_coordinateWiseSpecialSound +/-- **The corrected Lemma 10 extraction algorithm (skeleton).** + +**Sorried** — this def is the milestone's *algorithm* (the case split of the extraction plan on +`zeroCheck_coordinateWiseSpecialSoundWithEscape`). -/ +noncomputable def zeroCheckExtractor + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) (b : ℕ) : + Extractor.TreeBased (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) + (pSpecZeroCheck F) + (CWSSStructure.toShape (zeroCheckStructure F m₀ m₁)).arity := + sorry + +/-- **Corrected Hachi Lemma 10 (skeleton): one-round Kronecker-seed escape-threaded CWSS of the +zero-check, at the named `zeroCheckExtractor`.** The relations are `relBatched` / `relZeroCheck`; +the weak-binding failure mode is the escape disjunct `zeroCheckEsc`. + +**Sorried.** Extraction plan: an `SS(F, 2, D)` star of `2D − 1` accepting branches has `D` +pairwise-distinct `ρ₀`-values on its first arm (the second coordinate held at the center) and `D` +pairwise-distinct `ρ_α`-values on its second arm. If two branch witnesses are distinct openings of +`t`, they are short (by `relZeroCheck`'s downstream range content, the same weak-binding route as +Lemma 9's) and `zeroCheckEsc` fires — take the left disjunct. Otherwise all branches share one `w̃`: +the univariate pullback `K₀(T) := H₀^{w̃}(κ_{m₀}(T))` has degree `< 2^{m₀} ≤ D` (multilinearity +`hZero_degreeOf_le` + `LinearMvExtension.powAlgHom` degree bound) and `D` distinct roots on the +first arm, hence `K₀ = 0`; **Kronecker injectivity** of the pullback on multilinear polynomials (the +still-missing `powAlgHom_injective_on_multilinear`) gives `H₀^{w̃} ≡ 0`. The second arm gives +`H_α^{w̃} ≡ 0` identically. The axis-cross counterexample of the module docstring cannot survive: +its pullback is a nonzero univariate of degree `< 2^{m₀}`. -/ +theorem zeroCheck_coordinateWiseSpecialSoundWithEscape (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : - (zeroCheckVerifier (oSpec := oSpec) Φ (n := n) (μ := μ) (F := F) - (TCom := K.TCom)).coordinateWiseSpecialSound init impl + Verifier.coordinateWiseSpecialSoundWithEscape init impl (zeroCheckStructure F m₀ m₁) - (relBatchedE Φ m₀ m₁ bound ρBound K φF b) - (relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) := by + (zeroCheckEsc Φ m₀ m₁ bound ρBound K φF b) + (relBatched Φ m₀ m₁ bound ρBound K φF b) + (relZeroCheck Φ m₀ m₁ bound ρBound K φF b) + (zeroCheckVerifier (oSpec := oSpec) Φ (n := n) (μ := μ) (F := F) (TCom := K.TCom)) + (zeroCheckExtractor Φ m₀ m₁ bound ρBound K φF b) := by sorry -/-- **The zero-check as a `CWSSPackage`** (corrected Hachi Figure 5 / Lemma 10): the one-round -seed-pair verifier with the `(ℓ, k) = (2, D)` Kronecker structure, reducing `relBatchedE` to -`relZeroCheckE`. The certificate is the sorried `zeroCheck_coordinateWiseSpecialSound`. -/ -def zeroCheckPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- **The zero-check as an `EscapeCWSSPackage`** (corrected Hachi Figure 5 / Lemma 10): the +one-round seed-pair verifier with the `(ℓ, k) = (2, D)` Kronecker structure, reducing `relBatched` +to `relZeroCheck`, with the weak-binding event `zeroCheckEsc` as its one escape-specific field. -/ +noncomputable def zeroCheckPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) : - CWSSPackage init impl - (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) - (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + EscapeCWSSPackage init impl + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) + (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n) (pSpecZeroCheck F) where verifier := zeroCheckVerifier (oSpec := oSpec) Φ struct := zeroCheckStructure F m₀ m₁ - relIn := relBatchedE Φ m₀ m₁ bound ρBound K φF b - relOut := relZeroCheckE Φ m₀ m₁ bound ρBound K φF b + relIn := relBatched Φ m₀ m₁ bound ρBound K φF b + relOut := relZeroCheck Φ m₀ m₁ bound ρBound K φF b + esc := zeroCheckEsc Φ m₀ m₁ bound ρBound K φF b isPure := ⟨fun stmt tr => ⟨stmt.1, stmt.2.1, stmt.2.2, (tr.challenges ⟨0, rfl⟩).1, (tr.challenges ⟨0, rfl⟩).2⟩, fun _ _ => rfl⟩ - isCWSS := zeroCheck_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound init impl K φF b + extractor := zeroCheckExtractor Φ m₀ m₁ bound ρBound K φF b + isCWSS := zeroCheck_coordinateWiseSpecialSoundWithEscape Φ m₀ m₁ bound ρBound init impl K φF b end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html index d1a93e59cb..d2b75a87d5 100644 --- a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html +++ b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html @@ -413,7 +413,7 @@

On the reduction line

guarded - Guarded verifier — its runtime check reads data the next statement type drops (a sumcheck target, a packed value); it may failure at runtime and composes via ▷ᵍ. + Guarded verifier — its runtime check reads data the next statement type drops (a sumcheck target, a packed value); it may failure at runtime and composes via ▷ₑᵍ.
A / B @@ -447,9 +447,13 @@

Proof status

The reduction line — QuadEval → evaluation claim

Top to bottom is one opening of an Rq-committed multilinear polynomial, exactly as composed in Composition.lean (openingChain). Each link reduces - one relation to the next; witnesses everywhere carry the escape budget · ⊕ E - (threaded by Escape.lean). The final link outputs a bare multilinear-evaluation - claim mle[w̃](a) = y′ on the committed table.

+ one relation to the next, and every relation is the ordinary protocol relation. Where extraction can fail + cryptographically instead of producing a witness (QuadEval's Module-SIS break, the weak-binding collisions + of Figures 4–6), the link carries an escape event on the transcript tree + (EscapeCWSSPackage.esc): a predicate on + (statement, tree) entering the certificate as a disjunct of its conclusion, so no + extractor can fabricate it. The final link outputs a bare multilinear-evaluation claim + mle[w̃](a) = y′ on the committed table.

@@ -521,8 +525,8 @@

Beyond the current line

[LS18] Lyubashevsky & Seiler — Short, Invertible Elements in Partially Splitting Cyclotomic Rings

- Design notes live beside the tree as HACHI_*.md — the corrected Lemma 10 (HACHI_LEMMA10_GAP.md) - and the ring-switching / packing plans. The composition + Design notes live in the module docstrings themselves — the corrected Lemma 10 in + ZeroCheck/Reduction.lean and the open row-11 gap in Recursion/ZBatchBridge.lean. The composition home and its certificate provenance are documented in the Composition.lean module header. Status was read directly from the current working tree on the hachi-skeleton branch; “≈24 open sorries” counts genuine proof terms inside Hachi/ and excludes the generic guarded-append (B4) machinery, which lives under @@ -538,16 +542,16 @@

Beyond the current line

/* ---------------- DATA ---------------- */ var SPINE = [ { zt:"Polynomial Evaluation as a Quadratic Equation", zref:"§4.2 · Figure 3 · Lemma 8", - znote:"The finished core — sorry-free and axiom-clean. In the composed chain these two links run as their escape-threaded widenings (evalChainE, in Escape.lean).", + znote:"The finished core — sorry-free and axiom-clean. The composed evalChain states its certificate at the ordinary relations, with QuadEval's Module-SIS break appearing as the certificate's escape-event disjunct.", rows:[ { n:1, kind:"bridge", name:"Polynomial-level bridge", sub:"QuadEval", fn:"Bridge.lean", files:[["QuadEval","Bridge.lean"]], ref:"§4.2 · Eq. 12", rounds:"0 rounds", cwss:"any (0 challenges)", - relIn:"relPolyEvalE", relOut:"relInE", status:"proven", sorry:0, badges:["bridge"], + relIn:"relPolyEval", relOut:"relIn", status:"proven", sorry:0, badges:["bridge"], desc:"Zero-round head: reinterprets a CMlPolynomial evaluation claim as a QuadEval statement, taking the Eq. (12) bases to be the monomial tensor bases of the split point. Sorry-free." }, { n:2, kind:"step", name:"QuadEval — the quadratic-form fold", sub:"QuadEval", fn:"Reduction · Gadgets · Soundness", files:[["QuadEval","Reduction.lean"],["QuadEval","Gadgets.lean"],["QuadEval","Soundness.lean"]], ref:"§4.2 · Fig 3 · Lemma 8", rounds:"msg v ; c ∈ C^{2ʳ}", cwss:"ℓ = 2ʳ, k = 2", - relIn:"relInE", relOut:"relOutE (Eq. 20)", status:"proven", sorry:0, badges:["core"], + relIn:"relIn", relOut:"relOut (Eq. 20)", status:"proven", sorry:0, badges:["core"], desc:"The two-round fold with the subtract-and-divide extractor (buildWitness): a star of 2ʳ+1 accepting branches yields a valid weak opening or a Module-SIS solution for B or D. Lemma 8 CWSS is genuinely sorry-free and axiom-clean." }, ]}, { zt:"Ring-Switching Lift", zref:"§4.3 · Figure 4 · Lemma 9 · [HMZ25]", @@ -555,11 +559,11 @@

Beyond the current line

rows:[ { n:3, kind:"bridge", name:"Rˡⁱⁿ adapter", sub:"RingSwitch", fn:"Rlin.lean", files:[["RingSwitch","Rlin.lean"]], ref:"§4.3 · reads Eq. 20", rounds:"0 rounds", cwss:"any", - relIn:"relOutE", relOut:"relRlinE", status:"wip", sorry:3, badges:["bridge"], + relIn:"relOut", relOut:"relRlin", status:"wip", sorry:3, badges:["bridge"], desc:"Zero-round adapter reading QuadEval's Eq. (20) output as an unstructured linear relation Rˡⁱⁿ — stacks the response ζ against the five verification rows as one block matrix. Sorried: the block-matrix assembly / unstacking and the block-row pull-back." }, { n:4, kind:"step", name:"HMZ25 lift", sub:"RingSwitch", fn:"Reduction.lean", files:[["RingSwitch","Reduction.lean"]], ref:"§4.3 · Fig 4 · Lemma 9", rounds:"msg t ; α ∈ F", cwss:"ℓ = 1, k = 2d", - relIn:"relRlinE", relOut:"relLiftE", status:"wip", sorry:1, badges:[], + relIn:"relRlin", relOut:"relLift", status:"wip", sorry:1, badges:[], desc:"Commit t := Com(w̃), sample α ← F, evaluate the lifted rows at α. Introduces the abstract weak-binding commitment LiftCom. Sorried: Lemma 9's interpolation extraction." }, ]}, { zt:"Zero-Check", zref:"§4.3 · Figure 5 · corrected Lemma 10", @@ -567,11 +571,11 @@

Beyond the current line

rows:[ { n:5, kind:"bridge", name:"Batching bridge", sub:"ZeroCheck", fn:"Batch.lean", files:[["ZeroCheck","Batch.lean"]], ref:"§4.3 · Eqs. 22–23", rounds:"0 rounds", cwss:"any", - relIn:"relLiftE", relOut:"relBatchedE", status:"wip", sorry:1, badges:["bridge"], + relIn:"relLift", relOut:"relBatched", status:"wip", sorry:1, badges:["bridge"], desc:"Reads the lift's per-row/per-entry residual claims as the two MvPolynomial identities the zero-check tests. Statement reshaping only; the sole content is the sorried un-batching direction." }, { n:6, kind:"step", name:"Zero-check reduction", sub:"ZeroCheck", fn:"Reduction.lean", files:[["ZeroCheck","Reduction.lean"]], ref:"§4.3 · Fig 5 · Lemma 10*", rounds:"(ρ₀, ρ_α) ∈ F²", cwss:"ℓ = 2, k = D", - relIn:"relBatchedE", relOut:"relZeroChkE", status:"wip", sorry:1, badges:["repair"], + relIn:"relBatched", relOut:"relZeroCheck", status:"wip", sorry:1, badges:["repair"], desc:"One challenge round carrying a pair of scalar Kronecker seeds, with batching points derived on the curves κ_m(ρ) = (ρ, ρ², ρ⁴, …). Deliberately repairs the paper's unprovable uniform-vector Lemma 10 (an axis-cross counterexample) to recover genuine (ℓ,k) = (2, D) CWSS." }, ]}, { zt:"Sumcheck Loop", zref:"§4.3 · Figures 6–7 · Lemma 11", @@ -579,15 +583,15 @@

Beyond the current line

rows:[ { n:7, kind:"bridge", name:"Sumcheck entry bridge", sub:"Sumcheck", fn:"Bridge.lean", files:[["Sumcheck","Bridge.lean"]], ref:"§4.3", rounds:"0 rounds", cwss:"any", - relIn:"relZeroChkE", relOut:"roundRelE 0", status:"wip", sorry:1, badges:["bridge"], + relIn:"relZeroCheck", relOut:"roundRel 0", status:"wip", sorry:1, badges:["bridge"], desc:"Reshapes the zero-check's point-evaluation claims into the initial hypercube-sum claims (∑ F₀ = 0, ∑ F_α = a with the linear target a computed by the verifier)." }, { n:8, kind:"step", name:"Paired sumcheck rounds ×m₀", sub:"Sumcheck", fn:"Rounds.lean", files:[["Sumcheck","Rounds.lean"]], ref:"§4.3 · Fig 6 · Lemma 11", rounds:"per round: (g-pair ; aᵢ)", cwss:"ℓ = 1, k = 2b+1", - relIn:"roundRelE 0", relOut:"roundRelE m₀", status:"wip", sorry:1, badges:["guarded"], - desc:"Each round sends the univariate pair (gᵢ⁽⁰⁾, gᵢ⁽ᵅ⁾) under a shared challenge aᵢ; the guarded check is gᵢ(0)+gᵢ(1) = targetᵢ₋₁. Composed by recursion over the guarded append ▷ᵍ into roundsChain." }, + relIn:"roundRel 0", relOut:"roundRel m₀", status:"wip", sorry:1, badges:["guarded"], + desc:"Each round sends the univariate pair (gᵢ⁽⁰⁾, gᵢ⁽ᵅ⁾) under a shared challenge aᵢ; the guarded check is gᵢ(0)+gᵢ(1) = targetᵢ₋₁. Composed by recursion over the guarded escape append ▷ₑᵍ into roundsChain, whose recursion pins the relation seams." }, { n:9, kind:"step", name:"Final evaluation", sub:"Sumcheck", fn:"FinalEval.lean", files:[["Sumcheck","FinalEval.lean"]], ref:"§4.3 · Fig 7 tail", rounds:"msg y′ ∈ F", cwss:"any", - relIn:"roundRelE m₀", relOut:"relWEvalE", status:"wip", sorry:2, badges:["guarded"], + relIn:"roundRel m₀", relOut:"relWEvalClaim", status:"wip", sorry:2, badges:["guarded"], desc:"The prover sends the claimed evaluation y′ = w̃(a) in the clear; the guarded verifier checks both final sumcheck targets. Output is the evaluation claim mle[w̃](a) = y′ — the final currency the opening yields." }, ]}, ]; @@ -601,8 +605,8 @@

Beyond the current line

desc:"The two-layer commitment the whole line opens against: the scheme with its weak openings, perfect correctness, and the weak-binding reduction to Module-SIS — plus the pinned power-of-two ring. All sorry-free, down to the [LS18] short-element invertibility the soundness rests on." }, ]; var SUB_CROSS = [ - { name:"Escape threading", ref:"milestone F2.0", files:[["","Escape.lean"]], status:"wip", sorry:1, - desc:"Threads an abstract escape budget E through the finished front (evalChainE) so the Figure-4 commitment's binding break has a home at every upstream seam. Relations, pull-back and the bridge package are sorry-free; the one sorry is the widened Lemma 8." }, + { name:"Escape events", ref:"milestone F2.0", files:[["CWSS","Escape.lean"]], status:"proven", sorry:0, + desc:"Cryptographic escapes are events on the transcript tree: a package's esc field is a predicate on (statement, tree), and its certificate concludes esc ∨ extraction succeeds. Relations and extractors stay ordinary, so no choice of extractor can trivialize a certificate. The 2×2 package lattice (escape? × guarded?) composes through the universal with lossless lifts, matching only relation seams." }, { name:"Shared constraint encoding", ref:"§4.3 · Eqs. 21–23", files:[["ZeroCheck","Constraints.lean"]], status:"wip", sorry:12, desc:"The table w̃, the eq̃-batched constraint polynomials H₀/H_α, their sumcheck summands, and the per-round seam relations. Consumed by both the zero-check (row 6) and the sumcheck loop (rows 7–9). Definitions-only for now — the most sorried file in the tree." }, { name:"Composition home", ref:"the certificate", files:[["","Composition.lean"]], status:"wip", sorry:0, @@ -613,7 +617,7 @@

Beyond the current line

var ROADMAP = [ { title:"§3 packing head — external extension-field claims", tag:"planned", - body:"Wrap an F_{q^k} evaluation claim in front of relPolyEvalE, as an instance of the generalized RingSwitching packing phase — a separate track, not a Hachi-local head." }, + body:"Wrap an F_{q^k} evaluation claim in front of relPolyEval, as an instance of the generalized RingSwitching packing phase — a separate track, not a Hachi-local head." }, { title:"Honest-prover completeness layer", tag:"planned", body:"Materialize hachi.opening by instantiating QuadEval's computeV / computeResp from the carrier/decomposition defs, discharging Commitment.perfectCorrectness." }, { title:"Generic guarded-append machinery (B4)", tag:"planned", @@ -626,32 +630,35 @@

Beyond the current line

var INDEX = [ ["","Commitment.lean","commitment-interface","§2.1/§4.1","—","wip",1], ["","Composition.lean","composition","§4.2–4.3","—","wip",0], - ["","Escape.lean","infrastructure","F2.0","—","wip",1], ["","EvalSplit.lean","infrastructure","§4 · Eq.12","—","proven",0], - ["","Gadget.lean","umbrella","§2.1","—","umbrella",0], - ["Gadget","Basic.lean","infrastructure","§2.1","—","proven",0], + ["Gadget","Basic.lean","umbrella","§2.1","—","umbrella",0], + ["Gadget","Core.lean","infrastructure","§2.1","—","proven",0], ["Gadget","Norms.lean","infrastructure","§2.1","—","proven",0], - ["","InnerOuter.lean","umbrella","§4.1","—","umbrella",0], + ["InnerOuter","Basic.lean","umbrella","§4.1","—","umbrella",0], ["InnerOuter","Arithmetic.lean","infrastructure","§4.1","—","proven",0], ["InnerOuter","Correctness.lean","infrastructure","§4.1","—","proven",0], ["InnerOuter","Scheme.lean","infrastructure","§4.1","—","proven",0], ["InnerOuter","Security.lean","infrastructure","§4.1","—","proven",0], - ["","QuadEval.lean","umbrella","§4.2","—","umbrella",0], + ["QuadEval","Basic.lean","umbrella","§4.2","—","umbrella",0], ["QuadEval","Bridge.lean","bridge","§4.2 · Eq.12","1","proven",0], ["QuadEval","Gadgets.lean","subprotocol","§4.2 · L8","2","proven",0], ["QuadEval","Reduction.lean","subprotocol","§4.2 · Fig3","2","proven",0], ["QuadEval","Soundness.lean","subprotocol","§4.2 · L8","2","proven",0], - ["","RingSwitch.lean","umbrella","§4.3","—","umbrella",0], + ["RingSwitch","Basic.lean","umbrella","§4.3","—","umbrella",0], ["RingSwitch","Rlin.lean","bridge","§4.3","3","wip",3], ["RingSwitch","Reduction.lean","subprotocol","§4.3 · Fig4 · L9","4","wip",1], - ["","ZeroCheck.lean","umbrella","§4.3","—","umbrella",0], + ["ZeroCheck","Basic.lean","umbrella","§4.3","—","umbrella",0], ["ZeroCheck","Batch.lean","bridge","§4.3 · Eq22–23","5","wip",1], ["ZeroCheck","Constraints.lean","infrastructure","§4.3 · Eq21–23","—","wip",12], ["ZeroCheck","Reduction.lean","subprotocol","§4.3 · Fig5 · L10*","6","wip",1], - ["","Sumcheck.lean","umbrella","§4.3","—","umbrella",0], + ["Sumcheck","Basic.lean","umbrella","§4.3","—","umbrella",0], ["Sumcheck","Bridge.lean","bridge","§4.3","7","wip",1], ["Sumcheck","Rounds.lean","subprotocol","§4.3 · Fig6 · L11","8","wip",1], ["Sumcheck","FinalEval.lean","subprotocol","§4.3 · Fig7","9","wip",2], + ["Recursion","Basic.lean","umbrella","§4.5","—","umbrella",0], + ["Recursion","PartialEval.lean","adapter","§4.5 · Eq24","10","wip",5], + ["Recursion","ZBatchBridge.lean","bridge","§4.5 · Eqs25–26","11","gap",2], + ["Recursion","TraceHandoff.lean","adapter","§4.5 · Eqs27–28","12","wip",3], ]; /* ---------------- HELPERS ---------------- */ @@ -665,7 +672,7 @@

Beyond the current line

INDEX.forEach(function(r){ if(r[2]==="umbrella") return; if(counts[r[5]]!=null) counts[r[5]]++; }); var tiles = [ { cls:"ok", n:counts.proven, lbl:"Proven", sub:"Sorry-free & complete — all foundations plus the entire QuadEval subprotocol (rows 1–2)." }, - { cls:"warn", n:counts.wip, lbl:"In progress", sub:"Real skeletons: rows 3–9, escape threading, the shared encoding, composition and the scheme." }, + { cls:"warn", n:counts.wip, lbl:"In progress", sub:"Real skeletons: rows 3–9, the shared encoding, composition and the scheme." }, { cls:"plan", n:ROADMAP.length, lbl:"Planned", sub:"Named in the roadmap but not yet a file — the outer interface, the completeness layer & generic machinery." }, ]; var tilesEl = document.getElementById("tiles"); diff --git a/ArkLib/Data/Lattices/CyclotomicRing/Galois/Automorphism.lean b/ArkLib/Data/Lattices/CyclotomicRing/Galois/Automorphism.lean index 9294ed0487..8049158f9c 100644 --- a/ArkLib/Data/Lattices/CyclotomicRing/Galois/Automorphism.lean +++ b/ArkLib/Data/Lattices/CyclotomicRing/Galois/Automorphism.lean @@ -14,7 +14,7 @@ are the ring automorphisms `σ_i` induced by `X ↦ X^i` for `i` a unit modulo t `2d = 2^{α+1}` (equivalently, `i` odd). These are the maps used throughout Hachi [NOZ26, §3] to identify the finite-field extensions inside `R_q`. -Following the project's two-layer discipline (cf. `CyclotomicRing/Basic.lean`): +Following the project's two-layer discipline (cf. `CyclotomicRing/Core/Basic.lean`): * **Computable layer** (`galoisAut`): on a reduced representative `a = Σ_{k False) relIn relOut verifier Ext ↔ + coordinateWiseSpecialSoundWith init impl D relIn relOut verifier Ext := + treeSpecialSoundWithEscape_false_iff init impl _ relIn relOut verifier Ext + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Escape events are monotone at the CWSS shape. -/ +theorem coordinateWiseSpecialSoundWithEscape.mono {D : CWSSStructure pSpec} + {esc esc' : ChallengeTree.EscapeEvent StmtIn pSpec (CWSSStructure.toShape D).arity} + (hmono : ∀ s t, esc s t → esc' s t) + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec (CWSSStructure.toShape D).arity} + (h : coordinateWiseSpecialSoundWithEscape init impl D esc relIn relOut verifier Ext) : + coordinateWiseSpecialSoundWithEscape init impl D esc' relIn relOut verifier Ext := + treeSpecialSoundWithEscape.mono init impl hmono h + end Verifier namespace OracleVerifier @@ -228,13 +337,75 @@ variable {ι : Type} {oSpec : OracleSpec ι} [∀ i, OracleInterface (pSpec.Message i)] {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) +/-- A named tree-based extractor witnesses coordinate-wise special soundness of an oracle + reduction: `Verifier.coordinateWiseSpecialSoundWith` of the underlying non-oracle verifier on + the combined (oracle + non-oracle) statements. The named form is the content-bearing statement + (see `Verifier.treeSpecialSoundWith`). -/ +def coordinateWiseSpecialSoundWith (D : CWSSStructure pSpec) + (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) + (relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)) + (verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) + (Ext : Extractor.TreeBased (StmtIn × ∀ i, OStmtIn i) WitIn pSpec + (CWSSStructure.toShape D).arity) : Prop := + Verifier.coordinateWiseSpecialSoundWith init impl D relIn relOut verifier.toVerifier Ext + /-- Coordinate-wise special soundness of an oracle reduction, defined (as for round-by-round notions) via the underlying non-oracle verifier on the combined (oracle + non-oracle) statements. - The challenge structure `D` is unchanged, since the verifier's challenges are the same. -/ + The challenge structure `D` is unchanged, since the verifier's challenges are the same. + + As at the non-oracle level, the extractor is existential, so prefer + `coordinateWiseSpecialSoundWith` at a named extractor for advertised statements. -/ def coordinateWiseSpecialSound (D : CWSSStructure pSpec) (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) (relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)) (verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) : Prop := verifier.toVerifier.coordinateWiseSpecialSound init impl D relIn relOut +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Forget the name of the extractor (oracle level). -/ +theorem coordinateWiseSpecialSoundWith.toCWSS {D : CWSSStructure pSpec} + {relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)} + {relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)} + {verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec} + {Ext : Extractor.TreeBased (StmtIn × ∀ i, OStmtIn i) WitIn pSpec + (CWSSStructure.toShape D).arity} + (h : coordinateWiseSpecialSoundWith init impl D relIn relOut verifier Ext) : + verifier.coordinateWiseSpecialSound init impl D relIn relOut := ⟨Ext, h⟩ + +/-- Escape-threaded CWSS of an oracle reduction, **named form**: the non-oracle escape notion of + the underlying verifier on the combined (oracle + non-oracle) statements. The escape event is + indexed by the combined input statement, so it may read the oracle statements. -/ +def coordinateWiseSpecialSoundWithEscape (D : CWSSStructure pSpec) + (esc : ChallengeTree.EscapeEvent (StmtIn × ∀ i, OStmtIn i) pSpec + (CWSSStructure.toShape D).arity) + (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) + (relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)) + (verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) + (Ext : Extractor.TreeBased (StmtIn × ∀ i, OStmtIn i) WitIn pSpec + (CWSSStructure.toShape D).arity) : Prop := + Verifier.coordinateWiseSpecialSoundWithEscape init impl D esc relIn relOut + verifier.toVerifier Ext + +/-- Existential closure of the oracle-level escape-threaded CWSS. -/ +def coordinateWiseSpecialSoundEscape (D : CWSSStructure pSpec) + (esc : ChallengeTree.EscapeEvent (StmtIn × ∀ i, OStmtIn i) pSpec + (CWSSStructure.toShape D).arity) + (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) + (relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)) + (verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) : Prop := + Verifier.coordinateWiseSpecialSoundEscape init impl D esc relIn relOut verifier.toVerifier + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Forget the name of the extractor (oracle level, escape-threaded). -/ +theorem coordinateWiseSpecialSoundWithEscape.toEscape {D : CWSSStructure pSpec} + {esc : ChallengeTree.EscapeEvent (StmtIn × ∀ i, OStmtIn i) pSpec + (CWSSStructure.toShape D).arity} + {relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)} + {relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)} + {verifier : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec} + {Ext : Extractor.TreeBased (StmtIn × ∀ i, OStmtIn i) WitIn pSpec + (CWSSStructure.toShape D).arity} + (h : coordinateWiseSpecialSoundWithEscape init impl D esc relIn relOut verifier Ext) : + coordinateWiseSpecialSoundEscape init impl D esc relIn relOut verifier := ⟨Ext, h⟩ + end OracleVerifier diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean index af81b12e9b..63b69178f9 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean @@ -11,14 +11,22 @@ import ArkLib.OracleReduction.Composition.Sequential.Append # Composition for Coordinate-Wise Special Soundness This file contains the sequential-composition API for coordinate-wise special soundness (CWSS). - CWSS composition is factored through the generic `ChallengeTreeShape` API: + Composition is deliberately **binary**: longer chains are built by iterating the binary append + (the `CoordinateWise` packages' `▷`), which is all the protocol formalizations need and which + keeps the composed extractor a nameable function rather than a transport across an `n`-ary shape + identity. CWSS composition is factored through the generic `ChallengeTreeShape` API: - * `CWSSStructure.append` and `CWSSStructure.seqCompose` transport intrinsic CWSS data across - protocol composition. + * `CWSSStructure.append` transports intrinsic CWSS data across protocol append. * `CWSSStructure.toShape_append` identifies the CWSS shape of an appended structure with the generic appended tree shape. - * `Verifier.append_treeSpecialSound` is the generic structured-tree preservation statement. - * `Verifier.append_coordinateWiseSpecialSound` is the CWSS-specific wrapper. + * `Verifier.pure_accepting_of_mem` / `Verifier.mem_of_pure_accepting` — the two directions of + the pure-verifier acceptance bridge, used to certify prefix leaves' verdicts (and reused by + the zero-round `ProofSystem/Component` reductions). + * `Verifier.append_treeSpecialSoundWith` is the generic structured-tree preservation statement, + and `Verifier.append_treeSpecialSoundWithEscape` its escape-threaded twin, whose composed escape + event is `ChallengeTree.EscapeEvent.append`. + * `Verifier.append_coordinateWiseSpecialSoundWith` / `…WithEscape` are the CWSS-specific + wrappers, with `OracleVerifier` versions for oracle reductions. -/ noncomputable section @@ -28,40 +36,15 @@ open scoped NNReal universe u v -/-- Applying a `cast` of an `Equiv` (transported along equalities of its domain and codomain) agrees -with casting the argument into the original domain and the result out of the original codomain. This -is the single cast-commutation fact underlying the CWSS shape-composition theorems. -/ -theorem cast_equiv_apply {A B : Type u} {C D : Type v} (hAB : A = B) (hCD : C = D) (e : A ≃ C) - (b : B) : - cast (show (A ≃ C) = (B ≃ D) by rw [hAB, hCD]) e b = cast hCD (e (cast hAB.symm b)) := by - subst hAB; subst hCD; rfl - /-- Heterogeneous congruence for `Equiv` application: two heterogeneously-equal equivalences (over equal domains and codomains) send heterogeneously-equal arguments to heterogeneously-equal -results. -/ +results. This is the single cast-commutation fact underlying `CWSSStructure.toShape_append`. -/ theorem heq_equiv_apply {A A' : Type u} {B B' : Type v} (hA : A = A') (hB : B = B') {e₁ : A ≃ B} {e₂ : A' ≃ B'} (he : HEq e₁ e₂) {a : A} {a' : A'} (ha : HEq a a') : HEq (e₁ a) (e₂ a') := by subst hA; subst hB exact heq_of_eq (by rw [eq_of_heq he, eq_of_heq ha]) -namespace ChallengeTreeShape - -variable {r : ℕ} {len : Fin r → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} - -/-- Sequential composition of a finite family of generic challenge-tree shapes. -/ -def seqCompose (S : ∀ i, ChallengeTreeShape (pSpec i)) : - ChallengeTreeShape (ProtocolSpec.seqCompose pSpec) where - arity := fun combinedIdx => - let ij := seqComposeChallengeIdxToSigma combinedIdx - (S ij.1).arity ij.2 - nodeOk := fun combinedIdx challenges => - let ij := seqComposeChallengeIdxToSigma combinedIdx - (S ij.1).nodeOk ij.2 fun j => - cast (seqCompose_challenge_eq combinedIdx) (challenges j) - -end ChallengeTreeShape - namespace CWSSStructure variable {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} @@ -185,39 +168,6 @@ theorem append_decompose_inr (i₂ : pSpec₂.ChallengeIdx) : end AppendChar -variable {r : ℕ} {len : Fin r → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} - -/-- Sequential composition of a finite family of CWSS structures. -/ -def seqCompose (D : ∀ i, CWSSStructure (pSpec i)) : - CWSSStructure (ProtocolSpec.seqCompose pSpec) where - coordIndex := fun i => - let ij := seqComposeChallengeIdxToSigma i - (D ij.1).coordIndex ij.2 - alphabet := fun i => - let ij := seqComposeChallengeIdxToSigma i - (D ij.1).alphabet ij.2 - decompose := fun i => - cast (by rw [seqCompose_challenge_eq i]) - ((D (seqComposeChallengeIdxToSigma i).1).decompose (seqComposeChallengeIdxToSigma i).2) - soundnessParam := fun i => - let ij := seqComposeChallengeIdxToSigma i - (D ij.1).soundnessParam ij.2 - arity := fun i => - let ij := seqComposeChallengeIdxToSigma i - (D ij.1).arity ij.2 - arity_eq := by - funext i - exact congrFun ((D (seqComposeChallengeIdxToSigma i).1).arity_eq) - (seqComposeChallengeIdxToSigma i).2 - -/-- The arity of a sequentially composed CWSS structure is the component arity at the decoded -component challenge index. -/ -theorem seqCompose_arity (D : ∀ i, CWSSStructure (pSpec i)) : - (seqCompose D).arity = - fun combinedIdx => - let ij := seqComposeChallengeIdxToSigma combinedIdx - (D ij.1).arity ij.2 := rfl - /-- The shape induced by appended CWSS data is the generic append of the component shapes. -/ theorem toShape_append (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) : CWSSStructure.toShape (append D₁ D₂) = @@ -265,34 +215,6 @@ theorem toShape_append (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSp change j.val = j'.val exact (Fin.heq_ext_iff (by rw [hell, hk])).mp hj -/-- The sequentially-composed `decompose`, applied, equals the decoded component's `decompose` -applied to the cast-in challenge. The cast-commutation `cast_equiv_apply` applies directly here -because `seqCompose.decompose` is a single cast (no case split). -/ -theorem seqCompose_decompose_apply (D : ∀ i, CWSSStructure (pSpec i)) - (ci : (ProtocolSpec.seqCompose pSpec).ChallengeIdx) - (x : (ProtocolSpec.seqCompose pSpec).Challenge ci) : - (seqCompose D).decompose ci x = - (D (seqComposeChallengeIdxToSigma ci).1).decompose (seqComposeChallengeIdxToSigma ci).2 - (cast (seqCompose_challenge_eq ci) x) := - cast_equiv_apply (seqCompose_challenge_eq ci).symm rfl - ((D (seqComposeChallengeIdxToSigma ci).1).decompose (seqComposeChallengeIdxToSigma ci).2) x - -/-- The shape induced by finite sequential CWSS data is the generic sequential composition of the -component shapes. -/ -theorem toShape_seqCompose (D : ∀ i, CWSSStructure (pSpec i)) : - CWSSStructure.toShape (seqCompose D) = - ChallengeTreeShape.seqCompose (fun i => CWSSStructure.toShape (D i)) := by - refine ChallengeTreeShape.ext rfl (heq_of_eq ?_) - funext ci challenges - change CWSSStructure.nodeOk (seqCompose D) ci challenges = - (D (seqComposeChallengeIdxToSigma ci).1).nodeOk (seqComposeChallengeIdxToSigma ci).2 - (fun j => cast (seqCompose_challenge_eq ci) (challenges j)) - unfold CWSSStructure.nodeOk - congr 1 - funext j - rw [seqCompose_decompose_apply] - rfl - end CWSSStructure namespace Verifier @@ -361,23 +283,61 @@ theorem pure_accepting_of_mem subst x exact hout +/-- Converse of `pure_accepting_of_mem`: if a verifier deterministically outputs `out` on +`(stmt, tr)` and its run is accepted into `lang` with probability one, then `out ∈ lang`. -/ +theorem mem_of_pure_accepting + {n : ℕ} {pSpec : ProtocolSpec n} + (V : Verifier oSpec Stmt₁ Stmt₂ pSpec) + (stmt : Stmt₁) (tr : pSpec.FullTranscript) + (lang : Set Stmt₂) (out : Stmt₂) + (hV : V.verify stmt tr = pure out) + (hAcc : Pr[ (· ∈ lang) | + OptionT.mk do (simulateQ impl (V.run stmt tr)).run' (← init)] = 1) : + out ∈ lang := by + rw [probEvent_eq_one_iff] at hAcc + obtain ⟨hFail, hmem⟩ := hAcc + -- The underlying probabilistic computation is `init >>= fun _ => pure (some out)`. + have hrun : (do (simulateQ impl (V.run stmt tr)).run' (← init) : + ProbComp (Option Stmt₂)) = (init >>= fun _ => pure (some out)) := by + simp only [Verifier.run, hV] + congr 1 + refine hmem out ?_ + -- `init` has nonempty support, else the whole computation would fail with probability one. + have hne : (support init).Nonempty := by + by_contra hempty + rw [Set.not_nonempty_iff_eq_empty] at hempty + have hcfail : Pr[⊥ | + (init >>= fun _ => pure (some out) : ProbComp (Option Stmt₂))] = 0 := by + have h2 := hFail + rw [OptionT.probFailure_eq, OptionT.run_mk, hrun] at h2 + exact (add_eq_zero.mp h2).1 + have hcsupp : + support (init >>= fun _ => pure (some out) : ProbComp (Option Stmt₂)) = ∅ := by + rw [support_bind_const, support_pure]; simp [hempty] + rw [probFailure_eq_one hcsupp] at hcfail + exact one_ne_zero hcfail + rw [OptionT.mem_support_iff, OptionT.run_mk, hrun, support_bind_const, support_pure] + exact ⟨Set.mem_singleton _, hne⟩ + omit [∀ i, SampleableType (pSpec₂.Challenge i)] in -/-- Generic preservation of tree-special soundness under binary verifier append. -/ -theorem append_treeSpecialSound +/-- **Named-extractor preservation of tree-special soundness under binary verifier append.** The +composed extractor is a named function of the *left* factor's extractor alone: it runs `Ext₁` on +the prefix tree. The right factor's extractor enters only through `rel₂.language` (certifying the +left leaves' outputs), so it may stay existential. -/ +theorem append_treeSpecialSoundWith (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (S₁ : ChallengeTreeShape pSpec₁) (S₂ : ChallengeTreeShape pSpec₂) (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) (hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)) - (h₁ : V₁.treeSpecialSound init impl S₁ rel₁ rel₂) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ S₁.arity) + (h₁ : treeSpecialSoundWith init impl S₁ rel₁ rel₂ V₁ Ext₁) (h₂ : V₂.treeSpecialSound init impl S₂ rel₂ rel₃) : - (V₁.append V₂).treeSpecialSound init impl - (S₁.append S₂) rel₁ rel₃ := by - rcases h₁ with ⟨E₁, hE₁⟩ + treeSpecialSoundWith init impl (S₁.append S₂) rel₁ rel₃ (V₁.append V₂) + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by rcases h₂ with ⟨E₂, hE₂⟩ - refine ⟨fun stmt tree => E₁ stmt tree.appendSplit.fst, ?_⟩ intro stmt tree hStructured hAccept - apply hE₁ stmt tree.appendSplit.fst + apply h₁ stmt tree.appendSplit.fst · exact ChallengeTree.appendSplit_fst_isStructured tree hStructured · intro tr₁ htr₁ obtain ⟨path, rfl⟩ := @@ -407,25 +367,107 @@ theorem append_treeSpecialSound (verify₁ stmt path.fullTranscript) (hV₁ stmt path.fullTranscript) hLang₂ omit [∀ i, SampleableType (pSpec₂.Challenge i)] in -/-- Coordinate-wise special soundness is preserved by binary verifier append. +/-- **Escape-threaded preservation of tree special soundness under binary verifier append.** As in +the escape-free `append_treeSpecialSoundWith`, the composed extractor is the left extractor on the +prefix tree and the right factor's extractor stays existential; the composed escape event is +`ChallengeTree.EscapeEvent.append`. + +The proof is the escape-free one with one extra case split up front: if some prefix leaf's suffix +tree exhibits `esc₂`, the right disjunct of the composed event fires directly; otherwise every +prefix leaf's verdict is certified into `rel₂.language` by the right factor's extraction (its escape +branch being excluded by that case assumption), and the left certificate applies. -/ +theorem append_treeSpecialSoundWithEscape + (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) + (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) + (S₁ : ChallengeTreeShape pSpec₁) (S₂ : ChallengeTreeShape pSpec₂) + (esc₁ : ChallengeTree.EscapeEvent Stmt₁ pSpec₁ S₁.arity) + (esc₂ : ChallengeTree.EscapeEvent Stmt₂ pSpec₂ S₂.arity) + (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) + (hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ S₁.arity) + (h₁ : treeSpecialSoundWithEscape init impl S₁ esc₁ rel₁ rel₂ V₁ Ext₁) + (h₂ : treeSpecialSoundEscape init impl S₂ esc₂ rel₂ rel₃ V₂) : + treeSpecialSoundWithEscape init impl (S₁.append S₂) (esc₁.append esc₂ verify₁) + rel₁ rel₃ (V₁.append V₂) (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by + rcases h₂ with ⟨E₂, hE₂⟩ + intro stmt tree hStructured hAccept + by_cases hesc : ∃ path : LeafPath tree.appendSplit.fst, + esc₂ (verify₁ stmt path.fullTranscript) (tree.appendSplit.sndAt path) + · exact Or.inl (Or.inr hesc) + · push Not at hesc + have hLang : ∀ path : LeafPath tree.appendSplit.fst, + verify₁ stmt path.fullTranscript ∈ rel₂.language := by + intro path + have hSuffixStructured : (tree.appendSplit.sndAt path).IsStructured S₂ := + ChallengeTree.appendSplit_sndAt_isStructured tree hStructured path + have hSuffixAccept : + (tree.appendSplit.sndAt path).IsAccepting init impl V₂ + (verify₁ stmt path.fullTranscript) rel₃.language := by + intro tr₂ htr₂ + have hmem := + ChallengeTree.appendSplit_fullTranscripts_append_of_mem tree path htr₂ + have hfull := hAccept _ hmem + simpa [append_run_pure_left V₁ V₂ verify₁ hV₁ stmt path.fullTranscript tr₂] + using hfull + rcases hE₂ _ _ hSuffixStructured hSuffixAccept with hbad | hwit + · exact absurd hbad (hesc path) + · exact (Set.mem_language_iff rel₂ _).2 ⟨_, hwit⟩ + have hPrefixAccept : + tree.appendSplit.fst.IsAccepting init impl V₁ stmt rel₂.language := by + intro tr₁ htr₁ + obtain ⟨path, rfl⟩ := ChallengeTree.LeafPath.exists_of_mem_fullTranscripts htr₁ + exact pure_accepting_of_mem init impl V₁ stmt path.fullTranscript rel₂.language + (verify₁ stmt path.fullTranscript) (hV₁ stmt path.fullTranscript) (hLang path) + rcases h₁ stmt tree.appendSplit.fst + (ChallengeTree.appendSplit_fst_isStructured tree hStructured) hPrefixAccept with + hbad | hwit + · exact Or.inl (Or.inl hbad) + · exact Or.inr hwit -The deterministic first-verifier output identifies the input statement of each suffix tree consumed -by the second verifier's extractor. -/ -theorem append_coordinateWiseSpecialSound +omit [∀ i, SampleableType (pSpec₂.Challenge i)] in +/-- **Named-extractor preservation of CWSS under binary verifier append**: the composed extractor +is the left factor's extractor on the prefix tree, exactly as at the tree level +(`append_treeSpecialSoundWith`); the right factor's extractor stays existential. The shape +transport across `CWSSStructure.toShape_append` is `treeSpecialSoundWith_congr` — the arities of +the two shapes are definitionally equal, so the extractor crosses by `HEq.rfl`. -/ +theorem append_coordinateWiseSpecialSoundWith (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) (hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)) - (h₁ : V₁.coordinateWiseSpecialSound init impl D₁ rel₁ rel₂) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (h₁ : coordinateWiseSpecialSoundWith init impl D₁ rel₁ rel₂ V₁ Ext₁) (h₂ : V₂.coordinateWiseSpecialSound init impl D₂ rel₂ rel₃) : - (V₁.append V₂).coordinateWiseSpecialSound init impl - (CWSSStructure.append D₁ D₂) rel₁ rel₃ := by - change (V₁.append V₂).treeSpecialSound init impl - (CWSSStructure.toShape (CWSSStructure.append D₁ D₂)) rel₁ rel₃ - rw [CWSSStructure.toShape_append] - exact append_treeSpecialSound init impl V₁ V₂ - (CWSSStructure.toShape D₁) (CWSSStructure.toShape D₂) verify₁ hV₁ h₁ h₂ + coordinateWiseSpecialSoundWith init impl + (CWSSStructure.append D₁ D₂) rel₁ rel₃ (V₁.append V₂) + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := + treeSpecialSoundWith_congr init impl (CWSSStructure.toShape_append D₁ D₂).symm HEq.rfl + (append_treeSpecialSoundWith init impl V₁ V₂ + (CWSSStructure.toShape D₁) (CWSSStructure.toShape D₂) verify₁ hV₁ Ext₁ h₁ h₂) + +omit [∀ i, SampleableType (pSpec₂.Challenge i)] in +/-- **Escape-threaded preservation of CWSS under binary verifier append**: the CWSS-shape wrapper of +`append_treeSpecialSoundWithEscape`. Both the extractor and the event cross the shape equality +`CWSSStructure.toShape_append` by `HEq.rfl`, the two shapes' arities being definitionally equal. -/ +theorem append_coordinateWiseSpecialSoundWithEscape + (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) + (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) + (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) + (esc₁ : ChallengeTree.EscapeEvent Stmt₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (esc₂ : ChallengeTree.EscapeEvent Stmt₂ pSpec₂ (CWSSStructure.toShape D₂).arity) + (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) + (hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (h₁ : coordinateWiseSpecialSoundWithEscape init impl D₁ esc₁ rel₁ rel₂ V₁ Ext₁) + (h₂ : coordinateWiseSpecialSoundEscape init impl D₂ esc₂ rel₂ rel₃ V₂) : + coordinateWiseSpecialSoundWithEscape init impl (CWSSStructure.append D₁ D₂) + (esc₁.append esc₂ verify₁) rel₁ rel₃ (V₁.append V₂) + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := + treeSpecialSoundWithEscape_congr init impl (CWSSStructure.toShape_append D₁ D₂).symm + HEq.rfl HEq.rfl + (append_treeSpecialSoundWithEscape init impl V₁ V₂ + (CWSSStructure.toShape D₁) (CWSSStructure.toShape D₂) esc₁ esc₂ verify₁ hV₁ Ext₁ h₁ h₂) end Verifier @@ -447,22 +489,55 @@ variable {ι : Type} {oSpec : OracleSpec ι} {rel₃ : Set ((Stmt₃ × ∀ i, OStmt₃ i) × Wit₃)} omit [∀ i, SampleableType (pSpec₂.Challenge i)] in -/-- Oracle-verifier wrapper for binary CWSS append. -/ -theorem append_coordinateWiseSpecialSound +/-- Oracle-verifier wrapper for the named binary CWSS append: the composed extractor is the left +factor's extractor on the prefix tree. -/ +theorem append_coordinateWiseSpecialSoundWith (V₁ : OracleVerifier oSpec Stmt₁ OStmt₁ Stmt₂ OStmt₂ pSpec₁) (V₂ : OracleVerifier oSpec Stmt₂ OStmt₂ Stmt₃ OStmt₃ pSpec₂) (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) (verify₁ : (Stmt₁ × ∀ i, OStmt₁ i) → pSpec₁.FullTranscript → (Stmt₂ × ∀ i, OStmt₂ i)) (hV₁ : ∀ stmt tr, V₁.toVerifier.verify stmt tr = pure (verify₁ stmt tr)) - (h₁ : V₁.coordinateWiseSpecialSound init impl D₁ rel₁ rel₂) + (Ext₁ : Extractor.TreeBased (Stmt₁ × ∀ i, OStmt₁ i) Wit₁ pSpec₁ + (CWSSStructure.toShape D₁).arity) + (h₁ : V₁.coordinateWiseSpecialSoundWith init impl D₁ rel₁ rel₂ Ext₁) (h₂ : V₂.coordinateWiseSpecialSound init impl D₂ rel₂ rel₃) : - (V₁.append V₂).coordinateWiseSpecialSound init impl - (CWSSStructure.append D₁ D₂) rel₁ rel₃ := by - unfold OracleVerifier.coordinateWiseSpecialSound at h₁ h₂ ⊢ - convert Verifier.append_coordinateWiseSpecialSound init impl V₁.toVerifier V₂.toVerifier - D₁ D₂ verify₁ hV₁ h₁ h₂ - simp only [append_toVerifier] + (V₁.append V₂).coordinateWiseSpecialSoundWith init impl + (CWSSStructure.append D₁ D₂) rel₁ rel₃ + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by + unfold OracleVerifier.coordinateWiseSpecialSoundWith at h₁ ⊢ + unfold OracleVerifier.coordinateWiseSpecialSound at h₂ + rw [append_toVerifier] + exact Verifier.append_coordinateWiseSpecialSoundWith init impl V₁.toVerifier V₂.toVerifier + D₁ D₂ verify₁ hV₁ Ext₁ h₁ h₂ + +omit [∀ i, SampleableType (pSpec₂.Challenge i)] in +/-- Oracle-verifier wrapper for the escape-threaded binary CWSS append: the composed extractor is +the left factor's extractor on the prefix tree and the composed event is +`ChallengeTree.EscapeEvent.append` at the left factor's verdict map. -/ +theorem append_coordinateWiseSpecialSoundWithEscape + (V₁ : OracleVerifier oSpec Stmt₁ OStmt₁ Stmt₂ OStmt₂ pSpec₁) + (V₂ : OracleVerifier oSpec Stmt₂ OStmt₂ Stmt₃ OStmt₃ pSpec₂) + (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) + (esc₁ : ChallengeTree.EscapeEvent (Stmt₁ × ∀ i, OStmt₁ i) pSpec₁ + (CWSSStructure.toShape D₁).arity) + (esc₂ : ChallengeTree.EscapeEvent (Stmt₂ × ∀ i, OStmt₂ i) pSpec₂ + (CWSSStructure.toShape D₂).arity) + (verify₁ : + (Stmt₁ × ∀ i, OStmt₁ i) → pSpec₁.FullTranscript → (Stmt₂ × ∀ i, OStmt₂ i)) + (hV₁ : ∀ stmt tr, V₁.toVerifier.verify stmt tr = pure (verify₁ stmt tr)) + (Ext₁ : Extractor.TreeBased (Stmt₁ × ∀ i, OStmt₁ i) Wit₁ pSpec₁ + (CWSSStructure.toShape D₁).arity) + (h₁ : V₁.coordinateWiseSpecialSoundWithEscape init impl D₁ esc₁ rel₁ rel₂ Ext₁) + (h₂ : V₂.coordinateWiseSpecialSoundEscape init impl D₂ esc₂ rel₂ rel₃) : + (V₁.append V₂).coordinateWiseSpecialSoundWithEscape init impl + (CWSSStructure.append D₁ D₂) (esc₁.append esc₂ verify₁) rel₁ rel₃ + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by + unfold OracleVerifier.coordinateWiseSpecialSoundWithEscape at h₁ ⊢ + unfold OracleVerifier.coordinateWiseSpecialSoundEscape at h₂ + rw [append_toVerifier] + exact Verifier.append_coordinateWiseSpecialSoundWithEscape init impl V₁.toVerifier V₂.toVerifier + D₁ D₂ esc₁ esc₂ verify₁ hV₁ Ext₁ h₁ h₂ end OracleVerifier diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean index 9e6f69695d..84285091e1 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean @@ -3,25 +3,44 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.OracleReduction.Prelude +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded /-! - # Escape-threaded relations (`Set.withEscape`) + # Escape-aware CWSS packages and the package lattice - Protocol-agnostic plumbing for **escape threading** in composed special-soundness chains - (Hachi [NOZ26] §4.3+; design decision G1 of the sumcheck-track plan). + Some reductions cannot always extract a witness: instead their extraction exhibits a + cryptographic **escape**, e.g. a binding break of a commitment introduced mid-chain (Hachi's + `w̃`-commitment of Figure 4, whose collision is a Module-SIS solution by weak binding, + [NOZ26] Remark 2 / Lemma 7). An escape is an **event on the observable data** `(stmtIn, tree)` + (`ChallengeTree.EscapeEvent`) entering the certificate as a disjunct of its conclusion: - In a composed reduction chain, a downstream extractor may fail to produce a "real" witness and - instead produce a cryptographic **escape** — e.g. a binding break of a commitment introduced in - the middle of the chain (Hachi's `w̃`-commitment of Figure 4, whose collision is a Module-SIS - solution via weak binding, [NOZ26] Remark 2 / Lemma 7). Composed extraction feeds each - extractor's output into the *previous* seam relation, so every relation upstream of the escape's - origin must have a home for it. `Set.withEscape` widens a relation `Set (S × W)` to - `Set (S × (W ⊕ E))` by adjoining an escape set `esc : Set E` on the right summand. + ``` + ∀ stmt tree, IsStructured → IsAccepting → esc stmt tree ∨ (stmt, Ext stmt tree) ∈ relIn + ``` - Crucially, `esc` is **statement-independent**: an MSIS/collision solution is checkable against - the (parametric) commitment key alone, so escapes pass through statement maps trivially, and the - escape branch of every seam extractor is the identity `Sum.inr`. + Relations, witness types and extractors therefore stay plain; `esc` is the only escape-specific + field a package carries. Since `esc` never mentions the extractor, no choice of extractor can + discharge a certificate vacuously — the certificate is exactly as strong as its event is honest. + `esc` is a **trusted specification**, on the same footing as `relIn`/`relOut`; its contract is + stated once, on `ProtocolSpec.ChallengeTree.EscapeEvent`. Read it before writing an event. + + Packages carry their extraction algorithm as an explicit `extractor` field, so a composed chain + exposes an actual end-to-end extractor `chain.extractor` — the algorithm a later knowledge-error + accounting must run against the escape probability. + + ## The package lattice + + `CWSSPackage`, `EscapeCWSSPackage`, `GCWSSPackage`, `EscapeGCWSSPackage` form the 2×2 lattice + escape? × guarded?, ordered by two **lossless** lifts: `toEscape` (at the never-firing event + `fun _ _ => False`, so extractor and certificate are unchanged) and `toGuarded` (at the + trivially-true check). A package is declared in the weakest corner it honestly lives in, and + every ordered pair composes at the join through the universal `▷` — one scoped elaborator + dispatching on the factors' package kinds (`▷ᵍ`, `▷ₑ`, `▷ₑᵍ` remain as explicit synonyms). + + Composition identifies only the relation seam `hRel`: escape events are combined by + `ChallengeTree.EscapeEvent.append`, so factors tracking breaks of entirely different assumptions + compose freely. Two pure packages compose on the *proven* pure append theorem; a genuinely + guarded factor moves the composite — visibly in its type — onto the (sorried, B4) guarded one. ## References @@ -29,49 +48,359 @@ import ArkLib.OracleReduction.Prelude Polynomial Commitments over Extension Fields*][NOZ26] -/ -namespace Set - -variable {S W E : Type*} - -/-- Widen a relation by an escape disjunct: a witness is either a real witness `w : W` related to -the statement by `rel`, or an escape `e : E` in the statement-independent escape set `esc`. -/ -def withEscape (rel : Set (S × W)) (esc : Set E) : Set (S × (W ⊕ E)) := - {p | match p with - | (s, .inl w) => (s, w) ∈ rel - | (_, .inr e) => e ∈ esc} - -@[simp] -theorem mem_withEscape_inl (rel : Set (S × W)) (esc : Set E) (s : S) (w : W) : - (s, Sum.inl w) ∈ rel.withEscape esc ↔ (s, w) ∈ rel := Iff.rfl - -@[simp] -theorem mem_withEscape_inr (rel : Set (S × W)) (esc : Set E) (s : S) (e : E) : - (s, Sum.inr e) ∈ rel.withEscape esc ↔ e ∈ esc := Iff.rfl - -/-- The language of an escape-widened relation: a statement is in the language iff it is in the -original language, or *any* escape exists (escapes are statement-independent, so a single escape -puts every statement in the widened language). This is the formal price of escape threading: the -widened acceptance condition is meaningful *relative to the extractor structure*, exactly as the -MSIS disjuncts of Hachi's `relIn` already are. -/ -theorem mem_withEscape_language_iff (rel : Set (S × W)) (esc : Set E) (s : S) : - s ∈ (rel.withEscape esc).language ↔ s ∈ rel.language ∨ esc.Nonempty := by - simp only [Set.mem_language_iff] - constructor - · rintro ⟨w | e, hw⟩ - · exact Or.inl ⟨w, hw⟩ - · exact Or.inr ⟨e, hw⟩ - · rintro (⟨w, hw⟩ | ⟨e, he⟩) - · exact ⟨Sum.inl w, hw⟩ - · exact ⟨Sum.inr e, he⟩ - -/-- Degeneration: widening by the empty escape set over an empty escape type loses nothing — -membership is exactly membership of the underlying relation through `Sum.inl`. Together with -`Empty`'s emptiness this witnesses that the escape-threaded chain generalizes the un-threaded -one. -/ -theorem withEscape_empty_iff (rel : Set (S × W)) (s : S) (w : W ⊕ Empty) : - (s, w) ∈ rel.withEscape (∅ : Set Empty) ↔ ∃ w', w = Sum.inl w' ∧ (s, w') ∈ rel := by - rcases w with w' | e - · simp - · exact e.elim - -end Set +noncomputable section + +open OracleComp OracleSpec ProtocolSpec + +namespace CoordinateWise + +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- A **bundled escape-aware coordinate-wise-special-sound reduction**: `CWSSPackage` with one +extra field, the **escape event** `esc`. Its certificate `isCWSS` concludes +`esc stmt tree ∨ extraction succeeds` on every structured accepting tree, so `relIn`/`relOut` and +`extractor` stay ordinary. + +`esc` is a trusted specification — reading its definition is the reader's obligation, just as for +`relIn`/`relOut` (contract: `ChallengeTree.EscapeEvent`). Compose with `EscapeCWSSPackage.append` / +the universal infix `▷` (explicit synonym `▷ₑ`). -/ +structure EscapeCWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where + /-- The package's verifier. -/ + verifier : Verifier oSpec StmtIn StmtOut pSpec + /-- The coordinate-wise structure the verifier is special sound for. -/ + struct : CWSSStructure pSpec + /-- The input relation. -/ + relIn : Set (StmtIn × WitIn) + /-- The output relation. -/ + relOut : Set (StmtOut × WitOut) + /-- The **escape event**: the cryptographic failure this package's extraction may exhibit + instead of a witness. A trusted spec — see `ChallengeTree.EscapeEvent`. -/ + esc : ChallengeTree.EscapeEvent StmtIn pSpec (CWSSStructure.toShape struct).arity + /-- The verifier is pure: its verdict is a deterministic function of statement and transcript. + Needed to place this package as the left factor of an `append`. -/ + isPure : verifier.IsPure + /-- The package's named extraction algorithm. -/ + extractor : Extractor.TreeBased StmtIn WitIn pSpec (CWSSStructure.toShape struct).arity + /-- The certificate: on every structured accepting tree, either the tree exhibits the escape + event `esc`, or `extractor` produces a `relIn`-witness. -/ + isCWSS : Verifier.coordinateWiseSpecialSoundWithEscape init impl struct esc + relIn relOut verifier extractor + +namespace EscapeCWSSPackage + +/-- **Compose two escape-aware packages along a matching relation seam** `hRel` (discharged by +`rfl` when a chain uses named seam relations). The composed event is +`ChallengeTree.EscapeEvent.append`: the left event on the prefix tree, or the right event on the +suffix tree below some prefix leaf, at the verdict `L₁.isPure` computes there. Written infix as +`L₁ ▷ L₂` (explicit synonym `▷ₑ`). -/ +def append {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + (L₁ : EscapeCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) where + verifier := L₁.verifier.append L₂.verifier + struct := L₁.struct.append L₂.struct + relIn := L₁.relIn + relOut := L₂.relOut + esc := L₁.esc.append L₂.esc L₁.isPure.is_pure.choose + isPure := Verifier.IsPure.append L₁.verifier L₂.verifier L₁.isPure L₂.isPure + extractor := fun stmt tree => L₁.extractor stmt tree.appendSplit.fst + isCWSS := by + have h₂ := L₂.isCWSS.toEscape + rw [← hRel] at h₂ + exact Verifier.append_coordinateWiseSpecialSoundWithEscape init impl + L₁.verifier L₂.verifier L₁.struct L₂.struct L₁.esc L₂.esc + L₁.isPure.is_pure.choose L₁.isPure.is_pure.choose_spec L₁.extractor L₁.isCWSS h₂ + +end EscapeCWSSPackage + +@[inherit_doc EscapeCWSSPackage.append] +scoped infixr:65 " ▷ₑ " => EscapeCWSSPackage.append + +/-- A **guarded escape-aware CWSS package**: `EscapeCWSSPackage` with the purity witness relaxed +to a guardedness witness (the verifier may `failure` at runtime). -/ +structure EscapeGCWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where + /-- The package's verifier (which may reject at runtime). -/ + verifier : Verifier oSpec StmtIn StmtOut pSpec + /-- The coordinate-wise structure the verifier is special sound for. -/ + struct : CWSSStructure pSpec + /-- The input relation. -/ + relIn : Set (StmtIn × WitIn) + /-- The output relation. -/ + relOut : Set (StmtOut × WitOut) + /-- The **escape event**: a trusted spec (see `ChallengeTree.EscapeEvent`). -/ + esc : ChallengeTree.EscapeEvent StmtIn pSpec (CWSSStructure.toShape struct).arity + /-- The verifier is guarded by a deterministic Boolean check. -/ + isGuarded : verifier.IsGuarded + /-- The package's named extraction algorithm. -/ + extractor : Extractor.TreeBased StmtIn WitIn pSpec (CWSSStructure.toShape struct).arity + /-- The certificate: on every structured accepting tree, either the tree exhibits the escape + event `esc`, or `extractor` produces a `relIn`-witness. -/ + isCWSS : Verifier.coordinateWiseSpecialSoundWithEscape init impl struct esc + relIn relOut verifier extractor + +namespace EscapeGCWSSPackage + +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + +/-- Regard a pure escape-aware package as guarded, at the trivially-true check; every other field +carries over unchanged. Lossless. -/ +def _root_.CoordinateWise.EscapeCWSSPackage.toGuarded + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + (L : EscapeCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) : + EscapeGCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where + verifier := L.verifier + struct := L.struct + relIn := L.relIn + relOut := L.relOut + esc := L.esc + isGuarded := Verifier.IsGuarded.of_isPure L.verifier L.isPure + extractor := L.extractor + isCWSS := L.isCWSS + +/-- **Compose two guarded escape-aware packages along a matching relation seam.** As in +`EscapeCWSSPackage.append`, but the composed event is taken at the guard's output map `out₁`, which +`IsGuardedWith` leaves unconstrained on rejected prefixes — harmless, since escape events must be +honest at *all* `(stmt, tree)` pairs. Certificate: +`Verifier.append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft` (sorried, B4). Written infix +as `L₁ ▷ L₂` (explicit synonym `▷ₑᵍ`). -/ +def append {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + (L₁ : EscapeGCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeGCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) where + verifier := L₁.verifier.append L₂.verifier + struct := L₁.struct.append L₂.struct + relIn := L₁.relIn + relOut := L₂.relOut + esc := L₁.esc.append L₂.esc L₁.isGuarded.is_guarded.choose_spec.choose + isGuarded := Verifier.IsGuarded.append L₁.verifier L₂.verifier L₁.isGuarded L₂.isGuarded + extractor := fun stmt tree => L₁.extractor stmt tree.appendSplit.fst + isCWSS := by + have h₂ := L₂.isCWSS.toEscape + rw [← hRel] at h₂ + exact Verifier.append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft init impl + L₁.verifier L₂.verifier L₁.struct L₂.struct + L₁.isGuarded.is_guarded.choose L₁.isGuarded.is_guarded.choose_spec.choose + L₁.isGuarded.is_guarded.choose_spec.choose_spec + L₁.esc L₂.esc L₁.extractor L₁.isCWSS h₂ + +end EscapeGCWSSPackage + +@[inherit_doc EscapeGCWSSPackage.append] +scoped infixr:65 " ▷ₑᵍ " => EscapeGCWSSPackage.append + +/-! ### Lifting escape-free packages into an escape chain + +An escape-free package enters the escape world at the never-firing event `fun _ _ => False`, where +`Verifier.coordinateWiseSpecialSoundWith.withEscape` is the trivial `Or.inr`: extractor and +certificate are unchanged, and `coordinateWiseSpecialSoundWithEscape_false_iff` recovers the plain +notion exactly. Escape packages are therefore only *defined* for the subprotocols that genuinely +produce escapes; the mixed appends below (and the universal `▷`) insert the lifts on the fly. -/ + +section Lift + +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + +/-- Lift a pure escape-free package to the never-firing event; every other field carries over +unchanged. Lossless. -/ +def CWSSPackage.toEscape + (L : CWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) : + EscapeCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where + verifier := L.verifier + struct := L.struct + relIn := L.relIn + relOut := L.relOut + esc := fun _ _ => False + isPure := L.isPure + extractor := L.extractor + isCWSS := Verifier.coordinateWiseSpecialSoundWith.withEscape init impl _ L.isCWSS + +/-- Lift a guarded escape-free package to the never-firing event; every other field carries over +unchanged. Lossless. -/ +def GCWSSPackage.toEscape + (L : GCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) : + EscapeGCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where + verifier := L.verifier + struct := L.struct + relIn := L.relIn + relOut := L.relOut + esc := fun _ _ => False + isGuarded := L.isGuarded + extractor := L.extractor + isCWSS := Verifier.coordinateWiseSpecialSoundWith.withEscape init impl _ L.isCWSS + +end Lift + +/-! ### The mixed appends + +Every ordered pair of package kinds whose join is escape-aware (pure or guarded). Each lifts its +factors to the join and delegates, leaving only the relation seam `hRel` (discharged by `rfl`). All +are reached through the universal `▷` below; the escape-free appends live in `Package.lean` +(`CWSSPackage.append`) and `Guarded.lean` (`GCWSSPackage.append`, `CWSSPackage.appendGuarded`, +`GCWSSPackage.appendPure`). -/ + +section MixedAppend + +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + +/-- **Pure escape-free ▷ pure escape-aware.** Lifts the left factor. -/ +def CWSSPackage.appendEscape + (L₁ : CWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toEscape.append L₂ hRel + +/-- **Pure escape-aware ▷ pure escape-free.** Lifts the right factor, so the composed event is +the left event on the prefix (its right disjunct never fires). -/ +def EscapeCWSSPackage.appendPure + (L₁ : EscapeCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : CWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.append L₂.toEscape hRel + +/-- **Pure escape-free ▷ guarded escape-aware.** Lifts the left factor twice. -/ +def CWSSPackage.appendEscapeGuarded + (L₁ : CWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeGCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toEscape.toGuarded.append L₂ hRel + +/-- **Guarded escape-aware ▷ pure escape-free.** Lifts the right factor twice. -/ +def EscapeGCWSSPackage.appendPure + (L₁ : EscapeGCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : CWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.append L₂.toEscape.toGuarded hRel + +/-- **Guarded escape-free ▷ pure escape-aware.** Lifts the left factor to the never-event and the +right factor to the trivially-true guard. -/ +def GCWSSPackage.appendEscape + (L₁ : GCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toEscape.append L₂.toGuarded hRel + +/-- **Guarded escape-free ▷ guarded escape-aware.** Lifts the left factor to the never-event. -/ +def GCWSSPackage.appendEscapeGuarded + (L₁ : GCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeGCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toEscape.append L₂ hRel + +/-- **Pure escape-aware ▷ guarded escape-free.** Lifts the left factor to the trivially-true guard +and the right factor to the never-event. -/ +def EscapeCWSSPackage.appendGuarded + (L₁ : EscapeCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : GCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toGuarded.append L₂.toEscape hRel + +/-- **Pure escape-aware ▷ guarded escape-aware.** Lifts the left factor to the trivially-true +guard; both factors keep their own events. -/ +def EscapeCWSSPackage.appendEscapeGuarded + (L₁ : EscapeCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeGCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toGuarded.append L₂ hRel + +/-- **Guarded escape-aware ▷ guarded escape-free.** Lifts the right factor to the never-event. -/ +def EscapeGCWSSPackage.appendGuarded + (L₁ : EscapeGCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : GCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.append L₂.toEscape hRel + +/-- **Guarded escape-aware ▷ pure escape-aware.** Lifts the right factor to the trivially-true +guard; both factors keep their own events. -/ +def EscapeGCWSSPackage.appendEscape + (L₁ : EscapeGCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : EscapeCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + EscapeGCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.append L₂.toGuarded hRel + +end MixedAppend + +/-! ### The universal append `▷` + +A single (scoped) elaborator rather than sixteen overloaded notations: `L₁ ▷ L₂` elaborates both +factors once, reads the head constant of their types to determine the package kinds, and applies +the unique append that composes them at their join. Overloaded-notation `choice` nodes would +re-elaborate nested alternatives once per outer candidate — exponential in chain length, and a +five-link Hachi chain already exhausts the heartbeat budget — whereas this dispatch is linear. +The kind-marked infixes `▷ₑ`, `▷ᵍ`, `▷ₑᵍ` remain as single-target explicit synonyms. -/ + +section UniversalAppend + +open Lean Elab Term Meta + +/-- The dispatch table of the universal append `▷`: the two factors' package kinds determine the +append that composes them at their join. -/ +private def univAppendFn : Name → Name → Option Name + | ``CWSSPackage, ``CWSSPackage => some ``CWSSPackage.append + | ``CWSSPackage, ``EscapeCWSSPackage => some ``CWSSPackage.appendEscape + | ``CWSSPackage, ``GCWSSPackage => some ``CWSSPackage.appendGuarded + | ``CWSSPackage, ``EscapeGCWSSPackage => some ``CWSSPackage.appendEscapeGuarded + | ``EscapeCWSSPackage, ``CWSSPackage => some ``EscapeCWSSPackage.appendPure + | ``EscapeCWSSPackage, ``EscapeCWSSPackage => some ``EscapeCWSSPackage.append + | ``EscapeCWSSPackage, ``GCWSSPackage => some ``EscapeCWSSPackage.appendGuarded + | ``EscapeCWSSPackage, ``EscapeGCWSSPackage => some ``EscapeCWSSPackage.appendEscapeGuarded + | ``GCWSSPackage, ``CWSSPackage => some ``GCWSSPackage.appendPure + | ``GCWSSPackage, ``EscapeCWSSPackage => some ``GCWSSPackage.appendEscape + | ``GCWSSPackage, ``GCWSSPackage => some ``GCWSSPackage.append + | ``GCWSSPackage, ``EscapeGCWSSPackage => some ``GCWSSPackage.appendEscapeGuarded + | ``EscapeGCWSSPackage, ``CWSSPackage => some ``EscapeGCWSSPackage.appendPure + | ``EscapeGCWSSPackage, ``EscapeCWSSPackage => some ``EscapeGCWSSPackage.appendEscape + | ``EscapeGCWSSPackage, ``GCWSSPackage => some ``EscapeGCWSSPackage.appendGuarded + | ``EscapeGCWSSPackage, ``EscapeGCWSSPackage => some ``EscapeGCWSSPackage.append + | _, _ => none + +/-- The package kind — the head constant of the type — of an elaborated `▷` factor. -/ +private def packageKindOf (e : Expr) : TermElabM Name := do + let t ← whnf (← instantiateMVars (← inferType e)) + match t.getAppFn.constName? with + | some n => return n + | none => + throwError "▷: cannot determine the package kind of{indentExpr e}\nof type{indentExpr t}" + +/-- **The universal package append.** `L₁ ▷ L₂` composes any two CWSS packages — pure, guarded, +escape-aware, or both — at the join of their kinds, lifting each factor as needed. The relation +seam is discharged by `rfl`; for a non-definitional seam call the dispatched append (see +`univAppendFn`) explicitly with the seam proof. -/ +scoped elab:65 l:term:66 " ▷ " r:term:65 : term => do + let lE ← elabTerm l none + let rE ← elabTerm r none + let lN ← packageKindOf lE + let rN ← packageKindOf rE + let some fn := univAppendFn lN rN + | throwError "▷: no package append composes `{lN}` with `{rN}`" + let f ← mkConstWithFreshMVarLevels fn + elabAppArgs f #[] #[.expr lE, .expr rE] (expectedType? := none) + (explicit := false) (ellipsis := false) + +end UniversalAppend + +end CoordinateWise + +end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean index 6d3959f4d7..790ebdd6a9 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean @@ -6,15 +6,15 @@ Authors: Tobias Rothmann import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! - # Guarded verifiers and guarded CWSS composition (`GCWSSPackage`, `▷ᵍ`) + # Guarded verifiers and guarded CWSS composition (`GCWSSPackage`) - **Skeleton of milestone B4** of the Hachi sumcheck track (see - `HACHI_SUMCHECK_TRACK_PLAN.md` §2): coordinate-wise special soundness (CWSS) composition - where the *left* factor may **reject at runtime**. + **Skeleton of milestone B4** of the Hachi sumcheck track (inventoried as *generic machinery* in + `Commitments/Functional/Hachi/Composition.lean`): coordinate-wise special soundness (CWSS) + composition where the *left* factor may **reject at runtime**. ## Why guarded verifiers - The existing composition machinery (`Verifier.append_coordinateWiseSpecialSound`, + The existing composition machinery (`Verifier.append_coordinateWiseSpecialSoundWith`, `CWSSPackage.append` = `▷`) requires the left verifier to be *pure*: its verdict is a deterministic total function of statement and transcript, with all acceptance conditions living in the output **relation**. This works whenever the data a check reads survives into the output @@ -41,18 +41,19 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package * `Verifier.IsGuarded.append` — closure of guardedness under `Verifier.append` (**sorried**; B4.4: composite check `check₁ s tr.fst && check₂ (out₁ s tr.fst) tr.snd`, mirroring `Verifier.IsPure.append`). - * `Verifier.append_coordinateWiseSpecialSound_of_guardedLeft` — the guarded binary CWSS append - (**sorried**; B4.3: transplant of the pure proof with two deltas — (i) rewrite the composed - run via a guarded `append_run` lemma and dismiss the `check = false` branch against - acceptance-probability `1` vs `failure`'s probability `0`; (ii) certify left-leaf outputs in - `rel₂.language` via a guarded `accepting_of_mem`). + * `Verifier.append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft` — the escape-threaded + guarded binary CWSS append, and the *fundamental* B4.3 obligation (**sorried**; stated at + explicit guard data, since the composed escape event must name the left verdict map). + * `Verifier.append_coordinateWiseSpecialSoundWith_of_guardedLeft` — the plain guarded append, + **proven** as a corollary of the escape-threaded one at the never-firing events. * `GCWSSPackage` — the guarded analogue of `CWSSPackage` (`isPure` ↝ `isGuarded`), with - `CWSSPackage.toGuarded` and the composition `GCWSSPackage.append` = infix `▷ᵍ`. + `CWSSPackage.toGuarded` and the composition `GCWSSPackage.append` = infix `▷` + (explicit synonym `▷ᵍ`). - A guarded n-ary `seqCompose` variant (B4.4) is deliberately not skeletonized here: the Hachi - composition builds its guarded loop by *recursion over binary `▷ᵍ`* - (`ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean`), which only needs the binary - theorem. + As everywhere in the CWSS development, composition here is **binary only**: the Hachi composition + builds its guarded loop by *recursion over the binary guarded append* + (`ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean`), so no `n`-ary guarded variant is + needed (nor does an `n`-ary CWSS composition exist to mirror). ## References @@ -113,12 +114,17 @@ theorem IsGuarded.append (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₁.append V₂).IsGuarded := by sorry -/-- **Guarded binary CWSS append (skeleton of B4.3, the core of milestone B4).** Coordinate-wise -special soundness is preserved by `Verifier.append` when the left factor is merely *guarded* -(rather than pure). +/-- **Guarded binary CWSS append, escape-threaded named form (skeleton of B4.3, the core of +milestone B4).** Escape-threaded CWSS is preserved by `Verifier.append` when the left factor is +merely *guarded* rather than pure, at the same composed extractor and event as the pure append. -**Sorried.** Proof plan (transplant of `Verifier.append_coordinateWiseSpecialSound`, -`Composition.lean`, with two deltas): +Stated at **explicit guard data** `(check₁, out₁, hV₁)` rather than at the bare `V₁.IsGuarded`, +because the composed event has to *name* the left verdict map `out₁`. On rejected prefixes `out₁` is +unconstrained by `IsGuardedWith`, so the composed event may evaluate `esc₂` at junk intermediate +statements — harmless, since escape events must be honest breaks at *all* `(stmt, tree)` pairs. + +**Sorried.** Proof plan: transplant `Verifier.append_treeSpecialSoundWithEscape` +(`Composition.lean`) — the disjunction is handled exactly as there — with two deltas: 1. A guarded left-run lemma `append_run_guardedLeft`: `(V₁.append V₂).run stmt (tr₁ ++ₜ tr₂) = if check₁ stmt tr₁ then V₂.run (out₁ stmt tr₁) tr₂ else failure` (mirror of `append_run_pure_left`, plus `failure_bind`). On an accepting leaf @@ -131,15 +137,47 @@ special soundness is preserved by `Verifier.append` when the left factor is mere the pure proof already extracts via `LeafPath.exists_of_mem_fullTranscripts`.) The tree machinery (`appendSplit` and friends) is untouched. -/ -theorem append_coordinateWiseSpecialSound_of_guardedLeft +theorem append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft + (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) + (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) + (check₁ : Stmt₁ → pSpec₁.FullTranscript → Bool) + (out₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) + (hV₁ : V₁.IsGuardedWith check₁ out₁) + (esc₁ : ChallengeTree.EscapeEvent Stmt₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (esc₂ : ChallengeTree.EscapeEvent Stmt₂ pSpec₂ (CWSSStructure.toShape D₂).arity) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (h₁ : coordinateWiseSpecialSoundWithEscape init impl D₁ esc₁ rel₁ rel₂ V₁ Ext₁) + (h₂ : coordinateWiseSpecialSoundEscape init impl D₂ esc₂ rel₂ rel₃ V₂) : + coordinateWiseSpecialSoundWithEscape init impl + (CWSSStructure.append D₁ D₂) (esc₁.append esc₂ out₁) rel₁ rel₃ (V₁.append V₂) + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by + sorry + +/-- **Guarded binary CWSS append, plain named form** — a *proven corollary* of the escape-threaded +obligation above at `esc₁ = esc₂ = fun _ _ => False`, where the composed event is propositionally +never-firing and so eliminable. This is why the escape-threaded theorem, not this one, is the +fundamental B4.3 obligation. -/ +theorem append_coordinateWiseSpecialSoundWith_of_guardedLeft (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) (hV₁ : V₁.IsGuarded) - (h₁ : V₁.coordinateWiseSpecialSound init impl D₁ rel₁ rel₂) + (Ext₁ : Extractor.TreeBased Stmt₁ Wit₁ pSpec₁ (CWSSStructure.toShape D₁).arity) + (h₁ : coordinateWiseSpecialSoundWith init impl D₁ rel₁ rel₂ V₁ Ext₁) (h₂ : V₂.coordinateWiseSpecialSound init impl D₂ rel₂ rel₃) : - (V₁.append V₂).coordinateWiseSpecialSound init impl - (CWSSStructure.append D₁ D₂) rel₁ rel₃ := by - sorry + coordinateWiseSpecialSoundWith init impl + (CWSSStructure.append D₁ D₂) rel₁ rel₃ (V₁.append V₂) + (fun stmt tree => Ext₁ stmt tree.appendSplit.fst) := by + obtain ⟨E₂, hE₂⟩ := h₂ + have hesc := append_coordinateWiseSpecialSoundWithEscape_of_guardedLeft init impl V₁ V₂ D₁ D₂ + hV₁.is_guarded.choose hV₁.is_guarded.choose_spec.choose + hV₁.is_guarded.choose_spec.choose_spec (fun _ _ => False) (fun _ _ => False) Ext₁ + (Verifier.coordinateWiseSpecialSoundWith.withEscape init impl _ h₁) + (Verifier.coordinateWiseSpecialSoundWith.withEscape init impl _ hE₂).toEscape + intro stmt tree hStructured hAccept + rcases hesc stmt tree hStructured hAccept with (hf | ⟨_, hf⟩) | hwit + · exact hf.elim + · exact hf.elim + · exact hwit end Append @@ -151,7 +189,8 @@ variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- A **bundled guarded coordinate-wise-special-sound reduction**: `CWSSPackage` with the purity witness relaxed to a guardedness witness. Guarded packages compose with `GCWSSPackage.append` -(infix `▷ᵍ`); a pure package enters the guarded world via `CWSSPackage.toGuarded`. -/ +(infix `▷`, explicit synonym `▷ᵍ`); a pure package enters the guarded world via +`CWSSPackage.toGuarded`, or automatically through the mixed `▷` overloads below. -/ structure GCWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where /-- The package's verifier (may reject at runtime). -/ @@ -166,16 +205,19 @@ structure GCWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ P transcript behind a `Bool` check. Needed to place this package as the left factor of a guarded append. -/ isGuarded : verifier.IsGuarded - /-- The certificate: `verifier` is coordinate-wise special sound for `struct`, reducing `relIn` - to `relOut`. -/ - isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut + /-- The package's named extraction algorithm. -/ + extractor : Extractor.TreeBased StmtIn WitIn pSpec (CWSSStructure.toShape struct).arity + /-- The certificate: `extractor` witnesses that `verifier` is coordinate-wise special sound + for `struct`, reducing `relIn` to `relOut`. -/ + isCWSS : Verifier.coordinateWiseSpecialSoundWith init impl struct relIn relOut verifier + extractor namespace GCWSSPackage variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} /-- Forget purity: every (pure) `CWSSPackage` is a `GCWSSPackage` with the trivially-true -check. -/ +check; extractor and certificate carry over unchanged. -/ def _root_.CoordinateWise.CWSSPackage.toGuarded {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} (L : CWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) : @@ -185,14 +227,16 @@ def _root_.CoordinateWise.CWSSPackage.toGuarded relIn := L.relIn relOut := L.relOut isGuarded := Verifier.IsGuarded.of_isPure L.verifier L.isPure + extractor := L.extractor isCWSS := L.isCWSS /-- **Compose two guarded packages along a matching seam** (`hseam` discharged by `rfl`): the guarded analogue of `CWSSPackage.append`/`▷`. The composed verdict is guarded by the conjunction -of both checks (`Verifier.IsGuarded.append`), and the composed certificate is the guarded binary -append theorem `Verifier.append_coordinateWiseSpecialSound_of_guardedLeft` (both currently -sorried B4 milestones — this definition is the *interface* the Hachi chain composes through). -Written infix as `L₁ ▷ᵍ L₂`. -/ +of both checks (`Verifier.IsGuarded.append`), the composed extractor is the left extractor on +the prefix tree, and the composed certificate is the guarded binary append theorem +`Verifier.append_coordinateWiseSpecialSoundWith_of_guardedLeft` (both currently sorried B4 +milestones — this definition is the *interface* the Hachi chain composes through). Written infix +as `L₁ ▷ L₂` (explicit synonym `▷ᵍ`). -/ def append {StmtA WitA StmtB WitB StmtC WitC : Type} {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} [∀ i, SampleableType (pSpec₁.Challenge i)] @@ -205,17 +249,59 @@ def append {StmtA WitA StmtB WitB StmtC WitC : Type} relIn := L₁.relIn relOut := L₂.relOut isGuarded := Verifier.IsGuarded.append L₁.verifier L₂.verifier L₁.isGuarded L₂.isGuarded + extractor := fun stmt tree => L₁.extractor stmt tree.appendSplit.fst isCWSS := by - have h₂ := L₂.isCWSS + have h₂ := L₂.isCWSS.toCWSS rw [← hseam] at h₂ - exact Verifier.append_coordinateWiseSpecialSound_of_guardedLeft init impl - L₁.verifier L₂.verifier L₁.struct L₂.struct L₁.isGuarded L₁.isCWSS h₂ + exact Verifier.append_coordinateWiseSpecialSoundWith_of_guardedLeft init impl + L₁.verifier L₂.verifier L₁.struct L₂.struct L₁.isGuarded L₁.extractor L₁.isCWSS h₂ end GCWSSPackage @[inherit_doc GCWSSPackage.append] scoped infixr:65 " ▷ᵍ " => GCWSSPackage.append +/-! ### Lifting pure packages into a guarded chain + +A pure `CWSSPackage` enters the guarded world losslessly (`CWSSPackage.toGuarded`: the guard is +the trivially-true check and the certificate is unchanged). The mixed appends below insert this +lift automatically, so guarded packages need only be *defined* for the subprotocols whose checks +genuinely reject at runtime. Together with the escape-lifting appends in `Escape.lean`, every +ordered pair of package kinds (escape? × guarded?) composes at its join through the universal +`▷` elaborator defined in `Escape.lean`: two pure packages compose pure (staying on the proven +pure append theorem), while a single guarded factor moves the composite — visibly in its type — +onto the guarded append theorem. -/ + +section GuardedLift + +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + +/-- **Compose a pure left factor with a guarded right factor.** The left package is lifted with +`CWSSPackage.toGuarded`; only the relation seam `hRel` remains (discharged by `rfl`). +Dispatched by the universal `▷`. -/ +def CWSSPackage.appendGuarded {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + (L₁ : CWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : GCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + GCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.toGuarded.append L₂ hRel + +/-- **Compose a guarded left factor with a pure right factor.** The right package is lifted with +`CWSSPackage.toGuarded`; only the relation seam `hRel` remains (discharged by `rfl`). +Dispatched by the universal `▷`. -/ +def GCWSSPackage.appendPure {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + (L₁ : GCWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : CWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hRel : L₁.relOut = L₂.relIn := by rfl) : + GCWSSPackage init impl StmtA WitA StmtC WitC (pSpec₁ ++ₚ pSpec₂) := + L₁.append L₂.toGuarded hRel + +end GuardedLift + end CoordinateWise end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean index bfbfb8c025..14ba1ad19e 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean @@ -25,8 +25,8 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic * `ProtocolSpec.ChallengeTree.transcripts_eq_singleton` / `fullTranscripts_eq_singleton` — a no-challenge tree lists exactly one transcript. * `ProtocolSpec.ChallengeTree.onlyTranscript` (+ `onlyTranscript_mem`) — that unique transcript. - * `Verifier.treeSpecialSound_of_isEmpty_challengeIdx` — the bridge. - * `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx` and its `OracleVerifier` analogue. + * `Verifier.treeSpecialSoundWith_of_isEmpty_challengeIdx` — the bridge. + * `Verifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx` and its `OracleVerifier` analogue. -/ noncomputable section @@ -38,9 +38,9 @@ namespace CWSSStructure /-- The canonical coordinate-wise structure on a **challenge-free** protocol (`IsEmpty pSpec.ChallengeIdx`): every field is the empty eliminator, since there are no challenge - rounds to describe. `coordinateWiseSpecialSound_of_isEmpty_challengeIdx` proves CWSS for *any* + rounds to describe. `coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx` proves CWSS for *any* `D` on such a protocol, but the binary-append composition theorem - (`Verifier.append_coordinateWiseSpecialSound`) needs a *concrete* structure for the zero-round + (`Verifier.append_coordinateWiseSpecialSoundWith`) needs a *concrete* structure for the zero-round left factor (a `ReduceClaim`/`CheckClaim` head); this is that structure. -/ def ofIsEmpty {n : ℕ} {pSpec : ProtocolSpec n} [IsEmpty pSpec.ChallengeIdx] : CWSSStructure pSpec where @@ -97,11 +97,13 @@ variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) -/-- **Degenerate tree special soundness.** For a protocol with no challenge rounds, the tree is a - single message-chain, so tree special soundness reduces to a transcript-level extractor: any `e` - such that "the verifier accepts the (unique) transcript into `relOut.language`" implies the - extracted witness lies in `relIn`. The shape `S` is irrelevant (`IsStructured` is vacuous). -/ -theorem treeSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] +/-- **Degenerate tree special soundness, named form.** For a protocol with no challenge rounds, + the tree is a single message-chain, so tree special soundness reduces to a transcript-level + extractor: any `e` such that "the verifier accepts the (unique) transcript into + `relOut.language`" implies the extracted witness lies in `relIn`, witnesses tree special + soundness at the tree extractor `fun stmtIn tree => e stmtIn tree.onlyTranscript`. The shape + `S` is irrelevant (`IsStructured` is vacuous). -/ +theorem treeSpecialSoundWith_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] (S : ChallengeTreeShape pSpec) (V : Verifier oSpec StmtIn StmtOut pSpec) (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) (e : StmtIn → FullTranscript pSpec → WitIn) @@ -109,13 +111,14 @@ theorem treeSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] Pr[ (· ∈ relOut.language) | OptionT.mk do (simulateQ impl (V.run stmtIn tr)).run' (← init)] = 1 → (stmtIn, e stmtIn tr) ∈ relIn) : - V.treeSpecialSound init impl S relIn relOut := - ⟨fun stmtIn tree => e stmtIn tree.onlyTranscript, - fun stmtIn tree _ hAcc => h stmtIn _ (hAcc _ tree.onlyTranscript_mem)⟩ - -/-- CWSS corollary of `treeSpecialSound_of_isEmpty_challengeIdx`: any coordinate-wise structure `D` - works, since `IsStructured` is vacuous with no challenge rounds. -/ -theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] + treeSpecialSoundWith init impl S relIn relOut V + (fun stmtIn tree => e stmtIn tree.onlyTranscript) := + fun stmtIn tree _ hAcc => h stmtIn _ (hAcc _ tree.onlyTranscript_mem) + +/-- CWSS of a challenge-free protocol, **named form**: the transcript-level extractor `e` on the + tree's unique transcript witnesses CWSS. Any coordinate-wise structure `D` works, since + `IsStructured` is vacuous with no challenge rounds. -/ +theorem coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] (D : CWSSStructure pSpec) (V : Verifier oSpec StmtIn StmtOut pSpec) (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) (e : StmtIn → FullTranscript pSpec → WitIn) @@ -123,8 +126,9 @@ theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.Challe Pr[ (· ∈ relOut.language) | OptionT.mk do (simulateQ impl (V.run stmtIn tr)).run' (← init)] = 1 → (stmtIn, e stmtIn tr) ∈ relIn) : - V.coordinateWiseSpecialSound init impl D relIn relOut := - treeSpecialSound_of_isEmpty_challengeIdx init impl D.toShape V relIn relOut e h + coordinateWiseSpecialSoundWith init impl D relIn relOut V + (fun stmtIn tree => e stmtIn tree.onlyTranscript) := + treeSpecialSoundWith_of_isEmpty_challengeIdx init impl D.toShape V relIn relOut e h end Verifier @@ -140,9 +144,10 @@ variable {ι : Type} {oSpec : OracleSpec ι} [∀ i, OracleInterface (pSpec.Message i)] {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) -/-- Oracle-reduction analogue of `coordinateWiseSpecialSound_of_isEmpty_challengeIdx`, on the - combined `(StmtIn × ∀ i, OStmtIn i)` statement. -/ -theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] +/-- Oracle-reduction analogue of `coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`, on the + combined `(StmtIn × ∀ i, OStmtIn i)` statement: the transcript-level extractor `e` on the + tree's unique transcript witnesses CWSS. -/ +theorem coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] (D : CWSSStructure pSpec) (V : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) @@ -152,7 +157,9 @@ theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.Challe Pr[ (· ∈ relOut.language) | OptionT.mk do (simulateQ impl (V.toVerifier.run stmtIn tr)).run' (← init)] = 1 → (stmtIn, e stmtIn tr) ∈ relIn) : - V.coordinateWiseSpecialSound init impl D relIn relOut := - V.toVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D relIn relOut e h + V.coordinateWiseSpecialSoundWith init impl D relIn relOut + (fun stmtIn tree => e stmtIn tree.onlyTranscript) := + Verifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D V.toVerifier + relIn relOut e h end OracleVerifier diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean index 5ee5a89080..1c27bd9272 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean @@ -19,7 +19,7 @@ The point is composition. `CWSSPackage.append` — written with the infix `▷` along a matching seam (`L₁.relOut = L₂.relIn`, discharged by `rfl`): it appends the verifiers (`Verifier.append`), appends the structures (`CWSSStructure.append`), composes the purity witnesses (`Verifier.IsPure.append`), and threads the two certificates through -`Verifier.append_coordinateWiseSpecialSound`. Because purity is a package field, the composed +`Verifier.append_coordinateWiseSpecialSoundWith`. Because purity is a package field, the composed package is itself pure and can be a left factor again, so a multi-step reduction reads as a single pleasant chain: @@ -29,8 +29,9 @@ theorem chain_cwss := chain.isCWSS ``` Each protocol component exports its own package next to its CWSS theorem; the composition site only -imports and chains them. `▷` is `scoped` in `CoordinateWise`, so `open scoped CoordinateWise` -(or `open CoordinateWise`) activates it. +imports and chains them. The universal `▷` is a single elaborator defined in `Escape.lean` (it +dispatches over all four package kinds — pure, guarded, escape-aware, or both); it is `scoped` in +`CoordinateWise`, so `open scoped CoordinateWise` (or `open CoordinateWise`) activates it. ## References @@ -49,8 +50,15 @@ namespace CoordinateWise variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- A **bundled coordinate-wise-special-sound reduction**: a verifier together with its CWSS -structure, input/output relations, a purity witness, and the CWSS certificate, all with respect to -a fixed sampling `(init, impl)`. Compose packages with `CWSSPackage.append` / the infix `▷`. -/ +structure, input/output relations, a purity witness, the **named extraction algorithm** +`extractor`, and the certificate `isCWSS` that this extractor witnesses CWSS, all with respect +to a fixed sampling `(init, impl)`. Compose packages with `CWSSPackage.append` / the infix `▷`. + +Carrying the extractor as a *field* (rather than existentially inside the certificate) means a +composed chain exposes an actual end-to-end extractor — `chain.extractor` — which is what a +later knowledge-error accounting must run, and what keeps escape-widened certificates honest +(see `Verifier.treeSpecialSoundWith`). The existential form remains available as +`L.isCWSS.toCWSS`. -/ structure CWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where /-- The package's verifier. -/ @@ -64,20 +72,26 @@ structure CWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ Pr /-- The verifier is pure: its verdict is a deterministic function of statement and transcript. Needed to place this package as the left factor of an `append`. -/ isPure : verifier.IsPure - /-- The certificate: `verifier` is coordinate-wise special sound for `struct`, reducing `relIn` - to `relOut`. -/ - isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut + /-- The package's named extraction algorithm. -/ + extractor : Extractor.TreeBased StmtIn WitIn pSpec (CWSSStructure.toShape struct).arity + /-- The certificate: `extractor` witnesses that `verifier` is coordinate-wise special sound + for `struct`, reducing `relIn` to `relOut`. -/ + isCWSS : Verifier.coordinateWiseSpecialSoundWith init impl struct relIn relOut verifier + extractor namespace CWSSPackage +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + /-- **Compose two packages along a matching seam.** Given a left package `L₁ : relIn ⇒ mid`, a right package `L₂ : mid ⇒ relOut` whose seam agrees (`hseam : L₁.relOut = L₂.relIn`, discharged by `rfl`), this produces the composed package `relIn ⇒ relOut` over `pSpec₁ ++ₚ pSpec₂`: the verifiers are chained by `Verifier.append`, the structures by `CWSSStructure.append`, the purity witnesses by -`Verifier.IsPure.append`, and the certificates by `Verifier.append_coordinateWiseSpecialSound` -(the left package's `isPure` discharges its purity hypothesis). Written infix as `L₁ ▷ L₂`. -/ -def append {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} - {StmtA WitA StmtB WitB StmtC WitC : Type} +`Verifier.IsPure.append`, the extractors by running the left extractor on the prefix tree, and +the certificates by `Verifier.append_coordinateWiseSpecialSoundWith` (the left package's `isPure` +discharges its purity hypothesis). Written infix as `L₁ ▷ L₂`. -/ +noncomputable def append {StmtA WitA StmtB WitB StmtC WitC : Type} {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} [∀ i, SampleableType (pSpec₁.Challenge i)] (L₁ : CWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) @@ -89,18 +103,16 @@ def append {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} relIn := L₁.relIn relOut := L₂.relOut isPure := Verifier.IsPure.append L₁.verifier L₂.verifier L₁.isPure L₂.isPure + extractor := fun stmt tree => L₁.extractor stmt tree.appendSplit.fst isCWSS := by - obtain ⟨verify₁, hV₁⟩ := L₁.isPure.is_pure - have h₂ := L₂.isCWSS + have h₂ := L₂.isCWSS.toCWSS rw [← hseam] at h₂ - exact Verifier.append_coordinateWiseSpecialSound init impl - L₁.verifier L₂.verifier L₁.struct L₂.struct verify₁ hV₁ L₁.isCWSS h₂ + exact Verifier.append_coordinateWiseSpecialSoundWith init impl + L₁.verifier L₂.verifier L₁.struct L₂.struct + L₁.isPure.is_pure.choose L₁.isPure.is_pure.choose_spec L₁.extractor L₁.isCWSS h₂ end CWSSPackage -@[inherit_doc CWSSPackage.append] -scoped infixr:65 " ▷ " => CWSSPackage.append - end CoordinateWise end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean index e8d2d27860..3f79b97375 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean @@ -8,9 +8,9 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRoun /-! # Scalar single-challenge-round CWSS assembly (generic building block) - **Skeleton of milestone F4.1** of the Hachi sumcheck track (`HACHI_SUMCHECK_TRACK_PLAN.md` - §5): the `(ℓ = 1, k)` twin of `CoordinateWise.SingleRound` (which stays pinned to the - vector-challenge `(ℓ, k) = (2^r, 2)` fold shape of `QuadEval`). + **Skeleton of milestone F4.1** of the Hachi sumcheck track: the `(ℓ = 1, k)` twin of + `CoordinateWise.SingleRound` (which stays pinned to the vector-challenge `(ℓ, k) = (2^r, 2)` + fold shape of `QuadEval`). Several Hachi subprotocols are two-round reductions "one prover message, then one **scalar** challenge" whose special soundness is plain `k`-special soundness (`ℓ = 1`) at various `k`: @@ -19,16 +19,30 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRoun * each paired sumcheck round (Figure 6 / Lemma 11): message = round-polynomial pair, challenge `aᵢ ← F`, `k = max-degree + 1`. - This file provides their shared wire format `pSpecScalar`, the CWSS structure - `scalarStructure k` (= `CWSSStructure.ofSpecialSound`, arity `k`), the per-round instances, - and the **sorried** generic assembly `coordinateWiseSpecialSound_of_mkWitness_scalar`: any pure + ## What is finished here + + The shared wire format `pSpecScalar`; the CWSS structure `scalarStructure k` + (= `CWSSStructure.ofSpecialSound`, arity `1·(k−1)+1 = k`, with the bridge + `scalarStructure_arity`); the per-round instances; the **tree readers and shape recovery** + (`readPre`, `readChallenges`, `tree2`, `tree_shape` — the `(ℓ = 1, k)` transplant of + `SingleRound.lean`'s, index-generic in the same way); the named extractor + `treeExtractorScalar`; and the escape event `escEventScalar` induced by a local per-family + event. + + ## What is still sorried (F4.1) + + The two generic assemblies — `coordinateWiseSpecialSoundWith_of_mkWitness_scalar` and its + escape-threaded twin `coordinateWiseSpecialSoundWithEscape_of_mkWitness_scalar`: any pure statement-extending verifier of this shape is CWSS for `scalarStructure k`, given only a witness assembler `mkWitness` that turns `k` per-branch `relOut`-witnesses at *pairwise-distinct* - challenges into a `relIn`-witness. + challenges into a `relIn`-witness (escape variant: or into a local escape event). - Proof plan (F4.1): transplant `SingleRound.lean`'s tree readers/shape recovery at arity `k` - (`Fin.cast` along `1*(k−1)+1 = k`); at `ℓ = 1` the star machinery collapses to injectivity of - the challenge family (`isSpecialSoundFamily_one_iff_injective`), so `hmk` receives plain + Remaining proof plan: transplant `SingleRound.lean`'s per-branch transcript machinery + (`branchPath`/`branchTr`/`branch_pre`/`branch_challenge`/`branch_mem`/`branch_relOut_language`) + at arity `k`, then copy `SingleRound.coordinateWiseSpecialSoundWith_of_mkWitness` / + `…WithEscape_of_mkWitness` verbatim: at `ℓ = 1` the star machinery collapses to injectivity of + the challenge family (`isSpecialSoundFamily_one_iff_injective` composed with the + `Equiv.funUnique` decomposition of `scalarStructure`), so `hmk` receives plain `Function.Injective fam` instead of `StarAt`. ## References @@ -37,7 +51,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRoun Polynomial Commitments over Extension Fields*][NOZ26] -/ -open OracleComp OracleSpec ProtocolSpec CoordinateWise +open OracleComp OracleSpec ProtocolSpec ProtocolSpec.ChallengeTree CoordinateWise namespace CoordinateWise.ScalarRound @@ -46,7 +60,7 @@ namespace CoordinateWise.ScalarRound @[reducible] def pSpecScalar (Msg C : Type) : ProtocolSpec 2 := ⟨!v[.P_to_V, .V_to_P], !v[Msg, C]⟩ -variable {Msg C : Type} +variable {Msg C : Type} {arity : (pSpecScalar Msg C).ChallengeIdx → ℕ} /-- The scalar-round CWSS structure at soundness parameter `k`: a single challenge coordinate (`ℓ = 1`) over the alphabet `C`, i.e. plain `k`-special soundness — the shape of Hachi @@ -55,6 +69,13 @@ Lemmas 9 and 11. Arity `1·(k−1)+1 = k`. -/ CWSSStructure (pSpecScalar Msg C) := CWSSStructure.ofSpecialSound (fun _ => k) (fun _ => hk) +/-- The scalar-round arity is `k` (propositionally — `1 * (k - 1) + 1` is not `rfl`-equal to `k`; +this is the bridge the extractor's and the escape event's `Fin.cast`s use). The `(ℓ = 1, k)` +analogue of `SingleRound.foldStructure_arity`. -/ +theorem scalarStructure_arity {k : ℕ} (hk : 2 ≤ k) : + (scalarStructure (Msg := Msg) (C := C) k hk).arity ⟨1, rfl⟩ = k := + show 1 * (k - 1) + 1 = k from by omega + section Instances variable [SampleableType C] [OracleInterface Msg] @@ -70,35 +91,250 @@ instance : ∀ i, OracleInterface ((pSpecScalar Msg C).Message i) end Instances +/-! ## Round readers + +The `(ℓ = 1, k)` transplant of `SingleRound.lean`'s readers. Naive `match tree` on a +`ChallengeTree … 0` fails ("dependent elimination failed"), so each reader is index-generic: it +matches at an arbitrary round index `a` and carries the proof `a = 0` (resp. `a = 1`), discharged +per constructor via `congrArg Fin.val` + `Direction.noConfusion`. -/ + +/-- Index-generic round-0 message reader: peel the top `msgNode` of a tree at any index `a` +together with a proof `a = 0`. -/ +def topMsgAux : {a : Fin 3} → ChallengeTree (pSpecScalar Msg C) arity a → a = (0 : Fin 3) → + (pSpecScalar Msg C).Message ⟨0, rfl⟩ + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m _ msg _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact msg + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-- Read the round-0 message (the pre-challenge prover message) off a full tree. -/ +def readPre (tree : ChallengeTree (pSpecScalar Msg C) arity 0) : + (pSpecScalar Msg C).Message ⟨0, rfl⟩ := + topMsgAux tree rfl + +/-- Index-generic round-1 reader: peel the sibling-challenge family off a `chalNode` at any +index `a` together with a proof `a = 1`. -/ +def chalsAux : {a : Fin 3} → ChallengeTree (pSpecScalar Msg C) arity a → a = (1 : Fin 3) → + (Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + | _, .chalNode m h chals _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact chals + +/-- Read the round-1 sibling-challenge family off a full tree: a two-level peel — the round-0 +helper strips the top `msgNode` and hands its child (which sits at round 1) to `chalsAux`. -/ +def readChallenges (tree : ChallengeTree (pSpecScalar Msg C) arity 0) : + Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩ := + aux tree rfl +where + /-- Round-0 helper for `readChallenges`: strip the top `msgNode`, delegate to `chalsAux`. -/ + aux : {a : Fin 3} → ChallengeTree (pSpecScalar Msg C) arity a → a = (0 : Fin 3) → + (Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m _ _ child, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact chalsAux child rfl + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-! ## The star tree and shape recovery -/ + +/-- The star tree: one message node carrying `v`, one challenge node carrying the sibling +family, leaves below. Every tree of this `pSpec` has this shape (`tree_shape`). -/ +def tree2 (v : Msg) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩) : + ChallengeTree (pSpecScalar Msg C) arity 0 := + .msgNode 0 rfl v (.chalNode 1 rfl challenges (fun _ => .leaf)) + +/-- The round-0 reader computes on the star tree. -/ +@[simp] theorem readPre_tree2 (v : Msg) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩) : + readPre (tree2 v challenges) = v := rfl + +/-- The round-1 reader computes on the star tree. -/ +@[simp] theorem readChallenges_tree2 (v : Msg) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩) : + readChallenges (tree2 v challenges) = challenges := rfl + +/-- Shape recovery, level 2: every subtree at the last round is a leaf. -/ +theorem eq_leaf : {a : Fin 3} → (t : ChallengeTree (pSpecScalar Msg C) arity a) → + (ha : a = Fin.last 2) → + HEq t (ChallengeTree.leaf : ChallengeTree (pSpecScalar Msg C) arity (Fin.last 2)) + | _, .leaf, _ => HEq.rfl + | _, .msgNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simpa using m.isLt.ne) + | _, .chalNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simpa using m.isLt.ne) + +/-- Shape recovery, level 1: every subtree at round 1 is a `chalNode` over leaves. -/ +theorem chal_shape : {a : Fin 3} → (t : ChallengeTree (pSpecScalar Msg C) arity a) → + (ha : a = 1) → + ∃ challenges : Fin (arity ⟨1, rfl⟩) → (pSpecScalar Msg C).Challenge ⟨1, rfl⟩, + HEq t (ChallengeTree.chalNode (pSpec := pSpecScalar Msg C) (arity := arity) + 1 rfl challenges (fun _ => .leaf)) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + | _, .chalNode m h chals children, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + refine ⟨chals, ?_⟩ + have hch : children = fun _ => .leaf := by + funext j + exact eq_of_heq (eq_leaf (children j) rfl) + rw [hch] + +/-- Shape recovery, level 0: every tree at round 0 is a `tree2`. -/ +theorem tree_shape_aux : {a : Fin 3} → (t : ChallengeTree (pSpecScalar Msg C) arity a) → + (ha : a = 0) → + ∃ v challenges, HEq t (tree2 (arity := arity) v challenges) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h msg child, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + obtain ⟨challenges, hchild⟩ := chal_shape child rfl + refine ⟨msg, challenges, ?_⟩ + rw [eq_of_heq hchild] + exact HEq.rfl + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-- **Shape recovery.** Every full tree of the two-round scalar `pSpecScalar` is a star tree — +one message node, one challenge node of `arity ⟨1, rfl⟩` siblings, leaves below. -/ +theorem tree_shape (tree : ChallengeTree (pSpecScalar Msg C) arity 0) : + ∃ v challenges, tree = tree2 (arity := arity) v challenges := by + obtain ⟨v, challenges, h⟩ := tree_shape_aux tree rfl + exact ⟨v, challenges, eq_of_heq h⟩ + +/-! ## The extractor and the escape event -/ + +section Extractor + +variable {StmtIn WitIn WitOut : Type} [Nonempty WitOut] + +/-- Read the `k` sibling scalar challenges off a full tree at the `scalarStructure k` arity, +re-indexed by `Fin k` through the arity bridge `scalarStructure_arity`. Shared by the extractor +and the escape event so that both are pinned to the *same* reading of the tree. -/ +def readFam {k : ℕ} (hk : 2 ≤ k) + (tree : ChallengeTree (pSpecScalar Msg C) + (CWSSStructure.toShape (scalarStructure (Msg := Msg) (C := C) k hk)).arity 0) : + Fin k → C := + fun j => readChallenges tree (Fin.cast (scalarStructure_arity (Msg := Msg) (C := C) hk).symm j) + +open Classical in +/-- **The scalar-round tree extractor**, the `(ℓ = 1, k)` analogue of `SingleRound.treeExtractor`: +read the message and the `k` sibling scalar challenges off the tree, choose a per-branch +`relOut`-witness classically (`Classical.ofNonempty` where none exists; on structured accepting +trees every guard fires), and assemble via `mkWitness`. Hypothesis-free — all correctness lives in +the assembly below. -/ +noncomputable def treeExtractorScalar {k : ℕ} (hk : 2 ≤ k) + (relOut : Set ((StmtIn × Msg × C) × WitOut)) + (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn) : + Extractor.TreeBased StmtIn WitIn (pSpecScalar Msg C) + (CWSSStructure.toShape (scalarStructure (Msg := Msg) (C := C) k hk)).arity := + fun stmtIn tree => + let v := readPre tree + let fam : Fin k → C := readFam hk tree + let resp : Fin k → WitOut := fun j => + if h : ∃ w, ((stmtIn, v, fam j), w) ∈ relOut then h.choose else Classical.ofNonempty + mkWitness stmtIn v fam resp + +/-- The scalar-round tree-level escape event induced by a **local** (per-family) event `escLocal` +and a per-branch validity predicate `valid`: the tree's own message and challenge family admit +per-branch responses that are `valid` and on which `escLocal` fires. + +The `(ℓ = 1, k)` analogue of `SingleRound.escEvent`, generalized in `valid` so that rounds whose +verifier is *not* statement-extending (each sumcheck round replaces the targets rather than +appending to the statement) can still pin their responses to their own output relation — which is +what keeps the event **tight**. -/ +def escEventScalarOfValid {k : ℕ} (hk : 2 ≤ k) + (valid : StmtIn → Msg → (Fin k → C) → Fin k → WitOut → Prop) + (escLocal : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → Prop) : + ChallengeTree.EscapeEvent StmtIn (pSpecScalar Msg C) + (CWSSStructure.toShape (scalarStructure (Msg := Msg) (C := C) k hk)).arity := + fun stmtIn tree => + ∃ resp : Fin k → WitOut, + (∀ j, valid stmtIn (readPre tree) (readFam hk tree) j (resp j)) ∧ + escLocal stmtIn (readPre tree) (readFam hk tree) resp + +/-- `escEventScalarOfValid` at the branch validity of a **statement-extending** round: the branch's +output statement is the input statement extended by the message and that branch's challenge, so +per-branch validity is membership in `relOut` there. Used by the HMZ25 lift. -/ +def escEventScalar {k : ℕ} (hk : 2 ≤ k) + (relOut : Set ((StmtIn × Msg × C) × WitOut)) + (escLocal : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → Prop) : + ChallengeTree.EscapeEvent StmtIn (pSpecScalar Msg C) + (CWSSStructure.toShape (scalarStructure (Msg := Msg) (C := C) k hk)).arity := + escEventScalarOfValid hk (fun s v fam j w => ((s, v, fam j), w) ∈ relOut) escLocal + +end Extractor + section Assembly variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitIn WitOut : Type} [Nonempty WitOut] {σ : Type} [SampleableType C] -/-- **Generic scalar-round CWSS assembly (skeleton, F4.1).** Any pure statement-extending -verifier of the two-round scalar `pSpecScalar` is coordinate-wise special sound for -`scalarStructure k`, provided a witness assembler `mkWitness` that turns `k` per-branch -`relOut`-witnesses at pairwise-distinct challenges into a `relIn`-witness. This is the engine -behind Hachi Lemma 9 (`k = 2d`, interpolation) and Lemma 11 (`k = deg + 1`, per sumcheck round). - -**Sorried.** Proof plan: transplant `SingleRound.coordinateWiseSpecialSound_of_mkWitness` — the -tree at arity `k` is one message node over one challenge node over leaves (`tree_shape` at -arity `k`); the `SS(C, 1, k)` node predicate is injectivity of the challenge family -(`isSpecialSoundFamily_one_iff_injective` composed with the `Equiv.funUnique` decomposition of -`scalarStructure`); branch acceptance yields per-branch `relOut`-membership via -`mem_of_pure_accepting`. -/ -theorem coordinateWiseSpecialSound_of_mkWitness_scalar +/-- **Generic scalar-round CWSS assembly, named form (skeleton, F4.1).** Any pure +statement-extending verifier of the two-round scalar `pSpecScalar` is coordinate-wise special +sound for `scalarStructure k` **at the named extractor** `treeExtractorScalar`, provided a +witness assembler `mkWitness` that turns `k` per-branch `relOut`-witnesses at pairwise-distinct +challenges into a `relIn`-witness. + +**Sorried.** Proof plan: transplant `SingleRound.coordinateWiseSpecialSoundWith_of_mkWitness` — +the readers and `tree_shape` above already put an arbitrary structured accepting tree into star +form; what remains is the per-branch transcript machinery (`branchTr` and its `branch_pre` / +`branch_challenge` / `branch_mem` lemmas, then `branch_relOut_language` via +`Verifier.mem_of_pure_accepting`) and the `SS(C, 1, k)` node predicate unfolding, which at `ℓ = 1` +is injectivity of the challenge family (`isSpecialSoundFamily_one_iff_injective` composed with the +`Equiv.funUnique` decomposition of `scalarStructure`). -/ +theorem coordinateWiseSpecialSoundWith_of_mkWitness_scalar (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) {k : ℕ} (hk : 2 ≤ k) (V : Verifier oSpec StmtIn (StmtIn × Msg × C) (pSpecScalar Msg C)) - (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) + (hpure : ∀ s tr, + V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) (relIn : Set (StmtIn × WitIn)) (relOut : Set ((StmtIn × Msg × C) × WitOut)) (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn) (hmk : ∀ s v (fam : Fin k → C) (resp : Fin k → WitOut), (∀ j, ((s, v, fam j), resp j) ∈ relOut) → Function.Injective fam → (s, mkWitness s v fam resp) ∈ relIn) : - V.coordinateWiseSpecialSound init impl (scalarStructure k hk) relIn relOut := by + Verifier.coordinateWiseSpecialSoundWith init impl (scalarStructure k hk) relIn relOut V + (treeExtractorScalar hk relOut mkWitness) := by + sorry + +/-- **Generic scalar-round escape-threaded CWSS assembly, named form (skeleton, F4.1).** The +escape twin of `coordinateWiseSpecialSoundWith_of_mkWitness_scalar`: `hmk` may conclude a local +escape event `escLocal` instead of a `relIn`-witness, and the certificate carries the induced +tree-level event `escEventScalar relOut escLocal`. This is the engine behind Hachi Lemma 9 +(`k = 2d`, interpolation, weak-binding escape) and Lemma 11 (`k = deg + 1`, per sumcheck round). + +**Sorried.** Same proof plan as the plain assembly; the disjunction is threaded exactly as in +`SingleRound.coordinateWiseSpecialSoundWithEscape_of_mkWitness` (the extractor's own chosen +per-branch responses witness `escEventScalar`'s existential). -/ +theorem coordinateWiseSpecialSoundWithEscape_of_mkWitness_scalar + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + {k : ℕ} (hk : 2 ≤ k) + (V : Verifier oSpec StmtIn (StmtIn × Msg × C) (pSpecScalar Msg C)) + (hpure : ∀ s tr, + V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) + (relIn : Set (StmtIn × WitIn)) + (relOut : Set ((StmtIn × Msg × C) × WitOut)) + (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn) + (escLocal : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → Prop) + (hmk : ∀ s v (fam : Fin k → C) (resp : Fin k → WitOut), + (∀ j, ((s, v, fam j), resp j) ∈ relOut) → Function.Injective fam → + escLocal s v fam resp ∨ (s, mkWitness s v fam resp) ∈ relIn) : + Verifier.coordinateWiseSpecialSoundWithEscape init impl (scalarStructure k hk) + (escEventScalar hk relOut escLocal) relIn relOut V + (treeExtractorScalar hk relOut mkWitness) := by sorry end Assembly diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean deleted file mode 100644 index f0a4f57afc..0000000000 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean +++ /dev/null @@ -1,412 +0,0 @@ -/- -Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann --/ -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge -import ArkLib.OracleReduction.Composition.Sequential.IsPure - -/-! - # `n`-ary sequential composition for (coordinate-wise) special soundness - - This file lifts the binary append theory of - `CoordinateWiseSpecialSoundness.Composition` to the finite sequential composition - `Verifier.seqCompose`. The two ingredients are: - - * the **base case** `Verifier.id_treeSpecialSound`: the identity verifier (over the empty - protocol `!p[]`, which has no challenge rounds) is tree-special-sound for any shape with - `relIn = relOut`, via the no-challenge bridge `treeSpecialSound_of_isEmpty_challengeIdx`; and - * the **step case**, threading per-factor purity (`Verifier.IsPure`, from - `Composition.Sequential.IsPure`) into the deterministic-left hypothesis of - `Verifier.append_treeSpecialSound`, plus the shape identity - `ChallengeTreeShape.seqCompose_succ` that exposes the head/tail append structure of the - sequentially-composed shape. - - ## Main results - - * `Verifier.mem_of_pure_accepting` — converse of `Verifier.pure_accepting_of_mem`: a pure - verifier whose run is accepted with probability one has its output in the language. - * `Verifier.id_treeSpecialSound` — the n-ary base case. - * `ChallengeTreeShape.seqCompose_succ` — `seqCompose` of shapes unfolds to `append` of head/tail. - * `Verifier.seqCompose_treeSpecialSound` — generic n-ary tree-soundness composition. - * `Verifier.seqCompose_coordinateWiseSpecialSound` — the CWSS-specific wrapper. --/ - -noncomputable section - -open OracleComp OracleSpec ProtocolSpec -open scoped NNReal - -namespace Verifier - -open ProtocolSpec ProtocolSpec.ChallengeTree - -variable {ι : Type} {oSpec : OracleSpec ι} - {StmtIn StmtOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} - [∀ i, SampleableType (pSpec.Challenge i)] - {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - -omit [∀ i, SampleableType (pSpec.Challenge i)] in -/-- Converse of `pure_accepting_of_mem`: if a verifier deterministically outputs `out` on -`(stmt, tr)` and its run is accepted into `lang` with probability one, then `out ∈ lang`. -/ -theorem mem_of_pure_accepting - (V : Verifier oSpec StmtIn StmtOut pSpec) - (stmt : StmtIn) (tr : pSpec.FullTranscript) - (lang : Set StmtOut) (out : StmtOut) - (hV : V.verify stmt tr = pure out) - (hAcc : Pr[ (· ∈ lang) | - OptionT.mk do (simulateQ impl (V.run stmt tr)).run' (← init)] = 1) : - out ∈ lang := by - rw [probEvent_eq_one_iff] at hAcc - obtain ⟨hFail, hmem⟩ := hAcc - -- The underlying probabilistic computation is `init >>= fun _ => pure (some out)`. - have hrun : (do (simulateQ impl (V.run stmt tr)).run' (← init) : - ProbComp (Option StmtOut)) = (init >>= fun _ => pure (some out)) := by - simp only [Verifier.run, hV] - congr 1 - refine hmem out ?_ - -- `init` has nonempty support, else the whole computation would fail with probability one. - have hne : (support init).Nonempty := by - by_contra hempty - rw [Set.not_nonempty_iff_eq_empty] at hempty - have hcfail : Pr[⊥ | - (init >>= fun _ => pure (some out) : ProbComp (Option StmtOut))] = 0 := by - have h2 := hFail - rw [OptionT.probFailure_eq, OptionT.run_mk, hrun] at h2 - exact (add_eq_zero.mp h2).1 - have hcsupp : - support (init >>= fun _ => pure (some out) : ProbComp (Option StmtOut)) = ∅ := by - rw [support_bind_const, support_pure]; simp [hempty] - rw [probFailure_eq_one hcsupp] at hcfail - exact one_ne_zero hcfail - rw [OptionT.mem_support_iff, OptionT.run_mk, hrun, support_bind_const, support_pure] - exact ⟨Set.mem_singleton _, hne⟩ - -end Verifier - -namespace Verifier - -open ProtocolSpec ProtocolSpec.ChallengeTree - -variable {ι : Type} {oSpec : OracleSpec ι} - {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - -/-- **n-ary base case.** The identity verifier is tree-special-sound for any shape `S`, with input -relation equal to output relation: the empty protocol `!p[]` has no challenge rounds, so this is the -no-challenge bridge with the extractor that noncomputably picks a witness of `stmtIn` whenever one -exists. -/ -theorem id_treeSpecialSound {Statement Witness : Type} [Nonempty Witness] - (S : ChallengeTreeShape (!p[] : ProtocolSpec 0)) - (rel : Set (Statement × Witness)) : - (Verifier.id (oSpec := oSpec) (Statement := Statement)).treeSpecialSound - init impl S rel rel := by - -- For each statement, some candidate witness works as soon as any witness exists. - have hpick : ∀ stmt : Statement, ∃ w : Witness, (∃ w', (stmt, w') ∈ rel) → (stmt, w) ∈ rel := by - intro stmt - rcases or_not (p := ∃ w', (stmt, w') ∈ rel) with ⟨w, hw⟩ | h - · exact ⟨w, fun _ => hw⟩ - · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ - refine treeSpecialSound_of_isEmpty_challengeIdx init impl S Verifier.id rel rel - (fun stmt _ => (hpick stmt).choose) ?_ - intro stmtIn tr hAcc - have hlang : stmtIn ∈ rel.language := - mem_of_pure_accepting init impl Verifier.id stmtIn tr rel.language stmtIn rfl hAcc - exact (hpick stmtIn).choose_spec ((Set.mem_language_iff rel stmtIn).1 hlang) - -end Verifier - -namespace ChallengeTreeShape - -variable {m : ℕ} {len : Fin (m + 1) → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} - -/-- A sigma over `Fin` whose fibers are subtypes of `Fin` is determined by the underlying -`Fin`-level data: equal first components and equal underlying second values force equality. -/ -private theorem sigmaSubtype_ext {M : ℕ} {N : Fin M → ℕ} {P : (i : Fin M) → Fin (N i) → Prop} - {a a' : Fin M} {v : Fin (N a)} {v' : Fin (N a')} {p : P a v} {p' : P a' v'} - (ha : a = a') (hv : (v : ℕ) = (v' : ℕ)) : - (⟨a, ⟨v, p⟩⟩ : (i : Fin M) × {x : Fin (N i) // P i x}) = ⟨a', ⟨v', p'⟩⟩ := by - subst ha - have : v = v' := Fin.ext hv - subst this - rfl - -/-- Heterogeneous congruence for dependent function application. -/ -private theorem heq_app.{u, v} {α α' : Sort u} {β : α → Sort v} {β' : α' → Sort v} - (hα : α = α') (hβ : HEq β β') {f : (a : α) → β a} {f' : (a : α') → β' a} - (hf : HEq f f') {a : α} {a' : α'} (ha : HEq a a') : HEq (f a) (f' a') := by - subst hα - obtain rfl := eq_of_heq hβ - obtain rfl := eq_of_heq hf - obtain rfl := eq_of_heq ha - rfl - -/-- Heterogeneous congruence for `nodeOk`: a `ChallengeTreeShape`'s node predicate transports -across an equality of the underlying protocol, challenge index, arity, and a heterogeneous equality -of the sibling-challenge function. -/ -private theorem heq_nodeOk {n n' : ℕ} (hn : n = n') {p : ProtocolSpec n} {p' : ProtocolSpec n'} - (hp : HEq p p') {T : ChallengeTreeShape p} {T' : ChallengeTreeShape p'} (hT : HEq T T') - {i : p.ChallengeIdx} {i' : p'.ChallengeIdx} (hi : HEq i i') - {f : Fin (T.arity i) → p.Challenge i} {f' : Fin (T'.arity i') → p'.Challenge i'} - (hf : HEq f f') : - HEq (T.nodeOk i f) (T'.nodeOk i' f') := by - subst hn - obtain rfl := eq_of_heq hp - obtain rfl := eq_of_heq hT - obtain rfl := eq_of_heq hi - obtain rfl := eq_of_heq hf - exact HEq.rfl - -variable {a b : ℕ} {p₁ : ProtocolSpec a} {p₂ : ProtocolSpec b} - -/-- The append node predicate at a left-embedded index reduces to the left shape's predicate. -/ -theorem append_nodeOk_inl (S₁ : ChallengeTreeShape p₁) (S₂ : ChallengeTreeShape p₂) - (i₁ : p₁.ChallengeIdx) - (challenges : Fin ((S₁.append S₂).arity (ChallengeIdx.inl i₁)) → - (p₁ ++ₚ p₂).Challenge (ChallengeIdx.inl i₁)) : - (S₁.append S₂).nodeOk (ChallengeIdx.inl i₁) challenges - = S₁.nodeOk i₁ (fun j => cast (by simp [ProtocolSpec.append, ChallengeIdx.inl]) - (challenges (Fin.cast (by - change S₁.arity i₁ = ChallengeTree.appendArity S₁.arity S₂.arity (ChallengeIdx.inl i₁) - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl]) j))) := by - simp only [ChallengeTreeShape.append] - split - · rename_i i₁' heq - rw [ChallengeIdx.sumEquiv_symm_inl] at heq - obtain rfl : i₁' = i₁ := by simpa using heq.symm - rfl - · rename_i i₂' heq - rw [ChallengeIdx.sumEquiv_symm_inl] at heq - simp at heq - -/-- The append node predicate at a right-embedded index reduces to the right shape's predicate. -/ -theorem append_nodeOk_inr (S₁ : ChallengeTreeShape p₁) (S₂ : ChallengeTreeShape p₂) - (i₂ : p₂.ChallengeIdx) - (challenges : Fin ((S₁.append S₂).arity (ChallengeIdx.inr i₂)) → - (p₁ ++ₚ p₂).Challenge (ChallengeIdx.inr i₂)) : - (S₁.append S₂).nodeOk (ChallengeIdx.inr i₂) challenges - = S₂.nodeOk i₂ (fun j => cast (by simp [ProtocolSpec.append, ChallengeIdx.inr]) - (challenges (Fin.cast (by - change S₂.arity i₂ = ChallengeTree.appendArity S₁.arity S₂.arity (ChallengeIdx.inr i₂) - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr]) j))) := by - simp only [ChallengeTreeShape.append] - split - · rename_i i₁' heq - rw [ChallengeIdx.sumEquiv_symm_inr] at heq - simp at heq - · rename_i i₂' heq - rw [ChallengeIdx.sumEquiv_symm_inr] at heq - obtain rfl : i₂' = i₂ := by simpa using heq.symm - rfl - -/-- The `seqCompose` node predicate unfolds, by definition, to the decoded component's predicate -applied to the cast-in sibling challenges. -/ -theorem seqCompose_nodeOk_eq {r : ℕ} {ln : Fin r → ℕ} {ps : ∀ i, ProtocolSpec (ln i)} - (S : ∀ i, ChallengeTreeShape (ps i)) (ci : (ProtocolSpec.seqCompose ps).ChallengeIdx) - (f : Fin ((ChallengeTreeShape.seqCompose S).arity ci) → - (ProtocolSpec.seqCompose ps).Challenge ci) : - (ChallengeTreeShape.seqCompose S).nodeOk ci f - = (S (seqComposeChallengeIdxToSigma ci).1).nodeOk (seqComposeChallengeIdxToSigma ci).2 - (fun j => cast (seqCompose_challenge_eq ci) (f j)) := rfl - -/-- The decoded sigma of a left-embedded composed challenge index is `⟨0, i₁⟩`. -/ -private theorem toSigma_inl (i₁ : (pSpec 0).ChallengeIdx) : - seqComposeChallengeIdxToSigma - (pSpec := pSpec) - (ChallengeIdx.inl (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁) - = ⟨0, i₁⟩ := by - unfold seqComposeChallengeIdxToSigma - dsimp only - have hcoe : (ChallengeIdx.inl - (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁).1 - = Fin.embedSum (0 : Fin (m + 1)) i₁.1 := rfl - refine sigmaSubtype_ext (P := fun i x => (pSpec i).dir x = .V_to_P) - (a' := 0) (v' := i₁.1) (p' := i₁.2) ?_ ?_ - · rw [hcoe, Fin.splitSum_embedSum] - · rw [hcoe, Fin.splitSum_embedSum] - -/-- The decoded sigma of a right-embedded composed challenge index shifts by one round. -/ -private theorem toSigma_inr - (i₂ : (ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))).ChallengeIdx) : - seqComposeChallengeIdxToSigma (pSpec := pSpec) (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂) - = ⟨(seqComposeChallengeIdxToSigma i₂).1.succ, (seqComposeChallengeIdxToSigma i₂).2⟩ := by - have hcoe : (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂).1 - = Fin.natAdd (len 0) i₂.1 := rfl - conv_lhs => unfold seqComposeChallengeIdxToSigma - conv_rhs => unfold seqComposeChallengeIdxToSigma - dsimp only - refine sigmaSubtype_ext (P := fun i x => (pSpec i).dir x = .V_to_P) ?_ ?_ - · rw [hcoe, Fin.splitSum_succ]; erw [Fin.dappend_right] - · rw [hcoe, Fin.splitSum_succ]; erw [Fin.dappend_right] - -/-- **Successor unfolding of the sequentially-composed shape.** `ChallengeTreeShape.seqCompose` of -a family over `m + 1` factors is the binary `append` of the head shape with the sequential -composition of the tail. This is the shape-level analogue of -`ProtocolSpec.seqCompose_succ_eq_append`, and is what lets the `n`-ary tree-soundness induction -reduce its step to the binary `Verifier.append_treeSpecialSound`. -/ -theorem seqCompose_succ (S : ∀ i, ChallengeTreeShape (pSpec i)) : - ChallengeTreeShape.seqCompose S = - (S 0).append (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))) := by - have harity : (ChallengeTreeShape.seqCompose S).arity - = ((S 0).append (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i)))).arity := by - funext i - change (S (seqComposeChallengeIdxToSigma i).1).arity (seqComposeChallengeIdxToSigma i).2 - = ChallengeTree.appendArity (S 0).arity - (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i - rcases hsplit : (ChallengeIdx.sumEquiv (pSpec₁ := pSpec 0) - (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i)))).symm i with i₁ | i₂ - · obtain rfl : i = (ChallengeIdx.inl (pSpec₂ := - ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁ : - (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by - have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit - simp only [ChallengeIdx.sumEquiv_apply, Sum.elim_inl] at this - exact this - rw [toSigma_inl] - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl] - · obtain rfl : i = (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂ : - (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by - have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit - simp only [ChallengeIdx.sumEquiv_apply, Sum.elim_inr] at this - exact this - rw [toSigma_inr] - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr] - rfl - refine ChallengeTreeShape.ext harity ?_ - refine Function.hfunext rfl (fun i i' hi => ?_) - obtain rfl : i = i' := eq_of_heq hi - refine Function.hfunext (by rw [harity]) (fun challenges challenges' hch => ?_) - rcases hsplit : (ChallengeIdx.sumEquiv (pSpec₁ := pSpec 0) - (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i)))).symm i with i₁ | i₂ - · obtain rfl : i = (ChallengeIdx.inl (pSpec₂ := - ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁ : - (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by - have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit - simp only [ChallengeIdx.sumEquiv_apply, Sum.elim_inl] at this - exact this - apply heq_of_eq - rw [seqCompose_nodeOk_eq, append_nodeOk_inl] - have hsig := toSigma_inl (pSpec := pSpec) i₁ - have hfst := congrArg Sigma.fst hsig - have hsnd := (Sigma.ext_iff.mp hsig).2 - refine eq_of_heq (heq_nodeOk (congrArg len hfst) ?_ ?_ hsnd ?_) - · rw [hfst] - · rw [hfst] - · refine Function.hfunext (congrArg Fin ?hdom) (fun j j' hj => ?_) - case hdom => - change (ChallengeTreeShape.seqCompose S).arity _ = (S 0).arity i₁ - rw [harity] - change ChallengeTree.appendArity (S 0).arity - (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inl i₁) - = (S 0).arity i₁ - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl] - refine HEq.trans (cast_heq _ _) (HEq.trans ?_ (cast_heq _ _).symm) - refine heq_app (by rw [harity]) ?_ hch ?_ - · rw [harity] - · refine HEq.trans hj ?_ - exact (Fin.heq_ext_iff (by - change (S 0).arity i₁ = ChallengeTree.appendArity (S 0).arity - (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inl i₁) - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl])).mpr rfl - · obtain rfl : i = (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂ : - (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by - have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit - simp only [ChallengeIdx.sumEquiv_apply, Sum.elim_inr] at this - exact this - apply heq_of_eq - rw [seqCompose_nodeOk_eq, append_nodeOk_inr, seqCompose_nodeOk_eq] - have hsig := toSigma_inr (pSpec := pSpec) i₂ - have hfst := congrArg Sigma.fst hsig - have hsnd := (Sigma.ext_iff.mp hsig).2 - refine eq_of_heq (heq_nodeOk (congrArg len hfst) ?_ ?_ hsnd ?_) - · rw [hfst] - · rw [hfst] - · refine Function.hfunext (congrArg Fin ?hdomr) (fun j j' hj => ?_) - case hdomr => - change (ChallengeTreeShape.seqCompose S).arity _ - = (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ - rw [harity] - change ChallengeTree.appendArity (S 0).arity - (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inr i₂) - = (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr] - refine HEq.trans (cast_heq _ _) ?_ - refine HEq.trans ?_ (HEq.trans (cast_heq _ _) (cast_heq _ _)).symm - refine heq_app (by rw [harity]) ?_ hch ?_ - · rw [harity] - · refine HEq.trans hj ?_ - exact (Fin.heq_ext_iff (by - change (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ - = ChallengeTree.appendArity (S 0).arity - (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inr i₂) - simp only [ChallengeTree.appendArity, Function.comp_apply, - ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr])).mpr rfl - -end ChallengeTreeShape - -section NaryCompose - -variable {ι : Type} {oSpec : OracleSpec ι} - {m : ℕ} {Stmt : Fin (m + 1) → Type} {Wit : Fin (m + 1) → Type} - {len : Fin m → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} - [∀ i, ∀ j, SampleableType ((pSpec i).Challenge j)] - {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - -/-- **`n`-ary generic tree-soundness composition.** If each factor verifier is pure (`IsPure`) and -tree-special-sound for the seam relations `rel i.castSucc ↦ rel i.succ`, then the sequential -composition `Verifier.seqCompose` is tree-special-sound for the sequentially-composed shape from -`rel 0` to `rel (Fin.last m)`. The induction's base case is `Verifier.id_treeSpecialSound` and its -step is `Verifier.append_treeSpecialSound`, with the head's purity discharging the -deterministic-left hypothesis and `ChallengeTreeShape.seqCompose_succ` exposing the appended -shape. -/ -theorem Verifier.seqCompose_treeSpecialSound - (S : ∀ i, ChallengeTreeShape (pSpec i)) - (rel : ∀ i, Set (Stmt i × Wit i)) - (hWit : Nonempty (Wit (Fin.last m))) - (V : ∀ i, Verifier oSpec (Stmt i.castSucc) (Stmt i.succ) (pSpec i)) - (hV : ∀ i, (V i).IsPure) - (h : ∀ i, (V i).treeSpecialSound init impl (S i) (rel i.castSucc) (rel i.succ)) : - (Verifier.seqCompose Stmt V).treeSpecialSound init impl - (ChallengeTreeShape.seqCompose S) (rel 0) (rel (Fin.last m)) := by - induction m with - | zero => - haveI : Nonempty (Wit 0) := hWit - rw [Verifier.seqCompose_zero] - exact Verifier.id_treeSpecialSound init impl (ChallengeTreeShape.seqCompose S) (rel 0) - | succ m ih => - rw [Verifier.seqCompose_succ, ChallengeTreeShape.seqCompose_succ] - obtain ⟨f₀, hf₀⟩ := (hV 0).is_pure - have htail := ih (fun i => S i.succ) (fun i => rel i.succ) hWit - (fun i => V i.succ) (fun i => hV i.succ) (fun i => h i.succ) - refine Verifier.append_treeSpecialSound init impl (V 0) - (Verifier.seqCompose (Stmt ∘ Fin.succ) (fun i => V i.succ)) - (S 0) (ChallengeTreeShape.seqCompose (fun i => S i.succ)) f₀ hf₀ (h 0) ?_ - simpa [Function.comp_def] using htail - -/-- **`n`-ary CWSS composition.** The coordinate-wise special-soundness wrapper of -`seqCompose_treeSpecialSound`, obtained by unfolding `coordinateWiseSpecialSound` to tree-soundness -of the induced shape and rewriting with `CWSSStructure.toShape_seqCompose`. -/ -theorem Verifier.seqCompose_coordinateWiseSpecialSound - (D : ∀ i, CWSSStructure (pSpec i)) - (rel : ∀ i, Set (Stmt i × Wit i)) - (hWit : Nonempty (Wit (Fin.last m))) - (V : ∀ i, Verifier oSpec (Stmt i.castSucc) (Stmt i.succ) (pSpec i)) - (hV : ∀ i, (V i).IsPure) - (h : ∀ i, (V i).coordinateWiseSpecialSound init impl (D i) (rel i.castSucc) (rel i.succ)) : - (Verifier.seqCompose Stmt V).coordinateWiseSpecialSound init impl - (CWSSStructure.seqCompose D) (rel 0) (rel (Fin.last m)) := by - change (Verifier.seqCompose Stmt V).treeSpecialSound init impl - (CWSSStructure.toShape (CWSSStructure.seqCompose D)) (rel 0) (rel (Fin.last m)) - rw [CWSSStructure.toShape_seqCompose] - exact Verifier.seqCompose_treeSpecialSound init impl - (fun i => CWSSStructure.toShape (D i)) rel hWit V hV h - -end NaryCompose - -end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean index 2f634c822c..06d298f8a8 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition /-! # Single-challenge-round tree navigation (generic CWSS building block) @@ -23,11 +23,15 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose a special-sound sibling family has a center and, per coordinate `i`, a sibling differing from the center exactly at `i`; - the tree extractor `treeExtractor` and the **generic assembly** - `coordinateWiseSpecialSound_of_mkWitness`: any pure statement-extending verifier of this + `coordinateWiseSpecialSoundWith_of_mkWitness`: any pure statement-extending verifier of this `pSpec` is CWSS for `foldStructure`, given only a protocol-specific witness assembler `mkWitness` turning per-branch `relOut`-witnesses at star-shaped challenge families into a `relIn`-witness — all tree navigation, shape recovery, and guard-firing is discharged here - once. + once; + - the **escape-threaded** twin `escEvent` / `coordinateWiseSpecialSoundWithEscape_of_mkWitness`, + for reductions whose extraction may instead exhibit a cryptographic break: `hmk` concludes + `escLocal … ∨ (stmtIn, mkWitness …) ∈ relIn` and the certificate carries the induced tree-level + event `escEvent relOut escLocal` (contract for `escLocal`: `ChallengeTree.EscapeEvent`). ## References @@ -356,18 +360,41 @@ noncomputable def treeExtractor {StmtIn WitOut WitIn : Type} [Nonempty WitOut] if h : ∃ w, ((stmtIn, v, fam j), w) ∈ relOut then h.choose else Classical.ofNonempty mkWitness stmtIn v fam resp +/-! ## The single-round escape event -/ + +/-- The tree-level escape event induced by a **local** (per-star) event `escLocal`: the tree's own +message and challenge family admit per-branch `relOut`-responses on which `escLocal` fires. The +responses are existentially quantified but pinned to the tree's actual data through the round +readers (`readPre` / `readChallenges`), so the event is `(stmtIn, tree)`-determined and tight, as +`ChallengeTree.EscapeEvent`'s contract asks. Its honesty is exactly the honesty of `escLocal` — +the protocol's obligation. -/ +def escEvent {StmtIn WitOut : Type} + (relOut : Set ((StmtIn × CarrierCom × (Fin (2 ^ r) → C)) × WitOut)) + (escLocal : StmtIn → CarrierCom → (Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) → + (Fin (2 ^ r + 1) → WitOut) → Prop) : + ChallengeTree.EscapeEvent StmtIn (pSpec CarrierCom C r) + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity := + fun stmtIn tree => + ∃ resp : Fin (2 ^ r + 1) → WitOut, + (∀ j, ((stmtIn, readPre tree, + readChallenges tree (Fin.cast foldStructure_arity.symm j)), resp j) ∈ relOut) ∧ + escLocal stmtIn (readPre tree) + (fun j => readChallenges tree (Fin.cast foldStructure_arity.symm j)) resp + section Assembly variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitOut WitIn : Type} [Nonempty WitOut] {σ : Type} -/-- **Generic single-round CWSS assembly.** Any pure statement-extending verifier of the -two-round `pSpec` is coordinate-wise special sound for `foldStructure`, provided a witness -assembler `mkWitness` that turns per-branch `relOut`-witnesses at star-shaped challenge -families into a `relIn`-witness. This discharges all tree/extractor plumbing once; the -protocol-specific work (Hachi Lemma 8's case split and subtract-divide) lives entirely in -`hmk`. -/ -theorem coordinateWiseSpecialSound_of_mkWitness +/-- **Generic single-round CWSS assembly, named form.** Any pure statement-extending verifier of +the two-round `pSpec` is coordinate-wise special sound for `foldStructure` **at the named +extractor** `treeExtractor relOut mkWitness`, provided a witness assembler `mkWitness` that turns +per-branch `relOut`-witnesses at star-shaped challenge families into a `relIn`-witness. This +discharges all tree/extractor plumbing once; the protocol-specific work (Hachi Lemma 8's case +split and subtract-divide) lives entirely in `hmk`. Naming the extractor is what makes the +resulting certificate content-bearing over escape-widened relations (see +`Verifier.treeSpecialSoundWith`). -/ +theorem coordinateWiseSpecialSoundWith_of_mkWitness (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2 ^ r) → C)) (pSpec CarrierCom C r)) (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) @@ -379,10 +406,10 @@ theorem coordinateWiseSpecialSound_of_mkWitness (∀ j, ((stmtIn, v, fam j), resp j) ∈ relOut) → (∃ e, StarAt fam e) → (stmtIn, mkWitness stmtIn v fam resp) ∈ relIn) : - V.coordinateWiseSpecialSound init impl - (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)) relIn relOut := by + Verifier.coordinateWiseSpecialSoundWith init impl + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)) relIn relOut V + (treeExtractor relOut mkWitness) := by classical - refine ⟨treeExtractor relOut mkWitness, ?_⟩ intro stmtIn tree hStruct hAcc obtain ⟨v, challenges, rfl⟩ := tree_shape tree have harity := (foldStructure_arity (CarrierCom := CarrierCom) (C := C) (r := r)).symm @@ -409,6 +436,62 @@ theorem coordinateWiseSpecialSound_of_mkWitness -- the extractor computes definitionally on the recovered star tree; `exact` closes by defeq exact hmk stmtIn v _ _ hbranch hstar +/-- **Generic single-round escape-threaded CWSS assembly, named form.** +`coordinateWiseSpecialSoundWith_of_mkWitness` where the protocol-specific obligation `hmk` may +conclude a **local escape event** `escLocal` instead of a `relIn`-witness; the certificate then +carries the induced tree-level event `escEvent relOut escLocal`. This is the assembly for reductions +whose extraction can fail into a cryptographic break (e.g. Hachi Lemma 8's Module-SIS cases). + +The proof is the escape-free one verbatim up to its last step: the recovered star tree makes the +readers compute definitionally, so the extractor's own chosen per-branch responses (the ones +`hbranch` certifies) witness `escEvent`'s existential. -/ +theorem coordinateWiseSpecialSoundWithEscape_of_mkWitness + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2 ^ r) → C)) (pSpec CarrierCom C r)) + (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) + (relIn : Set (StmtIn × WitIn)) + (relOut : Set ((StmtIn × CarrierCom × (Fin (2 ^ r) → C)) × WitOut)) + (mkWitness : StmtIn → CarrierCom → (Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) → + (Fin (2 ^ r + 1) → WitOut) → WitIn) + (escLocal : StmtIn → CarrierCom → (Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) → + (Fin (2 ^ r + 1) → WitOut) → Prop) + (hmk : ∀ stmtIn v (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) (resp : Fin (2 ^ r + 1) → WitOut), + (∀ j, ((stmtIn, v, fam j), resp j) ∈ relOut) → + (∃ e, StarAt fam e) → + escLocal stmtIn v fam resp ∨ (stmtIn, mkWitness stmtIn v fam resp) ∈ relIn) : + Verifier.coordinateWiseSpecialSoundWithEscape init impl + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)) + (escEvent relOut escLocal) relIn relOut V + (treeExtractor relOut mkWitness) := by + classical + intro stmtIn tree hStruct hAcc + obtain ⟨v, challenges, rfl⟩ := tree_shape tree + have harity := (foldStructure_arity (CarrierCom := CarrierCom) (C := C) (r := r)).symm + -- each branch's guard fires: per-branch membership in `relOut.language` + have hmem : ∀ j : Fin (2 ^ r + 1), + ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut := by + intro j + have h := branch_relOut_language init impl V hpure relOut stmtIn v challenges hAcc + (Fin.cast harity j) + exact (Set.mem_language_iff relOut _).1 h + -- the sibling family is special sound, hence has a star center + have hfam := (nodeOk_iff_family challenges).1 hStruct.1 + have hstar : ∃ e, StarAt + (fun j : Fin (2 ^ r + 1) => challenges (Fin.cast harity j)) e := + exists_starAt (le_refl 2) (by omega) _ hfam + -- each chosen response satisfies the relation (the extractor's guards fire) + have hbranch : ∀ j : Fin (2 ^ r + 1), + ((stmtIn, v, challenges (Fin.cast harity j)), + if h : ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut + then h.choose else Classical.ofNonempty) ∈ relOut := by + intro j + rw [dif_pos (hmem j)] + exact (hmem j).choose_spec + -- either the local event fires on the extractor's own responses, or extraction succeeds + rcases hmk stmtIn v _ _ hbranch hstar with hbad | hgood + · exact Or.inl ⟨_, hbranch, hbad⟩ + · exact Or.inr hgood + end Assembly /-! ## The 2-round protocol instances (NOT auto-derived for `ProtocolSpec 2`) -/ diff --git a/ArkLib/OracleReduction/Security/TranscriptTree/Basic.lean b/ArkLib/OracleReduction/Security/TranscriptTree/Basic.lean index c13ebd9fbe..14ee40943e 100644 --- a/ArkLib/OracleReduction/Security/TranscriptTree/Basic.lean +++ b/ArkLib/OracleReduction/Security/TranscriptTree/Basic.lean @@ -55,6 +55,12 @@ import ArkLib.OracleReduction.Security.Basic that, on every `S`-structured accepting tree, recovers a witness. Plain special soundness (`Security.SpecialSoundness`) and coordinate-wise special soundness (`Security.CoordinateWiseSpecialSoundness`) are both instances, for different shapes. + - `ChallengeTree.EscapeEvent` / `Verifier.treeSpecialSoundWithEscape` — the escape-threaded + variant, for reductions whose extraction may instead break a cryptographic assumption: the + conclusion is `esc stmtIn tree ∨ extraction succeeds`, with the extractor still a plain + `Extractor.TreeBased`. The plain notion is the never-firing event + (`treeSpecialSoundWithEscape_false_iff`), and every plain certificate lifts losslessly + (`Verifier.treeSpecialSoundWith.withEscape`). ## Caveat @@ -258,6 +264,42 @@ def IsAccepting (verifier : Verifier oSpec StmtIn StmtOut pSpec) end IsAccepting +section EscapeEvent + +/-- An **escape event**: a statement-indexed predicate on full challenge trees, the + hypothesis-side home of cryptographic escapes (e.g. "the openings derived from this tree collide + under the fixed commitment key — a Module-SIS solution"). A tree-special-soundness certificate + concludes `esc stmt tree ∨ extraction succeeds`, so an escape is an event on the *observable + data*, never an output the extractor can fabricate. + + An escape event is a **trusted specification**, on the same footing as a package's + `relIn`/`relOut`: nothing in the framework checks it. Two conditions every instance must satisfy, + reviewed by reading its definition: + + - **hardness-tied and unconditional**: every `(stmt, tree)` satisfying the event must yield a + break of the ambient assumption, checked against protocol parameters fixed outside the + statement — at *every* pair, including statements no honest execution produces (composed + events evaluate factor events at adversarially controllable intermediate statements); + - **tree-determined**: the event may only constrain values computed from `(stmt, tree)`. It must + not mention the verifier, the sampling `(init, impl)`, the *input* relation `relIn`, an + extractor, or acceptance, since those smuggle in tautologies (e.g. + `fun s t => (s, Ext s t) ∉ relIn` makes any certificate at `Ext` vacuous). Constraining the + tree's per-branch responses by the *output* relation is fine and desirable — see below. + + Beyond honesty, aim for a **tight** event: one that fires only where extraction genuinely fails. + Tightness is not enforced; a wider event just yields a weaker certificate, and a statement-only + event like "some collision of this commitment exists" is honest yet worthless because it fires + almost everywhere. Pinning the tree's per-branch responses to `relOut` (as + `CoordinateWise.SingleRound.escEvent` does) is the standard way to get tightness. + + The trivial event `fun _ _ => False` is the escape-free degeneration (lossless: see + `Verifier.treeSpecialSoundWith.withEscape`). -/ +def EscapeEvent (Stmt : Type) (pSpec : ProtocolSpec n) + (arity : pSpec.ChallengeIdx → ℕ) : Type := + Stmt → ChallengeTree pSpec arity 0 → Prop + +end EscapeEvent + end ChallengeTree end ProtocolSpec @@ -289,14 +331,35 @@ variable {ι : Type} {oSpec : OracleSpec ι} [∀ i, SampleableType (pSpec.Challenge i)] {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) -/-- A verifier is **tree special sound** with respect to a generic challenge-tree shape `S`, an - input relation `relIn` and an output relation `relOut` if there is a tree-based extractor `E` - such that: for every input statement `stmtIn` and every tree of transcripts that is +/-- A named tree-based extractor `Ext` **witnesses tree special soundness** of a verifier with + respect to a generic challenge-tree shape `S`, an input relation `relIn` and an output relation + `relOut`: for every input statement `stmtIn` and every tree of transcripts that is - `S`-structured (its sibling challenges satisfy the shape's `nodeOk` predicate), and - accepting (the verifier accepts every root-to-leaf transcript, landing in `relOut.language`), - the extracted witness `E stmtIn tree` satisfies `(stmtIn, E stmtIn tree) ∈ relIn`. + the extracted witness `Ext stmtIn tree` satisfies `(stmtIn, Ext stmtIn tree) ∈ relIn`. + + This named form is the **content-bearing** statement of special soundness: it pins the + extraction *algorithm*, so it asserts something about the actual output of `Ext` on every + accepting tree. Its existential closure is `Verifier.treeSpecialSound` — prefer the named form + in advertised protocol statements, since a chain of named certificates exposes a runnable + end-to-end extractor (`chain.extractor`), which is what a later knowledge-error accounting has + to run. Reductions whose extraction may instead break a cryptographic assumption use + `Verifier.treeSpecialSoundWithEscape` below. -/ +def treeSpecialSoundWith (S : ChallengeTreeShape pSpec) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (verifier : Verifier oSpec StmtIn StmtOut pSpec) + (Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity) : Prop := + ∀ stmtIn : StmtIn, + ∀ tree : ChallengeTree pSpec S.arity 0, + tree.IsStructured S → + tree.IsAccepting init impl verifier stmtIn relOut.language → + (stmtIn, Ext stmtIn tree) ∈ relIn + +/-- A verifier is **tree special sound** with respect to a generic challenge-tree shape `S`, an + input relation `relIn` and an output relation `relOut` if *some* tree-based extractor witnesses + it (`Verifier.treeSpecialSoundWith`). This is the shape-generic core of tree-based knowledge extraction: every concrete special- soundness-style notion is an instance obtained by supplying a shape. Plain `k`-special soundness @@ -304,15 +367,147 @@ variable {ι : Type} {oSpec : OracleSpec ι} coordinate-wise special soundness (`Verifier.coordinateWiseSpecialSound`, `Security.CoordinateWiseSpecialSoundness`) supplies the CWSS shape `D.toShape`. Phrasing the notion over an arbitrary `ChallengeTreeShape` is what lets the composition theory be proved once - generically (see `Verifier.append_treeSpecialSound`) and reused by each concrete notion. -/ + generically (see `Verifier.append_treeSpecialSoundWith`) and reused by each concrete notion. + + The extractor is existential here, which loses the *algorithm*: advertised protocol statements + should use the named form `treeSpecialSoundWith` at an explicit extractor and keep this form for + plumbing, e.g. the right factor of an append (`Verifier.append_treeSpecialSoundWith`). -/ def treeSpecialSound (S : ChallengeTreeShape pSpec) (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) (verifier : Verifier oSpec StmtIn StmtOut pSpec) : Prop := ∃ E : Extractor.TreeBased StmtIn WitIn pSpec S.arity, + treeSpecialSoundWith init impl S relIn relOut verifier E + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- The existential notion is definitionally the existential closure of the named one. -/ +theorem treeSpecialSound_iff_exists (S : ChallengeTreeShape pSpec) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (verifier : Verifier oSpec StmtIn StmtOut pSpec) : + verifier.treeSpecialSound init impl S relIn relOut ↔ + ∃ Ext, treeSpecialSoundWith init impl S relIn relOut verifier Ext := Iff.rfl + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Forget the name of the extractor. -/ +theorem treeSpecialSoundWith.toTreeSpecialSound {S : ChallengeTreeShape pSpec} + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + (h : treeSpecialSoundWith init impl S relIn relOut verifier Ext) : + verifier.treeSpecialSound init impl S relIn relOut := ⟨Ext, h⟩ + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- **Shape-congruence transport for named tree special soundness.** The named notion transports + along an equality of shapes, with the extractor carried across heterogeneously. The extractor's + type mentions `S.arity`, so a plain `rw` at the shape is motive-incorrect; this lemma does the + transport once and for all (in practice `hExt := HEq.rfl`, since the relevant shape equalities + — e.g. `CWSSStructure.toShape_append` — have definitionally equal arities). -/ +theorem treeSpecialSoundWith_congr {S S' : ChallengeTreeShape pSpec} (hS : S = S') + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + {Ext' : Extractor.TreeBased StmtIn WitIn pSpec S'.arity} (hExt : HEq Ext Ext') + (h : treeSpecialSoundWith init impl S relIn relOut verifier Ext) : + treeSpecialSoundWith init impl S' relIn relOut verifier Ext' := by + subst hS + obtain rfl := eq_of_heq hExt + exact h + +/-! ## Escape-threaded tree special soundness + +For reductions whose extraction may instead break a cryptographic assumption: the conclusion becomes +a disjunction against an escape event (`ChallengeTree.EscapeEvent`). Relations and extractors stay +unchanged, and since the event never mentions the extractor, no choice of extractor can trivialize +the statement — a certificate is exactly as strong as its event is honest. -/ + +/-- **Escape-threaded tree special soundness, named form.** `Verifier.treeSpecialSoundWith` with + an escape-event disjunct: on every structured accepting tree, either the tree exhibits the escape + event `esc` (a trusted spec — see `ChallengeTree.EscapeEvent`) or the named extractor produces a + `relIn`-witness. -/ +def treeSpecialSoundWithEscape (S : ChallengeTreeShape pSpec) + (esc : ChallengeTree.EscapeEvent StmtIn pSpec S.arity) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (verifier : Verifier oSpec StmtIn StmtOut pSpec) + (Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity) : Prop := ∀ stmtIn : StmtIn, ∀ tree : ChallengeTree pSpec S.arity 0, tree.IsStructured S → tree.IsAccepting init impl verifier stmtIn relOut.language → - (stmtIn, E stmtIn tree) ∈ relIn + esc stmtIn tree ∨ (stmtIn, Ext stmtIn tree) ∈ relIn + +/-- Existential closure of `treeSpecialSoundWithEscape`, for use as the *right* factor of an + append. The named form is preferred in advertised statements, since a composed chain then exposes + a runnable end-to-end extractor. -/ +def treeSpecialSoundEscape (S : ChallengeTreeShape pSpec) + (esc : ChallengeTree.EscapeEvent StmtIn pSpec S.arity) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (verifier : Verifier oSpec StmtIn StmtOut pSpec) : Prop := + ∃ Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity, + treeSpecialSoundWithEscape init impl S esc relIn relOut verifier Ext + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Forget the name of the extractor. -/ +theorem treeSpecialSoundWithEscape.toEscape {S : ChallengeTreeShape pSpec} + {esc : ChallengeTree.EscapeEvent StmtIn pSpec S.arity} + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + (h : treeSpecialSoundWithEscape init impl S esc relIn relOut verifier Ext) : + treeSpecialSoundEscape init impl S esc relIn relOut verifier := ⟨Ext, h⟩ + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- At the never-firing event the escape notion is the plain notion: the escape layer is a + conservative extension. -/ +theorem treeSpecialSoundWithEscape_false_iff (S : ChallengeTreeShape pSpec) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (verifier : Verifier oSpec StmtIn StmtOut pSpec) + (Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity) : + treeSpecialSoundWithEscape init impl S (fun _ _ => False) relIn relOut verifier Ext ↔ + treeSpecialSoundWith init impl S relIn relOut verifier Ext := by + constructor <;> intro h stmtIn tree hstr hacc + · exact (h stmtIn tree hstr hacc).resolve_left id + · exact Or.inr (h stmtIn tree hstr hacc) + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- **Lossless escape lift**: a plain certificate holds at *any* escape event, via the right + disjunct — so an escape-free protocol enters an escape-threaded chain for free. -/ +theorem treeSpecialSoundWith.withEscape {S : ChallengeTreeShape pSpec} + (esc : ChallengeTree.EscapeEvent StmtIn pSpec S.arity) + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + (h : treeSpecialSoundWith init impl S relIn relOut verifier Ext) : + treeSpecialSoundWithEscape init impl S esc relIn relOut verifier Ext := + fun stmtIn tree hstr hacc => Or.inr (h stmtIn tree hstr hacc) + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Escape events are monotone: a certificate at `esc` holds at any weaker (larger) event. -/ +theorem treeSpecialSoundWithEscape.mono {S : ChallengeTreeShape pSpec} + {esc esc' : ChallengeTree.EscapeEvent StmtIn pSpec S.arity} + (hmono : ∀ s t, esc s t → esc' s t) + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + (h : treeSpecialSoundWithEscape init impl S esc relIn relOut verifier Ext) : + treeSpecialSoundWithEscape init impl S esc' relIn relOut verifier Ext := + fun stmtIn tree hstr hacc => (h stmtIn tree hstr hacc).imp (hmono _ _) id + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- **Shape-congruence transport for escape-threaded named tree special soundness.** Both the + extractor and the escape event have types mentioning `S.arity`, so a plain `rw` at the shape is + motive-incorrect; this lemma carries them across heterogeneously (in practice both `HEq`s are + `HEq.rfl`, the relevant shape equalities having definitionally equal arities). -/ +theorem treeSpecialSoundWithEscape_congr {S S' : ChallengeTreeShape pSpec} (hS : S = S') + {esc : ChallengeTree.EscapeEvent StmtIn pSpec S.arity} + {esc' : ChallengeTree.EscapeEvent StmtIn pSpec S'.arity} (hEsc : HEq esc esc') + {relIn : Set (StmtIn × WitIn)} {relOut : Set (StmtOut × WitOut)} + {verifier : Verifier oSpec StmtIn StmtOut pSpec} + {Ext : Extractor.TreeBased StmtIn WitIn pSpec S.arity} + {Ext' : Extractor.TreeBased StmtIn WitIn pSpec S'.arity} (hExt : HEq Ext Ext') + (h : treeSpecialSoundWithEscape init impl S esc relIn relOut verifier Ext) : + treeSpecialSoundWithEscape init impl S' esc' relIn relOut verifier Ext' := by + subst hS + obtain rfl := eq_of_heq hEsc + obtain rfl := eq_of_heq hExt + exact h end Verifier diff --git a/ArkLib/OracleReduction/Security/TranscriptTree/Composition.lean b/ArkLib/OracleReduction/Security/TranscriptTree/Composition.lean index c2b18913e8..aaaec3940f 100644 --- a/ArkLib/OracleReduction/Security/TranscriptTree/Composition.lean +++ b/ArkLib/OracleReduction/Security/TranscriptTree/Composition.lean @@ -25,6 +25,9 @@ import ArkLib.OracleReduction.Security.TranscriptTree.Basic - `ChallengeTree.AppendSplit` / `ChallengeTree.appendSplit` — the split of a tree over `pSpec₁ ++ₚ pSpec₂` into a first-stage tree (`fst`) and a path-indexed family of suffix trees (`sndAt`). + - `ChallengeTree.EscapeEvent.append` — composition of two escape events + (`ChallengeTree.EscapeEvent`) along the same split: the left event on the prefix tree, or the + right event on some suffix tree at the left verifier's verdict on that prefix leaf. ## Main theorems @@ -921,6 +924,40 @@ theorem appendSplit_fullTranscripts_append_of_mem end Membership +section EscapeEventAppend + +variable {arity₁ : pSpec₁.ChallengeIdx → ℕ} {arity₂ : pSpec₂.ChallengeIdx → ℕ} + +/-- Binary composition of escape events along a protocol append: the composed event fires iff the +left event fires on the prefix tree, or the right event fires on the suffix tree hanging off some +prefix leaf, at the intermediate statement `verify₁` computes on that leaf's transcript. Each +factor's event stays self-contained, so factors may track breaks of entirely different assumptions. + +Where `verify₁` is unconstrained the composed event evaluates `esc₂` at intermediate statements no +honest execution produces — harmless, since an honest event is a break at *every* `(stmt, tree)` +pair (`ChallengeTree.EscapeEvent`). -/ +def EscapeEvent.append {Stmt₁ Stmt₂ : Type} + (esc₁ : EscapeEvent Stmt₁ pSpec₁ arity₁) (esc₂ : EscapeEvent Stmt₂ pSpec₂ arity₂) + (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) : + EscapeEvent Stmt₁ (pSpec₁ ++ₚ pSpec₂) (appendArity arity₁ arity₂) := + fun stmt tree => + esc₁ stmt tree.appendSplit.fst ∨ + ∃ path : LeafPath tree.appendSplit.fst, + esc₂ (verify₁ stmt path.fullTranscript) (tree.appendSplit.sndAt path) + +/-- Unfolding lemma for the composed escape event (definitional; for readability at composition +sites and `simp`-driven characterizations of composed chains' events). -/ +theorem EscapeEvent.append_apply {Stmt₁ Stmt₂ : Type} + (esc₁ : EscapeEvent Stmt₁ pSpec₁ arity₁) (esc₂ : EscapeEvent Stmt₂ pSpec₂ arity₂) + (verify₁ : Stmt₁ → pSpec₁.FullTranscript → Stmt₂) (stmt : Stmt₁) + (tree : ChallengeTree (pSpec₁ ++ₚ pSpec₂) (appendArity arity₁ arity₂) 0) : + esc₁.append esc₂ verify₁ stmt tree ↔ + (esc₁ stmt tree.appendSplit.fst ∨ + ∃ path : LeafPath tree.appendSplit.fst, + esc₂ (verify₁ stmt path.fullTranscript) (tree.appendSplit.sndAt path)) := Iff.rfl + +end EscapeEventAppend + end AppendSplit end ChallengeTree diff --git a/ArkLib/ProofSystem/Binius/BinaryBasefold/Basic.lean b/ArkLib/ProofSystem/Binius/BinaryBasefold/Basic.lean index 66306d3d08..a7874850a1 100644 --- a/ArkLib/ProofSystem/Binius/BinaryBasefold/Basic.lean +++ b/ArkLib/ProofSystem/Binius/BinaryBasefold/Basic.lean @@ -369,7 +369,6 @@ end OracleStatementIndex -- `ArkLib.ProofSystem.Sumcheck.Structured`. -- We re-export them under the `Binius.BinaryBasefold` namespace so that existing -- references — qualified or unqualified — continue to resolve. --- See `GENERIC_RING_SWITCHING_PLAN.md` §1.5 for the rationale. export Sumcheck.Structured (MultilinearPoly MultiquadraticPoly SumcheckMultiplierParam computeInitialSumcheckPoly projectToMidSumcheckPoly projectToMidSumcheckPolyWithParam diff --git a/ArkLib/ProofSystem/Component/CheckClaim.lean b/ArkLib/ProofSystem/Component/CheckClaim.lean index e0ffe96e9c..556aee6555 100644 --- a/ArkLib/ProofSystem/Component/CheckClaim.lean +++ b/ArkLib/ProofSystem/Component/CheckClaim.lean @@ -5,7 +5,8 @@ Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge /-! # Simple (Oracle) Reduction: Check if a predicate / claim on a statement is satisfied @@ -37,7 +38,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose Consequently, the oracle output relation is no longer trivial: it is `oracleRelOut P relIn`, which refines `relIn` by `P`. Completeness therefore holds under the explicit hypothesis that every `relIn` input already satisfies `P` (`oracleReduction_completeness`), and soundness is captured by - `oracleVerifier_coordinateWiseSpecialSound`. + `oracleVerifier_coordinateWiseSpecialSoundWith`. Note: with the pure pass-through oracle verifier (and the refactor to disallow failure in `OracleComp`), this oracle reduction is a special case of `ReduceClaim` (identity maps). @@ -302,23 +303,26 @@ theorem oracleReduction_completeness cases hx exact ⟨⟨hIn, hP stmt oStmt hIn⟩, rfl⟩ -/-- **Coordinate-wise special soundness of `CheckClaim`.** The verifier is a pure pass-through with -no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge -`coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to a transcript-level obligation. The -extractor is trivial (`e := fun _ _ => ()`, there is no witness); since the pass-through output -equals the input and `oracleRelOut P relIn ⊆ relIn`, accepting into `oracleRelOut.language` forces -the input into `relIn`. Holds for any coordinate-wise structure `D`. -/ -theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (!p[] : ProtocolSpec 0)) : - (oracleVerifier oSpec Statement OStatement).coordinateWiseSpecialSound init impl D relIn - (oracleRelOut P relIn) := by - refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D - (oracleVerifier oSpec Statement OStatement) relIn (oracleRelOut P relIn) (fun _ _ => ()) ?_ - rintro ⟨stmt, oStmt⟩ tr hAcc - have hmem := Verifier.mem_of_pure_accepting init impl - (oracleVerifier oSpec Statement OStatement).toVerifier ⟨stmt, oStmt⟩ tr - (oracleRelOut P relIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc - obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem - exact hu.1 +/-- **Coordinate-wise special soundness of `CheckClaim`, named form.** The verifier is a pure +pass-through with no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge +`coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`) to a transcript-level obligation. The +named extractor is trivial (`fun _ _ => ()`, there is no witness); since the pass-through output +equals the input and `oracleRelOut P relIn ⊆ relIn`, accepting into `oracleRelOut.language` +forces the input into `relIn`. Holds for any coordinate-wise structure `D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSoundWith + (D : CWSSStructure (!p[] : ProtocolSpec 0)) : + (oracleVerifier oSpec Statement OStatement).coordinateWiseSpecialSoundWith init impl D relIn + (oracleRelOut P relIn) + (fun _ _ => ()) := by + have h := OracleVerifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec Statement OStatement) relIn (oracleRelOut P relIn) (fun _ _ => ()) + (fun s tr hAcc => by + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement).toVerifier s tr + (oracleRelOut P relIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu.1) + exact h end OracleReduction diff --git a/ArkLib/ProofSystem/Component/ReduceClaim.lean b/ArkLib/ProofSystem/Component/ReduceClaim.lean index 4394cadbc5..d9ff0db0f2 100644 --- a/ArkLib/ProofSystem/Component/ReduceClaim.lean +++ b/ArkLib/ProofSystem/Component/ReduceClaim.lean @@ -5,7 +5,8 @@ Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge /-! # Simple (Oracle) Reduction: Locally / non-interactively reduce a claim @@ -178,31 +179,43 @@ the deterministic-left hypothesis of the CWSS binary append. -/ instance instIsPure : (verifier oSpec mapStmt).IsPure := ⟨fun stmt _ => mapStmt stmt, fun _ _ => rfl⟩ -/-- **Coordinate-wise special soundness of `ReduceClaim`.** The verifier is pure with no challenge -rounds, so CWSS collapses (via the no-challenge bridge) to a transcript-level obligation. Given the -witness pull-back `mapWitInv` and the compatibility `hRel` (the same hypothesis as for RBR knowledge -soundness), the extractor `e stmtIn := mapWitInv stmtIn witOut` — where `witOut` is any output -witness making `mapStmt stmtIn` accepted — lands in `relIn`. Holds for any `D`. -/ -theorem verifier_coordinateWiseSpecialSound [Nonempty WitIn] +open Classical in +/-- **The `ReduceClaim` tree extractor**: pick (classically) any output witness that makes the +mapped statement accepted and pull it back along `mapWitInv`; junk if none exists. The choice is +inherent to `ReduceClaim` — it is relation-level bookkeeping with no transcript to compute from +(the tree of the zero-round protocol carries no information). -/ +noncomputable def treeExtractor [Nonempty WitIn] + (mapWitInv : StmtIn → WitOut → WitIn) (D : CWSSStructure (!p[] : ProtocolSpec 0)) : + Extractor.TreeBased StmtIn WitIn !p[] (CWSSStructure.toShape D).arity := + fun stmtIn _ => + if h : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut then mapWitInv stmtIn h.choose + else Classical.ofNonempty + +/-- **Coordinate-wise special soundness of `ReduceClaim`, named form.** The verifier is pure with +no challenge rounds, so CWSS collapses (via the no-challenge bridge) to a transcript-level +obligation: given the witness pull-back `mapWitInv` and the compatibility `hRel` (the same +hypothesis as for RBR knowledge soundness), the named `treeExtractor` lands in `relIn`. Holds for +any `D`. -/ +theorem verifier_coordinateWiseSpecialSoundWith [Nonempty WitIn] (D : CWSSStructure (!p[] : ProtocolSpec 0)) (hRel : ∀ stmtIn witOut, (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : - (verifier oSpec mapStmt).coordinateWiseSpecialSound init impl D relIn relOut := by - -- For each input statement, some candidate input witness works as soon as any output witness - -- makes the mapped statement accepted. - have hpick : ∀ stmtIn : StmtIn, ∃ witIn : WitIn, - (∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut) → (stmtIn, witIn) ∈ relIn := by - intro stmtIn - rcases or_not (p := ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut) with ⟨witOut, hw⟩ | h - · exact ⟨mapWitInv stmtIn witOut, fun _ => hRel stmtIn witOut hw⟩ - · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ - refine Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D - (verifier oSpec mapStmt) relIn relOut (fun stmtIn _ => (hpick stmtIn).choose) ?_ - intro stmtIn tr hAcc - have hlang : mapStmt stmtIn ∈ relOut.language := - Verifier.mem_of_pure_accepting init impl (verifier oSpec mapStmt) stmtIn tr - relOut.language (mapStmt stmtIn) rfl hAcc - exact (hpick stmtIn).choose_spec ((Set.mem_language_iff _ _).1 hlang) + Verifier.coordinateWiseSpecialSoundWith init impl D relIn relOut + (verifier oSpec mapStmt) (treeExtractor (mapStmt := mapStmt) relOut mapWitInv D) := by + classical + have h := Verifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D + (verifier oSpec mapStmt) relIn relOut + (fun stmtIn _ => + if h : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut then mapWitInv stmtIn h.choose + else Classical.ofNonempty) + (fun stmtIn tr hAcc => by + have hlang : mapStmt stmtIn ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl (verifier oSpec mapStmt) stmtIn tr + relOut.language (mapStmt stmtIn) rfl hAcc + have hex := (Set.mem_language_iff _ _).1 hlang + rw [dif_pos hex] + exact hRel stmtIn _ hex.choose_spec) + exact h end Reduction @@ -387,37 +400,48 @@ instance instIsPureOracle : ⟨fun p _ => ⟨mapStmt p.1, mapOStmt embedIdx hEq p.2⟩, fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ -/-- **Coordinate-wise special soundness of the `ReduceClaim` oracle reduction.** As in the -non-oracle case, the verifier is pure with no challenge rounds, so CWSS collapses to a +open Classical in +/-- **The `ReduceClaim` oracle tree extractor**: as in the non-oracle case, pick (classically) +any output witness that makes the mapped combined statement accepted and pull it back along +`mapWitInv`; junk if none exists. -/ +noncomputable def oracleTreeExtractor [Nonempty WitIn] + (mapWitInv : StmtIn × (∀ i, OStmtIn i) → WitOut → WitIn) + (D : CWSSStructure (!p[] : ProtocolSpec 0)) : + Extractor.TreeBased (StmtIn × (∀ i, OStmtIn i)) WitIn !p[] + (CWSSStructure.toShape D).arity := + fun s _ => + if h : ∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut + then mapWitInv s h.choose else Classical.ofNonempty + +/-- **Coordinate-wise special soundness of the `ReduceClaim` oracle reduction, named form.** As +in the non-oracle case, the verifier is pure with no challenge rounds, so CWSS collapses to a transcript-level obligation discharged by the witness pull-back `mapWitInv` and the compatibility `hRel` (identical to the RBR knowledge soundness hypothesis, `mapStmt` replaced by `mapStmt ⊗ -mapOStmt`). -/ -theorem oracleVerifier_coordinateWiseSpecialSound [Nonempty WitIn] +mapOStmt`), at the named `oracleTreeExtractor`. -/ +theorem oracleVerifier_coordinateWiseSpecialSoundWith [Nonempty WitIn] (D : CWSSStructure (!p[] : ProtocolSpec 0)) (hRel : ∀ stmtIn oStmtIn witOut, ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : - (oracleVerifier oSpec mapStmt embedIdx hEq).coordinateWiseSpecialSound init impl D - relIn relOut := by - -- For each combined input statement, some candidate input witness works as soon as any output - -- witness makes the mapped statement accepted. - have hpick : ∀ s : StmtIn × (∀ i, OStmtIn i), ∃ witIn : WitIn, - (∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut) → - (s, witIn) ∈ relIn := by - intro s - rcases or_not (p := ∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut) - with ⟨witOut, hw⟩ | h - · exact ⟨mapWitInv s witOut, fun _ => hRel s.1 s.2 witOut hw⟩ - · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ - refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec mapStmt embedIdx hEq).coordinateWiseSpecialSoundWith init impl D + relIn relOut + (oracleTreeExtractor (mapStmt := mapStmt) (embedIdx := embedIdx) (hEq := hEq) + relOut mapWitInv D) := by + classical + have h := OracleVerifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D (oracleVerifier oSpec mapStmt embedIdx hEq) relIn relOut - (fun s _ => (hpick s).choose) ?_ - rintro ⟨stmt, oStmt⟩ tr hAcc - have hlang : (mapStmt stmt, mapOStmt embedIdx hEq oStmt) ∈ relOut.language := - Verifier.mem_of_pure_accepting init impl - (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier ⟨stmt, oStmt⟩ tr relOut.language _ - (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc - exact (hpick (stmt, oStmt)).choose_spec ((Set.mem_language_iff _ _).1 hlang) + (fun s _ => + if h : ∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut + then mapWitInv s h.choose else Classical.ofNonempty) + (fun s tr hAcc => by + have hlang : (mapStmt s.1, mapOStmt embedIdx hEq s.2) ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier s tr relOut.language _ + (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + have hex := (Set.mem_language_iff _ _).1 hlang + rw [dif_pos hex] + exact hRel s.1 s.2 _ hex.choose_spec) + exact h end OracleReduction diff --git a/ArkLib/ProofSystem/Component/SendClaim.lean b/ArkLib/ProofSystem/Component/SendClaim.lean index ea6437dfdc..5a5c170c74 100644 --- a/ArkLib/ProofSystem/Component/SendClaim.lean +++ b/ArkLib/ProofSystem/Component/SendClaim.lean @@ -4,7 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Quang Dao, Tobias Rothmann -/ import ArkLib.OracleReduction.Security.RoundByRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge /-! # Simple Oracle Reduction - SendClaim @@ -25,8 +26,8 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose ## Security The verifier is pure and has no challenge rounds, hence **coordinate-wise special sound** - (`oracleVerifier_coordinateWiseSpecialSound`) for any `CWSSStructure`, via the no-challenge bridge - `OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx`. The extractor is trivial + (`oracleVerifier_coordinateWiseSpecialSoundWith`) for any `CWSSStructure`, via the no-challenge bridge + `OracleVerifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`. The extractor is trivial (`e := fun _ _ => ()`, there is no witness) and the output relation `toORelOut relIn P` refines the input relation by the claim predicate `P`, so accepting into its language forces the input into `relIn`. These results are `sorryAx`-free. This mirrors `SendSingleWitness` (the special @@ -143,22 +144,25 @@ def toORelOut : (⟨⟨stmt, fun i => oStmtAndMsg (Sum.inl i)⟩, ()⟩ ∈ relIn) ∧ P stmt (fun i => oStmtAndMsg (Sum.inl i)) (oStmtAndMsg (Sum.inr 0))) -/-- **Coordinate-wise special soundness of `SendClaim`.** The verifier is a pure pass-through with -no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge) to a transcript-level -obligation. The extractor is trivial (`e := fun _ _ => ()`, there is no witness); since the output -oracle statements at `inl` are the input oracles unchanged and `toORelOut relIn P` refines `relIn`, -accepting into `toORelOut.language` forces the input into `relIn`. Holds for any `D`. -/ -theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (pSpec Message)) : - (oracleVerifier oSpec Statement OStatement Message).coordinateWiseSpecialSound init impl D - relIn (toORelOut relIn P) := by - refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D +/-- **Coordinate-wise special soundness of `SendClaim`, named form.** The verifier is a pure +pass-through with no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge) to +a transcript-level obligation. The named extractor is trivial (`fun _ _ => ()`, there is no +witness); since the output oracle statements at `inl` are the input oracles unchanged and +`toORelOut relIn P` refines `relIn`, accepting into `toORelOut.language` forces the input into +`relIn`. Holds for any `D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSoundWith (D : CWSSStructure (pSpec Message)) : + (oracleVerifier oSpec Statement OStatement Message).coordinateWiseSpecialSoundWith init impl + D relIn (toORelOut relIn P) + (fun _ _ => ()) := by + have h := OracleVerifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D (oracleVerifier oSpec Statement OStatement Message) relIn (toORelOut relIn P) - (fun _ _ => ()) ?_ - rintro ⟨stmt, oStmt⟩ tr hAcc - have hmem := Verifier.mem_of_pure_accepting init impl - (oracleVerifier oSpec Statement OStatement Message).toVerifier ⟨stmt, oStmt⟩ tr - (toORelOut relIn P).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc - obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem - exact hu.1 + (fun _ _ => ()) + (fun s tr hAcc => by + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement Message).toVerifier s tr + (toORelOut relIn P).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu.1) + exact h end SendClaim diff --git a/ArkLib/ProofSystem/Component/SendWitness.lean b/ArkLib/ProofSystem/Component/SendWitness.lean index 017d9b5fbf..a07d473d9a 100644 --- a/ArkLib/ProofSystem/Component/SendWitness.lean +++ b/ArkLib/ProofSystem/Component/SendWitness.lean @@ -4,7 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge import Mathlib.Data.FinEnum /-! @@ -26,9 +27,9 @@ sends the (entire) witness to the verifier. There are two variants: The verifier of each variant is **pure** (`Verifier.IsPure` / `OracleVerifier.toVerifier.IsPure`) and has no challenge rounds, so it is **coordinate-wise special sound** for any `CWSSStructure` -(`verifier_coordinateWiseSpecialSound` and, for the oracle variant, -`SendSingleWitness.oracleVerifier_coordinateWiseSpecialSound`), via the no-challenge bridge -`Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx`. The extractor takes the witness to be +(`verifier_coordinateWiseSpecialSoundWith` and, for the oracle variant, +`SendSingleWitness.oracleVerifier_coordinateWiseSpecialSoundWith`), via the no-challenge bridge +`Verifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`. The extractor takes the witness to be the prover's single message (`e := fun _ tr => tr 0`) — the canonical "open in the clear" base case. These results are `sorryAx`-free. The indexed-family oracle variant (`section OracleReduction`) is deferred; see the note there. @@ -99,24 +100,27 @@ theorem reduction_completeness : intro stmtIn witIn hIn sorry -/-- **Coordinate-wise special soundness of `SendWitness`.** The verifier has no challenge rounds, so -CWSS collapses (via the no-challenge bridge `coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to -a transcript-level extraction obligation. The extractor is `e := fun _ tr => tr 0`: the witness *is* -the (single) prover message. Since the verifier is pure with output `⟨stmt, tr 0⟩` and +/-- **Coordinate-wise special soundness of `SendWitness`, named form.** The verifier has no +challenge rounds, so CWSS collapses (via the no-challenge bridge +`coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`) to a transcript-level extraction +obligation. The named extractor reads the witness off the tree's unique transcript — the witness +*is* the (single) prover message. Since the verifier is pure with output `⟨stmt, tr 0⟩` and `relOut = Prod.fst ⁻¹' relIn`, acceptance into `relOut.language` forces `⟨stmt, tr 0⟩ ∈ relIn`, which is exactly the extracted witness. This is the canonical "open in the clear" CWSS base case, and holds for *any* coordinate-wise structure `D`. -/ -theorem verifier_coordinateWiseSpecialSound (D : CWSSStructure (pSpec Witness)) : - (verifier oSpec Statement Witness).coordinateWiseSpecialSound init impl D relIn - (toRelOut relIn) := by - refine Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D - (verifier oSpec Statement Witness) relIn (toRelOut relIn) (fun _ tr => tr 0) ?_ - intro stmtIn tr hAcc - have hmem : (⟨stmtIn, tr 0⟩ : Statement × Witness) ∈ (toRelOut relIn).language := - Verifier.mem_of_pure_accepting init impl (verifier oSpec Statement Witness) stmtIn tr - (toRelOut relIn).language ⟨stmtIn, tr 0⟩ rfl hAcc - obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem - exact hu +theorem verifier_coordinateWiseSpecialSoundWith (D : CWSSStructure (pSpec Witness)) : + Verifier.coordinateWiseSpecialSoundWith init impl D relIn (toRelOut relIn) + (verifier oSpec Statement Witness) + (fun _ tree => tree.onlyTranscript 0) := by + have h := Verifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D + (verifier oSpec Statement Witness) relIn (toRelOut relIn) (fun _ tr => tr 0) + (fun stmtIn tr hAcc => by + have hmem : (⟨stmtIn, tr 0⟩ : Statement × Witness) ∈ (toRelOut relIn).language := + Verifier.mem_of_pure_accepting init impl (verifier oSpec Statement Witness) stmtIn tr + (toRelOut relIn).language ⟨stmtIn, tr 0⟩ rfl hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu) + exact h end Reduction @@ -355,25 +359,29 @@ theorem oracleReduction_completeness (h : NeverFail init) : -- and_true, Fin.isValue, and_imp, forall_const, true_and] -- aesop -/-- **Coordinate-wise special soundness of `SendSingleWitness`.** The oracle verifier has no -challenge rounds, so CWSS collapses (via the oracle no-challenge bridge -`coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to a transcript-level extraction obligation on -the combined statement `Statement × (∀ i, OStatement i)`. The extractor is `e := fun _ tr => tr 0`: -the extracted witness *is* the single oracle message. Since the verifier is pure with output -`⟨stmt, oStmtOut⟩` (where `oStmtOut` exposes the old oracle statements together with the message), -acceptance into `(toORelOut oRelIn).language` unfolds to exactly `⟨⟨stmt, oStmt⟩, tr 0⟩ ∈ oRelIn`. -Holds for *any* coordinate-wise structure `D`. -/ -theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (oraclePSpec Witness)) : - (oracleVerifier oSpec Statement OStatement Witness).coordinateWiseSpecialSound init impl D - oRelIn (toORelOut oRelIn) := by - refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D +/-- **Coordinate-wise special soundness of `SendSingleWitness`, named form.** The oracle verifier +has no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge +`coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx`) to a transcript-level extraction +obligation on the combined statement `Statement × (∀ i, OStatement i)`. The named extractor reads +the witness off the tree's unique transcript — the extracted witness *is* the single oracle +message. Since the verifier is pure with output `⟨stmt, oStmtOut⟩` (where `oStmtOut` exposes the +old oracle statements together with the message), acceptance into `(toORelOut oRelIn).language` +unfolds to exactly `⟨⟨stmt, oStmt⟩, tr 0⟩ ∈ oRelIn`. Holds for *any* coordinate-wise structure +`D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSoundWith + (D : CWSSStructure (oraclePSpec Witness)) : + (oracleVerifier oSpec Statement OStatement Witness).coordinateWiseSpecialSoundWith init impl + D oRelIn (toORelOut oRelIn) + (fun _ tree => tree.onlyTranscript 0) := by + have h := OracleVerifier.coordinateWiseSpecialSoundWith_of_isEmpty_challengeIdx init impl D (oracleVerifier oSpec Statement OStatement Witness) oRelIn (toORelOut oRelIn) - (fun _ tr => tr 0) ?_ - rintro ⟨stmt, oStmt⟩ tr hAcc - have hmem := Verifier.mem_of_pure_accepting init impl - (oracleVerifier oSpec Statement OStatement Witness).toVerifier ⟨stmt, oStmt⟩ tr - (toORelOut oRelIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc - obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem - exact hu + (fun _ tr => tr 0) + (fun s tr hAcc => by + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement Witness).toVerifier s tr + (toORelOut oRelIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu) + exact h end SendSingleWitness diff --git a/HACHI_GENERIC_RING_SWITCH_PLAN.md b/HACHI_GENERIC_RING_SWITCH_PLAN.md deleted file mode 100644 index b842b63097..0000000000 --- a/HACHI_GENERIC_RING_SWITCH_PLAN.md +++ /dev/null @@ -1,1079 +0,0 @@ -# Generic Ring Switch × Hachi — Reconciliation & Implementation Plan - -Target: extend `origin/feat/generic-ring-switch`'s `RingSwitching/Generic/` layer so it -accommodates **Hachi's §3.1 packing head** (NOZ26, ePrint 2026/156), and build that head — -guarded, zero-challenge, CWSS — composed onto the existing sorry-free chain -(`eval_coordinateWiseSpecialSound`, [Basic.lean:136](ArkLib/Commitments/Functional/Hachi/Basic.lean#L136)). -This plan supersedes Phases B–D of [`HACHI_RING_SWITCHING_PLAN.md`](HACHI_RING_SWITCHING_PLAN.md) -(the `PackingScheme`-over-`Profile` design); Phases A, E, F, G of that plan are unaffected except -for the deltas listed in §10 (Phase 7, item 3). Every file/line/signature anchor below was re-verified on the working -tree and on `origin/feat/generic-ring-switch` (2026-07-09); the working tree is branch -`hachi-polynomial-quadratic-eq`. - ---- - -## 0. Overview — what changes and why - -The branch `feat/generic-ring-switch` (author: Alexander Hicks; 3 commits over main; purely -additive, +1351 lines across 16 files) generalizes ring switching via a new -`RingSwitching/Generic/` layer: `RingSwitchCarrier` (packing algebra `P` + opening algebra `E`, -everything derived from two `Basis` witnesses), `BatchingStrategy` (challenge + Schwartz–Zippel -`separates` bound), an anchored relation chain `openingClaimRel → sliceRel → sumcheckClaimRel`, -and a `PackedCommitment`/`DenseMLPCS` PCS interface whose soundness field is **RBR knowledge -soundness**. Its docstrings envision Hachi as "the S8 non-domain sibling": a `BatchingStrategy` -instance over `R_q` supplying its own `separates` proof. - -That roadmap mis-models Hachi. Hachi's §3 ring switch is **deterministic**: the evaluation point -is engineered to be subfield-valued, so the reduction is one prover message `Y ∈ R_q`, one trace -check (Theorem 2), **zero challenges, zero sumcheck, zero soundness error** — and the residual -claim is already a native `R_q` evaluation claim consumed by the existing Fig. 3 chain. There is -nothing to batch and nothing to relocate. Moreover the Hachi chain's security currency is CWSS -(coordinate-wise special soundness, composed via `CWSSPackage`/`▷`), not RBR, and the repo has no -RBR↔CWSS bridge. - -**The five changes, and why each is necessary:** - -1. **Add a deterministic "packed-claim" exit stage to `Generic/`** (new `PackedClaim.lean` + - relation re-anchor). *Why necessary:* the branch's pipeline is hardwired - claims → eq-slices → batching → sumcheck → RBR-PCS; Hachi exits after the packing check, and - today that exit does not exist as a stage, a relation, or a lemma. Without it, Hachi cannot be - an instance of the generic layer at all — it would have to fake a degenerate - `BatchingStrategy`, which models an identity *fold*, not a trace *check*. - -2. **Retarget the "S8 Hachi sibling" docs.** *Why necessary:* three docstrings on the branch - (Batching.lean ×2, Relations.lean sanity) present Hachi as a future non-domain *batching* - instance. Left standing, the branch's own roadmap contradicts the protocol it claims to host, - and future work would build the wrong thing. The `CommRing`-only vocabulary is right; the - pipeline position is wrong. - -3. **Close the small lattice-layer gaps** (`psi_smul`, a bundled `psiLinearEquiv`, - `Nontrivial ↥(fixedSubring …)`, a named unit/cancellation lemma, one index-equiv). *Why - necessary:* the head's check reads the ψ-coordinates of the prover message; ψ exists only as a - bare bijective function today (`psi_bijective`), with no scalar-compatibility lemma and no - bundled inverse — the coordinate map `psiInv` cannot even be written down without them. - -4. **Build the head itself**: the unpack algebra (`unpackPoly` + the Theorem-2-powered - `unpackPoly_eval` / `traceCheck_iff`), the guarded 1-message verifier, `relRingSwitch`, its - CWSS theorem, and a **guarded CWSS append** in the framework. *Why necessary:* (a) the head's - check consumes `(xt, y)`, which the downstream `PolyEvalStatement` drops — the check can live - neither in a downstream relation nor in a pull-back, so the verifier must be able to *reject* - (`failure`), and (b) every existing CWSS composition theorem - ([Composition.lean:414](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Composition.lean#L414)) - requires a **totally pure** left verifier — a guarded head is inadmissible without the new - guarded append. The Basic.lean TODO block explicitly requests this - ("Guarded subprotocols need a guarded variant of `▷`", Basic.lean:256-272). - -5. **Compose into the chain** (`ringSwitchChain`, extending `evalChain`). *Why necessary:* the - deliverable is the end-to-end theorem — extension-field-style claim down to Eq. (20) — and the - chain's composition discipline (`CWSSPackage`, syntactic `rfl` seams at - `relPolyEval 𝓜(q,α) …`) imposes exact statement/relation shapes on the head that must be built - to fit, not adapted after the fact. - -**What this plan deliberately does NOT do** (and why): it does not route Hachi's proofs through -the branch's `MultilinearPoly`-based generic lemmas. The Hachi chain speaks `CMlPolynomial` -(computable, `Vector`-indexed: `relPolyEval`'s eval claim is -`CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y`), the branch speaks -`MultilinearPoly` (an `MvPolynomial` subtype), and CompPoly's `toMvPolynomial` bridge **has no -eval-agreement lemma** (verified missing). Building that bridge is real work with zero payoff for -soundness. Instead: the generic layer gets the stage + value-level lemmas in its own idiom -(consumable by Binius later), and the Hachi head proves its pull-back natively on -`CMlPolynomial` via the existing `PolynomialEvalSplit` machinery. The `hachiCarrier` instance -(Phase 7) pins the correspondence; the polynomial-level bridge is recorded as optional hygiene. - -**Phase order and dependencies:** - -``` -Phase 0 (branch setup) - → Phase 1 (Generic/ stage + docs) [independent of 2–6] - → Phase 2 (lattice glue) - → Phase 3 (unpack algebra, the heart) - → Phase 4 (head reduction + CWSS) - → Phase 5 (guarded append, framework) [independent of 1–4; needed by 6] - → Phase 6 (chain assembly) - → Phase 7 (carrier instance + hygiene) -``` - -Estimated effort: 0: 0.5 d · 1: 1.5 d · 2: 1.5 d · 3: 3–4 d · 4: 2–3 d · 5: 2–3 d · 6: 1 d · -7: 1 d. Total ≈ 12–15 focused days. - ---- - -## 1. Verified ground truth (do not re-derive; re-verify only if a step fails) - -### 1.1 The branch (`origin/feat/generic-ring-switch`, head `c14c1827`) - -- Full footprint: 16 files, +1351/−12, **zero edits to existing `RingSwitching/` files**. New: - `Generic/{Carrier,Packing,Batching,Recombine,Relations}.lean`, `ArkLib/Data/Module/Basis.lean` - (`Module.Basis.sum_smul_bijective/injective`). Modified: `ArkLib.lean` (+6 imports, generated), - `Data/MvPolynomial/{Degrees,Multilinear}.lean` (3 new lemmas incl. `MLE_eval_eq_sum_eqTilde`, - `MLE_totalDegree_le`), `Data/Probability/Instances.lean` (**breaking**: - `prob_schwartz_zippel_mv_polynomial` gains explicit `(d : ℕ)` arg — no callers on HEAD, safe), - `Binius/BinaryBasefold/Basic.lean` + `FRIBinius/{CoreInteractionPhase,Prelude}.lean` (the R7 - `witnessNovelCoeffs` semantic fix + `biniusCommitsTo`), `references.bib`, `repo-map.md`, - `docs/kb/concepts/ring-switching.md` (+81, "The Generic layer"). -- **Merge into HEAD is conflict-free** (verified `git merge-tree`): only `ArkLib.lean`, - `references.bib`, `repo-map.md` changed on both sides, all auto-merged. Caveat: the dry run - tested committed HEAD; the working tree is dirty. -- Key signatures (Carrier.lean): `RingSwitchCarrier B` with fields - `P E ιP ιE : Type`, `[commP commE : CommRing] [algP algE : Algebra B ·] [ntP ntE : Nontrivial ·] - [ftP ftE : Fintype ·]`, `packBasis : Basis ιP B P`, `openBasis : Basis ιE B E` (all registered - `attribute [instance]`); `packedMLE Ps = ∑ i, packBasis i • componentWise_embed_MLE B m - (algebraMap B car.P) (Ps i)`; `bridge_eqTilde` proven. -- Packing.lean: `packedMLE_eval (Ps) (pt : Fin m → B) : MvPolynomial.eval (fun i => algebraMap B - car.P (pt i)) (car.packedMLE Ps).val = ∑ i, algebraMap B car.P ((Ps i).val.eval pt) * - car.packBasis i` — **proven, CommRing-only, base-embedded points only** (docstring forbids - assuming more). `curryFamily` curries the **first** κ variables, `h_l : ℓ = ℓ' + κ`. -- Batching.lean: `BatchingStrategy P W` fields `Challenge [Fintype] [Nonempty]`, - `weight : Challenge → W → P`, `error : ℝ≥0`, `separates : ∀ s s', s ≠ s' → Pr_{c ←$ᵖ - Challenge}[∑ u, weight c u * s u = ∑ u, weight c u * s' u] ≤ error`. Instances - `gammaPowers`/`eqFold` gated `[IsDomain P] [Fintype P]` at the section level (line 108). -- Relations.lean: `openingClaimRel`/`sliceRel`/`sumcheckClaimRel` + `sumcheckClaim_of_slices` - (proven); `PackedCommitment` (`commitsTo` + `commitsTo_functional`, `commitsTo_not_top` proven); - `DenseMLPCS` with **unfilled** `perfectCompleteness`/`rbrKnowledgeSoundness` obligations. -- Hachi/S8 docstrings to retarget: Batching.lean module docstring bullet, `BatchingStrategy` - docstring (lines 60-69), sanity comment before line 246; Relations.lean:312-314. -- `git grep coordinateWiseSpecialSound` over the branch's `RingSwitching/` is **empty**; the - branch's CWSS dir has only `Basic.lean` + `Composition.lean` (no `NoChallenge`, no - `SeqCompose`, no `SingleRound`, no `Package`). - -### 1.2 CWSS infrastructure (working tree — strictly ahead of every remote on these files) - -- Files present: `Basic, Composition, SeqCompose, NoChallenge, SingleRound, Package` (Package.lean - is **staged-new, working-tree only**; `origin/cwss-components-infra` lacks `ofIsEmpty`, - `SingleRound`, `Package` — build on THIS branch, not infra). -- `Verifier.coordinateWiseSpecialSound (D : CWSSStructure pSpec) relIn relOut` = - `treeSpecialSound init impl (CWSSStructure.toShape D) relIn relOut` - ([Basic.lean:212](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Basic.lean#L212)); - call shape `V.coordinateWiseSpecialSound init impl D relIn relOut`. -- `CWSSStructure.ofIsEmpty` ([NoChallenge.lean:45](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean#L45)); - no-challenge bridge `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx` - (NoChallenge.lean:118-127) with premise - `h : ∀ stmtIn tr, Pr[(· ∈ relOut.language) | …V.run stmtIn tr…] = 1 → (stmtIn, e stmtIn tr) ∈ relIn` - and extractor `e : StmtIn → FullTranscript pSpec → WitIn` — **note: `e` sees only the - transcript, not the downstream witness**. For reductions whose input witness must be built from - the *output* witness (ours), the template is instead - `ReduceClaim.verifier_coordinateWiseSpecialSound` - ([ReduceClaim.lean:186](ArkLib/ProofSystem/Component/ReduceClaim.lean#L186), hypothesis - `hRel : ∀ stmtIn witOut, (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn`, - requires `[Nonempty WitIn]`) and `SendWitness.verifier_coordinateWiseSpecialSound` - (SendWitness.lean:109, non-oracle 1-message P→V) — **Phase 4 mirrors these proofs**. -- Pure append: `Verifier.append_coordinateWiseSpecialSound` (Composition.lean:414-428), purity - hypothesis exactly `hV₁ : ∀ stmt tr, V₁.verify stmt tr = pure (verify₁ stmt tr)`. Helpers to - generalize: `append_run_pure_left` (:311-319), `pure_accepting_of_mem` (:325-332), converse - `mem_of_pure_accepting` (SeqCompose.lean:53-60). Generic layer: `append_treeSpecialSound` - (:366-375) consumes `hV₁` at ~:396 and ~:407. -- Chain packaging: `CWSSPackage` (Package.lean:54-69; fields `verifier struct relIn relOut - isPure isCWSS`), `CWSSPackage.append` with autoparam seam `(hseam : L₁.relOut = L₂.relIn := by - rfl)`, infix `▷` (scoped `CoordinateWise`). -- The chain is **plain `Verifier`** (no oracle statements): `bridgeVerifier : Verifier …`, - QuadEval `verifier : Verifier …`, composed by `Verifier.append`. - -### 1.3 The Hachi chain seam (working tree) - -- Namespace `ArkLib.Lattices.Ajtai.InnerOuter`; **never** `open ArkLib.Lattices` (ambiguous `⬝ᵥ`); - `open WeakBinding` for `VerifiedOpening`. -- `PolyEvalStatement` (PolyEvalReduction.lean:81-92): fields `pp : Hachi.PublicParamsD Φ innerRows - (2^m) messageDigits outerRows (2^r) innerDigits dRows`, `u : Commitment Φ outerRows`, - `xl : Vector (Rq Φ) r`, `xh : Vector (Rq Φ) m`, `y : Rq Φ`. -- `relPolyEval Φ base βSq γ κ` (:151-159), opening case: - `VerifiedOpening Φ base βSq γ κ s.pp.toPublicParams s.u o ∧ - CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y`; msisB/msisD cases via - `ModuleSIS.relation`. `extractedPoly Φ base o : CMlPolynomial (Rq Φ) (r + m)` (:133-136). -- `evalChain` (Basic.lean:115-128) = `bridgePackage … ▷ quadEvalPackage …`; top theorem - `eval_coordinateWiseSpecialSound` (Basic.lean:136-149). **Seam discipline**: a new head's - `relOut` must be *syntactically* - `relPolyEval 𝓜(q,α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m - messageDigits) γ (2 * ω)` with output statement exactly - `PolyEvalStatement 𝓜(q,α) innerRows messageDigits outerRows innerDigits dRows m r` for the - `rfl` autoparam to close. Section variables at Basic.lean:103-106; `b ω γ` are implicit. -- TODO block at Basic.lean:256-272 names exactly this work; header diagram at :51-74 already - slots "§3.1 ring-switch packing head — planned (guarded, 1 msg)". - -### 1.4 Lattice layer (working tree) - -- `psi (α k) (a : Fin (2^α / k) → fixedSubring (R := R) α k) : Rq (powTwoCyclotomic α)` - (Subfield/Packing.lean:61-64) — a plain `def` (sum of `↑(a j) * Xpow (packExp α k j.val)`). - `psi_add` ✓ (:74), `psi_zero` ✓, `psi_bijective (α κ) (h2 : (2 : ZMod q) ≠ 0) - (hk : 2 * 2^κ ∣ 2^α)` ✓ (Bijectivity.lean:34). **`psi_smul` MISSING. Any bundled - LinearMap/LinearEquiv MISSING** (grep-verified). -- **Theorem 2**: `traceH_psi_mul_conj (α k) (h2 : (2 : R) ≠ 0) (hk2pow : ∃ κ, k = 2^κ) - (hk : 2 * k ∣ 2^α) (a b : Fin (2^α / k) → fixedSubring (R := R) α k) : - traceH α k (psi α k a * conjAut α (psi α k b)) = (2^α / k) • ((∑ i, a i * b i : fixedSubring - (R := R) α k) : Rq (powTwoCyclotomic (R := R) α))` — proven - (TraceInnerProduct.lean:229-234). RHS is ℕ-`nsmul` of a coerced subring sum. -- `fixedSubring (α k) : Subring (Rq (powTwoCyclotomic α))` (FixedSubring.lean:43); - `Fintype ↥(fixedSubring α k)` instance ✓ (Subfield/Basis.lean:305-307); - `Nontrivial ↥(fixedSubring …)` **not an instance** — must be derived (pattern: - Field.lean:288-302 `haveI`, or via `card_fixedSubring_eq q α κ h2 hk` q-explicit, - Cardinality.lean:99). -- `Algebra ↥(fixedSubring α k) (Rq …)`: **free from Mathlib** (`Algebra.ofSubring`, - `algebraMap = S.subtype`; rfl-lemma `algebraMap_ofSubsemiring`). No ArkLib code needed. -- Unit/cancellation: only **inline** today — - `IsUnit ((2^α / 2^κ : ℕ) : Rq …)` via `Nat.pow_div hκ (by norm_num), Nat.cast_pow, Nat.cast_ofNat` - + `(isUnit_two (powTwoCyclotomic α) h2).pow _` (TraceInnerProduct.lean:273-275); cancellation - pattern `rw [nsmul_eq_mul, nsmul_eq_mul] at heq; hunit.mul_left_cancel heq` (:282-283). - `isUnit_two` is named (Subfield/Basis.lean:227). -- Index arithmetic: everything is literally `2 ^ α / 2 ^ κ` (Nat division), **never** - `2 ^ (α − κ)`; `Nat.pow_div : n ≤ m → 0 < x → x^m / x^n = x^(m-n)` (Lean core; exponent - inequality FIRST). `succ_le_of_two_mul_two_pow_dvd (hk : 2 * 2^κ ∣ 2^α) : κ + 1 ≤ α` - (Galois/Order.lean:113) supplies `κ ≤ α`. -- ZMod-q section style: `variable (q : ℕ) [Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)] - [LawfulBEq (ZMod q)]` with `q` explicit. -- Split machinery (ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean, namespace - `ArkLib.Lattices.Hachi`, `[CommSemiring R]`): `splitEquiv nl nh : Fin (2^nh) × Fin (2^nl) ≃ - Fin (2^(nl+nh))` with `(splitEquiv nl nh (x,y)).val = y.val + 2^nl * x.val` (low bits = second - component = first `nl` variables); `eval_eq_sum (p) (v) : eval p v = ∑ i, p.get i * - (monomialBasis v).get i` (:126); `monomialBasis_get` (:131); - `toMatrix : CMlPolynomial R (nl+nh) → PolyMatrix R (2^nl) (2^nh)` (:141) / - `toPolynomial : PolyMatrix R (2^nl) (2^nh) → CMlPolynomial R (nl+nh)` (:190) + round-trips - (:202/:209); `splitForm_monomialBasis_eq_eval (M) (xl) (xh) : splitForm M (monomialBasis - xl).get (monomialBasis xh).get = CMlPolynomial.eval (toPolynomial M) (xl ++ xh)` (:221-224); - `evalSplit_eq_eval` (:163); `monomialBasis_split` (:145). **Name trap**: - `Hachi.toPolynomial` (matrix reshape) ≠ CompPoly's `CMlPolynomial.toMvPolynomial` (which has - NO eval-agreement lemma — do not plan around it). -- Sorries OFF this plan's path: `no_selfReciprocal_factor` (Field.lean:211), - `cInfNorm_psi_le` (NormBound.lean:103). Nothing here depends on either. - -### 1.5 Mathlib (rev v4.30.0) — exact names - -`Module.Basis.ofEquivFun [Finite ι] (e : M ≃ₗ[R] ι → R)` + `Basis.ofEquivFun_repr_apply` -(`(Basis.ofEquivFun e).repr x i = e x i`, rfl) + `Basis.equivFun_ofEquivFun`; -`Module.Basis.singleton ι R [Unique ι]` + `singleton_repr`; `Basis.equivFun_symm_apply`; -`Finsupp.mapRange(_apply/support_mapRange)`; `MvPolynomial.eval_eq'`; -`mem_restrictDegree_iff_degreeOf_le` (**ArkLib-local**, ArkLib/Data/MvPolynomial/Degrees.lean:183); -`Fin.append` + `append_left/right`; `finSumFinEquiv`; `finCongr`; `Algebra.smul_def`, -`algebraMap_smul`, `_root_.smul_eq_mul` (the `Algebra.id.` variant is deprecated); -`Nat.pow_div {x m n} (h : n ≤ m) (hx : 0 < x)` (Lean core). Basis namespace is `Module.Basis` — -write it qualified, per ArkLib convention. - ---- - -## 2. Design decisions (each with its reason) - -- **G1 — Two representations, one seam.** Generic layer additions are stated in the branch's - idiom (`RingSwitchCarrier` + `MultilinearPoly`); the Hachi head's soundness algebra is stated - natively on `CMlPolynomial` using `PolynomialEvalSplit`. They are tied by the `hachiCarrier` - instance (Phase 7) and a documented correspondence, NOT by a proof-level bridge. *Reason:* - CompPoly's `toMvPolynomial` has no eval lemma; the truly generic kernel of the head's soundness - is two lines of `Basis.repr` linear algebra, so a representation bridge buys nothing and risks - much. -- **G2 — The check is basis-coordinate-form; the trace form is an instance equivalence.** The - verifier's check is `y = ∑ v, w v * psiInv Y v` (all in `B := ↥(fixedSubring …)`); the paper's - `Tr_H(Y · σ₋₁(ψ(monomials))) = (2^α/2^κ)·y` is proven **equivalent** via `traceH_psi_mul_conj` - (Phase 3). *Reason:* coordinates via `Basis.repr`/`psiInv` are canonical — this kills the old - plan's R2 (rows-vs-columns) and D5 (σ₋₁ message twist) wholesale: the wire message is the - untwisted `Y`, and no `φ₀/φ₁` data is needed anywhere. -- **G3 — Guarded verifier, `failure` on check-failure.** `verify := fun s tr => if check … then - pure (toPolyEvalStatement s Y) else failure`. *Reason:* the check consumes `s.xt, s.y`, which - `PolyEvalStatement` drops; a pure pass-through head would be unsound (nothing downstream can - re-impose the check), and the dummy-state convention loses the constraint in CWSS extraction. -- **G4 — The head outputs `PolyEvalStatement` directly** (no separate σ₋₁/coercion adapter - reduction). *Reason:* G2 killed the twist, so the only statement work is coercion - `Vector B → Vector (Rq Φ)` and `y := Y` — folding it into the head's pure-branch avoids a - zero-round `ReduceClaim` factor and keeps the `▷` seam count minimal. -- **G5 — Batching stays untouched; Hachi is NOT a degenerate `BatchingStrategy`.** *Reason:* a - `Challenge := Unit, error := 0` instance would model an identity fold of claims, not a trace - check — the check predicate appears nowhere in `BatchingStrategy`'s vocabulary. The honest - reading: `BatchingStrategy` is the *relocation* phase's design axis (DP24-only); the packing - stage's axis is the weight family (Phase 1). -- **G6 — Laws as hypotheses, no new sorries in structures** (inherited from the old plan's D1 - and the branch's own "hypotheses live on theorems" discipline). Structures carry data; `Prop`s - are standalone and taken as theorem hypotheses. -- **G7 — Index conventions pinned once, in code, with `decide` examples.** All packing indices - are `Fin (2^α / 2^κ)`; the single named equiv `packIndexEquiv` (Phase 2) converts to - `Fin (2^(α−κ))` where `CMlPolynomial` arity arithmetic needs it. *Reason:* the two forms are - NOT defeq; every mid-proof cast is a bug factory (old plan R4). -- **G8 — Scope guard.** Honest-prover/completeness stays at skeleton level (QuadEval precedent); - knowledge-error accounting, Fiat–Shamir, and the branch's S6/S7 obligations are out of scope. - *Reason:* matches the chain's current discipline (TODO block) and keeps this plan mergeable. -- **G9 — The guard check is Bool-valued, defined once.** Phase 4 defines - `def headCheck … : Bool := decide (s.y = ∑ …)` (via `DecidableEq B`); the verifier is the Bool - `if headCheck … then pure … else failure`; `Verifier.IsGuarded` (Phase 5) stores a - `… → Bool` check so `verify_eq` matches *syntactically*; `relRingSwitch`-side proofs cross via - `decide_eq_true_eq`. CheckClaim's `[DecidablePred pred]` + `do guard …` convention is **not** - copied — only its guard/`failure` mechanics are precedent. *Reason:* three candidate - conventions exist in-tree; Bool is the only one that lets `IsGuarded.verify_eq` match without - instance-plumbing at the composition site. Decided now so Phases 4 and 5 cannot diverge. - -### 2.5 Delivery discipline - -- **Scratch files stay untracked.** Phase 0.1 commits only tracked changes plus explicitly - `git add`-ed new `.lean`/docs files (`git add -u` + named adds — never `git add .` at the - root). The four root-level `HACHI_*.md` planning notes remain untracked (CLAUDE.md: stable - guidance belongs in `docs/wiki/`, not ephemeral notes). -- **PR partition:** PR-A = Phases 0–1 (Generic/ additions + docs retarget; request review from - the branch author, per R8). PR-B = Phase 5 (framework-only; maintainer review, per R3). PR-C = - Phases 2–4 + 6–7 (the Hachi head; depends on A and B). -- **Cadence:** `./scripts/validate.sh` green at every phase boundary; `--lint` before each PR; - new files `git add`-ed before validation (generated `ArkLib.lean`). - ---- - -## 3. Phase 0 — Branch setup (0.5 d) - -*Why necessary:* the Generic/ files exist only on `origin/feat/generic-ring-switch`; the CWSS -infra (incl. `Package.lean`, staged-only) exists only on the current branch's working tree. No -single existing ref contains both. - -Steps (exact): - -1. Commit all current working-tree changes on `hachi-polynomial-quadratic-eq` (or have the user - do so / stash-confirm). **Do not proceed on a dirty tree** — the merge dry-run only covered - committed state. -2. `git checkout -b hachi-generic-ring-switch` (from the committed tip). -3. `git merge origin/feat/generic-ring-switch`. Expected: clean auto-merge; the only both-sides - files are `ArkLib.lean`, `blueprint/src/references.bib`, `docs/wiki/repo-map.md`. If - `ArkLib.lean` conflicts anyway: take either side, then `git add` all new `.lean` files and run - `./scripts/update-lib.sh` (it regenerates `ArkLib.lean` from `git ls-files`; it **hard-fails - on untracked** `ArkLib/**/*.lean` — always `git add` first). Never hand-edit `ArkLib.lean`. -4. `lake exe cache get` if needed, then `./scripts/validate.sh` — must be green before any new - work. Note the merge brings a breaking 4-arg `prob_schwartz_zippel_mv_polynomial` (no HEAD - callers — nothing to fix) and the Binius `witnessNovelCoeffs` semantic fix (HEAD does not - touch BinaryBasefold — nothing to fix). - -Acceptance: `./scripts/validate.sh` green on the merged branch; -`git grep -l RingSwitchCarrier -- 'ArkLib/ProofSystem/RingSwitching/Generic'` returns the five -Generic files (repo-wide the grep hits 8 paths — the five plus `Data/MvPolynomial/Multilinear.lean` -and two docs files; that is expected, not a bad merge). - ---- - -## 4. Phase 1 — Generic layer: the deterministic exit stage + docs retarget (1.5 d) - -### 4.1 New file `ArkLib/ProofSystem/RingSwitching/Generic/PackedClaim.lean` - -Imports: `ArkLib.ProofSystem.RingSwitching.Generic.Packing`. Namespace -`RingSwitching.Generic.RingSwitchCarrier`, `variable {B : Type} [CommRing B] -(car : RingSwitchCarrier B)`, inside `noncomputable section`, `open Module MvPolynomial -Sumcheck.Structured` (mirror Packing.lean's header exactly). - -Content (names indicative; keep docstring style of the sibling files): - -```lean -/-- The deterministic packed-claim check (design "step 2", packing-phase exit): the original -claim value `y : B` is the `w`-weighted recombination of the packed carrier value's -`packBasis`-coordinates. Hachi §3.1: `w` = tail monomials, `Y` = the one prover message; -DP24 continues past this stage into batching + sumcheck instead. -/ -def recombineCheck (w : car.ιP → B) (Y : car.P) (y : B) : Prop := - y = ∑ v, w v * car.packBasis.repr Y v - -/-- The residual native claim after a packing head: the packed polynomial evaluates to `Y` -at the (base-embedded) head point. This is the deterministic exit's output anchor — for a -carrier with `P` = the committed ring, it is already a native PCS claim. -/ -def packedClaimRel (m : ℕ) : - Set (((Fin m → B) × car.P) × MultilinearPoly car.P m) := - { x | x.1.2 = x.2.val.eval (fun i => algebraMap B car.P (x.1.1 i)) } - -/-- Coordinates of an honest packed evaluation are the family's evaluations — -`packedMLE_eval` pushed through `repr`. NB: `packBasis.repr` lands in `B`, so the RHS is the -bare `(Ps v).val.eval pt` — the `algebraMap` in `packedMLE_eval`'s reassembly is absorbed by -`repr`. -/ -theorem repr_packedMLE_eval {m : ℕ} (Ps : car.ιP → MultilinearPoly B m) - (pt : Fin m → B) (v : car.ιP) : - car.packBasis.repr - (MvPolynomial.eval (fun i => algebraMap B car.P (pt i)) (car.packedMLE Ps).val) v - = (Ps v).val.eval pt -``` - -Proof plan: rewrite with `car.packedMLE_eval`; convert each summand -`algebraMap B car.P c * car.packBasis i` to `c • car.packBasis i` -(`Algebra.smul_def`, symm — i.e. `simp_rw [← Algebra.smul_def]`); finish with -**`Module.Basis.repr_sum_self`** (Mathlib LinearAlgebra/Basis/Defs.lean:265, `[Fintype ι]`: -`b.repr (∑ i, c i • b i) = c` — verified to exist; do NOT reach for `Basis.repr_equivFun_symm`, -which does not exist) plus a `congrFun` at `v`. - -```lean -/-- Generic soundness kernel of the deterministic packing exit: if the check passes against -the honest packed value, the weighted family claim holds. (The Binius/Hachi instances feed -their own weight-law into `w` — hypotheses live on theorems, per the layer's discipline.) -/ -theorem recombineCheck_iff_of_packedClaim {m : ℕ} (Ps : car.ιP → MultilinearPoly B m) - (pt : Fin m → B) (w : car.ιP → B) (y : B) : - car.recombineCheck w - (MvPolynomial.eval (fun i => algebraMap B car.P (pt i)) (car.packedMLE Ps).val) y - ↔ y = ∑ v, w v * (Ps v).val.eval pt := by - unfold recombineCheck; simp [car.repr_packedMLE_eval] -``` - -Sanity section (mirror the siblings): exercise `recombineCheck` + `packedClaimRel` on -`decoupledToyCarrier` and `towerCarrier`; one value-level `example` computing -`repr_packedMLE_eval` on the toy carrier. - -*Why this change:* this is the missing stage — the branch's relation chain starts at -`openingClaimRel` and immediately eq-decomposes toward batching; Hachi's protocol content at this -layer is exactly (`recombineCheck`, `packedClaimRel`) and nothing else. Stating it generically -(with the weight family `w` as the knob and the check's coordinates fixed to `packBasis.repr`) -also gives Binius the *proven* step-2 identity for free later, replacing the old plan's -4-field `PackingScheme` with 1 knob + 1 proven kernel lemma. - -### 4.2 Edit `Generic/Relations.lean` — re-anchor the chain docstring - -Extend the module docstring's relation-chain bullet list: `packedClaimRel` (in -`PackedClaim.lean`) is the **shared deterministic segment**; `sliceRel → sumcheckClaimRel` is the -**DP24/relocation route** taken only when the head point is not base-embedded. Do not change any -existing definition. Add one `example` in the sanity section instantiating `packedClaimRel` on -both carriers. - -*Why:* prevents the next reader from assuming the batching route is the only route; zero proof -risk. - -### 4.3 Docs retarget (same PR) - -- Batching.lean module docstring + `BatchingStrategy` docstring + sanity comment (three verified - sites): replace "the S8 non-domain (Hachi) sibling … supply its own proven `separates`" with: - Hachi `R_q` is a **non-domain carrier of the packing stage only** — its head is deterministic - (one message + `recombineCheck`, zero challenges, zero error; see - `Generic/PackedClaim.lean` and `Commitments/Functional/Hachi/RingSwitch/`), and it does not - instantiate `BatchingStrategy`. Keep the `CommRing`-only-vocabulary sentence — it remains true - and load-bearing. -- Relations.lean:312-314 sanity comment: same correction (the `DenseMLPCS (ZMod 6) 3` statability - example stays; it is about vocabulary, not Hachi). -- `docs/kb/concepts/ring-switching.md` ("The Generic layer" section): add the packing-stage - paragraph + correct the S8 description. -- `docs/wiki/repo-map.md`: add `Generic/PackedClaim.lean` and (Phase 4's) - `Hachi/RingSwitch/` entries — CLAUDE.md guardrail: same PR as the code. - -*Why:* the branch's written roadmap currently models Hachi as a batching instance — the exact -misread this whole plan exists to prevent from ossifying. - -Acceptance (Phase 1): build green; `recombineCheck_iff_of_packedClaim` sorry-free; docstrings -contain no remaining claim that Hachi batches. - ---- - -## 5. Phase 2 — Lattice glue (1.5 d) - -New file `ArkLib/Data/Lattices/CyclotomicRing/Subfield/LinearEquiv.lean` (imports -`Subfield/Packing.lean`, `Subfield/Bijectivity.lean`, `Galois/Order.lean`). Naming note: this -phase lives in the lattice layer, whose house parameter name is `κ` (as in `psi_bijective`, -`card_fixedSubring_eq`) — keep it here; Phases 3–6 instantiate it as `κRS` (§11's dictionary), -since only the *chain* files have the `κ`-collision. Work in the ZMod-q section style (`variable (q : ℕ) [Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)] -[LawfulBEq (ZMod q)]`, q explicit) for the bundled equiv (it needs `psi_bijective`, which is -ZMod-q); the `psi_smul` lemma can stay in the generic `[Field R]` section of Packing.lean. - -Deliverables, in order: - -1. **`psi_smul`** (append to Subfield/Packing.lean, generic section): - ```lean - theorem psi_smul (α k : ℕ) (c : fixedSubring (R := R) α k) - (a : Fin (2 ^ α / k) → fixedSubring (R := R) α k) : - psi α k (c • a) = (c : Rq (powTwoCyclotomic α)) * psi α k a - ``` - Proof: unfold `psi`; `Finset.mul_sum`; per-summand `Subring` coe-of-mul + `mul_assoc`. (Pin - how `c • a` acts pointwise: `Pi.smul_apply` + subring `smul = mul` on the subtype — - if the pointwise action is not already `Mul`-defeq, state the lemma with - `(fun j => c * a j)` instead of `c • a`; either form serves step 2.) -2. **Nontriviality lemmas** (as theorems producing instances, not global instances): - - `nontrivial_Rq_powTwoCyclotomic : Nontrivial (Rq (powTwoCyclotomic (R := ZMod q) α))` — - export the inline `haveI` derivation at Subfield/Field.lean:298-302 (via `Rq.equivQuotient`; - needs only the standing `[Fact (Nat.Prime q)]`, no `h2`/`hk`) as a named lemma. Phase 7's - carrier needs it for its `ntP` field. - - `Nontrivial ↥(fixedSubring (R := ZMod q) α (2^κ))` under `(h2) (hk)`: derive from - `card_fixedSubring_eq q α κ h2 hk` (`Fintype.card … = q ^ 2^κ ≥ 2` since `q` prime ⇒ - `Fintype.one_lt_card_iff_nontrivial`), or from the subring's `0 ≠ 1` directly given the - ambient nontriviality above (Mathlib's `Subring` Nontrivial instance needs - `Nontrivial (Rq …)`, which is exactly the first lemma). -3. **`psiLinearEquiv`**: - ```lean - noncomputable def psiLinearEquiv (α κ : ℕ) (h2 : (2 : ZMod q) ≠ 0) - (hk : 2 * 2 ^ κ ∣ 2 ^ α) : - (Fin (2 ^ α / 2 ^ κ) → fixedSubring (R := ZMod q) α (2 ^ κ)) - ≃ₗ[fixedSubring (R := ZMod q) α (2 ^ κ)] Rq (powTwoCyclotomic (R := ZMod q) α) := - LinearEquiv.ofBijective - ({ toFun := psi α (2 ^ κ), map_add' := psi_add α (2 ^ κ), map_smul' := … } : _ →ₗ[_] _) - (psi_bijective q α κ h2 hk) - ``` - The `Module ↥(fixedSubring …) (Rq …)` instance is found by TC search via Mathlib's - `Algebra.ofSubring` (verified); `map_smul'` is `psi_smul` composed with - `Algebra.smul_def` + `algebraMap_ofSubsemiring` (`algebraMap = Subtype.val`-coe). Definitional - abbreviation: `noncomputable abbrev psiInv … := (psiLinearEquiv q α κ h2 hk).symm` with simp - lemmas `psiInv_psi`, `psi_psiInv` (from `LinearEquiv.symm_apply_apply` etc.). -4. **Named unit + cancellation** (append to TraceInnerProduct.lean or the new file): - ```lean - theorem isUnit_pow_div_cast (α κ : ℕ) (hκα : κ ≤ α) (h2 : (2 : ZMod q) ≠ 0) : - IsUnit ((2 ^ α / 2 ^ κ : ℕ) : Rq (powTwoCyclotomic (R := ZMod q) α)) - theorem nsmul_pow_div_cancel (α κ : ℕ) (hκα : κ ≤ α) (h2 : (2 : ZMod q) ≠ 0) - {x y : Rq (powTwoCyclotomic (R := ZMod q) α)} - (h : (2 ^ α / 2 ^ κ) • x = (2 ^ α / 2 ^ κ) • y) : x = y - ``` - Proofs: lift the verified inline pattern (TraceInnerProduct.lean:273-275 and :282-283) - verbatim into named lemmas. Get `hκα` from `succ_le_of_two_mul_two_pow_dvd hk` (κ+1 ≤ α ⇒ - κ ≤ α) at call sites. -5. **`packIndexEquiv`** (the one sanctioned index cast, G7): - ```lean - def packIndexEquiv (α κ : ℕ) (hκα : κ ≤ α) : - Fin (2 ^ α / 2 ^ κ) ≃ Fin (2 ^ (α - κ)) := - finCongr (Nat.pow_div hκα (by norm_num)) - ``` - Plus a `decide` example at `α = 2, κ = 1` pinning the round-trip (old plan A1's acceptance, - scoped down to what this plan uses). - -*Why each:* (1)+(3) — `psiInv` (the check's coordinate map) is `psiLinearEquiv.symm`; without -`psi_smul` the linear map cannot be bundled and `map_sum/map_smul` (Phase 3's whole proof engine) -are unavailable. (2) — `RingSwitchCarrier` requires `Nontrivial` for the Phase 7 instance, and -several Phase 3 rewrites need `0 ≠ 1` in `B`. (4) — Phase 3's `traceCheck_iff` must cancel the -`(2^α/2^κ) •` factor of Theorem 2; today that cancellation exists only inline inside another -proof. (5) — `CMlPolynomial` arities are `2^(vars)` while ψ's index is `2^α/2^κ`; G7 mandates -exactly one named crossing. - -Acceptance: all five sorry-free; `example : Fin (2^2/2^1) ≃ Fin (2^1) := packIndexEquiv 2 1 -(by omega)` compiles; `decide` example green. - ---- - -## 6. Phase 3 — The unpack algebra: Theorem 2 at the polynomial level (3–4 d, the heart) - -New file `ArkLib/Commitments/Functional/Hachi/RingSwitch/Unpack.lean`. Imports: -`Hachi/PolynomialEvalSplit.lean`, `Subfield/LinearEquiv.lean`, `Subfield/TraceInnerProduct.lean`. -Namespace `ArkLib.Lattices.Hachi` (the split layer's namespace), ZMod-q section. **Header opens -(mandatory, or nothing resolves):** `open CompPoly ArkLib.Lattices.CyclotomicModulus` — psi, -traceH, conjAut, fixedSubring, Rq, powTwoCyclotomic all live in -`ArkLib.Lattices.CyclotomicModulus` (mirror `Hachi/Basic.lean:97`'s open line; §12's ban is only -on `open ArkLib.Lattices` *itself*, whose `⬝ᵥ` is ambiguous — opening the leaf namespace is -safe and the chain already does it). - -Abbreviations: export a **public** `abbrev PackBase (q α κRS : ℕ) … : Type := -↥(fixedSubring (R := ZMod q) α (2 ^ κRS))` from this file — Phase 4's statement fields and -Phase 7's `rfl` examples must see through it, so it cannot be `local`. `Φα`, `N := 2^α / 2^κRS`, -`κ' := α - κRS` may stay local, with `hκα : κRS ≤ α` and crossing to `Fin (2^κ')` only via -`packIndexEquiv` — never inline-cast. **Coercion spelling (pin once, use everywhere):** the bare -`(↑·)` lambda does NOT elaborate (compile-verified: it degenerates to `fun x => x` and -type-errors); define `def coeVec {n} (x : Vector (PackBase q α κRS) n) : Vector (Rq Φα) n := -x.map (fun b => (b : Rq Φα))` and state every lemma through `coeVec`. - -One prerequisite edit in `PolynomialEvalSplit.lean`: `eval_eq_sum` is stated at arity -`nl + nh`, and `rw [eval_eq_sum]` does NOT fire at a bare arity `n` (compile-verified -unification failure). Either generalize it to `{n : ℕ}` (the existing proof compiles unchanged — -preferred) or invoke it as `eval_eq_sum (nl := n) (nh := 0)` (defeq `n + 0`); do not rely on -bare `rw [eval_eq_sum]`. - -Deliverables, in dependency order (each a lemma; sorry-free before moving on): - -1. **Coe/monomial commutation.** `B`'s coe into `Rq Φα` is `SubringClass` coe (a ring hom). - ```lean - theorem monomialBasis_map_coe {n : ℕ} (x : Vector (PackBase q α κRS) n) (j : Fin (2 ^ n)) : - (CMlPolynomial.monomialBasis (coeVec x)).get j - = ((CMlPolynomial.monomialBasis x).get j : Rq Φα) - ``` - Proof: `monomialBasis_get` on both sides; the RHS product of `if`-selected entries commutes - with the coe ring hom (`map_prod`, `apply_ite`). (If `CMlPolynomial.monomialBasis` has its own - `map` lemma in CompPoly, use it; otherwise `monomialBasis_get` + `Finset.prod_congr` is 10 - lines.) -2. **Eval at coerced points is a `B`-combination of coefficients.** - ```lean - theorem eval_coeVec {n : ℕ} (F : CMlPolynomial (Rq Φα) n) (x : Vector (PackBase q α κRS) n) : - CMlPolynomial.eval F (coeVec x) - = ∑ j : Fin (2 ^ n), (CMlPolynomial.monomialBasis x).get j • F.get j - ``` - Proof: `eval_eq_sum` (per the arity note above) + step 1 + `Algebra.smul_def`/ - `algebraMap_ofSubsemiring` to turn `↑c * F.get j` into `c • F.get j`. (Note `eval_eq_sum` is - stated `p.get i * (monomialBasis v).get i` — commute with `mul_comm` before the smul rewrite.) -3. **Coordinate/eval commutation** (`psiInv` is `B`-linear — the generic kernel, instance-side): - ```lean - theorem psiInv_eval_coeVec {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n) - (x : Vector (PackBase q α κRS) n) (v : Fin N) : - psiInv q α κRS h2 hk (CMlPolynomial.eval F (coeVec x)) v - = ∑ j : Fin (2 ^ n), (CMlPolynomial.monomialBasis x).get j - * psiInv q α κRS h2 hk (F.get j) v - ``` - Proof: step 2, then `map_sum` + `map_smul` of the linear equiv, then `Pi.smul_apply` + - `smul_eq_mul` in `B`. -4. **`unpackPoly`** — coefficient-wise ψ⁻¹, tail variables LAST. The compile-verified form - (row index = head `j` FIRST, column = tail `v` second — `toPolynomial` with target - `CMlPolynomial B (n + κ')` forces `PolyMatrix B (2^n) (2^κ')`): - ```lean - noncomputable def unpackPoly {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n) : - CMlPolynomial (PackBase q α κRS) (n + κ') := - Hachi.toPolynomial (fun (j : Fin (2 ^ n)) (v : Fin (2 ^ κ')) => - psiInv q α κRS h2 hk (F.get j) ((packIndexEquiv α κRS hκα).symm v)) - ``` - (This lambda was type-checked by a verification agent against - `CMlPolynomial ↥(fixedSubring α (2^κ)) (n + (α - κ))`. If your `PolyMatrix` literal needs a - different constructor than a bare function, read its definition in PolynomialEvalSplit.lean - and keep the SAME index order: `(j, v)`, head first.) The orientation acceptance test is - step 5 — if anything is transposed, fix it HERE, never by casting in step 5's proof (G7). -5. **The unpack-eval identity** (the C3 heart, replacing the old plan's - `traceH_packPoly_eval` at the same difficulty): - ```lean - theorem unpackPoly_eval {n : ℕ} (h2) (hk) (F : CMlPolynomial (Rq Φα) n) - (x : Vector (PackBase q α κRS) n) (xt : Vector (PackBase q α κRS) κ') : - CMlPolynomial.eval (unpackPoly h2 hk F) (x ++ xt) - = ∑ v : Fin (2 ^ κ'), (CMlPolynomial.monomialBasis xt).get v - * psiInv q α κRS h2 hk (CMlPolynomial.eval F (coeVec x)) - ((packIndexEquiv α κRS hκα).symm v) - ``` - Proof plan: LHS via `splitForm_monomialBasis_eq_eval` (with `toPolynomial_toMatrix`/the - round-trip to expose the matrix) = the double sum - `∑ v ∑ j (monomialBasis xt).get v * (monomialBasis x).get j * M j v` (note `M j v`, head - index first); RHS via step 3 expands to the same double sum; finish with `Finset.sum_comm` + - ring. All in `B` ([CommSemiring] suffices for the split machinery — verified). -6. **Head-soundness corollary** (what Phase 4's pull-back calls): - ```lean - theorem unpackPoly_eval_of_check {n : ℕ} (h2) (hk) - {F : CMlPolynomial (Rq Φα) n} {x : Vector (PackBase q α κRS) n} - {xt : Vector (PackBase q α κRS) κ'} {Y : Rq Φα} {y : PackBase q α κRS} - (hY : CMlPolynomial.eval F (coeVec x) = Y) - (hchk : y = ∑ v, (CMlPolynomial.monomialBasis xt).get v - * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v)) : - CMlPolynomial.eval (unpackPoly h2 hk F) (x ++ xt) = y := by - subst hY; rw [unpackPoly_eval, hchk] - ``` -7. **Trace-form equivalence** (paper faithfulness; Theorem 2 discharges): - ```lean - /-- Hachi's paper check (§3.1 / Theorem 2 form). Message is the UNTWISTED `Y` — - the σ₋₁ lives inside the trace identity, not on the wire (design G2). -/ - def traceCheck (h2) (hk) (xt : Vector (PackBase q α κRS) κ') (Y : Rq Φα) - (y : PackBase q α κRS) : Prop := - traceH α (2 ^ κRS) (Y * conjAut α (psi α (2 ^ κRS) - (fun j => (CMlPolynomial.monomialBasis xt).get (packIndexEquiv α κRS hκα j)))) - = (2 ^ α / 2 ^ κRS) • (y : Rq Φα) - - theorem traceCheck_iff_recombine (h2) (hk) (xt) (Y) (y) : - traceCheck h2 hk xt Y y - ↔ y = ∑ v, (CMlPolynomial.monomialBasis xt).get v - * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v) - ``` - (Type note: `j : Fin (2^α/2^κRS)`, so `packIndexEquiv … j : Fin (2^(α−κRS))`, and with - `κ'` an abbrev for `α − κRS` this is literally `Fin (2^κ')` — `monomialBasis xt |>.get` - accepts it with no cast; this is exactly why κ' must be an abbrev, G7.) - Proof plan: write `Y = psi α (2^κRS) (psiInv … Y)` (`psi_psiInv`); apply - `traceH_psi_mul_conj α (2^κRS) h2 ⟨κRS, rfl⟩ hk`; the RHS becomes - `(2^α/2^κRS) • ↑(∑ i, psiInv Y i * weights i)`; reindex the sum along `packIndexEquiv` - (`Equiv.sum_comp` / `Fintype.sum_equiv`) and commute the factors; conclude by - `nsmul_pow_div_cancel` (Phase 2.4) + `Subtype.val`-injectivity - (`Subtype.coe_injective` on the subring; both sides are coerced subring elements — the LHS via - `traceH_mem_fixed` + `mem_fixedSubring_iff` if needed, but the cleaner route is to cancel - first and compare inside `Rq`, then pull back along injectivity of the coe). - -*Why this phase:* steps 4–6 are the entire mathematical content of the head's soundness — the -paper's Theorem 2 lifted to "the unpacked polynomial's evaluation is check-determined". Step 7 is -what makes the formalization *the paper's protocol* (the wire check is provably the trace -equation) rather than a lookalike; it is also where the σ₋₁ twist is discharged once and for all. - -Acceptance (hard): steps 1–7 sorry-free and `./scripts/validate.sh` green. Note `unpackPoly` is -necessarily noncomputable (`psiInv` comes from `LinearEquiv.ofBijective`; `fixedSubring` itself -is noncomputable), so a `decide`/`native_decide` evaluation of `unpackPoly_eval` is -**infeasible, not merely impractical** — do not attempt it. Optional (30-min timebox): a -`decide` example on the computable ingredients only (`packIndexEquiv` round-trip, -`monomialBasis` values at `α = 2, κRS = 1`); if it doesn't land in the timebox, drop it — the -hard acceptance stands alone. - ---- - -## 7. Phase 4 — The head reduction + its CWSS theorem (2–3 d) - -New file `ArkLib/Commitments/Functional/Hachi/RingSwitch/Head.lean`. Imports: `Unpack.lean`, -`PolynomialQuadraticEq/PolyEvalReduction.lean`, CWSS `NoChallenge`/`Package`. Namespace -`ArkLib.Lattices.Ajtai.InnerOuter` (the chain's namespace; `open WeakBinding`, plus -`open CompPoly ArkLib.Lattices.CyclotomicModulus` as in Phase 3; do NOT -`open ArkLib.Lattices` itself). Section variables: copy Basic.lean:103-106 verbatim, **plus -`{κRS : ℕ}`** (it is NOT in Basic.lean's list; with `autoImplicit = false` forgetting to declare -it is a hard error) and `(hκα : κRS ≤ α) (h2 : (2 : ZMod q) ≠ 0) (hk : 2 * 2 ^ κRS ∣ 2 ^ α)` — -**the ring-switch parameter is named `κRS` throughout**: the chain already uses `κ` for the -challenge-set parameter (`relPolyEval … γ κ`, instantiated at `κ := 2 * ω`). This collision is -the old plan's R5; pin the dictionary in the file header. - -Deliverables: - -1. **Statement — pinned to the chain modulus `𝓜(q,α)`, NOT Φ-generic**: - ```lean - structure RingSwitchStatement (innerRows messageDigits outerRows innerDigits dRows m r : Nat) where - pp : Hachi.PublicParamsD 𝓜(q,α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows - u : Commitment 𝓜(q,α) outerRows - xl : Vector (PackBase q α κRS) r - xh : Vector (PackBase q α κRS) m - xt : Vector (PackBase q α κRS) κ' -- κ' := α - κRS, the packed tail - y : PackBase q α κRS - ``` - `q α κRS` enter as auto-bound section-variable parameters. Do NOT add a - `(Φ : CyclotomicModulus (ZMod q))` parameter: `PackBase` lives inside - `Rq (powTwoCyclotomic α)`, and nothing would tie a generic `Φ` to `α` — step 2 would then - fail to typecheck. The pin is sound because `𝓜(q,α)` is `@[reducible] hachiModulus q α := - primePowTwoModulus q α` (InnerOuter/Arithmetic.lean:58) `:= powTwoCyclotomic α` - (CyclotomicRing/PowTwo.lean:58) — so `Rq 𝓜(q,α)` unfolds to `PackBase`'s ambient ring and - the coercions land without casts. (Statement stores the split point, matching - `PolyEvalStatement`'s discipline — verified docstring: split storage avoids take/drop casts.) -2. **Output map** (fused adapter, G4): - ```lean - def toPolyEvalStatement (s : RingSwitchStatement …) (Y : Rq 𝓜(q,α)) : - PolyEvalStatement 𝓜(q,α) innerRows messageDigits outerRows innerDigits dRows m r := - { pp := s.pp, u := s.u, xl := coeVec s.xl, xh := coeVec s.xh, y := Y } - ``` - (`coeVec` from Phase 3 — the bare `(↑·)` lambda does not elaborate.) -3. **pSpec, instances, check, verifier** (guarded, G3/G9). First bullet, all mandatory: - ```lean - @[reducible, simp] - def pSpecHead : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[Rq 𝓜(q,α)]⟩ - - instance : IsEmpty (pSpecHead …).ChallengeIdx := ⟨fun ⟨0, h⟩ => nomatch h⟩ - -- and, if not found via reducibility from ProtocolSpec/Basic.lean:295/:304: - instance : ∀ i, SampleableType ((pSpecHead …).Challenge i) := fun i => isEmptyElim i - ``` - `@[reducible]` mirrors `SendClaim.pSpec` (SendClaim.lean:57) and is what lets the generic - 1-message instances (`IsEmpty ChallengeIdx`, `∀ i, SampleableType (Challenge …)` — - OracleReduction/ProtocolSpec/Basic.lean:295/:304) fire; a plain `def` stalls Phase 6's - package append on opaque instance-synthesis failures (`CWSSPackage.append` requires - `[∀ i, SampleableType (pSpec₁.Challenge i)]`, Package.lean:83). Then the Bool check (G9): - ```lean - noncomputable def headCheck (s : RingSwitchStatement …) (Y : Rq 𝓜(q,α)) : Bool := - decide (s.y = ∑ v, (CMlPolynomial.monomialBasis s.xt).get v - * psiInv q α κRS h2 hk Y ((packIndexEquiv α κRS hκα).symm v)) - - noncomputable def headVerifier … : - Verifier oSpec (RingSwitchStatement …) (PolyEvalStatement …) (pSpecHead …) where - verify := fun s tr => - if headCheck s (tr 0) then pure (toPolyEvalStatement s (tr 0)) else failure - ``` - Notes: message access is plain `tr 0` (the SendWitness/SendClaim idiom — SendWitness.lean:73, - SendClaim.lean:107), not `tr.messages ⟨0, rfl⟩`. The `decide` needs - `DecidableEq (PackBase q α κRS)` — via `Subtype.instDecidableEq` from - `DecidableEq (Rq 𝓜(q,α))` (the chain's `[BEq]/[LawfulBEq]` context; check how QuadEval-side - files obtain `DecidableEq (Rq Φ)` and copy the route; if none exists, add the instance next - to `Rq`'s `commRing`). Bridge lemma for proofs: `headCheck s Y = true ↔ s.y = ∑ …` by - `decide_eq_true_eq`. -4. **Input relation** (mirror `relPolyEval`'s three-case shape exactly): - ```lean - def relRingSwitch (base : ZMod q) (βSq γ κchal : ℕ) : - Set (RingSwitchStatement … × QuadEvalWitness 𝓜(q,α) innerRows (2^m) messageDigits (2^r) innerDigits) := - { p | match p with - | (s, .opening o) => - VerifiedOpening 𝓜(q,α) base βSq γ κchal s.pp.toPublicParams s.u o ∧ - CMlPolynomial.eval (unpackPoly h2 hk (extractedPoly 𝓜(q,α) base o)) - ((s.xl ++ s.xh) ++ s.xt) = s.y - | (s, .msisB z) => ModuleSIS.relation 𝓜(q,α) (outerShort 𝓜(q,α) γ) s.pp.outerMatrix z = true - | (s, .msisD z) => ModuleSIS.relation 𝓜(q,α) (dShort 𝓜(q,α) γ) s.pp.dMatrix z = true } - ``` - Arity check: `extractedPoly … : CMlPolynomial (Rq 𝓜(q,α)) (r + m)`; `unpackPoly` gives - `CMlPolynomial (PackBase q α κRS) ((r + m) + κ')`; the point - `(s.xl ++ s.xh) ++ s.xt : Vector (PackBase q α κRS) ((r+m)+κ')`. ✓ -5. **Pull-back lemma** (the CWSS engine): - ```lean - theorem mem_relRingSwitch_of_relPolyEval (s) (Y) (w) - (hchk : headCheck s Y = true) - (h : (toPolyEvalStatement s Y, w) ∈ relPolyEval 𝓜(q,α) base βSq γ κchal) : - (s, w) ∈ relRingSwitch base βSq γ κchal - ``` - Opening case: `relPolyEval` gives `CMlPolynomial.eval (extractedPoly …) - (coeVec s.xl ++ coeVec s.xh) = Y`; rewrite `coeVec xl ++ coeVec xh = coeVec (xl ++ xh)` - (a `Vector.map`/append commutation — verify the exact lemma name for the Vector type used by - `CMlPolynomial.eval`; if missing, prove it locally, 3 lines by `ext`/`get` — this is the only - Vector plumbing in the plan); cross `hchk` into the Prop form via `decide_eq_true_eq` (G9); - apply `unpackPoly_eval_of_check` (Phase 3.6). msisB/msisD: statement field `pp` passes - through unchanged — immediate. -6. **Head CWSS theorem.** The head is a 1-message reduction: `IsEmpty ChallengeIdx` holds for - `pSpecHead` (step 3's instance). Verified proof route (the verification pass read - `treeSpecialSound`, TranscriptTree/Basic.lean:308-323, and both templates end-to-end): - ```lean - theorem head_coordinateWiseSpecialSound (init impl) - (D : CWSSStructure (pSpecHead …)) … : - (headVerifier …).coordinateWiseSpecialSound init impl D - (relRingSwitch base βSq γ κchal) - (relPolyEval 𝓜(q,α) base βSq γ κchal) - ``` - — `D` **universally quantified** (both templates do this; the no-challenge bridge holds for - any `D`; a `(D := …)` default-value pseudo-binder is not valid syntax). Instantiate - `D := CWSSStructure.ofIsEmpty` only at the package (Phase 6). Proof skeleton, mirroring - `SendWitness.verifier_coordinateWiseSpecialSound` (SendWitness.lean:109) + - `ReduceClaim.verifier_coordinateWiseSpecialSound` (ReduceClaim.lean:186): - - Enter via `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx` (its premise - imposes **no purity** on the verifier — verified — so the guard needs no Phase-5 machinery - here). The extractor `e : StmtIn → FullTranscript → WitIn` cannot see the output witness - (the transcript tree carries **no** `WitOut` at leaves — leaves are bare); recover it by - classical choice à la ReduceClaim's `hpick`: from acceptance, - `toPolyEvalStatement s (tr 0) ∈ relPolyEval.language`, and `Set.mem_language_iff` gives - `∃ w, … ∈ relPolyEval`; choose it (`Exists.choose`), here per `(s, tr)` since the chosen - witness depends on the message `tr 0`. - - (a) Acceptance forces the check: case on `headCheck s (tr 0)`. In the `false` branch - `verify = failure` and the run's acceptance probability is 0 ≠ 1 — add the missing helper - `not_accepting_of_failure` next to `pure_accepting_of_mem` (verified absent; the executable - spec for the probability argument is `CheckClaim.knowledgeStateFunction.toFun_full`'s - guard-false branch, via `probEvent_pos_iff`/support-of-`OptionT.mk (pure none)`). - - (b) In the `true` branch the run is `pure (toPolyEvalStatement s (tr 0))`: apply - `Verifier.mem_of_pure_accepting` (SeqCompose.lean:53) with `hV := if_pos hchk` (in place of - the templates' `rfl`), unpack via `Set.mem_language_iff`, and close with step 5. - - `[Nonempty WitIn]`: `instance : Nonempty (QuadEvalWitness …)` exists at QuadEval.lean:114 - (verified) — same instance the bridge uses. -7. **Prover skeleton.** Honest prover sends - `Y := CMlPolynomial.eval (extractedPoly …) (coeVec (s.xl ++ s.xh))`-style packed value. - Type (cf. `QuadEval.prover`, QuadEval.lean:323): - `Prover oSpec (RingSwitchStatement …) (QuadEvalWitness 𝓜(q,α) …) - (PolyEvalStatement 𝓜(q,α) …) (QuadEvalWitness 𝓜(q,α) …) (pSpecHead …)` — skeleton only - (G8). The head *package* (`GuardedCWSSPackage` value) is **deferred to Phase 6**, which is - where its structure type exists (Phase 5) and where the seam is checked. -8. **Completeness-side lemma (statement only, G8):** `traceCheck_of_honest` — the honest `Y` - passes the check; provable from `psiInv_eval_coeVec` + the recombination identity; leave - proven if ≤ 1 day, else `sorry`-free *statement* deferred to the completeness TODO (do NOT - add a sorry — omit the lemma if unproven). - -*Why this phase:* this is the reduction itself. The guard (G3) is forced by information flow -(`xt, y` dropped downstream); the direct-to-`PolyEvalStatement` output (G4) is what makes the -`▷` seam close by `rfl`; the pull-back (step 5) is where Phase 3's algebra meets the chain's -relation shapes. - -Acceptance: `head_coordinateWiseSpecialSound` sorry-free; file compiles inside the chain's -namespace; `head_coordinateWiseSpecialSound`'s `relOut` argument is written **verbatim** as the -§1.3 seam expression (`relPolyEval 𝓜(q,α) (b : ZMod q) (quadEvalBetaSq γ b zDigits -((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω)` at the chain instantiation) and elaborates -without coercion. (The package-level `rfl`-seam example belongs to Phase 6 — the -`GuardedCWSSPackage` type does not exist until Phase 5.) - ---- - -## 8. Phase 5 — Guarded CWSS composition (2–3 d, framework; independent of Phases 1–4) - -Extend `CoordinateWiseSpecialSoundness/Composition.lean` + `Package.lean` (+ one helper in -`TranscriptTree/Basic.lean`). **Coordinate with maintainers before landing** (shared security -infrastructure — old plan R3; the Basic.lean TODO already sanctions the need). - -**Known tension to raise in that coordination:** CheckClaim.lean:26 and :185-189 record an -unfinished "no-failure `OracleComp`" refactor under which guard-based verifiers are "retained as -a rightmost-only factor" (the sanctioned workaround being: keep the verifier pure and move the -check into the output relation, as `CheckClaim.oracleRelOut` does). That workaround is **not -available** for the Hachi head: its check reads `s.xt, s.y`, which the output statement type -drops (G3), so the check cannot live in `relOut`. Basic.lean's TODO ("Guarded subprotocols need -a guarded variant of `▷`") is the sanctioned path; flag the refactor interaction explicitly. - -1. **`Verifier.IsGuarded`** (new, next to the pure machinery). Mirror `Verifier.IsPure`'s exact - style — it is a **class with an existential field** (OracleReduction/Basic.lean:748: - `is_pure : ∃ verify, ∀ …, V.verify … = pure …`), and `CWSSPackage.append` destructures it via - `obtain ⟨verify₁, hV₁⟩ := L₁.isPure.is_pure` (Package.lean:92); the guarded twin must - destructure the same way: - ```lean - /-- A verifier that either purely transforms the statement or rejects outright. - Pure verifiers are the `check := fun _ _ => true` case. -/ - class Verifier.IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where - is_guarded : ∃ (check : StmtIn → pSpec.FullTranscript → Bool) - (out : StmtIn → pSpec.FullTranscript → StmtOut), - ∀ s tr, V.verify s tr = if check s tr then pure (out s tr) else failure - ``` - (Bool check per G9 — matches Phase 4's `headVerifier` syntactically.) -2. **Guarded run lemmas** (generalizing the verified anchors): - - `append_run_guarded_left` (from `append_run_pure_left`, Composition.lean:311): under the - guarded hypothesis, if `check s tr₁ = true` then - `(V₁.append V₂).run s (tr₁ ++ₜ tr₂) = V₂.run (out s tr₁) tr₂`; if `check s tr₁ = false` - then the composed run is `failure` (`failure >>= _ = failure` on `OptionT`). - - `not_accepting_of_failure` : if `V.verify s tr = failure` then - `Pr[(· ∈ lang) | …V.run…] = 0` — **verified missing**; add next to - `pure_accepting_of_mem` (:325-332). Executable spec for the probability argument: - `CheckClaim.knowledgeStateFunction.toFun_full`'s guard-false branch - (`probEvent_pos_iff` + support of `OptionT.mk (pure none)` contains no `some`). - - **`ChallengeTree.transcripts_ne_nil`** (new, `TranscriptTree/Basic.lean`, next to - `transcripts` at :178-183): `(∀ i, 0 < arity i) → ∀ {m} (T : ChallengeTree pSpec arity m) - pre, T.transcripts pre ≠ []` — structural induction, ~10 lines. *Why:* the guarded false - branch must exhibit SOME composed transcript to contradict acceptance; a suffix tree with a - zero-arity node lists no transcripts, making the shape-generic guarded theorem otherwise - unprovable (verified gap — no nonemptiness lemma exists anywhere in TranscriptTree/). -3. **`Verifier.append_treeSpecialSound_of_guardedLeft`** — restate - `append_treeSpecialSound` (Composition.lean:366-375) with the guarded hypothesis replacing - the pure one, **plus the extra hypothesis `hS₂ : ∀ i, 0 < S₂.arity i`** (required for the - false branch, per the `transcripts_ne_nil` note above; the pure theorem needs no such - hypothesis, which is why this was invisible until now). Proof deltas: at the two `hV₁` - consumption sites (~:396, ~:407) case on `check s tr₁`: the `false` branch picks a suffix - transcript via `transcripts_ne_nil` + `hS₂` and contradicts composed acceptance - probability 1 via `not_accepting_of_failure` lifted along `append_run_guarded_left`; the - `true` branch reduces verbatim to the existing pure argument with `verify₁ := out`. - Corollary `append_coordinateWiseSpecialSound_of_guardedLeft` (mirror :414-428): discharge - `hS₂` from `D₂.arity_eq` — `arity i = ℓᵢ(kᵢ−1)+1 ≥ 1` by `coordIndex.2`/`soundnessParam.2` - (CWSSStructure fields carry `0 < ell` and `2 ≤ k`), so at the CWSS level the hypothesis is - free. - **3b. `Verifier.IsGuarded.append_isPureRight`** — `(hg : V₁.IsGuarded) (hp : V₂.IsPure) : - (V₁.append V₂).IsGuarded`, with `check := fun s tr => check₁ s tr.fst`, - `out := fun s tr => f₂ (out₁ s tr.fst) tr.snd` where `⟨f₂, hf₂⟩ := hp.is_pure`. Mirror - `Verifier.IsPure.append` (Composition/Sequential/IsPure.lean:37-43 — it supplies exactly the - `tr.fst`/`tr.snd` transcript split); `verify_eq` by `simp [Verifier.append, …]` + case-split - on the check. *Why:* Phase 6's composed package must certify its own `isGuarded` field; the - run lemmas of step 2 quantify over split transcripts and do not give this. -4. **`GuardedCWSSPackage` + guarded `▷`** (Package.lean), fields spelled out: - ```lean - structure GuardedCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where - verifier : Verifier oSpec StmtIn StmtOut pSpec - struct : CWSSStructure pSpec - relIn : Set (StmtIn × WitIn) - relOut : Set (StmtOut × WitOut) - isGuarded : verifier.IsGuarded - isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut - - def GuardedCWSSPackage.append [∀ i, SampleableType (pSpec₁.Challenge i)] - (L₁ : GuardedCWSSPackage … pSpec₁) (L₂ : CWSSPackage … pSpec₂) - (hseam : L₁.relOut = L₂.relIn := by rfl) : GuardedCWSSPackage … (pSpec₁ ++ₚ pSpec₂) where - verifier := L₁.verifier.append L₂.verifier - struct := L₁.struct.append L₂.struct - relIn := L₁.relIn - relOut := L₂.relOut - isGuarded := L₁.isGuarded.append_isPureRight L₂.isPure - isCWSS := append_coordinateWiseSpecialSound_of_guardedLeft … -- + hseam rewrite - - scoped infixr:65 " ▷! " => GuardedCWSSPackage.append -- name/notation: maintainer's call - ``` - The `[∀ i, SampleableType (pSpec₁.Challenge i)]` binder mirrors `CWSSPackage.append` - (Package.lean:83) and is what Phase 4.3's pSpecHead instances exist to satisfy. (A pure - package lifts to a guarded one via `check := fun _ _ => true` — provide - `CWSSPackage.toGuarded` so mixed chains need only the one append.) -5. Do **not** build the guarded n-ary `seqCompose` here (the old plan's B4 second half) — the - chain currently has exactly one guarded factor at the outer edge; the binary form suffices. - Record the n-ary variant in the Basic.lean TODO instead. *Reason:* smallest reviewable - framework change that unblocks Phase 6. - -*Why this phase:* verified fact — every CWSS composition theorem in the tree demands -`V₁.verify stmt tr = pure (verify₁ stmt tr)` for the left factor; a rejecting head is therefore -uncomposable today. The existing helper pair (`append_run_pure_left` / `pure_accepting_of_mem` / -`mem_of_pure_accepting`) was verified to be exactly the right generalization surface. - -Acceptance: guarded append theorem sorry-free; existing `append_*` theorems byte-identical -(`git diff` shows additions only); a toy `example` composing a trivially-guarded identity -verifier with a pure one. - ---- - -## 9. Phase 6 — Chain assembly (1 d) - -Host file (pinned): **new file `Hachi/RingSwitch/Chain.lean`**, importing `Hachi/Basic.lean` + -`Hachi/RingSwitch/Head.lean` — this keeps Basic.lean's imports free of the RingSwitch subtree; -Basic.lean receives only the diagram/TODO doc edits below. - -First define the head package here (deferred from Phase 4.7 — the structure type is Phase 5's): - -```lean -def headPackage (init impl) (h2) (hk) (hκα) {b ω γ : ℕ} … : - GuardedCWSSPackage init impl (RingSwitchStatement …) (QuadEvalWitness …) - (PolyEvalStatement 𝓜(q,α) …) (QuadEvalWitness …) (pSpecHead …) where - verifier := headVerifier … - struct := CWSSStructure.ofIsEmpty - relIn := relRingSwitch (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω) - relOut := relPolyEval 𝓜(q,α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits) γ (2 * ω) - isGuarded := ⟨headCheck …, toPolyEvalStatement …, fun _ _ => rfl⟩ -- shape per IsGuarded - isCWSS := head_coordinateWiseSpecialSound … CWSSStructure.ofIsEmpty … - -def ringSwitchChain (init impl) (hq5) (hκ) (hτ) (h2) (hk) (hκα) {b ω γ : ℕ} … : - GuardedCWSSPackage init impl - (RingSwitchStatement …) (QuadEvalWitness …) - (QuadEvalStatement … × CarrierCom … × (Fin (2^r) → ShortChallenge …)) - (QuadEvalResponse …) - (pSpecHead ++ₚ ((!p[] : ProtocolSpec 0) ++ₚ pSpec …)) := - headPackage … ▷! evalChain (b := b) (γ := γ) init impl hq5 hκ hτ - -theorem ringSwitch_eval_coordinateWiseSpecialSound … : - (…the composed verifier…).coordinateWiseSpecialSound init impl - (CWSSStructure.ofIsEmpty.append (CWSSStructure.ofIsEmpty.append (foldStructure …))) - (relRingSwitch (b : ZMod q) (quadEvalBetaSq …) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q,α) (b : ZMod q) ω γ) := - (ringSwitchChain …).isCWSS -``` - -Instantiation discipline (verified §1.3): the head package is constructed at base -`(b : ZMod q)`, `βSq := quadEvalBetaSq γ b zDigits ((𝓜(q,α)).φ.natDegree) m messageDigits`, -`κchal := 2 * ω` so the `▷!` autoparam seam closes by `rfl`. **Write the right factor as -`evalChain (b := b) (γ := γ) init impl hq5 hκ hτ`** — `b, γ` are implicit in `evalChain`'s -binders but absent from its result TYPE (verified), so leaving them to be solved through the -autoparam's `rfl` goal is elaboration-order-fragile; instantiate them explicitly. - -Doc edits in `Hachi/Basic.lean` (same PR): in the header diagram (:51-74) mark the §3.1 head -done AND **delete the now-obsolete "σ₋₁ statement adapter — planned (0-round ReduceClaim)" row** -(:~58) — resolved by design, G2/G4: the head outputs `PolyEvalStatement` directly and the σ₋₁ -twist is discharged inside `traceCheck_iff_recombine`; point the head's arrow straight at the -evalChain band. In the TODO block (:256-272): guarded-`▷` done, §3.1 head done, remaining items -unchanged. - -*Why:* the deliverable theorem — subfield-point evaluation claim (paper §3.1 input) reduced to -Eq. (20) + range checks with zero added soundness error, composed from sorry-free parts. - -Acceptance: theorem sorry-free; `./scripts/validate.sh` green; diagram/TODO updated in the same -PR (CLAUDE.md guardrail). - ---- - -## 10. Phase 7 — Carrier instance + hygiene (1 d) - -1. **`hachiCarrier`** (new `Hachi/RingSwitch/Carrier.lean`) — the corrected "S8 witness": a - genuinely non-domain, `P = E` carrier: - ```lean - noncomputable def hachiCarrier (h2) (hk) : - RingSwitchCarrier ↥(fixedSubring (R := ZMod q) α (2 ^ κRS)) where - P := Rq (powTwoCyclotomic (R := ZMod q) α) - E := ↥(fixedSubring (R := ZMod q) α (2 ^ κRS)) -- opening claims are base-valued - ιP := Fin (2 ^ α / 2 ^ κRS) - ιE := Unit - packBasis := Module.Basis.ofEquivFun (psiLinearEquiv q α κRS h2 hk).symm - openBasis := Module.Basis.singleton Unit _ - ntP := nontrivial_Rq_powTwoCyclotomic … -- MUST be an explicit named field: - ntE := … -- Nontrivial (Rq …) is NOT a global instance - ``` - **`ntP`/`ntE` must be assigned explicitly** — instance search cannot fill them (verified: - `Nontrivial (Rq …)` exists only as an inline `haveI` at Subfield/Field.lean:298-302, which - Phase 2.2 exports as `nontrivial_Rq_powTwoCyclotomic`; `ntE` = the subring nontriviality from - Phase 2.2). `ftP` (= `Fintype (Fin _)`) and `ftE` (= `Fintype Unit`) synthesize; `algP` via - Mathlib's `Algebra.ofSubring`; `algE` via `Algebra.id`. Note `Basis.ofEquivFun` wants - `M ≃ₗ[R] (ι → R)`, i.e. `psiLinearEquiv.symm` — then `packBasis.repr = psiLinearEquiv.symm` - definitionally (`ofEquivFun_repr_apply`, rfl), tying the carrier to Phase 3's `psiInv` by - `rfl`. Sanity `example`s: `hachiCarrier.packBasis.repr Y v = psiInv q α κRS h2 hk Y v := rfl` - (this one IS rfl); `recombineCheck (hachiCarrier …) w Y y ↔ (Phase 4's check)` — **not rfl**: - the sums range over `Fin (2^α/2^κRS)` vs `Fin (2^(α−κRS))` (not defeq, G7); prove via - `Fintype.sum_equiv (packIndexEquiv α κRS hκα)` with - `w := (CMlPolynomial.monomialBasis xt).get ∘ packIndexEquiv α κRS hκα`. - *Why:* pins the generic-layer correspondence, gives the branch its promised non-domain carrier - with the **correct** (packing-stage) role, and keeps `Generic/PackedClaim.lean`'s lemmas - honest against a real instance. Not on the head's proof-critical path — if instance plumbing - fights (`Nontrivial (Rq …)` derivation), timebox to half a day and land the carrier with the - Nontrivial argument as an explicit hypothesis-parameter instead. -2. Wiki/KB: `repo-map.md` (Generic/PackedClaim, Hachi/RingSwitch/), KB ring-switching page - (packing-stage + corrected S8 + the `hachiCarrier` pointer), `docs/kb/papers/NOZ26.md` gap - list if present. -3. Update `HACHI_RING_SWITCHING_PLAN.md`: mark B1–B3 superseded by `Generic/PackedClaim.lean`, - B4 superseded by Phase 5 (binary case; n-ary still open), C1–C3 + D1–D2 superseded by Phases - 2–4 + 6; note R2 and D5's twist are resolved-by-design (G2); Phases A, E–G unaffected - (Phase E's derive-`y₀` head and Phases F/G continue to apply downstream of this plan's head). -4. `./scripts/validate.sh --lint` (and `--docs` if docstrings were the day's work). - ---- - -## 11. Standing hypotheses & conventions (pin before writing any Lean) - -- `q` prime, `[Fact (Nat.Prime q)] [NeZero q] [BEq (ZMod q)] [LawfulBEq (ZMod q)]`; `q` explicit - in lattice-layer lemmas (house style). `h2 : (2 : ZMod q) ≠ 0` threaded explicitly (derivable - from `q % 8 = 5` at the chain level if desired — small lemma, optional). -- `hk : 2 * 2 ^ κRS ∣ 2 ^ α` everywhere ψ appears; `hκα : κRS ≤ α` via - `succ_le_of_two_mul_two_pow_dvd`. -- **`κRS` (packing) vs `κ`/`κchal` (chain challenge param, instantiated `2 * ω`) vs paper-`κ`**: - three different things; the file headers of Head.lean/Unpack.lean must carry the dictionary. -- Index forms: `2 ^ α / 2 ^ κRS` in all ψ-adjacent types; `2 ^ (α − κRS)` only after - `packIndexEquiv`; **no other casts**. -- Variable order: unpacked polynomial has the packed tail **last** (`(xl ++ xh) ++ xt`), matching - the paper and `relPolyEval`'s `xl ++ xh`; the branch's `curryFamily` (prefix packing) is NOT - used by the Hachi path — do not import its convention. -- All new Hachi-side defs `noncomputable` where ψ/traceH forces it; the guard check is - Bool-valued per **G9** (`headCheck := decide (…)`; no un-scoped `Classical`); coercions - `B → Rq` go through `coeVec` (Phase 3 — bare `(↑·)` does not elaborate). -- New files must be `git add`ed before `./scripts/update-lib.sh` / validation (generated - `ArkLib.lean`). - -## 12. What NOT to do (each has bitten before) - -- Do not hand-edit `ArkLib.lean` (generated; `scripts/update-lib.sh`). -- Do not `open ArkLib.Lattices` in chain files (ambiguous `⬝ᵥ` — verified docstring warning). - The ban is on that namespace **itself**; opening the leaf namespaces - `ArkLib.Lattices.CyclotomicModulus` (needed for psi/traceH/fixedSubring — Basic.lean:97 - already does it) and `ArkLib.Lattices.Hachi` is safe and required. -- Do not resolve `Fin (2^α/2^κ)` vs `Fin (2^(α−κ))` mid-proof — go through `packIndexEquiv`. -- Do not route the Hachi head through `BatchingStrategy`, `DenseMLPCS`, or the MvPolynomial - generic lemmas (G1/G5) — the RBR obligations are unfilled and there is no CWSS bridge. -- Do not add `sorry` inside structures or instances; laws are theorem hypotheses (G6). -- Do not modify `openingClaimRel`/`sliceRel`/`sumcheckClaimRel`/`BatchingStrategy` definitions — - Phase 1 is additive plus docstrings only (the branch's Binius path must stay intact). -- Do not build the CWSS work on `origin/cwss-components-infra` — it *lacks* `ofIsEmpty`, - `SingleRound.lean`, `Package.lean` (verified); the working tree is the source of truth. -- Do not conflate `Hachi.toPolynomial` (matrix reshape) with CompPoly's - `CMlPolynomial.toMvPolynomial` (no eval lemma). - -## 13. Risk register - -| # | Risk | Mitigation | -|---|---|---| -| R1 | `treeSpecialSound`'s output-witness flow differs from what Phase 4.6 assumes | **Resolved by verification**: the direct route is viable — the no-challenge bridge (NoChallenge.lean:118) imposes no purity, so the guarded 1-message head is provable by mirroring SendWitness (pSpec shape, `tr 0`) + ReduceClaim (`hpick`/`Exists.choose` witOut recovery from `Set.mem_language_iff`) + the new `not_accepting_of_failure` helper (executable spec: `CheckClaim.knowledgeStateFunction.toFun_full`). The 2-factor-decomposition fallback is legal only with the guarded factor as the RIGHT append factor, still requires Phase 5 for the outer composition, and reuses no existing CWSS theorem — prefer the direct route. | -| R2 | `PolyMatrix` orientation in `unpackPoly` transposed | Acceptance test is `unpackPoly_eval` itself; fix at the definition (G7), plus the Phase 3 toy example. | -| R3 | Framework changes (Phase 5) touch shared files | Coordinate with maintainers; additions only; existing theorems byte-identical (acceptance-checked). | -| R4 | `Vector.map_append` or similar plumbing missing in the Vector API used by CompPoly | Prove locally (3-line `ext`); do not refactor the Vector library. | -| R5 | `DecidableEq (Rq …)` not available where Phase 4.3's `decide` needs it | Derive from the chain's `[LawfulBEq (ZMod q)]` context (e.g. `instDecidableEqOfLawfulBEq`-style, or add the instance next to `Rq`'s `commRing`); last resort: `headCheck` via `==` (`BEq`) with a `LawfulBEq` bridge lemma into the Prop form used by `relRingSwitch` (still G9-conformant — the check stays Bool). | -| R6 | Merge conflicts from the dirty working tree at Phase 0 | Commit first (hard requirement in Phase 0.1). | -| R7 | `Nontrivial (Rq …)` instance derivation fights (Phase 7) | Timeboxed; hypothesis-parameter fallback specified. | -| R8 | Branch author's in-flight S6 work collides with Phase 1 | Phase 1 is additive + docstrings; raise the PackedClaim stage with the author before merging (it slots as a new stage between S2 and S5 in their numbering). | diff --git a/HACHI_LEMMA10_GAP.md b/HACHI_LEMMA10_GAP.md deleted file mode 100644 index 67b49d6686..0000000000 --- a/HACHI_LEMMA10_GAP.md +++ /dev/null @@ -1,852 +0,0 @@ -# The Lemma 10 Gap in Hachi — Analysis and Repair - -Companion to [`HACHI_RING_SWITCHING_PLAN.md`](HACHI_RING_SWITCHING_PLAN.md) (milestone F6, risk -R7) and [`HACHI_RING_SWITCHING_COMPARISON.md`](HACHI_RING_SWITCHING_COMPARISON.md). Subject: -**Lemma 10 of Hachi (NOZ26, ePrint 2026/156, §4.3, Fig. 5)** — the coordinate-wise special -soundness (CWSS) claim for the zero-check challenge round — is not provable as stated, and the -gap is witnessed by an explicit, protocol-level counterexample against the paper's own -range-check polynomial. This file states the gap precisely, shows which repairs work and which -do not, and records the rendering adopted by the formalization plan. - -**TL;DR.** A star-shaped family of accepting transcripts certifies that a batched -constraint polynomial vanishes on the *axis cross* through the star's center — and for a -multilinear polynomial in at least two challenge variables, cross-vanishing does **not** imply -that the polynomial is zero. An adversary can commit to a witness with a single out-of-range -entry and present a perfectly valid, correctly structured one-round star from which no -extractor can succeed without breaking binding. No choice of the paper's parameter `D` helps. -The protocol itself is very likely still sound for uniformly random challenges, but the stated -deterministic tree-extraction claim is false. - -The best repair is still **one round**. Restrict each random evaluation point to the Kronecker -curve - -``` -κ_m(ρ) := (ρ, ρ², ρ⁴, …, ρ^(2^(m-1))). -``` - -For an `m`-variate multilinear `H`, the pullback `H(κ_m(T))` is univariate of degree less than -`2^m`, and the pullback is injective: distinct multilinear monomials become the distinct powers -`T^0,…,T^(2^m−1)`. Sample independent scalar seeds `(ρ_0,ρ_α) ∈ F²` in one verifier round and -send `τ_0 := κ_{m_0}(ρ_0)` and `τ_α := κ_{m_α}(ρ_α)`. With - -``` -D := max(2^m_0, 2^m_α), -``` - -an `SS(F,2,D)` star gives `D` roots of each pullback on its corresponding arm, hence both -original identities. Its tree has `2D−1` leaves. If the two checks share one seed `ρ`, the same -argument gives ordinary `D`-special soundness with only `D` leaves. The existing equality-kernel -sumchecks remain unchanged because they simply receive the structured points `κ_m(ρ)`. The -tradeoff is that the evaluation points are curve-distributed rather than uniform in `F^m`, and -the error scale becomes `D/|F|` rather than `m/|F|`; one must assume `D ≤ |F|` and choose the -extension field accordingly. The coordinate-zipped construction below remains a sound fallback -when retaining the original uniform challenge distribution is more important than one-roundness. - -Throughout, `F := F_{q^k}` is the challenge field; `m_0` and `m_α` (also written `m_1`) are the -arities of `H_0` and `H_α`; `N_0 := 2^m_0`, `N_α := 2^m_α`, and -`D := max(N_0,N_α)`; and "multilinear" means -degree at most one in each challenge variable. The paper calls the second point `τ_1`; this file -also writes `τ_α` to make its role unambiguous. In the zipped fallback, `r := max(m_0,m_α)` and -`s := min(m_0,m_α)`. - -## 1. Faithful setting - -Hachi §4.3 must prove, for a committed witness `w̃` (Eq. (21): the `Z_q`-coefficient table of -the Eq. (20) solution `(ŵ, t̂, ẑ)` and the quotient digits, indexed by `(u, ℓ)`), that - -- the lifted linear rows hold at the challenge `α` (already reduced by Fig. 4 / Lemma 9), and -- every entry of `w̃` is in range: `w̃(u,ℓ) · ∏_{j=1}^{b−1} (w̃(u,ℓ) − j)(w̃(u,ℓ) + j) = 0`. - -Both constraint families are batched with the equality kernel (Eqs. (22), (23)): - -``` -H_α(t) := Σ_{i ∈ [n]} eq̃(t, i) · ( Σ_{u,ℓ} M̃_α(i,u) · w̃(u,ℓ) · α̃(ℓ) − y_i(α) ) -H_0(t) := Σ_{u,ℓ} eq̃(t, (u,ℓ)) · w̃(u,ℓ) · ∏_{j=1}^{b−1} (w̃(u,ℓ) − j)(w̃(u,ℓ) + j) -``` - -Both are **multilinear in `t`** (only `eq̃` depends on `t`, and `eq̃(·, i)` is multilinear). -Fig. 5 has the verifier send `τ_0` (for `H_0`) and `τ_1` (for `H_α`) in one round; the claims -`H_0(τ_0) = 0` and `H_α(τ_1) = 0` then seed the sumcheck (Figs. 6–7). - -**Lemma 10 (paper, condensed).** Given `D := max(2d, 2b−1)` valid transcripts -`((τ_{i,0}, τ_{i,1}), w̃_i)` with `(τ_{i,0}, τ_{i,1})_i ∈ SS(F_{q^k}, 2, D)`, one can either -extract a valid opening `w̃` of `t` satisfying `H_0 ≡ 0` and `H_α ≡ 0`, or break binding of -`Com`. *Proof (paper):* if two `w̃_i` differ, binding breaks; otherwise "by definition of -`D = max(2d, 2b−1)` and the coordinate-wise special soundness, we have found at least `2d` -(resp. `2b−1`) distinct roots for `H_α` (`H_0`), which implies that the aforementioned -polynomials are equal to zero." - -The statement is ambiguous about the CWSS shape — the lemma writes `SS(F, 2, D)` (which types -the challenge as a vector of `ℓ = 2` field elements, impossible for -`(τ_0, τ_1) ∈ F^{m_0} × F^{m_1}`), while the surrounding text says to treat `(τ_0, τ_1)` "as a -vector of `log μ + log d + log n` coordinates" (`ℓ = m := m_0 + m_1`; note this count is itself -inconsistent with the paper's own Eq. (21)/(23) index space `[μ+n] × [d]` — see plan F5 — and -the transcript count "`D` valid transcripts" disagrees with the `SS(S, ℓ, k)` set size -`K = ℓ(k−1)+1` under either reading: `2D−1` for `ℓ = 2`, `m(D−1)+1` coordinate-wise). §2 -refutes the coordinate-wise reading (the substantive one); §3.0 disposes of the literal -`ℓ = 2` reading. -Recall the CWSS/`SS(S, ℓ, k)` shape ([FMN24] Def. 2.9, NOZ26 §2.3, formalized as -`CoordinateWise.IsSpecialSoundFamily`, -[Basic.lean:81](ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Basic.lean#L81)): -a **star** — one central vector `c`, plus `k−1` siblings per coordinate `i`, each agreeing with -`c` everywhere except at coordinate `i`. - -## 2. The gap - -### 2.1 What a star actually certifies - -**Lemma A (star ⇒ cross, and nothing more).** Let `H ∈ F[t_1, …, t_m]` be multilinear and let a -family of points vanish `H`, consisting of a center `c ∈ F^m` and, for each coordinate `j`, at -least one point `c^{(j)}` with `c^{(j)}_i = c_i` for `i ≠ j` and `c^{(j)}_j ≠ c_j`. Then `H` -vanishes identically on every axis line `L_j := { p : p_i = c_i for all i ≠ j }`. - -*Proof.* The restriction of `H` to `L_j` is a univariate polynomial of degree ≤ 1 in `t_j` -(multilinearity). It vanishes at the two distinct points `c_j` and `c^{(j)}_j`, hence is the -zero polynomial. ∎ - -So *any* number `k ≥ 2` of siblings per coordinate certifies exactly: `H` vanishes on the -**axis cross** `⋃_j L_j` through `c`. The question is whether cross-vanishing forces `H ≡ 0`. -For `m = 1` it does (the cross is the whole space). For `m ≥ 2` it does not: - -**Counterexample B (polynomial level).** Let `m ≥ 2`, `a, b ∈ F`, and -`H*(t) := (t_1 − a)(t_2 − b)`. `H*` is multilinear and `H* ≢ 0` (it is `1` at -`(a+1, b+1, …)`), yet `H*` vanishes on the entire cross through any center `c` with `c_1 = a`, -`c_2 = b`: every point of every axis line through `c` retains `t_1 = a` or `t_2 = b`. Moreover, -since `{eq̃(·, i)}_{i ∈ {0,1}^m}` is a basis of the multilinear polynomials (evaluation at the -Boolean points is the identity matrix: `eq̃(i', i) = δ_{i,i'}`), we have -`H* = Σ_i eq̃(t, i) · H*(i)` — so `H*` is *exactly of the batched form* in Eqs. (22)/(23), with -coefficient vector `c_i := H*(i)` not all zero. ∎ - -Lemma A + Counterexample B already refute the paper's proof *step* ("roots ⇒ zero"). The -following upgrades this to a refutation of the lemma's *statement*, i.e. an attack an adversary -can actually mount inside the protocol. - -### 2.2 A constructive, protocol-level counterexample (via the range check) - -The range-check polynomial `H_0` is the easiest to weaponize because the adversary controls its -coefficients **entrywise**: the coefficient of `eq̃(t, (u,ℓ))` is -`P_b(w̃(u,ℓ))` where `P_b(v) := v·∏_{j=1}^{b−1}(v−j)(v+j)`, a fixed nonzero polynomial of -degree `2b−1`. - -**Construction.** Assume `q > 2b−1` (true for all real parameters; Fig. 9 has `q ≈ 2^32`, -`b = 16`) and `m_0 ≥ 2` (always: `m_0` is the log of the witness table size). Note the entries -of `w̃` live in `Z_q` (Eq. (21): they are `X`-coefficients of `R_q`-elements), so the -out-of-range value must be chosen in `Z_q`, and — because the paper instantiates *weak* binding -(Remark 2 / Lemma 7), whose second-opening consequence is norm-conditioned — it must also be -**small**, so that the eventual opening pair yields a genuinely short MSIS solution. - -1. Set `v* := b ∈ Z_q`. Then `P_b(v*) = b·∏_{j=1}^{b−1}(b−j)(b+j)`: every factor lies in - `{1, …, 2b−1}`, hence is nonzero mod `q` by `q > 2b−1`, so `P_b(v*) ≠ 0`. Pick any index - `(u*, ℓ*)` covered by the range check (all of them under Eq. (23); take `u* ≤ μ` if the - `1_{≤μ}` convention of `F_{0,τ_0}` is used). -2. Build `w̃` as the **all-zero table except** entry `(u*, ℓ*) := v*` (all other entries must be - in range, else `H_0` acquires further `eq̃`-monomials and step 4's cross argument breaks; - zero is in range). Then choose the *statement* to match the linear part — take the public - `y`-side to be whatever the lifted rows evaluate to on this `w̃`, with the honest quotient - (Lemma 10 is a statement about arbitrary public inputs; the adversary who controls the - earlier protocol messages controls the R^lin statement it feeds). Then `H_α ≡ 0` - identically, and `H_0(t) = P_b(v*) · eq̃(t, (u*, ℓ*)) ≢ 0`. -3. Commit honestly: `t := Com(w̃)`. Note the zero set of `H_0` is the union of hyperplanes - `⋃_j { t_j = 1 − i*_j }` where `i* ∈ {0,1}^{m_0}` is the bit pattern of `(u*, ℓ*)`. -4. Choose the star center `c ∈ F^{m_0}` with `c_{j_1} = 1 − i*_{j_1}` and - `c_{j_2} = 1 − i*_{j_2}` for two distinct coordinates `j_1 ≠ j_2` (arbitrary elsewhere). - Every axis line through `c` fixes all-but-one coordinate, so it retains at least one of the - two vanishing coordinates — the entire cross lies in the zero set of `H_0`. Populate the - star with any `D−1` siblings per coordinate; choose `τ_1`-parts arbitrarily. -5. Every transcript in the family is **valid**: `t = Com(w̃)` holds, `H_α(τ_1) = 0` holds - identically, and `H_0(τ_0) = 0` holds at every star point by step 4. - -All transcripts carry the *same* `w̃`, so the extractor's binding branch is unavailable, and its -main branch must output an opening of `t` whose entries are all in range (`H_0 ≡ 0` forces every -entry to be a root of `P_b`, i.e. in `[−(b−1), b−1]`) — any such opening differs from `w̃` -(whose entry `(u*,ℓ*) = b` is out of range), and the difference is entrywise bounded by -`2b−1` — **short**. The pair is therefore exactly a *weak-binding* break in the paper's sense -(a short Module-SIS solution via Lemma 7): the extractor itself would be an efficient -weak-binding breaker. Under the binding assumption, no efficient extractor exists. Lemma 10, -under the coordinate-wise reading, is false — for *every* value of `D`. ∎ - -Two remarks. (i) The attack lands on the **range check** — the exact-norm-proof feature that is -the paper's headline contribution — so this is not a peripheral technicality. (ii) The attack -does not need `H_α`: it is orthogonal to the Lemma 9 layer, whose own soundness (univariate, -`k = 2d`) is fine. - -### 2.3 Diagnostics: where the paper's proof goes wrong - -- **Degree confusion.** `H_α` and `H_0` are multilinear *in `t`*. The quantities `2d` and - `2b−1` are degrees in *other* variables — `2d−1` bounds the `X`-degree of the lifted rows - (that is Lemma 9's interpolation, over the `α`-challenge), and `2b−1` is the `w̃`-degree of - the range product (Lemma 11's per-round sumcheck degree is then `2b` — range product times - the multilinear `eq̃` — with `k = 2b+1` transcripts per round; plan F5/R8). Neither has - anything to do - with identity-testing the `t`-polynomials; "`2d` (resp. `2b−1`) distinct roots" for a - *multivariate multilinear* polynomial implies nothing. Even along a single coordinate line, - 2 points already suffice — and all `m` lines together still do not determine `H` (Lemma A + - Counterexample B). So `D = max(2d, 2b−1)` is simultaneously wasteful (per line) and - insufficient (globally). -- **Why the paper's *other* CWSS lemmas are unaffected.** Lemma 8 (QuadEval/folding, formalized - sorry-free) uses stars *correctly*: its verification equations are affine in each challenge - coordinate, and extraction subtracts the center transcript from a sibling to *isolate one - column* — the star is exactly the right shape for folding, and no "vanishing ⇒ zero - polynomial" step occurs. Lemmas 9 and 11 are single-scalar-challenge rounds — univariate - interpolation, rigorous. The misuse is specific to Lemma 10's *multivariate zero-check*. -- **What survives.** The *protocol* is almost certainly still knowledge-sound: for uniformly - random `τ`, a nonzero multilinear `H` in `m` variables vanishes with probability at most - `m/|F|` (Schwartz–Zippel; total degree ≤ m), and the adversarial cross is a - measure-`O(m/|F|)` event. What is broken is the *tree-extraction claim* — precisely the - currency in which the paper (via FMN24 Lemma 4) and the ArkLib formalization (via - `CWSSStructure` composition) do all their accounting. - -## 3. Repair approaches - -Summary table; details below. - -| # | Approach | Sound? | Verdict | -|---|---|---|---| -| K | **One-round Kronecker-curve challenges, `k = D`** | ✓ | **adopted** (plan F6) | -| 0 | Literal `ℓ = 2` reading, with unrestricted uniform vector blocks | ✗ | fails | -| 1 | Coordinate-zipped sequential CWSS rounds, `k_j = 2` | ✓ | uniform-challenge fallback | -| 2 | Fully sequential scalar rounds, `k_j = 2` | ✓ | sound fallback, but dominated by 1 | -| 3 | One round with a tensor-grid tree shape | ✓ | sound, but needs a new non-CWSS tree predicate | -| 4 | Keep the original one-round star and appeal only to the separation of `H_0` and `H_α` | ✗ | separation helps only after re-scheduling | -| 5 | Keep the original vector-coordinate star and increase `k`/`D` | ✗ | no parameter helps without re-encoding | -| 6 | Rewinding plus Schwartz–Zippel for the original uniform vectors | ✓* | sound, but not deterministic CWSS extraction | -| 7 | Direct scalar power fingerprint `Σ_i c_i T^i` | ✓ | one-round alternative; changes the multiplier | - -### 3.K One-round Kronecker-curve CWSS — works (adopted) - -The missing ingredient is not another tree shape; it is a challenge encoding on which ordinary -univariate interpolation is information-complete for multilinear polynomials. - -#### 3.K.1 The injective pullback - -For `m ≥ 1`, define the Kronecker curve - -``` -κ_m : F → F^m, -κ_m(ρ)_j := ρ^(2^j) for j = 0,…,m−1. -``` - -Write an arbitrary multilinear polynomial in the monomial basis: - -``` -H(X_0,…,X_{m-1}) = Σ_{e ∈ {0,1}^m} a_e · ∏_j X_j^(e_j). -``` - -Its pullback is - -``` -K_H(T) := H(κ_m(T)) - = Σ_{e ∈ {0,1}^m} a_e · T^(Σ_j e_j 2^j). -``` - -The binary encoding `e ↦ Σ_j e_j 2^j` is a bijection from `{0,1}^m` to -`{0,…,2^m−1}`. Consequently, - -``` -deg K_H < 2^m, -K_H = 0 ⇔ H = 0. (Kronecker injectivity) -``` - -This is stronger than a Schwartz–Zippel statement: it is a deterministic polynomial identity -equivalence. ArkLib already has the forward map as -`LinearMvExtension.powAlgHom` in -[`LinearMvExtension.lean`](ArkLib/Data/MvPolynomial/LinearMvExtension.lean); its existing -`powAlgHom_of_restrict_degree_natDegree` proves the degree bound. The main generic algebra lemma -still needed for this repair is injectivity of `powAlgHom` on the per-variable-degree-`≤1` -subtype. The file's inverse construction `linearMvExtension` already contains almost all of that -proof. - -#### 3.K.2 Protocol rendering - -Let - -``` -N_0 := 2^m_0, -N_α := 2^m_α, -D := max(N_0,N_α), -``` - -and assume `D ≤ |F|`. Replace Fig. 5's unrestricted vector sampling by the following single -public-coin round: - -``` -ρ_0, ρ_α ← F independently -τ_0 := κ_{m_0}(ρ_0) -τ_α := κ_{m_α}(ρ_α) -send (τ_0,τ_α) -- or send the two seeds and derive the vectors -``` - -The prover response and verifier equations stay exactly as in Fig. 5: - -``` -t = Com(w̃), -H_0^{w̃}(τ_0) = 0, -H_α^{w̃}(τ_α) = 0. -``` - -The protocol's challenge type should be modeled as `F²`, with the two vectors derived -deterministically. If the expanded vectors themselves are placed in the semantic transcript, -their types must be the **curve-image subtypes**, not unrestricted `F^m`: injectivity of `κ_m` -(its first coordinate is `ρ`) then gives the required equivalence between each subtype and `F`. -Merely serializing the expanded vectors on the wire is harmless, but ArkLib's `Challenge` type -must retain the on-curve invariant so that every family admitted by the `CWSSStructure` consists -of curve points. - -This Lemma 10 block is at a **fixed, previously extracted `α`**, exactly as in the paper's lemma -statement `(t,M̃_α,α)`. The `α` fork from Lemma 9 must remain an earlier/nested extraction node. -Even if an implementation coalesces `α,ρ_0,ρ_α` into one byte message, treating all three as one -flat CWSS star is not justified: the `H_α` check has mixed dependence on `α` and `ρ_α`, recreating -the same missing-corners problem. - -#### 3.K.3 Extraction from one CWSS star - -An `SS(F,2,D)` family has not `D` but - -``` -2(D−1)+1 = 2D−1 -``` - -members. Relabel it around its center as - -``` -(a,b), -(a_1,b), …, (a_{D-1},b), -(a,b_1), …, (a,b_{D-1}), -``` - -where `a,a_1,…,a_{D-1}` are distinct and so are `b,b_1,…,b_{D-1}`. - -If two accepting branches return different admissible openings of `t`, return the same -binding/weak-binding escape as in the paper. Otherwise binding fixes one common `w̃`. The first -arm now gives `D` distinct roots of - -``` -K_0(T) := H_0^{w̃}(κ_{m_0}(T)), deg K_0 < N_0 ≤ D, -``` - -so `K_0 = 0`, and Kronecker injectivity gives `H_0^{w̃} = 0`. The second arm identically gives -`H_α^{w̃} = 0`. The bad axis-cross polynomial from §2 cannot survive this challenge encoding: -its pullback is a nonzero univariate polynomial of degree less than `N_0`, hence it cannot vanish -at all `D` first-arm seeds. - -This yields the corrected statement: - -**Lemma 10 (corrected: one-round Kronecker CWSS).** Suppose `m_0,m_α ≥ 1`, -`D := max(2^m_0,2^m_α) ≤ |F|`, and the Fig. 5 points are derived from independent scalar seeds -by `κ`. There is an efficient deterministic extractor which, from a family of `2D−1` accepting -transcripts whose seed pairs lie in `SS(F,2,D)`, returns either - -1. one opening `w̃` of `t` satisfying `H_0^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0`, or -2. the commitment binding escape used by the surrounding Hachi proof. - -Thus the modified zero-check is one-round `(2,D)`-coordinate-wise special sound. The extractor -is polynomial time whenever the checked table sizes `N_0,N_α` are polynomial in the security -parameter, exactly the regime required for the protocol itself to be efficient. For a zero-arity -identity, test the resulting constant directly and take `D := max(2,N_0,N_α)` to meet ArkLib's -nontrivial-parameter convention. - -#### 3.K.4 Plain-special-soundness variant - -If independence between the two batching points is unnecessary, sample one `ρ ← F` and set - -``` -τ_0 := κ_{m_0}(ρ), -τ_α := κ_{m_α}(ρ). -``` - -Then any `D` accepting transcripts with distinct `ρ` give `D` roots of *both* pullbacks. The -protocol is ordinary `D`-special sound (`ℓ=1`) and its extraction input has only `D` leaves. This -is the smallest one-round rendering, but the independent-seed CWSS variant is closer to Fig. 5 -and lets the two tests retain cross-block independence. - -The independent-seed protocol is not ordinary `D`-special sound merely by treating a pair as -one challenge: `D` distinct pairs need not contain `D` distinct first coordinates or `D` -distinct second coordinates. Its two star arms are exactly what the CWSS hypothesis supplies. - -#### 3.K.5 Cost and faithfulness - -- **Rounds and payload.** There is one challenge round. Sending seeds costs two field elements; - sending the derived vectors retains Fig. 5's message shape but spends `m_0+m_α` elements. -- **Downstream sumchecks.** They are unchanged: substitute the derived `τ_s=κ_{m_s}(ρ_s)` into - the same `eq̃(τ_s,·)` multiplier. Repeated squaring computes each curve point in `O(m_s)` field - operations. -- **Tree size.** Independent seeds use `2D−1` leaves; a shared seed uses `D`. When - `N_0=N_α=N`, the zipped fallback uses `3^{log₂ N}=N^{log₂ 3}` leaves, so both curve variants - asymptotically improve the extraction tree. -- **Error tradeoff.** A false fixed opening makes at least one nonzero pullback of degree at most - `D−1`, so its relevant uniform seed lands on a root with probability at most - `(D−1)/|F|`. This is worse than the `O((m_0+m_α)/|F|)` scale of uniform vector evaluation. - Asymptotically `D=poly(λ)` and `|F|=2^{Ω(λ)}` still give negligible error. Concretely, the - paper's largest next-witness table has size about `2^26` over a field of size about `2^128`, - so a single curve test supplies only about 102 bits from this term; a 128-bit target needs a - larger extension field or parallel repetition. Repeated seeds can still be sent in the same - verifier message, although the corresponding CWSS coordinate count and star arity grow. - In particular, do not apply the `2D/|F|²`-shaped expression printed in Hachi's restatement of - FMN24 literally here: if only `H_0` is invalid while `H_α=0`, acceptance can already have - probability `(N_0−1)/|F|`. The direct root bound is the safe accounting. -- **Faithfulness.** The verifier no longer samples uniformly from all of `F^{m_0}×F^{m_α}`. - It samples uniformly from two size-`|F|` Kronecker curves. This is a genuine, localized - protocol change, though the checked equations and every downstream sumcheck formula are the - same. -- **Field-size condition.** `D ≤ |F|` is load-bearing: without it an `SS(F,2,D)` family cannot - exist, and the root argument cannot collect `D` distinct seeds. -- **Scalar-threshold optimality.** For `N` arbitrary residual coefficients, any linear - one-scalar batching defines a length-`|F|`, dimension-`N` evaluation code. If `k` accepting - roots always force the coefficient vector to vanish, its distance is at least `|F|−k+1`; - the Singleton bound gives distance at most `|F|−N+1`, hence `k ≥ N`. Kronecker and direct - powers attain this bound. The table-sized threshold is therefore inherent in this generic - scalar-linear model, not slack in the proof. -- **Why the change is necessary.** If the original unrestricted uniform vector challenge is - retained, the §2.2 range-check counterexample accepts on - `|F|^m−(|F|−1)^m` distinct points. Plain one-round special soundness would therefore require - more than that many transcripts, which is not polynomial in the relevant parameters. A - polynomial-size one-round SS/CWSS repair must restrict or re-encode the challenge space (or - change the tree predicate). - -### 3.0 The literal `ℓ = 2` unrestricted-block reading — fails - -Read `SS(F,2,D)` as treating the two *blocks* `τ_0` and `τ_1` as the two coordinates, so a -sibling may replace an entire block by an arbitrary new vector. Then the family merely gives -`D` adversarially chosen multivariate points at which `H_0` vanishes, and `D` such points can -all be placed on a nontrivial zero set. Even random points would supply only `D` linear -conditions for a multilinear polynomial having up to `2^{m_0}` coefficients. This reading is -strictly weaker than the coordinate-wise reading refuted in §2.2. ∎ - -### 3.1 Coordinate-zipped sequential CWSS rounds — works (uniform-challenge fallback) - -The key observation is that the zero-check contains **two separate identities in disjoint -variable sets**: - -``` -H_0 ∈ F[X_1, …, X_{m_0}], -H_α ∈ F[Y_1, …, Y_{m_1}]. -``` - -A CWSS star can safely process one fresh variable of each identity in parallel. What it cannot -do is process two fresh variables belonging to the *same* arbitrary multilinear identity in -one node. - -#### 3.1.1 Protocol rendering - -Write - -``` -τ_0 = (x_1, …, x_{m_0}), -τ_1 = (y_1, …, y_{m_1}), -r = max(m_0,m_1), -s = min(m_0,m_1). -``` - -Replace the single atomic Fig. 5 challenge by `r` successive public-coin challenge rounds. For -`j ∈ [r]`, send - -``` -χ_j := (x_j,y_j) ← F² if j ≤ m_0 and j ≤ m_1, -χ_j := x_j ← F if j ≤ m_0 and j > m_1, -χ_j := y_j ← F if j > m_0 and j ≤ m_1. -``` - -Equivalently, define - -``` -ℓ_j := 1_{j≤m_0} + 1_{j≤m_1} ∈ {1,2}, -k_j := 2. -``` - -At the end of these rounds, reconstruct the same vectors `τ_0,τ_1` and retain the same scalar -claims - -``` -H_0(τ_0) = 0, -H_α(τ_1) = 0, -``` - -which seed the unchanged downstream sumchecks. - -To fit a strictly alternating transcript syntax, insert a fixed empty prover message between -successive verifier challenges. The rounds must be genuine fork points in the extraction -object; merely sampling one atomic vector and parsing it afterward does not create the nested -CWSS tree used below. - -#### 3.1.2 Why the `SS(F,2,2)` star is now sufficient - -At a paired round, the three challenges in the CWSS family can be relabeled as - -``` -(a,b), (a',b), (a,b') -``` - -with `a' ≠ a` and `b' ≠ b`. The geometry is still a star: - -``` - (a,b') - | - | - (a,b) ----+---- (a',b) -``` - -But the two asserted polynomials use different arms: - -- `H_0` sees only the first coordinate, so `(a,b)` and `(a',b)` provide two distinct values of - its current variable; -- `H_α` sees only the second coordinate, so `(a,b)` and `(a,b')` provide two distinct values of - its current variable. - -There is no asserted polynomial in this round containing a mixed term in both `x_j` and `y_j`. -That is exactly what failed in the original rendering, where several coordinates of `H_0` -(and several coordinates of `H_α`) lived in one large star. - -The old toy counterexample illustrates why nesting matters. For -`H_0(X_1,X_2) = X_1X_2`, a one-round star at `(0,0)` misses `(1,1)`. In the repaired schedule, -`X_1` and `X_2` occur in different rounds. On the branch `X_1 = 1`, the next round must test two -distinct values of `X_2`, so the formerly missing corner appears and the nonzero polynomial is -caught. - -#### 3.1.3 Seam relations and bottom-up extraction - -For `0 ≤ j ≤ r`, define the intermediate relation `R_j` for an opening `w̃` of `t` by - -``` -t = Com(w̃), - -H_0^{w̃}(x_1,…,x_{min(j,m_0)}, X_{min(j,m_0)+1},…,X_{m_0}) ≡ 0, - -H_α^{w̃}(y_1,…,y_{min(j,m_1)}, Y_{min(j,m_1)+1},…,Y_{m_1}) ≡ 0, -``` - -where the last two equalities are polynomial identities in the as-yet unchallenged variables. -Then: - -- `R_r` is exactly the pair of scalar claims - `H_0^{w̃}(τ_0)=0 ∧ H_α^{w̃}(τ_1)=0` supplied by an accepting leaf; -- `R_0` is the desired conclusion - `H_0^{w̃}≡0 ∧ H_α^{w̃}≡0`. - -The extraction kernel is the usual two-point step. - -**Lemma C (two-point multilinear step).** Let `G ∈ F[U,Z_1,…,Z_h]` have degree at most one in -`U`. If `a ≠ a'` and - -``` -G(a, Z_1,…,Z_h) ≡ 0, -G(a',Z_1,…,Z_h) ≡ 0, -``` - -then `G ≡ 0`. - -*Proof.* Write `G = A + U·B`, with `A,B ∈ F[Z_1,…,Z_h]`. The two identities give -`A+aB≡0` and `A+a'B≡0`; subtraction yields `(a-a')B≡0`. Since `F` is a field and -`a≠a'`, `B≡0`, and then `A≡0`. ∎ - -Now extract from the transcript tree bottom-up. - -- **Paired round.** Recursively extract from the subtrees rooted at `(a,b)`, `(a',b)`, and - `(a,b')`. If any recursive call already returns a binding/MSIS escape, return it. If the - resulting openings of `t` differ, return the corresponding binding/MSIS escape. Otherwise - they are one common opening `w̃`. The center and first-coordinate sibling give two - identically-zero restrictions of the current variable of `H_0`, so Lemma C removes that - variable. The center and second-coordinate sibling do the same for `H_α`. Hence `R_j` - implies the parent relation `R_{j-1}`. - -- **Unpaired round.** There are two children with distinct scalar challenges. If their openings - differ, return the binding/MSIS escape; otherwise apply Lemma C to the one identity that - still has a fresh variable. The other identity is simply inherited. - -Induction to the root produces either the same binding/MSIS escape used elsewhere in Hachi or a -single opening `w̃` satisfying `R_0`. - -#### 3.1.4 Zipped fallback theorem - -**Fallback Lemma (CWSS of the coordinate-zipped zero-check).** Let -`Π_zc^zip` be the modified zero-check protocol above. For each challenge round `j ∈ [r]`, let -the verifier challenge lie in `F^{ℓ_j}`, where - -``` -ℓ_j = 1_{j≤m_0} + 1_{j≤m_1}, -k_j = 2. -``` - -There is an efficient deterministic extractor which, given the public statement and a valid -tree of accepting transcripts such that the children at every depth `j` form a family in -`SS(F,ℓ_j,2)`, outputs either - -1. an opening `w̃` of `t` satisfying - - ``` - t = Com(w̃), - H_0^{w̃} ≡ 0, - H_α^{w̃} ≡ 0, - ``` - - or - -2. two distinct admissible openings of `t` giving the same binding/MSIS violation as in the - paper's commitment analysis. - -Consequently, under the stated binding assumption, `Π_zc^zip` is - -``` -(ℓ_1,…,ℓ_r)-coordinate-wise (2,…,2)-special sound -``` - -for the zero-check relation. Its transcript tree has - -``` -K = ∏_{j=1}^r (ℓ_j(2−1)+1) - = 3^s · 2^{r−s} - = 3^{min(m_0,m_1)} · 2^{|m_0−m_1|} -``` - -leaves. - -*Proof.* The bottom-up induction through the seam relations `R_j` is given in §3.1.3. ∎ - -#### 3.1.5 Accounting and faithfulness - -- **Challenge payload.** The verifier still samples exactly `m_0+m_1` independent field - elements, and the final joint distribution of `(τ_0,τ_1)` is unchanged. They are grouped - into `r=max(m_0,m_1)` rounds instead of one atomic message. - -- **Tree size.** The fully scalar repair has `2^{m_0+m_1}` leaves. The zipped repair has - `3^s·2^{r-s}` leaves and - - ``` - 3^s·2^{r-s} ≤ 2^{r+s} = 2^{m_0+m_1}. - ``` - - When `m_0=m_1=m`, this improves `4^m` to `3^m`. Since the challenge arities are logarithmic - in the relevant table sizes, the tree remains polynomial-size under the same parameter - regime required by the paper. - -- **Knowledge error.** Using the FMN24 per-round term quoted in the formalization notes, - `ℓ_j(k_j−1)/|F|`, paired rounds contribute `2/|F|` and scalar rounds contribute `1/|F|`. - The total is therefore - - ``` - (2s + (r−s))/|F| = (m_0+m_1)/|F|, - ``` - - the same intended error as the all-scalar repair and much smaller than the paper's - `D`-dependent star accounting. The discrepancy between this formula and NOZ26's printed - restatement of FMN24 remains a separate upstream issue. - -- **Degree parameters.** The zero-check rounds use `k_j=2` because each identity is affine in - its one fresh variable. The values `2d` and `2b−1` concern other variables and other protocol - stages; `D=max(2d,2b−1)` should not occur in corrected Lemma 10. - -- **Downstream protocol.** Figs. 6–7 consume only the completed vectors `τ_0,τ_1` through - `eq̃(τ_0,·)` and `eq̃(τ_1,·)`. Their arithmetic is unchanged. - -- **Fiat–Shamir caveat.** The challenge rounds must become genuine, domain-separated random- - oracle queries or sequential sponge squeezes at which the extractor can fork. The payload - size and distribution are unchanged, but byte-for-byte transcript identity with the - original single-message rendering is not guaranteed and should not be claimed without fixing - a concrete derivation convention. - -### 3.2 Fully sequential scalar rounds — sound, but dominated - -A simpler repair sends all `m_0+m_1` coordinates in separate scalar rounds and applies Lemma C -once per round. This is rigorous and uses ordinary `2`-special soundness throughout. Its nested -tree is the full two-point tensor grid, with - -``` -K_scalar = 2^{m_0+m_1}. -``` - -The zipped repair performs exactly the same interpolation for each identity, but processes one -coordinate of `H_0` and one coordinate of `H_α` in parallel. It uses fewer rounds, no more -knowledge error, and a strictly smaller tree whenever `s>0`. The all-scalar rendering remains a -valid fallback if an implementation exposes only scalar challenge-round machinery, but it is -not the preferred formulation of Lemma 10. - -### 3.3 One round with a tensor-grid tree — sound, but unnecessary - -Keep one batched challenge round, but replace the star node predicate by a **grid**: the -children must contain a product set `S_1×⋯×S_m` with `|S_j|≥2` for every coordinate. - -**Lemma D (grid interpolation).** A multilinear `H ∈ F[t_1,…,t_m]` vanishing on -`S_1×⋯×S_m`, with every `|S_j|≥2`, is identically zero. - -*Proof.* Induct on `m`. Write `H=A+t_mB`. At each point of the first `m−1` coordinates, the -univariate restriction in `t_m` has two roots and degree at most one, so both `A` and `B` -vanish on the smaller grid; induction gives `A≡B≡0`. ∎ - -This repair is sound, but a two-point grid has `2^{m_0+m_1}` leaves, larger than the zipped -CWSS tree. It also requires a new non-star node predicate, new composition lemmas, and a new -knowledge-error theorem, whereas §3.1 stays inside the existing CWSS definition and its -`seqCompose` machinery. Rejected on cost. - -### 3.4 Keeping the original one-round star does not become sound merely because the identities are separate - -The disjointness of `H_0(X_1,…,X_{m_0})` and `H_α(Y_1,…,Y_{m_1})` is useful, but it does not -rescue the original one-round star. That star still places *all* `X`-coordinates in one node, -so `H_0` may contain mixed terms such as `X_1X_2`; similarly, `H_α` may contain mixed -`Y_iY_j` terms. The counterexample of §2.2 therefore remains valid. - -What the disjointness permits is the more precise scheduling rule used in §3.1: - -> A CWSS round may contain several challenge coordinates only if each asserted polynomial -> depends on at most one of those coordinates. - -For Hachi's two identities, this allows at most one `X`-coordinate and one `Y`-coordinate per -round — exactly the coordinate-zipped schedule. ∎ - -### 3.5 Keep the original vector-coordinate star and increase `k`/`D` — fails - -Counterexample B vanishes on every relevant axis line *identically*. A star may therefore have -arbitrarily many siblings per vector coordinate and all its transcripts still accept. No larger -value of `k` or `D` recovers the missing mixed-coordinate information *without changing the -challenge encoding*. The Kronecker repair changes it so that each star coordinate is a scalar -seed whose arm traces an information-complete curve. ∎ - -### 3.6 Rewinding plus Schwartz–Zippel — sound, but not a CWSS extractor - -One can prove the zero-check probabilistically: rewind the prover with fresh uniformly random -`(τ_0,τ_1)` and use Schwartz–Zippel to bound the acceptance probability of a nonzero -multilinear polynomial by its total degree divided by `|F|`. This gives a sound standalone -argument, but it replaces deterministic CWSS tree extraction by a different rewinding proof. -Neither the paper's FMN24-based composition nor ArkLib's `CWSSStructure.append/seqCompose` -currently composes that mixed proof style. The Kronecker rendering in §3.K turns the same -root-counting intuition into deterministic one-round CWSS, so there is no need to leave the -framework. ∎ - -### 3.7 Direct scalar power fingerprint — sound one-round alternative - -Instead of retaining `H` and restricting its evaluation point as in §3.K, one can replace the -equality-kernel batching directly by - -``` -G(τ) := Σ_i τ^{⟨i⟩} c_i. -``` - -Here `⟨i⟩ = Σ_j i_j2^j`. This is univariate of degree less than the padded constraint-table size, -and it is zero exactly when every coefficient `c_i` is zero. The same independent-seed -`SS(F,2,D)` proof and shared-seed plain-SS proof therefore apply. - -Contrary to the earlier version of this note, this choice does **not** destroy the tensor -structure needed by sumcheck. The Boolean weight `τ^{⟨i⟩}` is the evaluation at `i` of the public -multilinear polynomial - -``` -W_τ(X) := ∏_j ((1-X_j) + X_j · τ^(2^j)), -``` - -so `W_τ(i)=τ^{⟨i⟩}` and it can replace `eq̃(τ_vector,i)` in the structured multiplier at the same -per-variable degree. Direct power batching makes the coefficient-extraction theorem especially -simple, but it changes Eqs. (22)–(23) and the downstream public multiplier. The Kronecker-curve -rendering is preferred because it obtains the same `D`, tree size, and error while leaving the -paper's `H_0`, `H_α`, and sumcheck formulas intact. Algebraically the two versions are basis -changes of the same degree-`<2^m` Reed–Solomon fingerprint. ∎ - -## 4. Recommendation and status - -- **Adopted rendering (plan F6):** use the one-round independent-seed Kronecker challenge of - §3.K, with `ℓ=2` and `k=D=max(2^m_0,2^m_α)`. Derive the vector points from the two scalar - seeds and retain the paper's `H_0`, `H_α`, and downstream sumchecks unchanged. The shared-seed - plain-SS variant is available if minimizing the extraction tree matters more than preserving - independence between the two tests. - -- **Replace Lemma 10:** use the corrected statement in §3.K.3. The *shape* `SS(F,2,D)` can stay, - but its coordinates must be scalar curve seeds, its family has `2D−1` transcripts, and - `D` is the maximum padded constraint-table size. It is not the paper's - `max(2d,2b−1)`. - -- **Fix parameter provenance:** `2d` belongs to the scalar `α` interpolation of Lemma 9; - `2b+1` belongs to the degree-`2b` range sumcheck rounds of Lemma 11; and corrected Lemma 10 - uses `D=max(N_0,N_α)` because those are the dimensions of the two multilinear coefficient - spaces. - -- **Record the protocol deviation accurately:** round count and checked equations are unchanged, - but the points are sampled from Kronecker curves rather than uniformly from the full vector - spaces. Under Fiat–Shamir, squeeze two scalar seeds and expand them by repeated squaring. - -- **Formalization impact:** the negative result of §2 remains useful as a regression test. The - repair requires (i) injectivity of `LinearMvExtension.powAlgHom` on multilinear polynomials, - (ii) the existing degree bound for that map, (iii) univariate root counting, and (iv) a - one-round `CWSSStructure` with two scalar coordinates and parameter `D`. No seam-relation - induction, new grid predicate, or non-CWSS soundness framework is needed. Lemma 8's existing - star-based folding proof is unaffected. - -## Appendix: statements suitable for formalization - -**Pseudo-code sketches, not compilable Lean.** In the repo, `MultilinearPoly` is the -degree-restricted subtype `L⦃≤ 1⦄[X Fin ℓ]`; evaluation goes through `.val`; `eqTilde` is the -scalar equality kernel and `eqPolynomial` its polynomial form. The exact names below are -indicative. - -``` --- The Kronecker point underlying the one-round challenge. -def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F := - fun j => ρ ^ (2 ^ j.val) - --- The missing companion to the existing --- `LinearMvExtension.powAlgHom_of_restrict_degree_natDegree` bound. -theorem powAlgHom_injective_on_multilinear {F} [CommRing F] [Nontrivial F] {m : ℕ} : - Function.Injective (fun H : MultilinearPoly F m => - LinearMvExtension.powAlgHom H.val) - --- Evaluation of the univariate pullback agrees with evaluation on the curve. -theorem eval_powAlgHom_eq_eval_kronecker {F} [CommRing F] {m : ℕ} - (H : MultilinearPoly F m) (ρ : F) : - Polynomial.eval ρ (LinearMvExtension.powAlgHom H.val) = - H.val.eval (kroneckerPoint m ρ) - --- One-round, two-seed corrected Lemma 10. -theorem zeroCheck_kronecker_coordinateWiseSpecialSound - (m0 mα : ℕ) (D : ℕ := max (2 ^ m0) (2 ^ mα)) - (hcard : D ≤ Fintype.card F) : - CoordinateWiseSpecialSound - (ell := 2) - (k := D) - zeroCheckRelation - --- Optional shared-seed version: ordinary D-special soundness. -theorem zeroCheck_kronecker_specialSound - (m0 mα : ℕ) (D : ℕ := max (2 ^ m0) (2 ^ mα)) - (hcard : D ≤ Fintype.card F) : - SpecialSound D zeroCheckRelation - --- The following lemmas support only the uniform-challenge zipped fallback. - --- Lemma C: the one-variable interpolation kernel used by both arms of a zipped node. -theorem multilinear_eq_zero_of_two_instantiations {F} [Field F] {m : ℕ} - (H : MultilinearPoly F (m + 1)) {u v : F} (huv : u ≠ v) - (hu : instantiateFirst H u = 0) - (hv : instantiateFirst H v = 0) : H = 0 - --- One paired SS(F,2,2) node removes one variable from each independent identity. -theorem zipped_pair_step {F} [Field F] {m0 m1 : ℕ} - (H0 : MultilinearPoly F (m0 + 1)) - (H1 : MultilinearPoly F (m1 + 1)) - {a a' b b' : F} (haa' : a ≠ a') (hbb' : b ≠ b') - (h0_center : instantiateFirst H0 a = 0) - (h0_xSibling : instantiateFirst H0 a' = 0) - (h1_center : instantiateFirst H1 b = 0) - (h1_ySibling : instantiateFirst H1 b' = 0) : - H0 = 0 ∧ H1 = 0 - --- Equality-kernel basis nondegeneracy: the R_0 bridge. -theorem eqTilde_batch_eq_zero_iff {F} [CommRing F] [Nontrivial F] {m : ℕ} - (c : (Fin m → Fin 2) → F) : - (∑ i, (eqPolynomial i) * C (c i)) = 0 ↔ ∀ i, c i = 0 - --- The fallback zero-check extractor, parameterized by the zipped round schedule. -theorem zeroCheck_zipped_coordinateWiseSpecialSound - (m0 m1 : ℕ) - (ell : Fin (max m0 m1) → ℕ := fun j => - (if j < m0 then 1 else 0) + (if j < m1 then 1 else 0)) : - CoordinateWiseSpecialSound - challengeSets ell - (fun _ => 2) - zeroCheckRelation - --- Optional only: grid interpolation, not needed by the adopted repair. -theorem multilinear_eq_zero_of_grid {F} [Field F] {m : ℕ} - (H : MultilinearPoly F m) (S : Fin m → Finset F) - (hS : ∀ j, 2 ≤ (S j).card) - (h : ∀ p ∈ Fintype.piFinset S, eval p H = 0) : H = 0 - --- Negative regression example: a one-round star does not identify a 2-variate multilinear polynomial. -example : ∃ (H : MultilinearPoly F 2), - H ≠ 0 ∧ (∀ p on the axis cross through (a,b), eval p H = 0) -``` diff --git a/HACHI_RECURSION_GAP.md b/HACHI_RECURSION_GAP.md deleted file mode 100644 index fa4f2f21b9..0000000000 --- a/HACHI_RECURSION_GAP.md +++ /dev/null @@ -1,357 +0,0 @@ -# The §4.5/§3.2 Partial-Evaluation Gap in Hachi — Analysis - -Companion to [`HACHI_LEMMA10_GAP.md`](HACHI_LEMMA10_GAP.md) (which documents the Lemma 10 -zero-check gap and its adopted Kronecker repair). Subject: **the partial-evaluation step of -Hachi (NOZ26, ePrint 2026/156) §4.5 (Eqs. (24)–(26)), and its generic form §3.2** — the -reduction from the per-slice well-formedness claims `yᵢ = fᵢ(x)` to the single `Z`-packed -evaluation claim `f′(x) = ∑ᵢ yᵢ·Z^{⟨i⟩}` is **not knowledge-sound as stated**. The -skeleton isolates this in one zero-round bridge, -[`Recursion/ZBatchBridge.lean`](ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean), -whose pull-back sorry is flagged as *expected unprovable as stated*. - -**Status: confirmed algebraic gap as written, found during the skeleton design review -(2026-07-13) and re-audited against the paper (2026-07-15). The explicit counterexample below is -information-theoretic and survives an honest downstream proof. A CWSS-native repair is adopted at -the design level in §3: reconstruct the DP24 tensor carrier from the already-sent `yᵢ`, fingerprint -it after the `yᵢ` are fixed, relocate the resulting claim by a degree-two sumcheck, and reuse the -paper's existing Eq. (27) element `p` at the endpoint. The repair has not yet replaced the skeleton's -faithful-but-unprovable `ZBatchBridge`. It has not been cross-checked with the authors.** - -## 1. The step in question - -§3.2 (and §4.5's recursion step, which follows the same pattern with `eq`-weights in place of -monomials): to prove `f(x) = y` for a committed polynomial `f` with **base-field** (`Z_q`) -coefficients at a point `x` with coordinates in the **extension field** `F = F_{q^k}` -(`k = 2^κ`), the prover sends the `2^κ` partial evaluations - -``` -yᵢ := fᵢ(x_rest) ∈ F, i ∈ {0,1}^κ, -``` - -the verifier checks (or, for purity, derives `y₀` from) - -``` -y = ∑ᵢ mᵢ(x_top) · yᵢ (Eq. (24) / §3.2 display) -``` - -and the remaining obligation — "all the yᵢ are well-formed" — is **replaced** by the single -packed claim (Eq. (26) / §3.2's f′-display), which the downstream protocol then proves: - -``` -f′(x_rest) = ∑ᵢ yᵢ · Z^{⟨i⟩}, where f′ := ∑ᵢ fᵢ · Z^{⟨i⟩}. -``` - -The paper's implicit claim is that this replacement is an equivalence ("proving well-formedness -of all (yᵢ)ᵢ is equivalent to proving (26)"). - -## 2. The gap - -The packed claim pins only **one** `F`-linear combination of the per-slice defects -`εᵢ := yᵢ − fᵢ(x_rest) ∈ F`: - -``` -∑ᵢ Z^{⟨i⟩} · εᵢ = 0. (*) -``` - -If the `εᵢ` were base-field scalars, (\*) would force `εᵢ = 0` (the `Z`-powers are an -`F_q`-basis). But `fᵢ(x_rest)` is evaluated at extension-field coordinates, so `εᵢ` ranges over -all of `F`: (\*) is `k` `F_q`-linear conditions on `k²` `F_q`-dimensions — a `k(k−1)`-dimensional -kernel for every `k ≥ 2`. - -**Concrete cheat (`κ = 1`, `k = 2`, `F = F_q[Z]`).** Let `f(X₁, X₂)` be committed honestly with -slices `f₀, f₁` and true partials `tᵢ = fᵢ(x₂)`. The adversary sends - -``` -y₁ := t₁ − δ, y₀ := t₀ + Z·δ, δ ∈ F arbitrary. -``` - -Then `y₀ + Z·y₁ = t₀ + Z·t₁` — the packed claim (26) is **true for the honest committed `f′`** -and is proven by an entirely honest downstream run. The verifier's Eq. (24) check accepts the -claimed value - -``` -y = y₀ + x₁·y₁ = f(x) + δ·(Z − x₁), -``` - -so for any `x₁ ≠ Z` **every** target value `y` is reachable: the extractor holds the honest -`f`, all checks pass, and `f(x) ≠ y`. The same computation goes through with `eq`-weights -(§4.5) in place of monomials, and with the derive-`y₀` (footnote 5/10) convention — the cheat -vector simply enters through the sent `y₁`. - -Note the contrast with the **generic §3.1 transformation** (Lemma 5/Theorem 2, the trace -check): there the downstream claim pins the packed element `Y` *exactly* (the residual claim is -an equality of ring elements), and the trace check transfers it to `y` with no slack — §3.1 is -unaffected. The slack is created precisely by §3.2/§4.5's `k²`-dimensional `yᵢ`-layer between -the two. - -Also note Remark 1 of the paper flags a *different* issue with applying §3.1 to base-field -polynomials (extracted `f` lands in `F_{q^k}[X]`), and offers §3.2 as the fix — the gap above -says the fix itself does not extract. - -## 3. Adopted repair: carrier-free-on-the-wire CWSS relocation - -The repair uses the tensor carrier from DP24/Binius, but the prover does **not** send that carrier: -the verifier can reconstruct it from the `yᵢ` that Hachi already sends. Likewise, the usual -terminal field evaluation produced by the relocation sumcheck is not sent: the verifier derives it -from Hachi's existing Eq. (27) ring element `p`. Thus the values carried across the recursion -boundary remain exactly the paper's `(k−1)` extension-field elements and one ring element `p`. - -The price is interaction: one post-`yᵢ` batching challenge and a degree-two relocation sumcheck. -This is a change to the paper's protocol, but it stays entirely within Hachi's CWSS proof currency. - -### 3.1 The existing `yᵢ` determine the tensor carrier - -Write `B := F_q`, `L := F_{q^k}`, `k := 2^κ`, and choose a `B`-basis -`(βᵢ)_{i < k}` of `L` (the paper's `Z`-basis). Split the Boolean index as `j ‖ i`, where -`j ∈ {0,1}^{mLow}`, `i ∈ {0,1}^κ`, and `mLow = ℓ−κ`. For the short base-field table `w̃`, define - -``` -ŵⱼ := ∑ᵢ w̃_{j‖i} · βᵢ ∈ L, -tᵢ := ∑ⱼ eq(j, a₀) · w̃_{j‖i} ∈ L. (true partials) -``` - -This is §4.5's multilinear/`eq` notation. For generic §3.2, replace `eq(j, a₀)` by the -corresponding tail-monomial weight; the tensor and CWSS arguments below use only `B`-linearity and -are otherwise identical. - -Consider the tensor algebra `A := L ⊗_B L`. Once the verifier has the full derived family -`(yᵢ)ᵢ`, it can form locally - -``` -S_y := ∑ᵢ yᵢ ⊗ βᵢ ∈ A. (public; not sent) -``` - -The committed table determines - -``` -S_w := ∑ⱼ eq(j, a₀) ⊗ ŵⱼ - = ∑ᵢ tᵢ ⊗ βᵢ. (witness carrier) -``` - -The second equality follows by expanding `ŵⱼ` and exchanging the sums. Since -`(1 ⊗ βᵢ)ᵢ` is an `L`-basis of `A`, - -``` -S_y = S_w ↔ ∀ i, yᵢ = tᵢ. -``` - -This is exactly the missing `k²`-dimensional statement. The paper's Eq. (26) applies only one -non-injective projection to it; the repair tests the full tensor equality across a CWSS family. -The tensor is a proof device and verifier-local computation, not a new prover message. - -### 3.2 Post-`yᵢ` scalar fingerprint - -After the `yᵢ` are fixed, the verifier samples a fresh scalar `ρ ∈ L`. The ordering is -load-bearing: an earlier Hachi challenge cannot be reused, because then a malicious prover could -choose its `yᵢ` after seeing the fingerprint. - -For each `ρ`, define the `B`-linear map `λ_ρ : L → L` by - -``` -λ_ρ(βᵤ) := ρᵘ, 0 ≤ u < k, -``` - -and the induced `B`-balanced map `Λ_ρ : A → L` by - -``` -Λ_ρ(x ⊗ z) := λ_ρ(x) · z. -``` - -The verifier computes the initial target directly from the existing partials: - -``` -s₀(ρ) := Λ_ρ(S_y) = ∑ᵢ λ_ρ(yᵢ) · βᵢ. (public) -``` - -On the witness side, define the public table - -``` -A_ρ(j) := λ_ρ(eq(j, a₀)). -``` - -Then - -``` -Λ_ρ(S_w) = ∑ⱼ A_ρ(j) · ŵⱼ. -``` - -The direct Vandermonde weights `ρᵘ` are the simplest choice. An equivalent DP24-shaped choice is -`λ_ρ(βᵤ) := eq(u, (ρ, ρ², …, ρ^{2^{κ−1}}))`; its Kronecker pull-back also has degree `< k` and is -injective. The rest of the protocol is unchanged by this choice. - -The multiplier is efficiently evaluable without materializing `S_y`. If `(βᵤ*)ᵤ` is the -trace-dual basis, then - -``` -λ_ρ(x) = ∑ᵤ ρᵘ · Tr_{L/B}(βᵤ* · x). -``` - -Equivalently, write this `B`-linear map as a linearized polynomial -`λ_ρ(x) = ∑_{h if S.check s.original_claim s.t_eval_point (tr ⟨0,_⟩) then pure ⟨s, tr ⟨0,_⟩⟩ else failure`; - prover sends `S.carrierEval t' (suffix)`; witness pass-through (`BatchingWitIn`). - Output relation `relPackedClaim := { (⟨s, ŝ⟩, wit) | ŝ = S.carrierEval wit.t' … ∧ wit.t' = S.pack wit.t ∧ compat }`. -- `Relocation.lean`: the challenge round + `compute_s0`; StmtIn `PackedClaimStatement`, StmtOut - `Statement (RingSwitchingBaseContext …) 0` (types unchanged); DP24-only, keeps RBR statements. -- Re-plumb `BatchingPhase.lean` (monolith = append; restate the sorried KState/RBR/completeness - at the same outer boundaries), `General.lean`, `FRIBinius/General.lean`. Build green; no - hand-edits to `ArkLib.lean`. - -**B3. Generic packing CWSS (1 d).** In `Packing.lean`: - -```lean -theorem PackingPhase.coordinateWiseSpecialSound_of_checkSound - (hlaw : S.CheckSound) (D : CWSSStructure _) : - (packingVerifier S).coordinateWiseSpecialSound init impl D - relOriginalClaim relPackedClaim -``` - -via `coordinateWiseSpecialSound_of_isEmpty_challengeIdx` (probability-phrased hypothesis already -accommodates the guard; acceptance forces `check = true`, then `hlaw` converts the packed-claim -witness into the original-claim witness through `unpack`). One P→V message ⇒ -`IsEmpty ChallengeIdx` holds. Also state the RBR-error-0 analogue for Binius symmetry (optional). - -**B4. Guarded CWSS composition (2–3 d, independent).** Extend -`CoordinateWiseSpecialSoundness/Composition.lean` and `SeqCompose.lean`: -- `Verifier.append_treeSpecialSound_of_guard` — hypothesis - `hV₁ : ∀ stmt tr, V₁.verify stmt tr = if check stmt tr then pure (verify₁ stmt tr) else failure`. - Proof deltas against :366: a guarded `append_run_pure_left` (composed acceptance probability 1 - forces `check = true` — the `failure` branch has success probability 0 — then reduces to the - pure case; uses nonemptiness of the suffix tree's transcript list, cf. the `LeafPath` machinery - already used at Composition.lean:383) and a guarded `pure_accepting_of_mem`. Corollaries: - `append_coordinateWiseSpecialSound_of_guard` + the OracleVerifier wrapper. -- `Verifier.seqCompose_treeSpecialSound_of_guard` — the n-ary variant with per-factor - `IsGuarded` (a `check`-indexed generalization of `IsPure`; pure = trivially-true check), by the - same induction as SeqCompose.lean:364 with the guarded append as the step. -Four consumers in this plan: the §3.1 head (D1), the guarded sumcheck-round loop (F7), the -final-eval step (F8), and the §4.5 handoff head (G3). Generic security infrastructure — -coordinate with maintainers (R3). - -**B5 (optional, parallel). Binius packing law (2–4 d).** Prove -`biniusPackingScheme_checkSound` from `decomposeColumns_spec` + tensor-algebra + MLE partial -evaluation. Payoff: the first *proven* soundness statement in the Binius ring-switching stack -(via B3). Not on Hachi's critical path. - -### Phase C — Hachi profile + packed-evaluation algebra (~4–6 days; the mathematical heart) - -**C1. `hachiProfile` (1 d).** New `Commitments/Functional/Hachi/RingSwitch/Profile.lean` -(imports `RingSwitching/Profile`, `Lattices/CyclotomicRing/Subfield`). Parameter dictionary -(**`κ` clash**: paper `κ` = log extension degree; profile rank is `α − κ` — spell it out -everywhere, R5): - -```lean -noncomputable def hachiPackBasis (h2 …) (hk …) : - Basis (Fin (α − κ) → Fin 2) ↥(fixedSubring (R := ZMod q) α (2^κ)) (Rq (powTwoCyclotomic α)) := - -- Basis.ofEquivFun on psiLinearEquiv.symm (A2.5), reindexed along hypercubeEquivFin (A1) - -noncomputable def hachiProfile (h2 …) (hk …) : - RingSwitchingProfile ↥(fixedSubring (R := ZMod q) α (2^κ)) (Rq (powTwoCyclotomic α)) (α − κ) where - basis := hachiPackBasis h2 hk - A := Rq (powTwoCyclotomic α); φ₀ := RingHom.id _; φ₁ := (conjAut α : _ →+* _) - decomposeColumns z v := ↑(hachiPackBasis h2 hk |>.repr z v) - decomposeRows z u := ↑(hachiPackBasis h2 hk |>.repr (conjAut α z) u) - decomposeColumns_spec := … -- ~10 lines: coords in B are conjAut-fixed (A2.2) + Basis.sum_repr - decomposeRows_spec := … -- conjAut ring-hom + involution (A2.1) + Basis.sum_repr -``` - -Acceptance: `example` instantiation at the paper's Fig. 9 shape (`q ≡ 5 (mod 8)`, `α = 10`, -`κ = 2`). - -**C2. `hachiPackingScheme` (1 d).** `RingSwitch/Scheme.lean`: -`pack` = ψ on coefficient blocks of a `CMlPolynomial B (μ + (α−κ))` (block structure by the A1 -convention; agreement-with-`packMLE` lemma is Phase H hygiene, not a dependency); -`weights xt j := ↑((CMlPolynomial.monomialBasis xt).get j)` (tail monomials, values in `B`); -`decomp` = rows or columns per the C3 proof (record the outcome as a one-line note in -Profile.lean's table, R2); `carrierEval := embedded_MLP_eval (hachiProfile …)`. Plus the -paper-form check `traceCheck s Y := traceH α (2^κ) (Y * conjAut α (psi … (monomialVec s.xt))) = (2^α/2^κ) • ↑s.y` -with a `Decidable` instance via `traceHComp`. - -**C3. Packed-evaluation lemma (2–4 d).** `RingSwitch/PackedEval.lean` — Theorem 2 lifted to the -multilinear level; this discharges both scheme laws and the paper-check equivalence: - -```lean -theorem traceH_packPoly_eval (h2) (hk) - (f : CMlPolynomial B (μ + (α−κ))) (x : Fin μ → B) (xt : Fin (α−κ) → B) : - traceH α (2^κ) ((packPoly f).eval (coe ∘ arrange x xt) * conjAut α (psi … (monomialVec xt))) - = (2^α / 2^κ) • ↑(f.eval (paper-order x xt)) -``` - -Proof plan: (i) expand `(packPoly f).eval` by `evalSplit_eq_eval`/`eval_eq_sum` -(PolynomialEvalSplit, instantiated at the subring `B` — check `CMlPolynomial`'s ring-hom -`map`/`eval_map` support early, R1) into `Σ_i headMonomial i * ψ(block i)`; (ii) push `traceH` -through the sum (additivity); (iii) extract the `B`-valued, σ-fixed `headMonomial i` via -`traceH_smul_fixed`; (iv) apply `traceH_psi_mul_conj` per block; (v) reassemble via -`evalSplit_eq_eval` over `B`. Corollaries: -- `hachiPackingScheme_checkSound` / `_checkComplete` (the B3/B1 law props); -- `traceCheck_iff_check` (paper trace equation ⟺ generic decomposition check, via A2.6 - unit-cancellation and `Subtype.val`-injectivity). - -### Phase D — §3.1 head, composed end-to-end (~3–4 days) - -**D1. The head as an instance + adapter (2–3 d).** `RingSwitch/Head.lean`: -- `RingSwitchStatement := { pp, u, xl : Vector B r, xh : Vector B m, xt : Vector B (α−κ), y : B }` - (point pre-split to match `PolyEvalStatement`'s `r`/`m` split; `xt` = packed tail). -- The head verifier **is** `packingVerifier hachiPackingScheme` specialized with - `Aux := (pp, u)` payload (statement-shape functor around `PackedClaimStatement`), i.e. one - message `Y' ∈ Rq` and the guarded check — **no new protocol code**, only statement plumbing. -- Zero-round σ₋₁ adapter (`ReduceClaim`, D5): `toPolyEvalStatement (s) (Y') := - { pp := s.pp, u := s.u, xl := coe ∘ s.xl, xh := coe ∘ s.xh, y := conjAut α Y' }`. -- `relRingSwitch` — same three-case shape as `relPolyEval`; opening case: - `VerifiedOpening … ∧ (unpackPoly (extractedPoly Φ base o)).eval (xl ++ xh ++ xt) = ↑y`. -- Pull-back `mem_relRingSwitch_of_relPolyEval` (opening case = C3's soundness corollary; MSIS - cases pass through) → head CWSS via B3 + `ReduceClaim.verifier_coordinateWiseSpecialSound`. -- Prover skeleton + `traceCheck_of_honest` (D12 scope). - -**D2. Composition + doc fixes (0.5–1 d).** In `Hachi/Basic.lean`: -`ringSwitchEvalVerifier := headVerifier.append (adapter.append evalVerifier)` and - -```lean -theorem hachi_ringSwitch_eval_coordinateWiseSpecialSound : - ringSwitchEvalVerifier.coordinateWiseSpecialSound init impl - (…ofIsEmpty-append chain…) (relRingSwitch …) (relOut …) -``` - -via **B4's guarded append** at the head seam + the existing -`eval_coordinateWiseSpecialSound`. Migrate the (now ≥3) binary appends to `seqCompose` -where factors are pure (the guarded head stays an outer binary append). Fix the "§4.1" → "§3" -cross-references (Basic.lean:37/:212, PolyEvalReduction.lean:46-47). - -### Phase E — §3.2 base-field head (~3–5 days, parallel with F) - -One-message, **pure** (D7) head for `f` with `ZMod q` coefficients at a `B`-valued point -(Eq. (11); reduces variables to `ℓ − α` instead of `ℓ − α + κ`): -- Message: `(y_i)_{i ≠ 0} : Fin (2^κ − 1) → B`; verifier *derives* `y₀`, outputs the claim - `f′(x_{κ+1..ℓ}) = Σ_i y_i · Z^{Σ i_t 2^{t−1}}` with the `Z`-powers realized by - `vElt`/`fixedBasisMap` (Eq. (7) generators, §1.2). -- Formally a second `PackingScheme`-adjacent step at the **field-level profile shape** - `B := ZMod q`, `L := ↥(fixedSubring α (2^κ))`, basis = `Z`-powers — reuse `packMLE` here - (coefficients are already the right shape) or the CMlPolynomial analogue per A1 conventions. -- CWSS via `ReduceClaim`/one-message-pure + NoChallenge; zero soundness error; new algebra: the - `Z`-power reindexing lemma `f′(x) = Σ_i y_i Z^{…}` (paper §3.2 display). -- Then Phase D applies downstream unchanged. - -### Phase F — Hachi's sumcheck, §4.3 (~23–32 days total; ~21–27 excluding the deferrable F1) - -**F1. Field upgrade (2–5 d, or defer via D10).** Close `no_selfReciprocal_factor` -(Field.lean:207; 4-step docstring plan, blueprint difficulty 8/10) to obtain -`Field ↥(fixedSubring α (2^κ))` / `fixedSubringEquivGaloisField` under `q % 8 = 5`. Everything -in F2–F9 is stated over an abstract `[Field F]` (D10), so F1 can land last; it gates only the -final Hachi-concrete instantiation. Also needed here: `SampleableType F` / `Fintype F` transport -along the subring (finite subring of a finite ring — easy), since F's challenges are sampled. - -**F2. Eq. (20) → `R^lin` adapter (2 d).** Zero-round `ReduceClaim` from QuadEval's output -statement `(QuadEvalStatement × CarrierCom × challenges)` to - -```lean -structure RlinStatement (Φ) (n μ : ℕ) where - M : PolyMatrix (Rq Φ) n μ; yvec : PolyVec (Rq Φ) n; bound : ℕ -- ‖·‖∞ ≤ bound -``` - -assembling the Eq. (20) block matrix from `(pp, v, u, y, avec, bvec, c)` (rows = c1..c5 blocks; -`jMatrix`, `gadgetMatrix`, `tensorG1`, `tensorG` from QuadEvalGadgets). Witness map: stack -`QuadEvalResponse` into `ζ = (ŵ, flatten t̂, ẑ)`; `mapWitInv` un-stacks. Deliverables: the -block-row equivalence lemmas `rlin_iff_relOut_linear` (c1–c5 ⟺ `M ζ = yvec`) and -`range_iff_relOut_norm` (c6 ⟺ `‖ζ‖∞ ≤ γ`), then -`ReduceClaim.verifier_coordinateWiseSpecialSound` with pull-back = the ⟸ directions. This -adapter is pure — plain append. - -**F3. Quotient-lift algebra (2–3 d, independent).** `Data/Lattices/CyclotomicRing/` addition -(generic, reusable by LatticeFold-style work): for the quotient `π : (ZMod q)[X] → Rq Φ`, -- `exists_quotient_witness : M ζ = y (in Rq) ↔ ∃ ρ, deg-bounds ∧ M̂ ζ̂ = ŷ + (X^d + 1) · ρ (in (ZMod q)[X])` - (coefficient-lift of matrices/vectors; `ρ` degree `< d − 1`, plus its base-`b` gadget - decomposition per the paper's hidden-decomposition remark); -- evaluation compatibility: `evalAt (α : F) : (ZMod q)[X] →+* F` via the `ZMod q ↪ F` embedding, - and the degree bound `natDegree (Σ M̂ᵢⱼ ζ̂ⱼ − ŷᵢ − (X^d+1)ρᵢ) ≤ 2d − 1`; -- the interpolation kernel: a degree-`≤ 2d−1` polynomial over a field vanishing at `2d` distinct - points is zero (Mathlib: `Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero`-family). - -**F4. HMZ25 lift reduction — Fig. 4 / Lemma 9 (3–4 d).** Two-round reduction -`pSpec := ⟨![.P_to_V, .V_to_P], ![WCommitment, F]⟩`: -- Message: `t := Com(w̃)` — the **inner-outer commitment without initial decomposition** of the - next-iteration witness `w̃` (Eq. (21): the `(ZMod q)`-coefficient rows of `ζ` and of the - quotient digits `ρ_u`); reuse `InnerOuter` commitment types + `WeakBinding`. -- Challenge: `α ← F`. Output statement: `{ rlin-data, t, α }`; **output witness** (never sent, - D6): `w̃` itself. Output relation `relLift`: `t = Com(w̃) ∧ (rows of M̂ ζ̂(w̃) − ŷ − (X^d+1)ρ(w̃) - evaluated at α are 0) ∧ ranges(w̃)` ∨ binding/MSIS escapes. -- CWSS: `ofSpecialSound (k := 2d)` on the single scalar challenge; extraction: `2d` accepting - branches either yield two distinct `w̃` openings of `t` (→ weak-binding escape, Lemma 7 route) - or one `w̃` with `2d` roots (F3's interpolation) ⇒ `R^lin` membership. The star machinery - needed is the `ℓ = 1` case (`isSpecialSoundFamily_one_iff_injective`); generalize - `CoordinateWise.SingleRound`'s star readers from `Fin (2^r) → C` challenges to plain scalar - challenges (small refactor: its `pSpec` at `r := 0` + `Equiv.funUnique`, or a scalar twin). - -**F5. Constraint encoding — Eqs. (21)–(23) (2–3 d).** Definitions only (no protocol): -`w̃` as a `CMlPolynomial F (log (μ+n) + log d)`-shaped table per Eq. (21) (index bookkeeping via -A1's conventions); `α̃(ℓ) = α^ℓ` and `M̃_α(i,u)` as multilinear extensions (`mle`-style, using -the repo's MLE infrastructure); the batched `H_α` (Eq. (22)) and `H_0` (Eq. (23)); the sumcheck -polynomials `F_{0,τ₀} = eq̃·range-product·1_{≤μ}` and `F_{α,τ_α} = w̃·α̃·(Σ eq̃ M̃_α)`, expressed -through `SumcheckMultiplierParam` with a Hachi `Context` type carrying scalar seeds `(ρ₀,ρ_α)`, -their derived Kronecker points `(τ₀,τ_α)`, and `(t, α, public M̃_α data)` (Context is generic, -§1.4). **Pin the exact per-round degree here**: the range -product `∏_{j=-(b-1)}^{b-1} (X − j)` has `2b−1` factors; with the multilinear `w̃` and `eq̃` -multiplier the round polynomial degree is `2b`, hence `k = 2b+1` transcripts per round -(verified independently; the repo docstring's "Q of degree 2b / round degree 2b+1" at -Structured.lean:79-80 is off by one against its own printed product — fix it here — and the -paper's "b+1 elements per round" matches neither, likely an unstated digit-range convention). -Thread the result as `d₀ := degCombinator + 1` uniformly; everything degree-parametric -downstream, so any residual convention change costs a constant rename. **Also pin the challenge -arities here**: the paper's `τ₀ ← F^{log μ + log d}` is in tension with `w̃`'s own index arity -`log(μ+n) + log d` (Eq. (23)'s `eq̃(t,(u,ℓ))` needs `t`-arity equal to `w̃`'s index arity; the -`1_{≤μ}` indicator restricts the *range check*, not the index space). Pin these as `m₀,m_α` and -set F6's interpolation parameter to `D := max(2^m₀,2^m_α)`; require `D ≤ |F|`. - -**F6. One-round Kronecker zero-check (3–4 d).** Keep Fig. 5 as one challenge round, but sample -two independent scalar seeds `(ρ₀,ρ_α) ∈ F²` and derive - -``` -τ₀ := (ρ₀, ρ₀², ρ₀⁴, …, ρ₀^(2^(m₀-1))), -τ_α := (ρ_α, ρ_α², ρ_α⁴, …, ρ_α^(2^(m_α-1))). -``` - -This block runs at the fixed `α` produced by F4. Keep F4's `α` fork as an earlier/nested CWSS -node even if the concrete transcript serializes `α,ρ₀,ρ_α` contiguously: one flat three-coordinate -star does not interpolate the mixed `(α,ρ_α)` dependence. - -Use a single `CWSSStructure` with `ℓ=2` and `k=D=max(2^m₀,2^m_α)`, hence `2D−1` branches. -The mathematical work is: - -- reuse `LinearMvExtension.powAlgHom` and - `powAlgHom_of_restrict_degree_natDegree` from - `ArkLib/Data/MvPolynomial/LinearMvExtension.lean`; -- prove `powAlgHom` injective on the per-variable-degree-`≤1` subtype (the same file's - `linearMvExtension` inverse machinery supplies the coefficient argument); -- prove evaluation compatibility with the derived Kronecker point; -- generalize the single-round CWSS assembly helper beyond its current `k=2` specialization, or - prove the local `ℓ=2,k=D` transcript-tree theorem directly; -- use `D` distinct roots on the first star arm for `H₀` and on the second arm for `H_α`; differing - leaf openings return the existing weak-binding/MSIS escape; -- bridge `H₀ ≡ 0 ∧ H_α ≡ 0` to the entrywise range and row constraints, while the accepting leaf - claims `H₀(τ₀)=H_α(τ_α)=0` feed F7 unchanged. - -The equality-kernel multipliers and sumcheck formulas remain exactly those of Eqs. (22)–(23). -What changes is the challenge distribution: the points lie on Kronecker curves rather than being -uniform in the full vector spaces. Record the `D/|F|` error scale and require a larger concrete -extension or same-message parallel repetition if the `D≈2^26`, `|F|≈2^128` instance must meet a -full 128-bit target. -The shared-seed plain-`D`-SS variant is a smaller optional fallback; the independent-seed CWSS -version is the default because it preserves cross-block independence. Full proof and alternatives: -[`HACHI_LEMMA10_GAP.md`](HACHI_LEMMA10_GAP.md). - -**F7. Per-round sumcheck CWSS on the substrate (5–6 d; the second heart).** New -`Sumcheck/Structured/CWSS.lean` (or Hachi-local first, promoted later): - -```lean -theorem guardedRound_coordinateWiseSpecialSound (d : ℕ) (i : Fin ℓsc) (hcons : …) : - (guardedRoundOracleVerifier … d i).coordinateWiseSpecialSound init impl - (CWSSStructure.ofSpecialSound (fun _ => d + 1) …) - (sumcheckRoundRel … i.castSucc) (sumcheckRoundRel … i.succ) -``` - -(stated over the **guarded** round verifier introduced below — per R10 the theorem is -unprovable for the substrate's pure-with-dummy `roundOracleVerifier`). - -- New per-round relation family `sumcheckRoundRel` in the CWSS currency: "committed `w̃` opens - `t` ∧ `H`-projection structural invariant ∧ `sumcheckConsistencyProp` at the current target" - (∨ escapes) — the CWSS analogue of `masterKStateProp`, but Hachi-shaped and paper-faithful - (Lemma 11's statement). -- Extraction per round: `d+1` distinct scalar challenges; branches share the message `g_i`; - either two branches disagree on the (relation-level) `w̃` ⇒ binding escape, or the univariate - `Σ_b H(a_{ nomatch h | ⟨1,_⟩ => infer_instance` matching - ([Sumcheck/Structured/SingleRound.lean:106-116](ArkLib/ProofSystem/Sumcheck/Structured/SingleRound.lean#L106)). -- Pass-through verifier idiom: `fun stmt tr => pure (stmt, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩)` - ([QuadEval/Reduction.lean:251](ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean#L251)); - prover skeleton with `computeV`/`computeResp` params (:265-298). -- `SampleableType`: `FinEnum (ZMod n)` instance exists (VCVio `SampleableType.lean:332`), so - `SampleableType (ZMod q)` is derivable; for an abstract field take `[SampleableType F]` as a - hypothesis (v2's D10). -- Weak binding: `VerifiedOpening` / `outputToModuleSIS_valid_of_verified` - ([InnerOuter/Security.lean:163/332](ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean#L163)) — - the pattern (not necessarily the instance) F4's collision escape follows. - ---- - -## 1. Global design decisions (new in this plan; supersede v2 where they conflict) - -**G1 — Escape threading via `⊕` (the one v2 under-specified point).** Once F4's extractor can -hit a *binding break of the new `w̃`-commitment*, that escape must flow **backwards** through -every seam up to the chain head (composed extraction: `E₂` feeds `E₁`). Today's -`relOut`/`relRlin` seams have no home for it. Fix: thread a single **escape budget type** `E` -through the chain as a plain `Sum`: - -```lean --- sketch (F2.0); Set.withEscape is ~5 lines -def Set.withEscape (rel : Set (S × W)) (esc : Set E) : Set (S × (W ⊕ E)) := - {p | match p with - | (s, .inl w) => (s, w) ∈ rel - | (_, .inr e) => e ∈ esc} -``` - -Crucially `esc` is **statement-independent** (an MSIS/collision solution is checkable against -the parametric commitment key alone), so pass-through across statement maps is trivial. The -threaded variants of the finished theorems are built in **new files** by wrapping the exported -extraction cores (`buildWitness_mem_relIn`, `ReduceClaim.verifier_coordinateWiseSpecialSound`) -— zero edits to sorry-free proofs. Concrete instantiation: `E := LiftEscape` (F4.2). - -**G2 — the `w̃`-commitment key is a *parameter*, not a statement field.** `ReduceClaim.mapStmt` -is a pure function `StmtIn → StmtOut`, so F2 cannot conjure a fresh commitment key into the -`R^lin` statement. Instead the key rides as a section `variable` of the F2/F4 files (repo -precedent: relations already take `base ω γ` as plain arguments). F4's verifier never reads it -(pure pass-through); only the relations do. - -**G3 — guards are `Bool`-valued.** `Verifier.IsGuardedWith V check verify` with -`check : StmtIn → FullTranscript pSpec → Bool` and body -`if check s tr then pure (verify s tr) else failure`. Consumers with decidable Prop checks use -`decide`. Purity is the `check := fun _ _ => true` special case. - -**G4 — plain `Verifier` only.** `OracleVerifier.append` is sorried; all B4/F2/F4 statements stay -at the plain-`Verifier` level (exact precedent: the comment before `quadEvalPackage`, -Soundness.lean:451-453). Oracle wrappers are deferred, tracked in the B4 file header. - -**G5 — field abstraction (v2's D10, made concrete).** F3/F4 are stated over -`{F : Type} [Field F]` plus an embedding `φF : ZMod q →+* F` (injective for free: -`RingHom.injective` from a field domain) and `[SampleableType F]` where challenges are drawn. -No dependence on `fixedSubring`/`GaloisField`/`no_selfReciprocal_factor`. - -**G6 — seams are definitional.** Because `▷`'s `hseam` is by `rfl`, each milestone *exports* its -`relOut` as a named `def` and the next milestone's `relIn` *is that name*. Never restate a seam -relation. - -**Standing hypotheses** (unchanged from v2's D11): `[Fact (Nat.Prime q)]`, `Φ := 𝓜(q, α)` with -`1 ≤ α` (so `d = 2^α ≥ 2`), plus `hq5 : q % 8 = 5`, `hκ : (2ω)² < q`, `hτ : 0 < zDigits` -wherever the QuadEval layer is consumed. - -**Validation protocol per milestone** (CLAUDE.md guardrails): `git add` new files, then -`./scripts/validate.sh` (add `--lint` before PR); never hand-edit `ArkLib.lean`; update -`docs/wiki/repo-map.md` in the same PR that creates a new directory. - ---- - -## 2. Milestone B4 — guarded CWSS composition (~2.5–3.5 d) - -**Goal.** `append`/`seqCompose` CWSS theorems whose *left* factors may reject at runtime, plus -the guarded `▷`. Generic security infrastructure — **coordinate with maintainers before -starting** (v2's R3); no existing theorem changes byte-wise. - -**New file** `ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean` -(imports `Composition`, `SeqCompose`, `Package`). - -### B4.1 The guard predicate and rejection lemmas (0.5 d) - -```lean --- sketches; binders as in Composition.lean's section variables -def Verifier.IsGuardedWith (V : Verifier oSpec StmtIn StmtOut pSpec) - (check : StmtIn → FullTranscript pSpec → Bool) - (verify : StmtIn → FullTranscript pSpec → StmtOut) : Prop := - ∀ stmt tr, V.verify stmt tr = if check stmt tr then pure (verify stmt tr) else failure - -class Verifier.IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where - is_guarded : ∃ check verify, V.IsGuardedWith check verify - -instance : V.IsPure → V.IsGuarded -- check := fun _ _ => true -``` - -Rejection lemma (the guarded half of `pure_accepting_of_mem`'s dichotomy): - -```lean -theorem Verifier.failure_not_accepting (lang : Set StmtOut) : - Pr[(· ∈ lang) | OptionT.mk do - (simulateQ impl ((failure : OptionT (OracleComp oSpec) StmtOut)).run' (← init))] = 0 -``` - -*Proof plan:* the computation's support contains no `some` outcome; unfold as in -`pure_accepting_of_mem`'s step (a) (Composition.lean:333-362) with `simulateQ_failure` / -`OptionT.run_failure` simp lemmas in place of `simulateQ_pure`. Then the two directional -workhorses (both ~10-line wrappers over the existing pure lemmas after an `if`-split): - -```lean -theorem Verifier.guarded_accepting_of_mem (hV : V.IsGuardedWith check verify) - (hcheck : check stmt tr = true) (hout : verify stmt tr ∈ lang) : Pr[…] = 1 -theorem Verifier.check_eq_true_of_guarded_accepting (hV : V.IsGuardedWith check verify) - (hacc : Pr[…] = 1) : check stmt tr = true -theorem Verifier.mem_of_guarded_accepting … : verify stmt tr ∈ lang - -- mirrors mem_of_pure_accepting (SeqCompose.lean:53-84), incl. its nonempty-init-support step -``` - -### B4.2 Guarded left-run lemma (0.25 d) - -```lean -theorem Verifier.append_run_guardedLeft (hV₁ : V₁.IsGuardedWith check₁ verify₁) : - (V₁.append V₂).run stmt (tr₁ ++ₜ tr₂) = - if check₁ stmt tr₁ then V₂.run (verify₁ stmt tr₁) tr₂ else failure -``` - -*Proof plan:* mirror `append_run_pure_left` (Composition.lean:311-319): `simp [Verifier.append_run, -Verifier.run, hV₁]`, split the `if`; the `false` branch is `failure_bind`. - -### B4.3 Guarded append theorem (1–1.5 d; the core) - -```lean -theorem Verifier.append_treeSpecialSound_of_guardedLeft - (hV₁ : V₁.IsGuardedWith check₁ verify₁) - (h₁ : V₁.treeSpecialSound init impl S₁ rel₁ rel₂) - (h₂ : V₂.treeSpecialSound init impl S₂ rel₂ rel₃) : - (V₁.append V₂).treeSpecialSound init impl (S₁.append S₂) rel₁ rel₃ - -theorem Verifier.append_coordinateWiseSpecialSound_of_guardedLeft - (D₁ : CWSSStructure pSpec₁) (D₂ : CWSSStructure pSpec₂) … -- same corollary shape as :414-428 -``` - -*Proof plan* — transplant Composition.lean:366-407 with two deltas: -1. Where the pure proof rewrites the composed run via `append_run_pure_left`, use - `append_run_guardedLeft` and case-split on `check₁`. In the `false` branch the composed run - is `failure`, so the leaf's acceptance (`Pr = 1`) contradicts `failure_not_accepting` - (`Pr = 0`, and `0 ≠ 1`) — the branch is vacuous. Every surviving leaf has `check₁ = true` - and the proof is *literally* the pure proof from there. -2. Where the pure proof certifies each left-leaf output in `rel₂.language` via - `pure_accepting_of_mem`, use `guarded_accepting_of_mem` fed by the `check₁ = true` fact of - delta 1 (the tree machinery — `appendSplit`, `appendSplit_fst_isStructured`, - `appendSplit_sndAt_isStructured`, `appendSplit_fullTranscripts_append_of_mem` — is untouched). - -Watch: each left leaf needs *some* suffix transcript to learn `check₁ = true` from; that is the -same nonemptiness the pure proof already extracts via `LeafPath.exists_of_mem_fullTranscripts` -(used at Composition.lean:393). No new tree lemma expected. - -### B4.4 Guarded n-ary composition (0.5 d) - -```lean -theorem Verifier.seqCompose_treeSpecialSound_of_guarded - (hV : ∀ i, (V i).IsGuarded) … -- otherwise verbatim SeqCompose.lean:364-386 -theorem Verifier.seqCompose_coordinateWiseSpecialSound_of_guarded … -``` - -*Proof plan:* same induction as SeqCompose.lean:364-386 (base `Verifier.id` is pure hence -guarded; step uses B4.3 with `(hV 0).is_guarded`). Also the closure lemma the induction needs: - -```lean -theorem Verifier.IsGuarded.append : V₁.IsGuarded → V₂.IsGuarded → (V₁.append V₂).IsGuarded --- composite check := fun s tr => check₁ s tr.fst && check₂ (verify₁ s tr.fst) tr.snd -``` - -(mirror of `IsPure.append`, [IsPure.lean:37](ArkLib/OracleReduction/Composition/Sequential/IsPure.lean#L37)). - -### B4.5 Guarded package and `▷ᵍ` (0.5 d) - -```lean -structure GCWSSPackage … where -- CWSSPackage with isPure ↝ isGuarded - verifier … struct … relIn … relOut … - isGuarded : verifier.IsGuarded - isCWSS : … - -def CWSSPackage.toGuarded : CWSSPackage … → GCWSSPackage … -def GCWSSPackage.append (L₁ L₂ : GCWSSPackage …) (hseam := by rfl) : GCWSSPackage … -scoped infixr:65 " ▷ᵍ " => GCWSSPackage.append -``` - -`GCWSSPackage.append` mirrors Package.lean:79-97 with B4.3/B4.4's theorem and `IsGuarded.append`. -This discharges the Hachi TODO's "guarded variant of `▷`" (Composition.lean:163). - -### B4 acceptance - -- `Guarded.lean` compiles sorry-free; `./scripts/validate.sh` green; existing files byte-identical. -- A minimal `example`: a 1-message guarded verifier (`check := fun s tr => decide (tr 0 = s)`, - over `⟨!v[.P_to_V], !v[Nat]⟩`) `▷ᵍ`-composed with a pure identity package, its CWSS certificate - obtained via the no-challenge bridge + B4.3. -- File-header note: oracle-level composition deferred (G4); `docs/wiki/repo-map.md` updated. - ---- - -## 3. Milestone F2 — Eq. (20) → `R^lin` adapter (~3–4.5 d, incl. F2.0) - -**Goal.** A zero-round `ReduceClaim` package `rlinPackage` with -`relIn = relOutE (QuadEval, escape-threaded)` and `relOut = relRlinE`, `▷`-appended onto the -(escape-threaded) finished chain. New directory -`ArkLib/Commitments/Functional/Hachi/LinSumcheck/` (F2, F4, and later F5–F9 live here). - -### F2.0 Escape threading (1–1.5 d) — `LinSumcheck/Escape.lean` - -Per G1. Deliverables: - -1. `Set.withEscape` (+ 3 simp lemmas: `mem_withEscape_inl/inr`, `withEscape_language`), placed - in the CWSS folder (it is protocol-agnostic): new small file - `CoordinateWiseSpecialSoundness/Escape.lean`, or the top of `LinSumcheck/Escape.lean` if - maintainers prefer zero framework surface — decide at PR time, default the former. -2. Threaded ReduceClaim: **no new framework lemma** — instantiate the existing - `ReduceClaim.verifier_coordinateWiseSpecialSound` - ([ReduceClaim.lean:186](ArkLib/ProofSystem/Component/ReduceClaim.lean#L186)) at witness types - `WitIn ⊕ E` / `WitOut ⊕ E` with `mapWitInv' := Sum.map (mapWitInv s) id` and the case-split - `hRel`. -3. Threaded QuadEval, in `LinSumcheck/Escape.lean` (all *new* declarations): - -```lean -def relInE (esc : Set E) := (relIn Φ base βSq γ κ).withEscape esc -- witness: QuadEvalWitness ⊕ E -def relOutE (esc : Set E) := (relOut Φ base ω γ).withEscape esc -- witness: QuadEvalResponse ⊕ E - -noncomputable def buildWitnessE … : -- branch responses now `QuadEvalResponse ⊕ E` - (Fin (2^r + 1) → QuadEvalResponse … ⊕ E) → … → QuadEvalWitness … ⊕ E --- if ∃ j, resp j = .inr e (pick least j): output .inr e; else delegate to buildWitness - -theorem buildWitnessE_mem_relInE … -- hmk: escape branch = pass-through (relOutE gives e ∈ esc); - -- all-inl branch = `buildWitness_mem_relIn` verbatim -theorem quadEval_coordinateWiseSpecialSound_withEscape … -- via coordinateWiseSpecialSound_of_mkWitness -def quadEvalPackageE … : CWSSPackage … ; def bridgePackageE … ; def evalChainE := bridgePackageE ▷ quadEvalPackageE -``` - -*Proof plan for the hmk:* case-split on `∃ j, (resp j).isRight`. Escape case: the chosen -branch's `relOutE`-membership is exactly `e ∈ esc`, and `relInE`'s `.inr` case is the same -`e ∈ esc` — done. All-`inl` case: strip the `Sum.inl`s and apply `buildWitness_mem_relIn` -unchanged. `Nonempty (QuadEvalWitness … ⊕ E)` from the existing `Nonempty` instance via `.inl`. - -*Faithfulness note:* `relInE/relOutE` at `E := Empty` are equivalent to `relIn/relOut` — state -this as two one-line lemmas so nothing is lost. - -### F2.1 Block-vector/matrix helpers (0.5–1 d) — extend `ArkLib/Data/Lattices/Vectors.lean` - -All generic over `[NonUnitalNonAssocSemiring P]` (or whatever `dot` currently assumes): - -```lean -def PolyVec.finAppend (u : PolyVec P a) (v : PolyVec P b) : PolyVec P (a + b) := Fin.append u v -def PolyMatrix.stackRows (M₁ : PolyMatrix P n₁ c) (M₂ : PolyMatrix P n₂ c) : PolyMatrix P (n₁+n₂) c - -- Fin.addCases on the row index -def PolyMatrix.pasteCols (M₁ : PolyMatrix P n c₁) (M₂ : PolyMatrix P n c₂) : PolyMatrix P n (c₁+c₂) -def vecMatMul (u : PolyVec P n) (M : PolyMatrix P n c) : PolyVec P c -- row-vector · matrix - -theorem dot_finAppend : dot (finAppend u₁ u₂) (finAppend v₁ v₂) = dot u₁ v₁ + dot u₂ v₂ -theorem matVecMul_stackRows : (stackRows M₁ M₂) *ᵥ v = finAppend (M₁ *ᵥ v) (M₂ *ᵥ v) -theorem matVecMul_pasteCols : (pasteCols M₁ M₂) *ᵥ (finAppend v₁ v₂) = M₁ *ᵥ v₁ + M₂ *ᵥ v₂ -theorem dot_matVecMul : dot u (M *ᵥ v) = dot (vecMatMul u M) v -- splitForm associativity -``` - -`dot_finAppend` reduces via `dot_eq_sum` (Vectors.lean:112) + `Fin.sum_univ_add`. Also the norm -splitter in `NormBounds/Basic.lean`: - -```lean -theorem vecLInftyNorm_finAppend : - vecLInftyNorm Φ (finAppend u v) = max (vecLInftyNorm Φ u) (vecLInftyNorm Φ v) -``` - -and two rewriting lemmas in `QuadEval/Gadgets.lean`'s namespace (new file -`LinSumcheck/Rows.lean` if maintainers prefer not to touch Gadgets.lean): - -```lean -theorem tensorG1_eq_dot_vecMatMul : tensorG1 Φ base δ c x = dot (vecMatMul c (gadgetMatrix …)) x -theorem tensorG_eq_matVecMul_flattenBlocks : - tensorG Φ base k δ c x = (tensorGMatrix Φ base k δ c) *ᵥ PolyVec.flattenBlocks x - -- tensorGMatrix := the k × (blocks·k·δ) block-row [c₁·G | … | c_{2^r}·G], defined via finProdFinEquiv -``` - -**Convention pin (v2's A1/R4, scoped down):** one `example` block with `decide` fixing the -`Fin.addCases` orientation of `stackRows`/`finAppend` and the `finProdFinEquiv` block order of -`flattenBlocks` at a `2×2` toy instance. Do this *first*; never resolve an index cast mid-proof. - -### F2.2 `RlinStatement`, `relRlin(E)`, the adapter (1.5–2 d) — `LinSumcheck/Rlin.lean` - -Column layout of the stacked witness `ζ := ŵ ++ flatten t̂ ++ ẑ`, row layout c1–c5: - -``` -μ := (2^r · messageDigits) + (2^r · (innerRows · innerDigits)) + ((2^m · messageDigits) · zDigits) -n := dRows + (outerRows + (1 + (1 + innerRows))) -- fix associativity once, in this order - - ŵ flatten t̂ ẑ rhs -c1 [ D | 0 | 0 ] = v -c2 [ 0 | B | 0 ] = u -c3 [ (bᵀG_{2^r,δ}) row | 0 | 0 ] = y -c4 [ (cᵀ⊗G₁) row | 0 | −(aᵀG_{2^m}J) ] = 0 -c5 [ 0 | tensorGMatrix c | −(A·J) ] = 0 -``` - -```lean -structure RlinStatement (Φ) (n μ : ℕ) where - M : PolyMatrix (Rq Φ) n μ - yvec : PolyVec (Rq Φ) n - bound : ℕ - -def relRlin : Set (RlinStatement Φ n μ × PolyVec (Rq Φ) μ) := - {p | p.1.M *ᵥ p.2 = p.1.yvec ∧ vecLInftyNorm Φ p.2 ≤ p.1.bound} -def relRlinE (esc : Set E) := relRlin.withEscape esc - -def rlinStmt (X : QuadEvalStatement … × CarrierCom Φ dRows × (Fin (2^r) → ShortChallenge Φ ω)) : - RlinStatement Φ n μ -- assemble via stackRows/pasteCols; bound := γ -def unstack : PolyVec (Rq Φ) μ → QuadEvalResponse … -- Fin.addCases splits + finProdFinEquiv un-flatten -``` - -Key lemma (state as an **iff** — the `→` direction is F2's pull-back, the `←` direction is the -honest-prover side needed later): - -```lean -theorem mem_relRlin_iff_mem_relOut : - (rlinStmt X, ζ) ∈ relRlin ↔ (X, unstack ζ) ∈ relOut Φ base ω γ -``` - -*Proof plan:* `matVecMul_stackRows` + `matVecMul_pasteCols` split `Mζ = yvec` into five -`finAppend`-component equations; c3/c4 via `dot_matVecMul`/`tensorG1_eq_dot_vecMatMul`; c5 via -`tensorG_eq_matVecMul_flattenBlocks` + `matVecMul_matMul` (for `A·J`); move `−` blocks across -(`sub_eq_zero`); the norm conjunct by `vecLInftyNorm_finAppend` (`max ≤ γ ↔` three `≤ γ`); -`unstack ∘ stack = id` component lemmas from the F2.1 convention pins. This is pure index -bookkeeping — the budgeted risk item (R-F2 below). - -Package and composition: - -```lean -def rlinPackage … : CWSSPackage init impl - (QuadEvalStatement … × CarrierCom … × (Fin (2^r) → ShortChallenge …)) (QuadEvalResponse … ⊕ E) - (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E) !p[] := - -- ReduceClaim.verifier (mapStmt := rlinStmt); struct := CWSSStructure.ofIsEmpty - -- isCWSS via ReduceClaim.verifier_coordinateWiseSpecialSound, - -- hRel := Sum-case-split: .inl from (mem_relRlin_iff_mem_relOut).mp; .inr pass-through -def evalRlinChain := evalChainE ▷ rlinPackage -theorem evalRlin_coordinateWiseSpecialSound := evalRlinChain.isCWSS -``` - -### F2 acceptance - -- `Escape.lean`, `Rlin.lean` (+ `Rows.lean`, Vectors additions) compile sorry-free; - `evalRlin_coordinateWiseSpecialSound` end-to-end from `relPolyEvalE` to `relRlinE`. -- The `E := Empty` degeneration lemmas compile. -- `decide` convention examples compile; validate.sh green; repo-map entry for `LinSumcheck/`. - ---- - -## 4. Milestone F3 — quotient-lift algebra (~2–3 d) - -**Goal.** The generic `Rq ↔ (ZMod q)[X] ↔ F` bridge Lemma 9 consumes. **New file** -`ArkLib/Data/Lattices/CyclotomicRing/QuotientLift.lean` (generic over `R` where possible; no -Hachi imports — this is reusable, LatticeFold-adjacent material). Everything below is stated -against `Polynomial R` via `(a : Rq Φ).1.toPoly`; write the one-line abbreviation -`Rq.rep (a : Rq Φ) : Polynomial R := a.1.toPoly` first and use it throughout. - -### F3.1 Scalar quotient-witness lemma (0.5–1 d) - -```lean --- d := Φ.φ.toPoly.natDegree (= 2^α for powTwoCyclotomic); hypothesis hd : 2 ≤ d -theorem exists_quotient_witness_of_quotient_eq - (hS : S.natDegree ≤ 2*d - 2) (hy : y.natDegree < d) - (h : Ideal.Quotient.mk Φ.modIdeal S = Ideal.Quotient.mk Φ.modIdeal y) : - ∃ ρ : Polynomial R, ρ.natDegree ≤ d - 2 ∧ S = y + Φ.φ.toPoly * ρ - -theorem quotient_eq_of_eq_add_mul -- the trivial converse: apply mk, mk φ = 0 -``` - -*Proof plan:* `Ideal.Quotient.eq` + `Ideal.mem_span_singleton` give `Φ.φ.toPoly ∣ (S − y)` -(exactly the step inside `Rq.toQuotient_injective`, Rq.lean:107-121 — imitate, don't reuse, its -proof body); set `ρ := (S − y) /ₘ Φ.φ.toPoly`; the identity from `Polynomial.modByMonic_add_div` -+ `(Polynomial.modByMonic_eq_zero_iff_dvd hmonic).mpr`; the degree bound from -`Polynomial.natDegree_divByMonic` and `natDegree (S − y) ≤ 2d − 2` (max of `hS`, `hy`). -Monicity: the `IsCyclotomic` field. Mind ℕ-subtraction: `hd : 2 ≤ d` keeps `d - 2`, `2*d - 2` -well-behaved; add the `powTwoCyclotomic` corollary with `hα : 1 ≤ α` discharging `hd`. - -### F3.2 Row form over `Rq` (0.5 d) - -```lean -theorem Rq.dot_eq_iff_exists_quotient (Mrow z : PolyVec (Rq Φ) μ) (y : Rq Φ) : - dot Mrow z = y ↔ - ∃ ρ : Polynomial R, ρ.natDegree ≤ d - 2 ∧ - (∑ j, (Mrow j).rep * (z j).rep) = y.rep + Φ.φ.toPoly * ρ -``` - -*Proof plan:* `dot_eq_sum`; equality in `Rq` ↔ equality of `equivQuotient` images -(`RingEquiv.injective`) ↔ `mk (∑ reps·reps) = mk y.rep` (push `mk`/`toPoly` through sum and -product: `map_sum`, `map_mul`, `quotientHom_apply`); then F3.1 with -`hS := natDegree_sum_le + natDegree_mul_le + natDegree_val_toPoly_lt` (each `rep` has -`natDegree < d`) and `hy := natDegree_val_toPoly_lt`. Matrix corollary -`Rq.matVecMul_eq_iff_exists_quotient` (row-indexed `ρ : Fin n → Polynomial R`) by -`funext`-style row aggregation + `Classical.choice`/`Finset` packaging. - -### F3.3 Evaluation and interpolation (1 d) - -```lean -variable {F : Type} [Field F] (φF : ZMod q →+* F) -- injective: RingHom.injective - -abbrev evalAt (a : F) : Polynomial (ZMod q) →+* F := Polynomial.eval₂RingHom φF a - --- completeness direction: ring-hom push-through, `map_sum/map_mul/map_add` -theorem evalAt_row_eq_of_lift (h : S = y + φ * ρ) (a : F) : - evalAt φF a S = evalAt φF a y + evalAt φF a φ * evalAt φF a ρ - --- soundness kernel (the Lemma 9 engine): -theorem lift_eq_of_eval_eq_at_distinct - (hdeg : (S - y - φ * ρ).natDegree < N) (A : Fin N ↪ F) - (h : ∀ i, evalAt φF (A i) S = evalAt φF (A i) y + evalAt φF (A i) φ * evalAt φF (A i) ρ) : - S = y + φ * ρ -``` - -*Proof plan:* let `defect := Polynomial.map φF (S − y − φ*ρ)`; `eval₂ = eval ∘ map` -(`Polynomial.eval₂_eq_eval_map`), so `h` says `defect.eval (A i) = 0`; -`natDegree defect ≤ natDegree (S − y − φρ) < N` (`Polynomial.natDegree_map_le`); Mathlib's -`Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero` (Roots.lean:690) with the embedding -`A` kills `defect`; `Polynomial.map_injective φF (RingHom.injective φF)` transfers `= 0` back; -`sub_eq_zero`. Degree arithmetic for the intended use: `S` at `≤ 2d−2`, `φρ` at `≤ d + (d−2)`, -so `hdeg` holds with `N := 2*d` — record this as the packaged corollary: - -```lean -theorem Rq.dot_eq_of_eval_rows_at_distinct -- 2d distinct α's + per-α row equations - (hρdeg : ρ.natDegree ≤ d - 2) … : dot Mrow z = y -- composes F3.3 + F3.2 (←) -``` - -### F3.4 (thin, optional — may slide to F5) digit decomposition of ρ (0.5 d) - -`ρ = ∑ u, (b^u : ZMod q) • ρdig u` with per-digit coefficient bounds, as a `Polynomial`-level -wrapper over `zmodDigitDecomposition` (Gadget/Basic.lean:113) applied coefficient-wise -(`Polynomial.ofFinsupp`/`∑ k, C (digit …) * X^k` over `Finset.range d`). Only F5's `w̃`-table -needs it; F4 carries `ρ` whole. Implement only if time permits inside F3's budget. - -### F3 acceptance - -- `QuotientLift.lean` sorry-free, no Hachi imports, validate.sh green. -- A `powTwoCyclotomic`-instantiated `example` at `q = 5, α = 1` (degree-2 ring) checking F3.1's - statement shape by `decide`/`native_decide` on a concrete instance, plus one `example` - instantiating F3.3 at `F := ZMod 5`, `φF := RingHom.id` (sanity: the abstraction admits the - base field itself). - ---- - -## 5. Milestone F4 — HMZ25 lift, Fig. 4 / Lemma 9 (~4–5.5 d) - -**Goal.** The two-round reduction (`t = Com(w̃)` then `α ← F`), CWSS at `k = 2d`, output -relation `relLiftE`, packaged and `▷`-appended onto `evalRlinChain`. - -### F4.1 Scalar single-round CWSS lemma (1.5–2 d) — generic framework - -**New file** `ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean`, -the `(ℓ = 1, k)` twin of `SingleRound.lean` (which stays untouched at `(2^r, 2)`): - -```lean -@[reducible] def pSpecScalar (Msg C : Type) : ProtocolSpec 2 := ⟨!v[.P_to_V, .V_to_P], !v[Msg, C]⟩ - -@[reducible] def scalarStructure (k : ℕ) (hk : 2 ≤ k) : CWSSStructure (pSpecScalar Msg C) := - CWSSStructure.ofSpecialSound (fun _ => k) (fun _ => hk) -- arity k - -theorem coordinateWiseSpecialSound_of_mkWitness_scalar - (V : Verifier oSpec StmtIn (StmtIn × Msg × C) (pSpecScalar Msg C)) - (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩)) - (relIn : Set (StmtIn × WitIn)) (relOut : Set ((StmtIn × Msg × C) × WitOut)) [Nonempty WitOut] - (mkWitness : StmtIn → Msg → (Fin k → C) → (Fin k → WitOut) → WitIn) - (hmk : ∀ s v (fam : Fin k → C) resp, - (∀ j, ((s, v, fam j), resp j) ∈ relOut) → Function.Injective fam → - (s, mkWitness s v fam resp) ∈ relIn) : - V.coordinateWiseSpecialSound init impl (scalarStructure k hk) relIn relOut -``` - -*Proof plan:* transplant SingleRound.lean:345-410. The tree at arity `k` has one message node -and one challenge node with `k` children; `readPre`/`readChallenges`/`tree_shape` re-derive with -`Fin.cast` along `scalarStructure`'s `arity = 1*(k−1)+1 = k` (one `Nat` simp lemma). The star -machinery collapses: `nodeOk` at `ℓ = 1` is injectivity by `isSpecialSoundFamily_one_iff_injective` -(Basic.lean:111) composed with the `Equiv.funUnique` decomposition — so `hmk` receives plain -`Function.Injective fam` instead of `StarAt`. Branch acceptance → `relOut`-membership via the -same `branch_relOut_language` pattern (uses `mem_of_pure_accepting`). This lemma is also the -substrate for F6/F7's rounds later — build it clean. - -### F4.2 Witness, commitment abstraction, relations (1–1.5 d) — `LinSumcheck/Lift.lean` - -```lean -/-- Eq. (21)'s committed data in polynomial form: the R^lin witness plus the per-row -quotient polynomials with their structural degree bound. (Digit form arrives in F5.) -/ -structure LiftedWitness (Φ) (μ n : ℕ) where - z : PolyVec (Rq Φ) μ - ρ : Fin n → Polynomial (ZMod q) - hρ : ∀ i, (ρ i).natDegree ≤ d - 2 - -/-- Abstract binding commitment for `w̃` (G2: instantiated later; Lemma 9 needs only binding). -/ -structure LiftCom (W E : Type) where - TCom : Type - com : W → TCom - esc : Set E - escOfCollision : W → W → E - collision_mem : ∀ w w', w ≠ w' → com w = com w' → - Short w → Short w' → escOfCollision w w' ∈ esc - -- `Short` = the relLift range predicate below, threaded as a parameter of the structure - -- (weak binding is norm-conditioned — Lemma 7 / `outputToModuleSIS_valid_of_verified` pattern) - -variable (K : LiftCom (LiftedWitness Φ μ n) E) (φF : ZMod q →+* F) - -def LiftStatement := RlinStatement Φ n μ × K.TCom × F -- pass-through shape - -def relLift : Set (LiftStatement × LiftedWitness Φ μ n) := - {p | let ((s, t, a), w) := p - K.com w = t ∧ - (∀ i, evalAt φF a (rowSum s.M w.z i) = - evalAt φF a ((s.yvec i).rep) + evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ i)) ∧ - vecLInftyNorm Φ w.z ≤ s.bound ∧ RhoShort w.ρ} -def relLiftE := relLift.withEscape K.esc -``` - -where `rowSum s.M w.z i := ∑ j, (s.M i j).rep * (w.z j).rep` (definition shared with F3.2) and -`RhoShort` is the coefficient-range predicate on `ρ` (bounded by the digit range; exact constant -pinned here, feeding F5). Decision recorded in the file header: `LiftCom` stays **abstract** -in F4; the concrete inner-outer instantiation (paper §4.5 "commit without re-decomposition") is -a Phase-G/F5 deliverable, and `collision_mem` is exactly the obligation -`outputToModuleSIS_valid_of_verified` will discharge there. - -### F4.3 pSpec, prover, verifier (0.5 d) - -```lean --- pSpec := pSpecScalar K.TCom F ; instances by the ⟨0,h⟩/⟨1,_⟩ matching idiom; --- SampleableType F is a section hypothesis (G5) -def liftVerifier : Verifier oSpec (RlinStatement Φ n μ) (LiftStatement …) (pSpecScalar K.TCom F) where - verify := fun stmt tr => pure (stmt, tr.messages ⟨0,rfl⟩, tr.challenges ⟨1,rfl⟩) -def liftProver (computeW : …) : Prover … -- QuadEval Reduction.lean:265-298 skeleton, honest w̃ -``` - -### F4.4 Extraction — Lemma 9 (1.5–2 d) - -```lean -noncomputable def liftBuildWitness - (s : RlinStatement Φ n μ) (t : K.TCom) (fam : Fin (2*d) → F) - (resp : Fin (2*d) → LiftedWitness Φ μ n ⊕ E) : PolyVec (Rq Φ) μ ⊕ E --- (a) some branch is .inr e → .inr e --- (b) two branches carry w ≠ w' → .inr (K.escOfCollision w w') --- (c) all branches carry the same w → .inl w.z - -theorem liftBuildWitness_mem_relRlinE - (hd : 2 ≤ d) (hresp : ∀ j, ((s, t, fam j), resp j) ∈ relLiftE) (hinj : Function.Injective fam) : - (s, liftBuildWitness …) ∈ relRlinE K.esc -``` - -*Proof plan* (the paper's Lemma 9, case-faithful): -- (a): pass-through, as in F2.0. -- (b): both branches' `relLift` give `K.com w = t = K.com w'` and both `Short`; - `K.collision_mem` puts the escape in `K.esc` — `relRlinE`'s `.inr` case. (This is Remark 2's - weak-binding route.) -- (c): the shared `w` satisfies, for each row `i`, the `evalAt`-equation at all `2d` **distinct** - (injective `fam`) points; `(rowSum − yvec.rep − φ·ρ i).natDegree ≤ 2d − 2 < 2d` from `w.hρ` + - representative degree bounds; `lift_eq_of_eval_eq_at_distinct` (F3.3, `N := 2d`) yields the - `(ZMod q)[X]`-identity per row; `Rq.dot_eq_iff_exists_quotient` (F3.2, `←` direction — or - directly the packaged `Rq.dot_eq_of_eval_rows_at_distinct`) yields `s.M *ᵥ w.z = s.yvec`; - the norm conjunct of `relLift` is already `vecLInftyNorm w.z ≤ s.bound`. Both `relRlin` - conjuncts hold — `.inl` case. - -Then: - -```lean -theorem lift_coordinateWiseSpecialSound … : - liftVerifier.coordinateWiseSpecialSound init impl (scalarStructure (2*d) (by omega)) - (relRlinE K.esc) (relLiftE …) - -- coordinateWiseSpecialSound_of_mkWitness_scalar with mkWitness := liftBuildWitness, - -- hmk := liftBuildWitness_mem_relRlinE -``` - -Note `2 ≤ 2*d` from `hd`; no field-size hypothesis is needed for CWSS (an injective -`Fin (2*d) ↪ F` family is the *tree's* problem — knowledge error, out of scope, only needs -`2d ≤ |F|` which the eventual instantiation satisfies). - -### F4.5 Package and chain (0.5 d) - -```lean -def liftPackage … : CWSSPackage init impl (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E) - (LiftStatement …) (LiftedWitness Φ μ n ⊕ E) (pSpecScalar K.TCom F) -def evalLiftChain := evalRlinChain ▷ liftPackage -- seam relRlinE, by rfl (G6) -theorem evalLift_coordinateWiseSpecialSound := evalLiftChain.isCWSS -``` - -`evalLift_coordinateWiseSpecialSound` is this plan's end-to-end deliverable: CWSS from -`relPolyEvalE` all the way to `relLiftE` — i.e. paper Figures 3 + 4 (Lemmas 8 + 9) composed, -escape-threaded, ready for F5/F6 to consume `relLiftE` as their `relIn`. - -### F4 acceptance - -- `ScalarRound.lean` and `Lift.lean` sorry-free; `evalLift_coordinateWiseSpecialSound` compiles; - validate.sh green (`--lint` before PR). -- Prover skeleton `liftProver` compiles (completeness stays a skeleton, D12). -- An `example` instantiating `F := ZMod q`, `φF := RingHom.id` and a trivial `LiftCom` - (`TCom := LiftedWitness …`, `com := id`, `esc := ∅` unreachable since `com` injective) — - proving the abstraction is inhabitable without any Phase-B–E material. -- File-header faithfulness notes: (i) Fig. 4 sends `(z, r)` in the clear — here they are the - never-sent output witness (v2's D6, QuadEval precedent); (ii) `Com` abstract pending the - §4.5 inner-outer instantiation. - ---- - -## 6. The sub-chain after these four milestones - -| # | Stage | pSpec | Verifier | CWSS structure | relIn → relOut | Status after this plan | -|---|---|---|---|---|---|---| -| 1 | bridge (threaded) | `!p[]` | pure | `ofIsEmpty` | `relPolyEvalE → relInE` | F2.0 | -| 2 | QuadEval (threaded) | msg + vector chal | pure | `foldStructure` | `relInE → relOutE` | F2.0 | -| 3 | R^lin adapter | `!p[]` | pure | `ofIsEmpty` | `relOutE → relRlinE` | F2 | -| 4 | HMZ25 lift | `t` + scalar `α` | pure | `ofSpecialSound k = 2d` | `relRlinE → relLiftE` | F4 | - -All four verifiers are pure — **B4 is not consumed inside this plan's chain**; it is the -groundwork the *next* milestones (F7 sumcheck loop, F8 final eval, G3 handoff) compose with, -built first per the agreed ordering while its design is fresh from the framework recon. -Escape budget `E` is a single parameter threaded through rows 1–4, instantiated at `K.esc`. - -## 7. Effort summary and sequencing - -| Milestone | Estimate | Hard prerequisites | -|---|---|---| -| B4 | 2.5–3.5 d | maintainer ping (R3) | -| F2 (incl. F2.0) | 3–4.5 d | none | -| F3 | 2–3 d | none (parallelizable with F2 if desired; sequential per instruction) | -| F4 | 4–5.5 d | F2 (seam), F3 (F3.2/F3.3), F4.1 | -| **Total** | **~12–16.5 d** | | - -## 8. Risks - -- **R-B4-a (VCVio probability plumbing).** `failure_not_accepting` needs the right - `simulateQ`/`OptionT` simp set; the pure proofs (Composition.lean:333-362, SeqCompose.lean:70-82) - are the map. Budgeted inside B4.1; if it fights back, extract the acceptance-probability facts - as standalone `OracleComp` lemmas and ask maintainers where they belong. -- **R-B4-b (framework churn).** Same mitigation as v2's R3: everything in new files, existing - theorems byte-identical, PR flagged as security-infrastructure. -- **R-F2-a (index bookkeeping).** The block-matrix equivalence is the milestone's real cost. - Mitigation: F2.1's `decide` convention pins *first*; every cast through `Fin.addCases`/ - `finProdFinEquiv` gets its own simp lemma; treat any mid-proof `Fin.cast` as a red flag (v2 R4). -- **R-F2-b (escape-threading reception).** The `withEscape` design changes no existing - declaration but *does* add a parallel chain (`evalChainE`). Alternative if maintainers object: - make `E` an argument of the original relations with `E := Empty` as the old theorems — more - invasive; keep as fallback only. -- **R-F3-a (CPolynomial ↔ Polynomial friction).** All identities are stated over - `Polynomial R` via `.rep`; the only CompPoly surface is `toPoly` of ring operations - (`ringEquiv` handles it). Half-day slack budgeted. -- **R-F4-a (arity-`k` re-derivation).** `ScalarRound.lean` re-derives SingleRound's tree readers - at arity `k`; the `(2^r, 2)` proofs are the template but `Fin.cast` normalization along - `1*(k−1)+1 = k` needs one careful simp lemma. Budgeted in F4.1. -- **R-F4-b (commitment abstraction).** If F5 later needs `TCom` data F4 hid (e.g. homomorphic - structure for the avoid-re-decomposition trick), `LiftCom` grows fields — additive, not - breaking. The abstract-now/instantiate-later split is deliberate (G2). - -## 9. Documentation obligations (same PRs as the code — CLAUDE.md guardrail) - -- `docs/wiki/repo-map.md`: `CoordinateWiseSpecialSoundness/{Guarded,Escape,ScalarRound}.lean`, - `Hachi/LinSumcheck/`, `CyclotomicRing/QuotientLift.lean`. -- `docs/kb/papers/NOZ26.md`: mark Lemma 9 formalized; record the escape-threading design and - the `LiftCom` abstraction decision. -- Blueprint: `blueprint/src/proof_systems/hachi_sumcheck.tex` stub covering Fig. 4 / Lemma 9 - (F6–F9 will extend it). -- Update `HACHI_RING_SWITCHING_PLAN.md` §4's F2/F3/F4 entries with a pointer to this file - (anchors there are stale; this file is authoritative for the four milestones). diff --git a/ROADMAP.md b/ROADMAP.md index e8b3308df2..fe8f035e61 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -43,12 +43,13 @@ It would be nice to use the theories in ArkLib to prove foundational results suc The below are content for an older version of the roadmap. Some of these contents are being actively worked on (especially computable polynomials). - - [ ] [Computable Univariate Polynomials](ArkLib/Data/UniPoly) + - [ ] Computable Univariate Polynomials — now developed upstream in the `CompPoly` project; local + additions live in [`ArkLib/ToCompPoly/Univariate/`](ArkLib/ToCompPoly/Univariate) - [x] Define `UniPoly` as the type of univariate polynomials with computable representations (interally as an `Array` of coefficients). Define operations on `UniPoly` as operations on the underlying `Array` of coefficients. - [x] Define an equivalence relation on `UniPoly` that says two `UniPoly`s are equivalent iff they are equal up to zero-padding. Show that this is an equivalence relation. - [ ] Show that operations on `UniPoly` descends to the quotient (i.e. are the same up to zero-padding). Show that the quotient is isomorphic as semirings to `Polynomial` in `Mathlib`. Show that the same functions (e.g. `eval`) on `UniPoly` are the same as those of `Polynomial`. - [ ] For more efficient evaluation, and use in univariate-based SNARKs, define the coefficient representation of `UniPoly` (on `2`-adic roots of unity), and show conversions between the coefficient and evaluation representations. - - [ ] [Computable Multilinear Polynomials](ArkLib/Data/MlPoly) + - [ ] Computable Multilinear Polynomials — now developed upstream in the `CompPoly` project - [ ] Define `MlPoly` as the type of multilinear polynomials with computable representations (internally as an `Array` of coefficients). Define operations on `MlPoly` as operations on the underlying `Array` of coefficients. - [ ] Define alternative definition of `MlPoly` where the evaluations on the hypercube are stored instead of the coefficients. Define conversions between the two definitions, and show that they commute with basic operations. - [ ] Will need to expand `Mathlib`'s support for indexing by bits (i.e. further develop `BitVec`). @@ -63,9 +64,9 @@ The below are content for an older version of the roadmap. Some of these content - [ ] Define and develop basic results on linear codes. - [ ] Define basic codes such as Reed-Solomon. - [ ] Prove proximity gap and interleaved distance results (up to one-third of the unique decoding distance). - - [ ] [Binary Tower Fields](ArkLib/Data/BinaryTowerField) + - [ ] Binary Tower Fields — not yet formalized - [ ] Define iterated quadratic extensions of the binary field (Wiedermann construction), and prove that the resulting ring is a field. - [ ] Define efficient representation of elements in a binary tower field (using `BitVec`), efficient operations on them (see Binius paper), and prove that the resulting structure is a field isomorphic to the definition above. - - [ ] [Large Scalar Fields used in Curves](ArkLib/Data/ScalarPrimeField) + - [ ] Large Scalar Fields used in Curves — not yet formalized - [ ] Low-priority for now. - [ ] Development on this should be done over at [`FFaCiL`](https://github.com/argumentcomputer/FFaCiL.lean/tree/main). diff --git a/blueprint/src/references.bib b/blueprint/src/references.bib index f56087f560..00a2dc296a 100644 --- a/blueprint/src/references.bib +++ b/blueprint/src/references.bib @@ -502,3 +502,20 @@ @inproceedings{NS24 year = {2024}, publisher = {Springer} } + +@article{HMZ25, + title = {Sublinear Proofs over Polynomial Rings}, + author = {Huang, Mi-Ying Miryam and Mao, Xinyu and Zhang, Jiapeng}, + journal = {Cryptology ePrint Archive}, + year = {2025}, + url = {https://eprint.iacr.org/2025/199} +} + +@article{NOZ26, + title = {Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over + Extension Fields}, + author = {Nguyen, Ngoc Khanh and O'Rourke, George and Zhang, Jiapeng}, + journal = {Cryptology ePrint Archive}, + year = {2026}, + url = {https://eprint.iacr.org/2026/156} +} diff --git a/docs/kb/README.md b/docs/kb/README.md index 8d934c4f2f..995229d3a0 100644 --- a/docs/kb/README.md +++ b/docs/kb/README.md @@ -35,7 +35,8 @@ Useful commands: python3 ./scripts/kb/sync_from_bib.py python3 ./scripts/kb/extract_lean_citations.py python3 ./scripts/kb/lint.py -python3 ./scripts/kb/review_context.py --files ArkLib/ProofSystem/Whir/ProximityGen.lean --format review +python3 ./scripts/kb/review_context.py \ + --files ArkLib/Data/CodingTheory/ProximityGap/Basic.lean --format review ``` ## When To Touch The KB @@ -192,11 +193,11 @@ will run `python3 ./scripts/kb/regenerate.py` after merge and open a generated-f ### Example: use the KB during review -If a PR changes `ArkLib/ProofSystem/Whir/ProximityGen.lean`, run: +If a PR changes `ArkLib/Data/CodingTheory/ProximityGap/Basic.lean`, run: ```bash python3 ./scripts/kb/review_context.py \ - --files ArkLib/ProofSystem/Whir/ProximityGen.lean \ + --files ArkLib/Data/CodingTheory/ProximityGap/Basic.lean \ --format review ``` diff --git a/docs/kb/concepts/ring-switching.md b/docs/kb/concepts/ring-switching.md index 62cf587f25..31ac36a4b3 100644 --- a/docs/kb/concepts/ring-switching.md +++ b/docs/kb/concepts/ring-switching.md @@ -68,7 +68,7 @@ and soundness requires `[IsDomain L]` (Schwartz–Zippel). ## Notes - The protocol skeleton and security *statements* are generic and final; the leaf - completeness/soundness *proofs* are tracked as follow-up (see `M5_BOOTSTRAP.md` at repo root). + completeness/soundness *proofs* remain `sorry`ed follow-up work in the files listed above. - Soundness reuse across instances is weaker than data-layer reuse: the `[IsDomain L]` theorems fit field instances (Binius) but not non-domain rings (Hachi `R_q`), whose soundness is a sibling theorem with a different error. diff --git a/docs/kb/index.md b/docs/kb/index.md index 9d0df3d662..7bf87e98f4 100644 --- a/docs/kb/index.md +++ b/docs/kb/index.md @@ -12,7 +12,7 @@ This is the main catalog for ArkLib's knowledge base. ## Paper Pages - [`papers/ACFY24.md`](papers/ACFY24.md) - WHIR ePrint paper and its ArkLib touchpoints in - `ReedSolomon` and `ProofSystem/Whir`. + `ReedSolomon`, `ListDecodability`, and `ProximityGap`. - [`papers/ACFY24stir.md`](papers/ACFY24stir.md) - STIR paper page for the active `ProofSystem/Stir` development. - [`papers/BCIKS20.md`](papers/BCIKS20.md) - proximity gaps for Reed-Solomon codes and the main diff --git a/docs/kb/papers/ACFY24.md b/docs/kb/papers/ACFY24.md index b8ee8f2a6f..3b8c02ad93 100644 --- a/docs/kb/papers/ACFY24.md +++ b/docs/kb/papers/ACFY24.md @@ -11,28 +11,33 @@ related_concepts: - reed-solomon-proximity related_modules: - ArkLib/Data/CodingTheory/ReedSolomon.lean - - ArkLib/ProofSystem/Whir + - ArkLib/Data/CodingTheory/ListDecodability.lean + - ArkLib/Data/CodingTheory/ProximityGap/Folding.lean --- # ACFY24 ## At A Glance -`ACFY24` is the ePrint reference for WHIR and is the main paper currently cited by ArkLib's WHIR -development. -It influences both coding-theory definitions in `ReedSolomon.lean` and protocol-level files under -`ProofSystem/Whir/`. +`ACFY24` is the ePrint reference for WHIR. The WHIR protocol files were removed from +`ArkLib/ProofSystem/`; what the paper still drives lives in the coding-theory layer — Reed-Solomon +definitions, list-decodability notions, and the folding/proximity-gap development. ## What ArkLib Uses From This Paper -- WHIR-specific Reed-Solomon definitions currently introduced in +- WHIR-specific Reed-Solomon definitions in [`ArkLib/Data/CodingTheory/ReedSolomon.lean`](../../../ArkLib/Data/CodingTheory/ReedSolomon.lean). -- Protocol-level soundness and folding interfaces for the current WHIR formalization. +- The list-decoding notion `Λ (C, y, r)` in + [`ListDecodability.lean`](../../../ArkLib/Data/CodingTheory/ListDecodability.lean). +- Folding and mutual-correlated-agreement material under + [`ArkLib/Data/CodingTheory/ProximityGap/`](../../../ArkLib/Data/CodingTheory/ProximityGap/). ## Main ArkLib Touchpoints - [`ArkLib/Data/CodingTheory/ReedSolomon.lean`](../../../ArkLib/Data/CodingTheory/ReedSolomon.lean) cites the paper directly for WHIR-specific definitions. +- [`ProximityGap/Folding.lean`](../../../ArkLib/Data/CodingTheory/ProximityGap/Folding.lean) + carries the folding lemmas the WHIR analysis needs. ## Version Notes @@ -45,8 +50,8 @@ It influences both coding-theory definitions in `ReedSolomon.lean` and protocol- - ArkLib frequently lifts paper notions into more reusable abstractions than the paper's original presentation. -- Some WHIR-related interfaces currently live at the protocol layer and may later move downward - into more general coding-theory abstractions. +- The WHIR-related interfaces that used to live at the protocol layer have been folded into the + more general coding-theory abstractions under `ArkLib/Data/CodingTheory/`. ## Open Formalization Gaps diff --git a/docs/kb/papers/HMZ25.md b/docs/kb/papers/HMZ25.md new file mode 100644 index 0000000000..09ad42448b --- /dev/null +++ b/docs/kb/papers/HMZ25.md @@ -0,0 +1,62 @@ +--- +kind: paper +bibkey: HMZ25 +title: "Sublinear Proofs over Polynomial Rings" +year: "2025" +bib_source: blueprint/src/references.bib +canonical_url: https://eprint.iacr.org/2025/199 +source_metadata: ../sources/HMZ25/metadata.yml +status: seeded +related_modules: + - ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean + - ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean + - ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean +--- + +# HMZ25 + +## At A Glance + +`HMZ25` (Huang–Mao–Zhang, ePrint 2025/199) builds sublinear-size proof systems for rank-one +constraint satisfaction over polynomial rings `Z_Q[X]/(X^N + 1)`. ArkLib uses one specific idea +from it: the **ring-switching lift**, which Hachi ([`NOZ26`](NOZ26.md), §4.3, Figure 4 / Lemma 9) +adopts to move a linear claim over the cyclotomic ring `Rq` into an extension field where the +sumcheck runs. + +## What ArkLib Uses From This Paper + +The lift itself: `M z = y` over `Rq = Zq[X]/(X^d + 1)` holds **iff** there is a quotient `r` with +`M z = y + (X^d + 1)·r` over `Zq[X]`. The prover commits to the lifted witness `(z, r)`, the +verifier samples a random evaluation point `X := α` in an extension field `F ⊇ Zq`, and both +sides evaluate the lifted rows at `α`. This "switches" the `Rq`-statement into `F`. + +## Main ArkLib Touchpoints + +- [`ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean`](../../../ArkLib/Commitments/Functional/Hachi/RingSwitch/Basic.lean) + — umbrella module; overview of the lift and the folder structure. +- [`ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean`](../../../ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean) + — the two-round lift reduction and its CWSS skeleton (Hachi Figure 4 / Lemma 9). +- [`ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean`](../../../ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean) + — the zero-round entry adapter reshaping Hachi's Eq. (20) output into the unstructured linear + relation `R^lin` the lift addresses. + +## Version Notes + +Cited via the ePrint version (2025/199). ArkLib follows Hachi's [`NOZ26`](NOZ26.md) presentation +of the lift rather than the original Ring-R1CS setting. + +## Known Divergences From ArkLib + +ArkLib formalizes only the lift step as used inside Hachi's opening argument, not the paper's +full Ring-R1CS proof system. + +## Open Formalization Gaps + +The interpolation-based extraction for Hachi's Lemma 9 (`lift_coordinateWiseSpecialSoundWith`) is +currently a `sorry`-level skeleton; see the module docstrings under +`ArkLib/Commitments/Functional/Hachi/RingSwitch/`. + +## Source Access + +- Source metadata: [`../sources/HMZ25/metadata.yml`](../sources/HMZ25/metadata.yml) +- Public reference: [`blueprint/src/references.bib`](../../../blueprint/src/references.bib) diff --git a/docs/kb/papers/NOZ26.md b/docs/kb/papers/NOZ26.md index dd98e61ab0..6cf91aefd2 100644 --- a/docs/kb/papers/NOZ26.md +++ b/docs/kb/papers/NOZ26.md @@ -10,7 +10,7 @@ status: seeded related_modules: - ArkLib/ProofSystem/RingSwitching/Profile.lean - ArkLib/Data/Lattices/CyclotomicRing/Core/Modulus.lean - - ArkLib/Commitments/Functional/Hachi/Gadget.lean + - ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean - ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean - ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean --- @@ -52,7 +52,7 @@ Ring-switching layer: - [`../../../ArkLib/ProofSystem/RingSwitching/Profile.lean`](../../../ArkLib/ProofSystem/RingSwitching/Profile.lean) - [`ArkLib/Data/Lattices/CyclotomicRing/Core/Modulus.lean`](../../../ArkLib/Data/Lattices/CyclotomicRing/Core/Modulus.lean) — `powTwoCyclotomic`. -- [`ArkLib/Commitments/Functional/Hachi/Gadget.lean`](../../../ArkLib/Commitments/Functional/Hachi/Gadget.lean) +- [`ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean`](../../../ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean) — the gadget matrix and `gadgetDecompose`. - [`ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean`](../../../ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean) — weak binding. diff --git a/docs/kb/papers/NS24.md b/docs/kb/papers/NS24.md index 9c05dbfba5..1fb1aac5e9 100644 --- a/docs/kb/papers/NS24.md +++ b/docs/kb/papers/NS24.md @@ -9,7 +9,7 @@ status: seeded related_modules: - ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean - ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean - - ArkLib/Commitments/Functional/Hachi/Gadget.lean + - ArkLib/Commitments/Functional/Hachi/Gadget/Core.lean --- # NS24 diff --git a/docs/kb/sources/HMZ25/metadata.yml b/docs/kb/sources/HMZ25/metadata.yml new file mode 100644 index 0000000000..16f2d2e8d4 --- /dev/null +++ b/docs/kb/sources/HMZ25/metadata.yml @@ -0,0 +1,5 @@ +bibkey: HMZ25 +source_kind: bibliography-only +canonical_url: https://eprint.iacr.org/2025/199 +committed_artifacts: [] +notes: "Sublinear Proofs over Polynomial Rings (ePrint 2025/199): sublinear proofs for Ring-R1CS over Z_Q[X]/(X^N+1). ArkLib uses its ring-switching lift as adopted by Hachi (NOZ26, Figure 4 / Lemma 9) in ArkLib/Commitments/Functional/Hachi/RingSwitch/." diff --git a/docs/skills/README.md b/docs/skills/README.md index 1cd3ddaaea..ab56076f17 100644 --- a/docs/skills/README.md +++ b/docs/skills/README.md @@ -26,4 +26,5 @@ After using a skill, review whether it should be updated: - [`fix-lean-warnings.md`](fix-lean-warnings.md) - workflow for cleaning Lean 4 linter and style warnings safely and incrementally. - [`make-pr-ready.md`](make-pr-ready.md) - checklist to get a branch PR-ready: follow the - contribution guidelines, fix Lean warnings, regenerate citations, and suggest skill improvements. + contribution guidelines, fix Lean warnings, regenerate citations, clean up references to files + the branch deleted, and suggest skill improvements. diff --git a/docs/skills/make-pr-ready.md b/docs/skills/make-pr-ready.md index d01653c7cf..b790dd3740 100644 --- a/docs/skills/make-pr-ready.md +++ b/docs/skills/make-pr-ready.md @@ -42,6 +42,32 @@ Work through these in order. Do not stop until every item is complete. revert, `git diff --cached --stat` shows huge index-vs-HEAD numbers for `_generated/` — that is the size of the revert, **not** new drift; do not panic. Confirm the post-commit guard view with `git diff --cached --quiet origin/main -- docs/kb/_generated/` (must be clean). +- **Report stray planning/scratch `.md` files — do not stage them, and do not delete them.** A + Markdown plan, design note, or working-notes file (`PLAN.md`, `NOTES.md`, `scratch/*.md`, an + agent handoff/TODO dump) is a working artifact, not repo documentation, and should not land in + the PR. It is also the author's live working state, so this step is **detect-and-report only**: + never `git add`, `git commit`, `git stash`, `git rm`, or delete one. Stashing hides work the + author may still be using; deleting destroys it; and either way the call is theirs. List the + candidates: + + ```bash + git diff --name-only --diff-filter=A origin/main...HEAD -- '*.md' # newly-added, committed + git status --short -- '*.md' # staged / untracked + ``` + + Subtract the curated docs a PR may legitimately add (`docs/kb/papers/`, `docs/kb/sources/`, + `docs/wiki/`, `docs/skills/`, `blueprint/`, and top-level pages like + `README`/`ROADMAP`/`CONTRIBUTING`) — a real `docs/kb/papers/.md` or `docs/wiki/` page stays. + For anything left that reads as a working plan, **warn the user**, naming each file and whether + it is untracked, staged, or already committed, and let them decide. Leave untracked plans + untracked and staged ones staged; just do not carry them into a commit you make on the author's + behalf. Prevention lives upstream of this step: [`prove-milestone.md`](prove-milestone.md) + requires scratch manifests and experiments to be written under `/tmp` rather than as root-level + planning files, so a plan surfacing here means that rule was bypassed — worth saying out loud. + + **Stripping the plan file is only half the job.** Lean docstrings written alongside it almost + always cite it (`see \`PLAN.md\` §3.K`), and those citations become dead the moment the file is + dropped. Step 4 below sweeps for them; do not consider the strip done until that sweep is clean. ### 1. Follow the contribution guidelines @@ -85,7 +111,10 @@ Work through these in order. Do not stop until every item is complete. with Python `len(line)`, never `awk`/`wc -c`. Otherwise treat the **default** `validate.sh` (build + Data warning budget + `check-imports` + `check-docs-integrity` + `kb/lint`) as the real gate. Capture its true exit with `rc=$?` on its own line — a trailing - `… ; echo "EXIT $?"` reports the `echo`'s exit (always 0) and masks a failing validate. + `… ; echo "EXIT $?"` reports the `echo`'s exit (always 0) and masks a failing validate. Piping + has the same trap: `./scripts/validate.sh | tail -40` reports `tail`'s exit and truncates the + failure detail (kb lint errors print near the end). Run it as + `./scripts/validate.sh > validate.log 2>&1` with `rc=$?` on the next line, then grep the log. - The **Data warning budget** fails on any non-`sorry` warning under `ArkLib/Data/`. A toolchain/Mathlib bump commonly introduces **deprecation** warnings (e.g. `X has been deprecated: Use Y instead`) — fix these by switching to the suggested name. @@ -135,6 +164,15 @@ Work through these in order. Do not stop until every item is complete. keys yourself: grep each `[KEY]` used in docstrings against `blueprint/src/references.bib` and add any missing entry (then regenerate). A key can be "present-looking" but actually a different paper — confirm the entry's title/authors match the citation, not just that the key exists. +- Know the `kb/lint.py` severity split: a **paper page whose `bibkey` has no BibTeX entry** is an + *Error* (fails `validate.sh`), while a **cited key with no paper page** is only a *Warning*. + Fix the warning too: `python3 scripts/kb/scaffold_paper.py ` scaffolds + `docs/kb/papers/.md` + `docs/kb/sources//metadata.yml` from the bib entry — then + replace the TODO sections with real content (what the paper is, what ArkLib uses, touchpoint + modules) before staging; a page of TODOs is reviewer bait. +- A validate/kb failure is not necessarily yours: it can be **pre-existing on `main`** (e.g. a kb + paper page merged before its BibTeX entry). Attribute it (`git show origin/main:`), but + fix it in your PR anyway if cheap — it blocks *your* CI regardless of who introduced it. - Also check for **duplicate BibTeX keys**: `grep -oE '^@[a-z]+\{[^,]+' blueprint/src/references.bib | sort | uniq -d`. Neither `kb/lint` nor the sync script flags a key defined twice (the JSON dict silently collapses it), but it is real bib cruft a reviewer will hit. Keep the better-formatted @@ -153,13 +191,111 @@ Work through these in order. Do not stop until every item is complete. `papers/.md` + `sources//` pages. After removing, grep tracked files for any markdown link to the deleted page (`check-docs-integrity.py` fails on a broken link). - If you **moved or renamed** any `.lean` file, regeneration does **not** fix hand-maintained - `docs/kb/papers/*.md` pages (they are scaffolded once, then curated). Grep `docs/**/*.md` for the - old path and update curated links + `related_modules` frontmatter — the default `validate.sh` - `check-docs-integrity.py` step fails on broken links. Running `kb/regenerate.py` after adding a - new cited key also **scaffolds** a new `docs/kb/papers/.md` + `docs/kb/sources//`; - stage those too. + `docs/kb/papers/*.md` pages (they are scaffolded once, then curated) — their curated links and + `related_modules` frontmatter still point at the old path. Step 4 sweeps for these; run it rather + than relying on `check-docs-integrity.py`, which only sees Markdown *links* and never inspects + frontmatter. Running `kb/regenerate.py` after adding a new cited key also **scaffolds** a new + `docs/kb/papers/.md` + `docs/kb/sources//`; stage those too. + +### 4. Clean up references to deleted files + +If this branch deletes, moves, or renames **any** file, sweep the whole library for references to +the old path and fix them. Do this near the end of the pass — step 0's plan-file strip and step 1's +refactors both create deletions. + +**Assume nothing catches this for you.** Coverage is much thinner than it looks: + +- `lake build` catches stale `import` lines — and *only* those. A path named in a docstring, a + comment, or a Markdown file is invisible to it. +- `check-imports.sh` regenerates `ArkLib.lean` from `git ls-files`, so the deletion must be + **staged** (`git rm `, or `git add -A -- ` after an `rm`). A file deleted in the + working tree but still in the index keeps its `import` line and fails the build instead. +- `check-docs-integrity.py` checks **only** inline Markdown links (bracketed text followed by a + parenthesised path), and **only** in `AGENTS.md`, `scripts/README.md`, and `docs/**/*.md` (minus + `_generated/`). Note it resolves links inside backticks too, so do not write a literal + link-shaped example in prose — it will be chased and reported broken. It does **not** see: + bare or backticked paths anywhere, `.lean` docstrings, `blueprint/**`, `.github/**`, or the + top-level `README.md` / `CONTRIBUTING.md` / `ROADMAP.md` / `BACKGROUND.md`. +- `kb/lint.py` does **not** validate the `related_modules:` paths in `docs/kb/papers/*.md` + frontmatter. A module listed there can be long gone and lint still passes. + +Net effect: **a branch can carry dozens of dead path references with a fully green +`./scripts/validate.sh`.** Only the sweep finds them. + +Enumerate every path the branch removes — committed, uncommitted, and rename sources: + +```bash +{ git diff --name-status -M origin/main...HEAD; git diff --name-status -M HEAD; } \ + | awk '/^[DR]/ {print $2}' | sort -u > /tmp/deleted-paths.txt +``` + +Then sweep the tracked tree for each one. Match the path, the dotted module name, and the bare +basename (relative Markdown links and prose usually mention only the basename): + +```bash +while read -r p; do + [ -e "$p" ] && continue # skip paths a later commit restored + pats=(-e "$p" -e "$(basename "$p")") + case "$p" in *.lean) + mod="$(printf '%s' "${p%.lean}" | tr '/' '.')" + pats+=(-e "${mod//./\\.}($|[^.A-Za-z0-9_])") # boundary-anchored: see trap 1 + esac + hits="$(git grep -nE "${pats[@]}" -- . ':!docs/kb/_generated' ':!ArkLib.lean')" + [ -n "$hits" ] && { echo "### stale refs to $p"; printf '%s\n' "$hits"; echo; } +done < /tmp/deleted-paths.txt +``` + +Three false-positive traps, the first two of which fire routinely on this repo's refactors: + +1. **File promoted to a same-named directory** (`Hachi/Gadget.lean` → `Hachi/Gadget/`). A plain + `git grep -F ArkLib.Commitments.Functional.Hachi.Gadget` prefix-matches every live + `import ...Hachi.Gadget.Core` and reports the whole new folder as stale. That is why the module + pattern above is anchored with `($|[^.A-Za-z0-9_])`. Never sweep module names with bare `-F`. +2. **Basename collision with a surviving file.** Deleting `Hachi/Escape.lean` while + `CoordinateWiseSpecialSoundness/Escape.lean` still exists makes every bare `` `Escape.lean` `` + mention ambiguous. Before touching a basename-only hit, run `git ls-files | grep `: + if a namesake survives, the prose may be correctly pointing at it — read the surrounding + sentence and decide, do not bulk-delete. +3. **This file matches its own examples.** Hits in `docs/skills/make-pr-ready.md` are the sample + paths quoted above, not stale references. Same for any changelog or migration note that + deliberately records an old path. + +For each **confirmed** stale reference, pick a disposition — never just delete the line and move on: + +1. **Moved or renamed** → repoint to the new path (and fix the link text, which usually still + spells the old name). +2. **Content absorbed into a sibling** → repoint to the sibling and reword the sentence so it + describes what is actually there now. +3. **Genuinely gone** → remove the reference, including the clause that introduced it. A stranded + "see also" with its target excised reads worse than no cross-reference. +4. **Pointing at a scratch plan stripped in step 0** → **never** repoint it; that file never + existed on `main`, so the reference was born dead. Restate the reasoning inline in the docstring + and cite the underlying paper with a `[KEY]`. This is the same rule as the "cite papers, not + internal planning documents" bullet in step 3 — the strip is what makes it urgent. + +Then check the places a path grep structurally cannot reach: + +- `docs/kb/papers/*.md` — `related_modules:` frontmatter (unlinted; also verify the entries still + make sense after a move, not just that they resolve). +- `blueprint/src/**/*.tex` — `\texttt{ArkLib/...}` path mentions, plus `\lean{Decl.Name}` refs to + declarations that died with the file. Only `./scripts/validate.sh --site` checks the latter. +- [`../wiki/repo-map.md`](../wiki/repo-map.md) — the structure map goes stale on *every* move, + rename, and delete, and nothing verifies it. +- `.github/workflows/*.yml` — path config such as `upstream_path:` silently no-ops when its target + disappears rather than failing. +- Non-Lean assets living under `ArkLib/` (generated overview HTML, diagrams) — they escape both the + Lean build and the Markdown link check. + +**Do not "fix" `docs/kb/_generated/`.** `declarations.json` and `lean-citations.json` still index +the deleted path; that is expected. Regenerating and committing them trips the CI guard described in +step 3. Leave them at `main`'s state; +[`kb-generated.yml`](../../.github/workflows/kb-generated.yml) refreshes them after merge. + +Finish by re-running `./scripts/update-lib.sh` (then `git add ArkLib.lean`) and +`./scripts/validate.sh`, and re-run the sweep itself — repointing one reference can introduce +another stale path. -### 4. Suggest skill improvements +### 5. Suggest skill improvements - After completing the pass, tell the user whether this skill could be improved: any new recurring guideline gap, a missing or stale step, a better ordering, or a check worth adding. Follow the @@ -173,4 +309,6 @@ Only consider the PR ready when: 1. `./scripts/validate.sh` (with `--lint` / `--docs` as appropriate) succeeds. 2. `ReadLints` is clean for every changed `.lean` file. 3. Citation metadata is regenerated and consistent. -4. You have reported any suggested improvements to this skill. +4. The deleted-file sweep in step 4 comes back clean — no reference anywhere in the tree points at + a path this branch removed. A green `validate.sh` does **not** imply this. +5. You have reported any suggested improvements to this skill. diff --git a/docs/skills/prove-milestone.md b/docs/skills/prove-milestone.md index 89baadaa95..58f6a8e5cb 100644 --- a/docs/skills/prove-milestone.md +++ b/docs/skills/prove-milestone.md @@ -25,7 +25,7 @@ from all of these sources: figure, equations, lemma, bounds, and surrounding qualifications; - the target Lean files and their imports, docstrings, exported package, and consumers; - the seam table and sorry provenance in `Hachi/Composition.lean`; -- `docs/kb/papers/NOZ26.md`, relevant `docs/kb/audits/`, and applicable `HACHI_*.md` design notes; +- `docs/kb/papers/NOZ26.md` and relevant `docs/kb/audits/`; - the generic ArkLib definitions of the claimed security notion and composition operator. The paper is primary. Repository notes explain intent and known deviations but cannot establish diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index 1d12b949a5..43ebdd2706 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -66,13 +66,13 @@ home_page/ site assets and assembled website root CPolynomial/Polynomial division bridge lemmas live under `ArkLib/ToCompPoly/`. - Hachi commitment-scheme modules live under `ArkLib/Commitments/Functional/Hachi/` and formalize the Greyhound [NS24] / Hachi [NOZ26] *inner-outer* Ajtai lattice commitment over a cyclotomic - ring `Rq Φ`. **This development is in progress.** The folder is organized by paper section, each - subfolder carrying an umbrella `.lean` re-export next to it (the `Ajtai/Simple.lean + Simple/` - convention); `ArkLib/Commitments/Functional/Hachi.lean` is the folder-level landing page, with - the full folder map in its module docstring. Layout: - - `Gadget/` (§2.1) — `Gadget/Basic` is the base-`b` gadget matrix `G` and its norm-reducing digit + ring `Rq Φ`. **This development is in progress.** The folder is organized by paper section; + every subfolder carries its umbrella as `Basic.lean` inside that subfolder. + `ArkLib/Commitments/Functional/Hachi.lean` is the folder-level landing page, with the full + folder map in its module docstring. Layout: + - `Gadget/` (§2.1) — `Gadget/Core` is the base-`b` gadget matrix `G` and its norm-reducing digit decomposition `G⁻¹`; `Gadget/Norms` is the centered `ℓ₂²`/`ℓ∞` shortness bounds for both - directions the honest case and Lemma 8 need. `Gadget.lean` re-exports both. + directions the honest case and Lemma 8 need. `Gadget/Basic.lean` re-exports both. - `EvalSplit.lean` (§4, Eq. (12)) — the matrix split underlying the evaluation argument: multilinear evaluation `eval p (xl ++ xh)` factors as the vector–matrix–vector product `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))` (`evalSplit_eq_eval`), with the inverse reshape @@ -82,8 +82,8 @@ home_page/ site assets and assembled website root *weak opening*, following [NOZ26, §4.1]), `Correctness` (perfect correctness for lawful gadget decompositions), `Security` (the weak-binding reduction to Module-SIS via `verify_weak`), and `Arithmetic` (pins the modulus to the power-of-two cyclotomic - `X^{2^α}+1`, which the security proofs genuinely require). `InnerOuter.lean` re-exports the - scheme, its correctness, and its weak-binding reduction. + `X^{2^α}+1`, which the security proofs genuinely require). `InnerOuter/Basic.lean` + re-exports the scheme, its correctness, and its weak-binding reduction. - `QuadEval/` (§4.2, "Polynomial Evaluation as Quadratic Equation", Figure 3) — Hachi's polynomial-evaluation reduction, which proves `f(x) = y` by expressing the evaluation as the quadratic form `bᵀ M a` and folding the `2ʳ` carrier blocks under the challenge vector (hence @@ -91,9 +91,15 @@ home_page/ site assets and assembled website root folding protocol. `QuadEval/Gadgets` holds the gadget algebra (`PublicParamsD`, the honest-prover carrier/short commitment `v = D ŵ`, the `J`-decomposition of `z`, and the `tensorG`/`tensorG1` challenge combinations). `QuadEval/Reduction` is the 2-round protocol with - its types, `relOut` (Eq. (20) + range balls), and `relIn` (weak opening ∨ MSIS(B) ∨ MSIS(D)). - `QuadEval/Soundness` is the subtract-and-divide extractor `buildWitness`, **Lemma 8** - (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound` (`sorryAx`-free), + its types, plain `relOut` (Eq. (20) + range balls), plain `relIn` (eval-consistent weak + opening), and the `QuadEvalSISBreak`/`quadEvalSISSet` **break vocabulary** for MSIS(B/D) + outcomes — key-tied: breaks are validated against the fixed key parameter `pp`, which (like + the relations' key) is never statement data. + `QuadEval/Soundness` is the subtract-and-divide extraction `buildWitness`, split into the plain + assembler `quadEvalMkWitness` and the **escape event** `quadEvalEscLocal`, and **Lemma 8** + (coordinate-wise special soundness) as the single + `quadEval_coordinateWiseSpecialSoundWithEscape` (named-extractor, *plain* input and output + relations, escape as a disjunct of the conclusion; `sorryAx`-free) feeding the package, the composable `quadEvalPackage`, and the reduction's derived norm constants `quadEvalZL2SqBound` = `B_z` / `quadEvalBetaSq` = `4·B_z` (the generic tree plumbing lives in `Security/CoordinateWiseSpecialSoundness/SingleRound`; the supporting norm growth is in @@ -101,20 +107,19 @@ home_page/ site assets and assembled website root **polynomial-level bridge**: a zero-round `ReduceClaim` head (`bridgeVerifier`) reinterpreting a `CMlPolynomial`-level `PolyEvalStatement` as a `QuadEvalStatement` via the monomial tensor bases (`toQuadEvalStatement`), the pulled-back input relation `relPolyEval`, and its CWSS - `bridge_coordinateWiseSpecialSound`. `QuadEval.lean` re-exports the reduction, its soundness, - and the bridge. + `bridge_coordinateWiseSpecialSoundWith`. `QuadEval/Basic.lean` re-exports the reduction, its + soundness, and the bridge. - §4.3 (Hachi's sumcheck-based opening, Figures 4–7) is a **skeleton** split into one flat - folder per paper subprotocol figure (peers of `QuadEval/`), each file exporting a - `CWSSPackage`/`GCWSSPackage` with a sorried CWSS theorem, plus the front-threading file - `Escape.lean` at the Hachi root: - - `Escape.lean` — the escape-threaded front `evalChainE` (design G1): widens the finished - `QuadEval` front relations with an abstract weak-binding escape budget so every §4.3 seam has a - home for the `w̃`-commitment's binding break. Front glue, not a §4.3 subprotocol; sits at the - Hachi root beside `EvalSplit`/`Composition`. + folder per paper subprotocol figure (peers of `QuadEval/`), each file exporting a CWSS package + in the weakest kind it honestly lives in: plain `CWSSPackage`/`GCWSSPackage` for the reshaping + and guarded-check links, `EscapeCWSSPackage`/`EscapeGCWSSPackage` (plain relations plus an + escape *event*) for the links whose extraction can break an assumption. - `RingSwitch/` (§4.3 entry, Figure 4 / Lemma 9) — the HMZ25 **ring-switching lift** reducing `R^lin` to a claim about the committed lifted witness evaluated at a random `α`. `RingSwitch/Rlin` - is the zero-round Eq. (20) → `R^lin` adapter (F2); `RingSwitch/Reduction` is the two-round lift - (`k = 2d`, the abstract `w̃`-commitment `LiftCom`). `RingSwitch.lean` re-exports the folder. + is the zero-round Eq. (20) → `R^lin` adapter (F2, a plain `CWSSPackage`); `RingSwitch/Reduction` + is the two-round lift (`k = 2d`, the abstract `w̃`-commitment `LiftCom` with its short-collision + set `LiftCom.Collision` and the weak-binding escape event `liftEscLocal`). + `RingSwitch/Basic.lean` re-exports the folder. (Distinct from the §3 packing reduction under `ProofSystem/RingSwitching/`, also a ring-switch.) - `ZeroCheck/` (§4.3, Figure 5 / **corrected** Lemma 10) — reduces the batched identities `H₀ ≡ 0 ∧ H_α ≡ 0` to random-point evaluations. `ZeroCheck/Constraints` is the **shared** @@ -122,23 +127,27 @@ home_page/ site assets and assembled website root the Kronecker curve `kroneckerPoint`, per-round seam `roundRel`), consumed by both this zero-check and `Sumcheck/`; `ZeroCheck/Batch` is the per-row/range ⇄ `H₀/H_α ≡ 0` batching bridge; `ZeroCheck/Reduction` is the corrected Lemma 10 (Kronecker seed pair, `(ℓ, k) = (2, D)`; - see `HACHI_LEMMA10_GAP.md`). `ZeroCheck.lean` re-exports the folder. + its module docstring carries the counterexample and the repair). `ZeroCheck/Basic.lean` + re-exports the folder. - `Sumcheck/` (§4.3, Figure 6 / Lemma 11 + Figure 7 tail) — the sumcheck loop finishing the opening. `Sumcheck/Bridge` reshapes the zero-check's point claims into the initial hypercube sums; `Sumcheck/Rounds` is the `m₀`-round guarded paired sumcheck (loop by recursion over `▷ᵍ`); `Sumcheck/FinalEval` is the guarded reveal of `w̃(a)` (Figure 7 tail) landing on the - recursion's evaluation claim. `Sumcheck.lean` re-exports the folder. + recursion's evaluation claim. `Sumcheck/Basic.lean` re-exports the folder. - `Recursion/` (§4.5) — the recursion adapters: `PartialEval` (Eq. (24) peeling, pure derive-`y₀`), `ZBatchBridge` (Eqs. (25)–(26) `Z`-packing — ⚠ carries the open - partial-evaluation soundness gap, `HACHI_RECURSION_GAP.md`), `TraceHandoff` (Eqs. (27)–(28) + partial-evaluation soundness gap, analyzed in its module docstring), `TraceHandoff` + (Eqs. (27)–(28) — guarded trace check, lands on the next iteration's `QuadEval` seam over `Φ'`). - - `Composition.lean` — the **CWSS composition home**: `evalChain` is the `bridgePackage ▷ - quadEvalPackage` chain and `eval_coordinateWiseSpecialSound` is its composed CWSS certificate - (`sorryAx`-free). `openCore` chains the escape-threaded front with the pure §4.3 links (rows - 1–7 of the header's seam table), and `openingChain` / - `hachi_iteration_coordinateWiseSpecialSound` compose the guarded tail (sumcheck loop, final - eval, recursion adapters) into the full one-iteration certificate — a skeleton whose sorry - provenance is inventoried in the module header. + `Recursion/Basic.lean` re-exports the folder. + - `Composition.lean` — the **CWSS composition home**: `evalChain` is the + `bridgePackage ▷ quadEvalPackage` chain and `eval_coordinateWiseSpecialSoundWithEscape` is its + composed named-extractor CWSS certificate (`sorryAx`-free). `openCore` chains the pure §4.3 links + (rows 1–7 of the header's seam table), and `openingChain` / + `hachi_iteration_coordinateWiseSpecialSoundWithEscape` compose the guarded tail (sumcheck loop, + final eval, recursion adapters) into the full one-iteration certificate — a skeleton whose sorry + provenance is inventoried in the module header. Escape events compose along the chain by + `ChallengeTree.EscapeEvent.append`, so only relation seams have to match. - `Commitment.lean` — **Hachi as a `Commitment.Scheme`**: the eval `OracleInterface`, honest `keygen`/`commit` (canonical base-`b` gadget decomposition at width `δ = ⌈log_b q⌉`), and the `hachi` scheme value (its opening `Proof` is a documented `sorry` pending the remaining §4.3+ @@ -159,9 +168,15 @@ home_page/ site assets and assembled website root `Security/TranscriptTree/`: `Basic` defines `ChallengeTree`, `LeafPath`, `ChallengeTreeShape`, `ChallengeTree.IsStructured`, `ChallengeTree.IsAccepting`, `Extractor.TreeBased`, and the shape-generic soundness core `Verifier.treeSpecialSound` (a - tree-based extractor recovering a witness from every `S`-structured accepting tree); `Composition` - defines shape append, `appendSplit`, and the generic structure-preservation/recombination lemmas - for sequential protocol append. The umbrella `Security/TranscriptTree.lean` re-exports both files. + tree-based extractor recovering a witness from every `S`-structured accepting tree). `Basic` also + defines the **escape layer**: `ChallengeTree.EscapeEvent` (a statement-indexed predicate on full + challenge trees, with the trusted-spec contract in its docstring) and + `Verifier.treeSpecialSoundWithEscape`, whose conclusion is `esc stmt tree ∨ extraction succeeds`; + the plain notion is the never-firing event (`treeSpecialSoundWithEscape_false_iff`) and every plain + certificate lifts losslessly (`treeSpecialSoundWith.withEscape`). `Composition` + defines shape append, `appendSplit`, the generic structure-preservation/recombination lemmas + for sequential protocol append, and `ChallengeTree.EscapeEvent.append` (composition of escape + events along that split). The umbrella `Security/TranscriptTree.lean` re-exports both files. Both plain and coordinate-wise special soundness are instances of `Verifier.treeSpecialSound` for different shapes; neither special-soundness file imports the other. - Plain `(k)`-special soundness lives in `Security/SpecialSoundness.lean`. It is the instance of @@ -172,22 +187,40 @@ home_page/ site assets and assembled website root `Security/Implications.lean`. - Coordinate-wise special soundness ([FMN24]/[NOZ26]) lives in `Security/CoordinateWiseSpecialSoundness/`: `Basic` defines the `SS(S, ℓ, k)` combinatorics - (`CoordEq`, `IsSpecialSoundFamily`), `CWSSStructure`, `CWSSStructure.toShape`, and - `Verifier.coordinateWiseSpecialSound`; `Composition` transports CWSS structures across - sequential composition and proves binary append preservation via the generic transcript-tree - split; `NoChallenge` and `SeqCompose` supply the empty-challenge base case and the n-ary - sequential wrappers. `NoChallenge` also provides `CWSSStructure.ofIsEmpty`, the concrete + (`CoordEq`, `IsSpecialSoundFamily`), `CWSSStructure`, `CWSSStructure.toShape`, and both forms + of the soundness notion — the **named-extractor form** + `Verifier.coordinateWiseSpecialSoundWith` (the content-bearing statement; the extractor is an + explicit parameter) and its existential closure `Verifier.coordinateWiseSpecialSound` (plumbing; + it loses the algorithm, so advertised protocol statements use the named form) — plus their + escape-threaded twins `…WithEscape` / `…Escape` and the lossless lift + `coordinateWiseSpecialSoundWith.withEscape`; `Composition` + transports CWSS structures across protocol append and proves binary append preservation + via the generic transcript-tree split, in all forms (the composed extractor is the left + factor's on the prefix tree, the composed event is `ChallengeEvent.append`), and hosts the two + directions of the pure-verifier acceptance bridge (`pure_accepting_of_mem` / + `mem_of_pure_accepting`); `NoChallenge` supplies the empty-challenge base case. **Composition is + binary only** — there is no n-ary CWSS `seqCompose`; chains are built by recursion over the binary + append (`▷`), which keeps the composed extractor a nameable function. All CWSS packages + (`CWSSPackage` and its guarded / escape-aware variants) carry their extraction algorithm as an + explicit `extractor` field, with the `isCWSS` certificate stated at it — so a composed chain + exposes an actual end-to-end extractor (`chain.extractor`). `NoChallenge` also provides + `CWSSStructure.ofIsEmpty`, the concrete challenge-free structure used as the left factor when appending a zero-round `ReduceClaim` head (e.g. Hachi's `bridgeVerifier`). `SingleRound` is the generic single-challenge-round navigation layer (tree shape recovery `tree_shape`, the star-center machinery, the tree extractor - `E`, and the assembly `coordinateWiseSpecialSound_of_mkWitness`) used by Hachi's polynomial- - evaluation reduction `QuadEval` (Lemma 8). `ScalarRound` is its skeletonized `(ℓ = 1, k)` - scalar-challenge twin (`pSpecScalar`, `scalarStructure`; assembly sorried) for Hachi's - Lemmas 9/11-shaped rounds. `Escape` provides `Set.withEscape`, the escape-threading of - relations (`W ⊕ E` witnesses) used by composed extraction chains that can emit binding-break - escapes mid-chain. `Guarded` is the **B4 skeleton**: `Verifier.IsGuardedWith`/`IsGuarded` - (runtime-rejecting verifiers), the guarded package `GCWSSPackage` with its append `▷ᵍ`, and - the (sorried) guarded binary CWSS append theorem. The umbrella + `treeExtractor`, and the assemblies `coordinateWiseSpecialSoundWith_of_mkWitness` and its escape + twin at the induced event `escEvent`) used by Hachi's polynomial-evaluation reduction `QuadEval` + (Lemma 8). `ScalarRound` is its `(ℓ = 1, k)` scalar-challenge twin (`pSpecScalar`, + `scalarStructure`, readers/shape recovery, `treeExtractorScalar`, `escEventScalar(OfValid)`; the + two assemblies are sorried) for Hachi's Lemmas 9/11-shaped rounds. `Escape` is the **package + lattice**: the escape-aware packages `EscapeCWSSPackage`/`EscapeGCWSSPackage` (ordinary relations + and extractor, plus one `esc` **event** field), the lossless kind lifts `toEscape`/`toGuarded`, + all mixed appends, and the universal `▷` elaborator dispatching over the 2×2 grid + escape? × guarded?. Since escapes are events on `(statement, tree)`, composition matches only + relation seams. `Guarded` is the **B4 skeleton**: `Verifier.IsGuardedWith`/`IsGuarded` + (runtime-rejecting verifiers), the guarded package `GCWSSPackage` with its append `▷ᵍ`, the + (sorried) escape-threaded guarded binary CWSS append theorem, and the plain guarded append proven + from it at the never-firing events. The umbrella `CoordinateWiseSpecialSoundness.lean` re-exports the core files. - Active areas are often grouped by paper or protocol family, for example `Data/CodingTheory/ProximityGap/BCIKS20/...` or `ProofSystem/Binius/...`. diff --git a/scripts/lint-style.py b/scripts/lint-style.py index 9a544f5894..026f19bb33 100755 --- a/scripts/lint-style.py +++ b/scripts/lint-style.py @@ -23,11 +23,8 @@ absolute path. This script can also be used to regenerate the list of allowed / ignored style -exceptions by redirecting the output to ``style-exceptions.txt``. Use: - - $ ./scripts/update-style-exceptions.sh - -to perform this update. +exceptions by redirecting its output to ``scripts/style-exceptions.txt`` +(ArkLib has no wrapper script for that step). """ # TODO: This is copied from mathlib currently. eventually that could be avoided with some better scripting