diff --git a/ArkLib.lean b/ArkLib.lean index 0de3d6b0b6..becaefba7b 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -3,6 +3,7 @@ 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 @@ -17,6 +18,20 @@ 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.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.Reduction +import ArkLib.Commitments.Functional.Hachi.RingSwitch.Rlin +import ArkLib.Commitments.Functional.Hachi.Sumcheck +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.Batch +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction import ArkLib.Commitments.Functional.KZG.Algebra import ArkLib.Commitments.Functional.KZG.Basic import ArkLib.Commitments.Functional.KZG.Binding @@ -202,8 +217,11 @@ import ArkLib.OracleReduction.Security.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape +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 diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 347eeb7ead..778f77184c 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -4,111 +4,117 @@ 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.Sumcheck.FinalEval +import ArkLib.Commitments.Functional.Hachi.Recursion.TraceHandoff import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded /-! # Hachi — the CWSS composition home 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` — a verifier bundled with its proof of coordinate-wise special -soundness (CWSS), the knowledge-soundness notion under which a witness can be extracted from a -suitably structured tree of accepting transcripts. This file only **imports those packages and -chains them** with the `▷` operator (`CWSSPackage.append`). The composed chain's `isCWSS` field is -the CWSS certificate for the whole reduction, so growing the protocol is a one-line `▷`. (Hachi as -a `Commitment.Scheme` — the honest committer `keygen`/`commit` and the `hachi` functional -commitment — lives in the sibling `Commitment.lean`.) - -Right now the finished core chains exactly two links — the polynomial-level bridge and `QuadEval` -(`evalChain = bridgePackage ▷ quadEvalPackage`, certificate `eval_coordinateWiseSpecialSound`). The -remaining §4.3+ opening stages and the §3/§4.5 recursion adapters are placeholders (see the `TODO` -block). - -## Components — where each piece lives and which part of the paper it is - -Finished pieces, each in its own file (paths under `Commitments/Functional/Hachi/` unless marked -*generic*); paper references are to Hachi [NOZ26]: - -* **Ajtai gadget matrices** (§2.1) — `Gadget/{Basic, Norms}`. -* **Inner-outer Ajtai commitment** + weak binding (§4.1) — - `InnerOuter/{Scheme, Correctness, Security, Arithmetic}`. -* **Multilinear evaluation as a matrix–vector product** (§4, Eq. (12)) — `EvalSplit`. -* **`QuadEval` gadget algebra** (§4.2, Figure 3) — `QuadEval/Gadgets`. -* **`QuadEval` reduction** (§4.2) — `QuadEval/Reduction` (types, relations, protocol); its - Lemma 8 CWSS soundness and `quadEvalPackage` live in `QuadEval/Soundness`. -* **Polynomial-level bridge** (§4.2) — `QuadEval/Bridge`, exported as `bridgePackage`. -* **Functional-commitment interface** (`Commitment.Scheme`) — `Commitment` (honest committer). -* **Single-round CWSS tree navigation** *(generic)* — - `OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound`. -* **`CWSSPackage` + the `▷` chain operator** *(generic)* — - `OracleReduction/Security/CoordinateWiseSpecialSoundness/Package`. - -## The composed verifier chain +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 +`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`.) + +## 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.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 + the **next iteration's** `QuadEval` input relation over the next ring `Φ'` — the recursion + loop's closing seam. + +## 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. Consequently the opening starts with the Figure 3 path, not -with §3. Section 3 only converts the smaller extension-field evaluation produced at the end back -into an `Rq` evaluation for the **next** iteration (and may separately wrap an external -extension-field claim). It is not the HMZ25 ring-switching subprotocol: that is Figure 4 in §4.3, -after `QuadEval`. The `═══` band is the finished core (`evalChain`); all other links are planned -packages (plus zero-round relation adapters where statement shapes differ): +`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`). ```text - committed f : CMlPolynomial Rq (r + m), with an Rq evaluation query (x, y) - │ - ═══════════════════════ evalChain (finished core) ═══════════════════════ - ▼ - bridge PolyEvalStatement ⇒ QuadEval.relIn bridgePackage (§4.2, 0-round) - │ ▷ - ▼ - QuadEval QuadEval.relIn ⇒ Eq. (20) relOut quadEvalPackage (§4.2, Figure 3, - Lemma 8) - ══════════════════════════════════════════════════════════════════════════ - │ ▷ read (t̂, ŵ, ẑ) and the block equation as an Rq-linear relation R^lin - ├─ optional concrete cutoff: use §4.5 JL / LaBRADOR instead of §4.3 - │ - ▼ - §4.3, Figure 4 HMZ25 ring switching: commit to (z, r), sample α, - reduce R^lin over Rq to constraints over F_{q^k} planned (Lemma 9) - │ ▷ - ▼ - §4.3, Figure 5 batch the linear and range constraints into - H_α and H_0; sample evaluation points τ₁ and τ₀ planned (Lemma 10) - │ ▷ - ▼ - §4.3, Figures 6–7 sumcheck rounds g_i(X_i), challenges a_i, - then open w̃(a₁, …, a_ℓ) planned (Lemma 11) - │ - ▼ - smaller evaluation claim over F_{q^k} - ├─ recurse (§4.4): - │ §3.2 partial evaluations (the recursive polynomial has F_q coefficients) - │ │ ▷ - │ ▼ - │ §3.1 packing / trace encoding ⇒ Rq polynomial evaluation - │ └────────────────────────────── loop to bridge for the next iteration - ├─ asymptotic termination: reveal the final polynomial once it is small (§4.4) - └─ concrete cutoff: §4.5 repacking without re-decomposition, then Greyhound + # | 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 + | — 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 + | | | = next iteration's row 2 | ``` -## Growing the composition +- 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. +- 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. +- 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 + 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. -Each §4.3 opening subprotocol is exported from its file as a `CWSSPackage` and appended after -`evalChain`. Recursive composition then routes its final extension-field claim through the §3 -adapters before invoking `evalChain` again. A shape mismatch between one package's `relOut` and the -next's `relIn` gets its own zero-round `ReduceClaim` package (the same recipe as `bridgePackage`). -Links whose runtime check reads data the next statement type drops need a guarded variant of `▷`; -the pure links compose as above. Once the chain is long, the binary `▷` can be replaced by the -n-ary `Verifier.seqCompose` (`seqCompose_succ_eq_append` is `rfl` for the finished pure factors, so -their existing proofs are not reworked). +## 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`). +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). ## 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] +* [Huang, M.-Y. M., Mao, X., and Zhang, J., *Sublinear Proofs over Polynomial Rings*][HMZ25] * [Lyubashevsky, V., and Seiler, G., *Short, Invertible Elements in Partially Splitting Cyclotomic Rings and Applications to Lattice-Based Zero-Knowledge Proofs*][LS18] -/ @@ -131,9 +137,9 @@ 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`; each -further §4.3 opening subprotocol is appended after this core, while §3 closes the recursion back -to the next invocation (see the module header). -/ +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 @@ -172,17 +178,186 @@ theorem eval_coordinateWiseSpecialSound (init : ProbComp σ) end Composition -/-! ## TODO — growing the composition +section OpeningChain + +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} +variable {E : Type} {F : Type} [Field F] [DecidableEq F] [SampleableType F] + +/-- Shorthand for the §4.3 chain's `R^lin` column count at the Eq. (20) instantiation. -/ +local notation "μ₀" => rlinCols innerRows messageDigits innerDigits zDigits m r +/-- Shorthand for the §4.3 chain's `R^lin` row count at the Eq. (20) instantiation. -/ +local notation "n₀" => rlinRows innerRows outerRows dRows + +/-- The wire format of the pure prefix `openCore` (rows 1–7): bridge (0) ⧺ `QuadEval` (2) ⧺ +R^lin adapter (0) ⧺ lift (2) ⧺ batching (0) ⧺ zero-check (1) ⧺ sumcheck bridge (0), +right-associated as `▷` composes them. -/ +abbrev openCoreSpec (ω : ℕ) (TCom F : Type) := + (((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r)) ++ₚ + ((!p[] : ProtocolSpec 0) ++ₚ + (CoordinateWise.ScalarRound.pSpecScalar TCom F ++ₚ + ((!p[] : ProtocolSpec 0) ++ₚ (pSpecZeroCheck F ++ₚ (!p[] : ProtocolSpec 0))))) -`evalChain` / `eval_coordinateWiseSpecialSound` is the finished `Rq`-level CWSS core (bridge ▷ -`QuadEval`). Still open: +/-- Sampleability of the pure prefix's challenges, assembled **by name** from the per-link +instances (the generic append instance does not fire through the reducible `++ₚ` — its +discrimination keys degenerate — so compound wire formats get their instances built explicitly; +same workaround as `roundsSpecSampleable`). Requires a sampler for the fold challenges +(`ShortChallenge`), which the repo does not yet provide as an instance. -/ +@[reducible] def openCoreSpecSampleable (ω : ℕ) (TCom F : Type) [SampleableType F] + [SampleableType (ShortChallenge 𝓜(q, α) ω)] : + ∀ i, SampleableType + ((openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω TCom F).Challenge i) := + ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := ProtocolSpec.instSampleableTypeChallengeEmpty) + (h₂ := CoordinateWise.SingleRound.instSampleableTypeChallengePSpec)) + (h₂ := ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := ProtocolSpec.instSampleableTypeChallengeEmpty) + (h₂ := ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := CoordinateWise.ScalarRound.instSampleableTypeChallengePSpecScalar) + (h₂ := ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := ProtocolSpec.instSampleableTypeChallengeEmpty) + (h₂ := ProtocolSpec.instSampleableTypeChallengeAppend + (h₁ := instSampleableTypeChallengePSpecZeroCheck) + (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`. -/ +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)) + (φF : ZMod q →+* F) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) : + CWSSPackage init impl + (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) + (RoundStatement 𝓜(q, α) K.TCom F n₀ μ₀ 0) + (LiftedWitness 𝓜(q, α) μ₀ n₀ ⊕ E) + (openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F) := + haveI : ∀ i, SampleableType + ((((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r)).Challenge i) := + 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 ▷ + 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). + +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 +sumcheck arity is pinned to `m₀ := mLow + κ` so the recursion adapters can peel the top `κ` +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)) + (φF : ZMod q →+* F) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) + (zpow : Fin (2 ^ κ) → F) + (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] + {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} + (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') + innerDigits' dRows') + (reinterpretCom : K.TCom → Commitment Φ' outerRows') + (base' : ZMod q) (βSq' γ' κ' : ℕ) : + GCWSSPackage init impl + (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) + (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' + dRows') + (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits' ⊕ E) + ((openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F ++ₚ + roundsSpec F b (mLow + κ) ++ₚ pSpecFinalEval F) ++ₚ + (pSpecPartialEval F κ ++ₚ ((!p[] : ProtocolSpec 0) ++ₚ pSpecHandoff Φ'))) := + haveI i₀ := openCoreSpecSampleable (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F + haveI i₁ : ∀ i, SampleableType + (((openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F) ++ₚ + roundsSpec F b (mLow + κ)).Challenge i) := + ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := i₀) + (h₂ := roundsSpecSampleable F b (mLow + κ)) + haveI i₂ : ∀ i, SampleableType + ((((openCoreSpec (q := q) (α := α) (dRows := dRows) (r := r) ω K.TCom F) ++ₚ + 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 + (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 + (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 ▷ᵍ + 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 σ) + (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)) + (φF : ZMod q →+* F) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hq2 : 2 * b ≤ q + 1) (hb : b - 1 ≤ γ) + (zpow : Fin (2 ^ κ) → F) + (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] + {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} + (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') + 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 + (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) := + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ hτ + K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' γ' κ').isCWSS + +end OpeningChain + +/-! ## TODO — growing the composition -* **Remaining §3/§4.3+/§4.5 subprotocols.** Append the §4.3 packages after `evalChain`, then route - the final extension-field evaluation through §3 before the next iteration (or take a §4.5 - cutoff), as shown in the module header. Insert a zero-round `ReduceClaim` package when one - `relOut` and the next `relIn` disagree in shape. Guarded subprotocols need a guarded variant of - `▷`. Once the chain is long, migrate the binary `▷` to the n-ary - `Verifier.seqCompose` + `seqCompose_coordinateWiseSpecialSound` (every factor is `IsPure`, - `seqCompose_succ_eq_append` is `rfl`). -/ +* **§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. +* **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). +* **Knowledge-error accounting** (FMN24 Lemma 4), `Commitment.extractability`, and Fiat–Shamir + remain out of scope (design D12/R6). -/ end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Escape.lean b/ArkLib/Commitments/Functional/Hachi/Escape.lean new file mode 100644 index 0000000000..8d82089465 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Escape.lean @@ -0,0 +1,193 @@ +/- +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/Recursion/PartialEval.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean new file mode 100644 index 0000000000..2d03bce824 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean @@ -0,0 +1,204 @@ +/- +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.Sumcheck.FinalEval + +/-! + # Partial evaluations — Hachi §4.5, Eq. (24) — skeleton (milestone G2) + + First recursion adapter: peel the top `κ` variables of the evaluation claim + `mle[w̃](a) = y′` produced by the §4.3 chain, in preparation for the `Z`-packing that closes + the recursion (Hachi §4.5 "avoiding re-decomposition"; the §3.2 pattern). + + Writing `a = (a₀, a₁)` with `a₀ ∈ F^{mLow}` and `a₁ ∈ F^κ` (the extension degree is + `k = 2^κ`), Eq. (24) factors the claim as + + `y′ = ∑_{i ∈ {0,1}^κ} eq(i, a₁) · yᵢ`, where `yᵢ := ∑_{j ∈ {0,1}^{mLow}} w̃_{j‖i}·eq(j, a₀)`. + + * **message (P→V)** — the partial evaluations `(yᵢ)_{i ≠ 0}` (all but one); + * **derive-`y₀`** (design D7, paper footnote 10) — the verifier *derives* + `y₀ := (y′ − ∑_{i ≠ 0} eq(i, a₁)·yᵢ) / eq(0, a₁)`-style instead of checking the display + equation, keeping this head **pure** (total, no guard) and the Eq. (24) consistency true by + construction; + * **output** — the statement extended by the full derived family `(yᵢ)ᵢ`; residual claims: + the per-`i` well-formedness `yᵢ = partialEvalAt w̃ a₀ i` for **every** `i ∈ {0,1}^κ`. + + This seam is **sound**: from the per-`i` claims and the derivation identity, the input claim + `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`.) + + **Sorried**: the encoding defs (`partialEvalAt`, `deriveFamily`, `wTableMleEval_split`) and + the CWSS theorem. + + ## 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 OracleComp OracleSpec ProtocolSpec CoordinateWise + +/-- The partial-evaluation wire format: one prover message carrying the `2^κ − 1` partial +evaluations `(yᵢ)_{i ≠ 0}` (the remaining `y₀` is derived, design D7). -/ +@[reducible] def pSpecPartialEval (F : Type) (κ : ℕ) : ProtocolSpec 1 := + ⟨!v[.P_to_V], !v[{i : Fin (2 ^ κ) // i ≠ 0} → F]⟩ + +instance {F : Type} {κ : ℕ} : IsEmpty (pSpecPartialEval F κ).ChallengeIdx := + ⟨fun ⟨0, h⟩ => nomatch h⟩ + +instance {F : Type} {κ : ℕ} [SampleableType F] : + ∀ i, SampleableType ((pSpecPartialEval F κ).Challenge i) := + fun i => isEmptyElim i + +/-- The statement after the partial-evaluation step: the commitment, the *split* evaluation +point, and the full derived family of partial evaluations. -/ +structure PartialEvalStatement (TCom F : Type) (mLow κ : ℕ) where + /-- The `w̃`-commitment. -/ + t : TCom + /-- The low point half `a₀ ∈ F^{mLow}` (the first `mLow` variables). -/ + pointLow : Fin mLow → F + /-- The high point half `a₁ ∈ F^κ` (the last `κ` variables, to be peeled). -/ + pointHigh : Fin κ → F + /-- The full family of partial evaluations `(yᵢ)ᵢ` — the sent `(yᵢ)_{i≠0}` together with the + derived `y₀`. -/ + partials : Fin (2 ^ κ) → F + +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 (mLow κ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The `i`-th true partial evaluation of the table (Eq. (24)): +`partialEvalAt w a₀ i = ∑_{j ∈ {0,1}^{mLow}} w̃_{j‖i}·eq(j, a₀)`. **Sorried (G2)** — index +bookkeeping over the `wTable` encoding. -/ +def partialEvalAt (φF : ZMod q →+* F) (w : LiftedWitness Φ μ n) + (a₀ : Fin mLow → F) (i : Fin (2 ^ κ)) : F := + sorry + +/-- The Lagrange/equality weight `eq(i, a₁)` of the Boolean index `i ∈ {0,1}^κ` at the point +`a₁ ∈ F^κ` (little-endian bits of `i`, matching the `wTable` convention). **Sorried (G2)** — +`MvPolynomial.eqTilde` at the bit vector of `i`. -/ +def eqWeight (a₁ : Fin κ → F) (i : Fin (2 ^ κ)) : F := + sorry + +/-- The mle splitting identity behind Eq. (24): +`mle[w̃](a₀ ++ a₁) = ∑ᵢ eq(i, a₁)·partialEvalAt w̃ a₀ i`. **Sorried (G2)** — the `eq` +factorization `eq(j‖i, a₀‖a₁) = eq(j, a₀)·eq(i, a₁)`. -/ +theorem wTableMleEval_split (φF : ZMod q →+* F) (w : LiftedWitness Φ μ n) + (a : Fin (mLow + κ) → F) : + wTableMleEval Φ (mLow + κ) φF b w a = + ∑ i : Fin (2 ^ κ), + eqWeight κ (fun j => a (Fin.natAdd mLow j)) i * + partialEvalAt Φ mLow κ φF w (fun j => a (Fin.castAdd κ j)) i := by + sorry + +/-- Derive the full partial-evaluation family from the message (design D7, paper footnote 10): +install the sent `(yᵢ)_{i≠0}` and *derive* `y₀` so that Eq. (24)'s display equation holds by +construction. **Sorried (G2)** — needs `eq(0, a₁)`'s invertibility handling (the honest +derivation divides by `∏ⱼ (1 − a₁ⱼ)`; the degenerate case is absorbed into the derivation's +convention and the CWSS proof). -/ +def deriveFamily (value : F) (pointHigh : Fin κ → F) + (msg : {i : Fin (2 ^ κ) // i ≠ 0} → F) : Fin (2 ^ κ) → F := + sorry + +/-- The partial-evaluation verifier (Hachi §4.5, Eq. (24)): a **pure** head — it splits the +point, installs the sent partials, and derives `y₀`. No runtime check (design D7). -/ +def partialEvalVerifier {TCom : Type} : + Verifier oSpec (WEvalStatement TCom F (mLow + κ)) (PartialEvalStatement TCom F mLow κ) + (pSpecPartialEval F κ) where + verify := fun stmt tr => + pure ⟨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)⟩ + +/-- The honest partial-evaluation prover skeleton: sends the true partials at the nonzero +indices (the parameter `computeY`, honestly `partialEvalAt`). -/ +def partialEvalProver {TCom : Type} + (computeY : WEvalStatement TCom F (mLow + κ) → LiftedWitness Φ μ n → + {i : Fin (2 ^ κ) // i ≠ 0} → F) : + Prover oSpec (WEvalStatement TCom F (mLow + κ)) (LiftedWitness Φ μ n) + (PartialEvalStatement TCom F mLow κ) (LiftedWitness Φ μ n) + (pSpecPartialEval F κ) where + PrvState + | 0 => WEvalStatement TCom F (mLow + κ) × LiftedWitness Φ μ n + | 1 => WEvalStatement TCom F (mLow + κ) × LiftedWitness Φ μ n + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeY st.1 st.2, st) + receiveChallenge + | ⟨0, h⟩ => nomatch h + output := fun ⟨stmt, wit⟩ => + pure (⟨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)) + (computeY stmt wit)⟩, wit) + +/-- **The per-`i` partial-evaluation relation** (the residual claims of Eq. (24)): `w̃` opens +`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)) + (φ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.** + +**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 +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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) : + (partialEvalVerifier (oSpec := oSpec) mLow κ (TCom := K.TCom) + (F := F)).coordinateWiseSpecialSound init impl + CWSSStructure.ofIsEmpty + (relWEvalClaimE Φ (mLow + κ) bound ρBound b K φF) + (relPartialEvalE Φ mLow κ bound ρBound K φF) := by + sorry + +/-- **The partial-evaluation head as a `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)) + (φF : ZMod q →+* F) : + CWSSPackage init impl + (WEvalStatement K.TCom F (mLow + κ)) (LiftedWitness Φ μ n ⊕ E) + (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n ⊕ E) + (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 + 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 + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean new file mode 100644 index 0000000000..629e01e159 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean @@ -0,0 +1,216 @@ +/- +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.ZBatchBridge + +/-! + # Trace handoff — Hachi §4.5, Eqs. (27)–(28) — skeleton (milestone G3) + + The recursion-closing adapter: convert the `Z`-packed `F`-claim of Eq. (26) into the **next + iteration's `Rq`-quadratic statement**, re-entering the chain at the `QuadEval` seam + (the paper: "this is exactly the type of statement supported natively by Greyhound"). + + ## Protocol (one message, guarded) + + * **message (P→V)** — the packed evaluation `p ∈ R′_q` (`Rq Φ'`, the **next** ring, dimension + `d′`), Eq. (27): `p := eᵀ(σ₋₁(ψ(f))ᵀ ⊗ I)ψ(ŵ)`, where `e`/`f` are the `eq`-tensor halves + of the low point and `ψ` is the packing bijection of Theorem 2 + (`ArkLib/Data/Lattices/CyclotomicRing/Subfield/Packing.lean`, `psi`); + * **check (guarded, design D6)** — the trace equation `Tr_H(p·…) = (d′/k)·value` (Theorem 2 / + `traceH_psi_mul_conj`): it reads the packed claim `value`, which the pinned next-iteration + statement type drops, so it must be a runtime guard — the same argument as the §3.1 head; + * **output** — the next iteration's `QuadEvalStatement` over `Φ'`: bases `avec`/`bvec` are the + `eq`-tensor packings of the low point (σ₋₁-twisted per design D5), the evaluation is `p`, + and the outer commitment is the **reinterpretation** of `t` at ring dimension `d′`. + + ## No new commitment — reinterpretation of `t` + + §4.5 sends only `(yᵢ)ᵢ` and `p` (Eq. (28)): the next iteration's commitment **is** the lift + commitment `t` from Figure 4, *re-read* at ring dimension `d′` — the `Z`-packing (Eq. (25)) + composed with `ψ` is a fixed `Zq`-linear bijection on coefficient tables, and the lift + commitment's message-packing convention is chosen (Phase G, `LiftCom` instantiation) to make + `Com_{d}(w̃) = Com'_{d′}(ψ(ŵ))` a definitional re-indexing. This is what ties the next + iteration's extracted openings back to `t` (`reinterpretCom` below abstracts the re-reading); + norm growth under `ψ` is Lemma 6 (`‖ψ(a)‖∞ ≤ 2β`, the `cInfNorm_psi_le` sorry, gate G1). + + ## Soundness shape + + 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 + `ψ`/`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`. + (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.) + + **Sorried**: the encoding defs (`traceCheck`, `toNextQuadEvalStatement`) and the CWSS theorem. + + ## References + + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] + * [Lyubashevsky, V., Nguyen, N. K., and Plançon, M., *Lattice-Based Zero-Knowledge Proofs and + Applications: Shorter, Simpler, and More General*][LNP22] +-/ + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open OracleComp OracleSpec ProtocolSpec CoordinateWise + +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 (mLow κ : ℕ) (bound ρBound : ℕ) +variable {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The handoff wire format: one prover message carrying the packed evaluation `p ∈ R′_q` +(Eq. (27)) over the **next** ring `Φ'`. -/ +@[reducible] def pSpecHandoff (Φ' : CyclotomicModulus (ZMod q)) : ProtocolSpec 1 := + ⟨!v[.P_to_V], !v[Rq Φ']⟩ + +instance : IsEmpty (pSpecHandoff Φ').ChallengeIdx := ⟨fun ⟨0, h⟩ => nomatch h⟩ + +instance : ∀ i, SampleableType ((pSpecHandoff Φ').Challenge i) := fun i => isEmptyElim i + +/-- The trace check (Eq. (26)/(27) right-hand side, Theorem 2): `Tr_H` of `p` against the +`σ₋₁`-twisted packed `eq`-tail equals `(d′/k)·value`. **Sorried (G3)** — `traceHComp` at the +`fixedSubring` instantiation of `F` (decidable via the computable trace). -/ +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 +`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' + dRows' := + sorry + +/-- 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' + dRows') + (pSpecHandoff Φ') where + verify := fun stmt tr => + if traceCheck Φ' mLow φF stmt (tr 0) then + pure (toNextQuadEvalStatement Φ' mLow φF pp' 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 := + ⟨fun stmt tr => traceCheck Φ' mLow φF stmt (tr 0), + fun stmt tr => toNextQuadEvalStatement Φ' mLow φF pp' 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) : + Prover oSpec (HatEvalStatement TCom F mLow) (LiftedWitness Φ μ n) + (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' + dRows') + WitOut (pSpecHandoff Φ') where + PrvState + | 0 => HatEvalStatement TCom F mLow × LiftedWitness Φ μ n + | 1 => HatEvalStatement TCom F mLow × LiftedWitness Φ μ n + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeP st.1 st.2, st) + receiveChallenge + | ⟨0, h⟩ => nomatch h + output := fun ⟨stmt, wit⟩ => + pure (toNextQuadEvalStatement Φ' mLow φF pp' reinterpretCom stmt (computeP stmt wit), + computeWit wit) + +variable [SampleableType F] + +/-- **CWSS of the trace handoff (skeleton, G3) — closing the recursion loop.** + +**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 +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` +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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (zpow : Fin (2 ^ κ) → F) + (K : LiftCom (LiftedWitness Φ μ n) E (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 + CWSSStructure.ofIsEmpty + (relHatEvalE Φ mLow κ bound ρBound zpow K φF) + (relInE Φ' base' βSq' γ' κ' K.esc) := 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)) + (zpow : Fin (2 ^ κ) → F) + (K : LiftCom (LiftedWitness Φ μ n) E (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) + (QuadEvalStatement Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' + dRows') + (QuadEvalWitness Φ' innerRows' (2 ^ m') messageDigits' (2 ^ r') innerDigits' ⊕ E) + (pSpecHandoff Φ') where + verifier := handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' 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' γ' κ' + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean new file mode 100644 index 0000000000..51a3569c2d --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean @@ -0,0 +1,146 @@ +/- +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.PartialEval + +/-! + # `Z`-packing bridge — Hachi §4.5, Eqs. (25)–(26) — skeleton, ⚠ **open soundness question** + + 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ᵢ` + (`Recursion/PartialEval.lean`); + * `relOut = relHatEvalE` — `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 + **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 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 + full field elements (the point `a₀` lies in `F`, not in the base field), this one equation + has a `(k−1)·k`-dimensional `F_q`-kernel for `k = 2^κ ≥ 2` — the per-`i` claims do **not** + follow. Concretely (`κ = 1`, `F = F_q[Z]`): choosing `y₁ := p₁ − δ`, `y₀ := p₀ + Zδ` keeps + 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). + + 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 + where the paper's argument stands. + + ## 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 OracleComp OracleSpec ProtocolSpec CoordinateWise + +/-- The `Z`-packed evaluation-claim statement (Hachi Eq. (26)): the commitment, the low point +half, and the public packed value `∑ᵢ yᵢ·Z^{⟨i⟩}`. -/ +structure HatEvalStatement (TCom F : Type) (mLow : ℕ) where + /-- The `w̃`-commitment. -/ + t : TCom + /-- The low point half `a₀ ∈ F^{mLow}`. -/ + pointLow : Fin mLow → F + /-- The packed claim value `∑ᵢ yᵢ·Z^{⟨i⟩} ∈ F ≅ F_{q^k}`. -/ + value : F + +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 (mLow κ : ℕ) (bound ρBound : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The `Z`-packed table evaluation (Hachi Eqs. (25)–(26) left-hand side): +`hatEval w̃ a₀ = ∑_{j ∈ {0,1}^{mLow}} ŵⱼ·eq(j, a₀)` with `ŵⱼ := ∑ᵢ w̃_{j‖i}·zpow i` — the +reading of the committed table as an `F`-entried table along the `Z`-power basis `zpow` +(honestly `zpow i = Z^{⟨i⟩}`, the power basis of `F/F_q`). **Sorried (G3-adjacent)**. -/ +def hatEval (φF : ZMod q →+* F) (zpow : Fin (2 ^ κ) → F) (w : LiftedWitness Φ μ n) + (a₀ : Fin mLow → F) : F := + sorry + +/-- **The `Z`-packed claim relation** (Hachi Eq. (26)): `w̃` opens `t` and its `Z`-packed table +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)) + (φ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 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)) + (φ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 + sorry + +/-- **The `Z`-packing bridge as a `CWSSPackage`** (Hachi §4.5, Eqs. (25)–(26)): zero-round +`ReduceClaim` at `mapStmt := toHatEvalStatement`, reducing `relPartialEvalE` to `relHatEvalE`. +⚠ 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)) + (zpow : Fin (2 ^ κ) → F) + (K : LiftCom (LiftedWitness Φ μ n) E (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) + (!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 + 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) + (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) + (mem_relPartialEvalE_of_relHatEvalE Φ mLow κ bound ρBound zpow K φF) + +end Bridge + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch.lean new file mode 100644 index 0000000000..b24d17d02a --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch.lean @@ -0,0 +1,40 @@ +/- +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.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 +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 +`X := α ∈ F` (an extension field `F ⊇ Zq`). This "switches" the R_q-statement into the extension +field where the sumcheck runs. (This is the §4.3 lift; the *separate* §3 F_{q^k}↔R_q packing +reduction — also a ring-switching idea — lives under `ArkLib/ProofSystem/RingSwitching/`.) + +## 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. +* `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). + +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 +`Composition.lean`. + +## References + +* [Huang, M.-Y. M., Mao, X., and Zhang, J., *Sublinear Proofs over Polynomial Rings*][HMZ25] +* [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/RingSwitch/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean new file mode 100644 index 0000000000..6db2a68ea0 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean @@ -0,0 +1,246 @@ +/- +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.RingSwitch.Rlin +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRound + +/-! + # HMZ25 lift — Hachi Figure 4 / Lemma 9 — skeleton (sumcheck-track milestone F4) + + The first interactive stage of Hachi's §4.3 sumcheck chain, following the ring-switching idea + of Huang–Mao–Zhang [HMZ25]: `M z = y` over `Rq = Zq[X]/(X^d + 1)` holds **iff** there are + quotient polynomials `ρᵢ ∈ Zq[X]` of degree `≤ d − 2` with + + `∑ⱼ Mᵢⱼ(X)·zⱼ(X) = yᵢ(X) + (X^d + 1)·ρᵢ(X)` in `Zq[X]`, for every row `i`. + + ## Protocol (two rounds, `pSpecScalar`) + + * **Round 0 (P→V)** — the prover sends `t := Com(w̃)`, a binding commitment to the *lifted + witness* `w̃` — the `R^lin` witness `z` together with the quotients `ρ` (Hachi Eq. (21); the + gadget digits of `ρ` arrive with the F5 table encoding, `ZeroCheck/Constraints.lean`). + Figure 4 draws `(z, r)` as the prover's last message, but in the composed scheme it is + **never sent** — it is the output-relation witness (QuadEval precedent, design D6). + * **Round 1 (V→P)** — the verifier samples `α ← F` (an extension field `F ⊇ Zq`, abstract per + design G5) and both sides evaluate the lifted rows at `X := α`. The verifier itself is a pure + statement-extending pass-through — the row checks at `α` constrain the never-sent witness, + so they live in the output relation. + + ## Soundness shape (Lemma 9) + + Output relation `relLift`: an opening `w̃` of `t` whose lifted rows vanish at `α` and which is + 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. + + ## 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). + + ## References + + * [Huang, M.-Y. M., Mao, X., and Zhang, J., *Sublinear Proofs over Polynomial Rings*][HMZ25] + * [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.ScalarRound + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] +variable {n μ : ℕ} {E : Type} + +/-- **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 +bound `deg ρᵢ ≤ d − 2`. This is the committed data of Figure 4. -/ +structure LiftedWitness (Φ : CyclotomicModulus (ZMod q)) (μ n : ℕ) where + /-- The `R^lin` witness `z ∈ Rq^μ`. -/ + z : PolyVec (Rq Φ) μ + /-- The per-row quotient polynomials `ρᵢ ∈ Zq[X]`. -/ + ρ : Fin n → Polynomial (ZMod q) + /-- Structural degree bound: `deg ρᵢ ≤ d − 2` (from `deg (∑ Mᵢⱼzⱼ − yᵢ) ≤ 2d − 2`). -/ + hρ : ∀ i, (ρ i).natDegree ≤ Φ.φ.natDegree - 2 + +/-- `LiftedWitness` is inhabited (the all-zero witness). -/ +instance : Nonempty (LiftedWitness Φ μ n) := + ⟨⟨fun _ => 0, fun _ => 0, fun _ => by simp⟩⟩ + +/-- Coefficient-range predicate on the quotient polynomials (the `ρ`-side of the Eq. (21) range +claims; the exact constant is pinned by the F5 digit decomposition). -/ +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, +[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 + /-- 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 + +variable {F : Type} [Field F] (bound ρBound : ℕ) + +/-- The lift's output statement: the `R^lin` statement extended by the commitment `t` and the +evaluation challenge `α` (the statement-extending pass-through shape of `pSpecScalar`). -/ +abbrev LiftStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) : Type := + RlinStatement Φ n μ × TCom × F + +/-- The `i`-th lifted row's left-hand side `∑ⱼ Mᵢⱼ(X)·zⱼ(X) ∈ Zq[X]`, on canonical +representatives (`CPolynomial.toPoly` of the reduced forms; each factor has degree `< d`, so the +row sum has degree `≤ 2d − 2`). -/ +noncomputable def rowSum (s : RlinStatement Φ n μ) (z : PolyVec (Rq Φ) μ) (i : Fin n) : + Polynomial (ZMod q) := + ∑ j, (s.M i j).1.toPoly * (z j).1.toPoly + +/-- Evaluation of a `Zq[X]`-polynomial at `a ∈ F` through the base-field embedding `φF` +(the `Rq → Zq[X] → F` bridge of milestone F3). -/ +noncomputable def evalAt (φF : ZMod q →+* F) (a : F) : Polynomial (ZMod q) →+* F := + Polynomial.eval₂RingHom φF a + +/-- **The lift's output relation** (Hachi Figure 4 / Lemma 9 residual claims, at the fixed +challenge `α` of the transcript): `w̃ = (z, ρ)` opens `t`; every lifted row vanishes at `α`, +i.e. `∑ⱼ Mᵢⱼ(α)·zⱼ(α) = yᵢ(α) + (α^d + 1)·ρᵢ(α)`; and `w̃` is short. The range claims are +*witness-level* — proven downstream by the zero-check/sumcheck stages and consumed upstream by +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)) + (φF : ZMod q →+* F) : + Set (LiftStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := + {p | + K.com p.2 = p.1.2.1 ∧ + (∀ i, evalAt φF p.1.2.2 (rowSum Φ p.1.1 p.2.z i) = + evalAt φF p.1.2.2 ((p.1.1.yvec i).1.toPoly) + + evalAt φF p.1.2.2 Φ.φ.toPoly * evalAt φF p.1.2.2 (p.2.ρ i)) ∧ + 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)) + (φF : ZMod q →+* F) + +/-- The lift's verifier (Hachi Figure 4): a **pure pass-through** extending the statement by the +round-0 commitment `t` and the round-1 challenge `α`. All checks constrain the never-sent +witness and live in `relLift`. -/ +def liftVerifier : + Verifier oSpec (RlinStatement Φ n μ) (LiftStatement Φ K.TCom F n μ) + (pSpecScalar K.TCom F) where + verify := fun stmt tr => pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) + +/-- The honest prover skeleton (Hachi Figure 4; completeness is out of scope for Lemma 9): round +0 sends `t := Com(w̃)` for the honestly lifted witness, round 1 receives `α`, and the output +witness is `w̃` itself. The honest computations (quotient extraction `ρᵢ := (∑ Mᵢⱼzⱼ − yᵢ) /ₘ φ` +and the commitment) are the parameters `computeW`/`computeT`, to be instantiated by the +completeness layer from the F3 quotient-lift algebra. -/ +def liftProver (WitIn : Type) + (computeW : RlinStatement Φ n μ → WitIn → LiftedWitness Φ μ n) + (computeT : RlinStatement Φ n μ → WitIn → K.TCom) : + Prover oSpec (RlinStatement Φ n μ) WitIn (LiftStatement Φ K.TCom F n μ) + (LiftedWitness Φ μ n) (pSpecScalar K.TCom F) where + PrvState + | 0 => RlinStatement Φ n μ × WitIn + | 1 => RlinStatement Φ n μ × WitIn + | 2 => (RlinStatement Φ n μ × WitIn) × F + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeT st.1 st.2, st) + | ⟨1, h⟩ => nomatch h + receiveChallenge + | ⟨0, h⟩ => nomatch h + | ⟨1, _⟩ => fun st => pure fun c => (st, c) + output := fun ⟨⟨stmt, wit⟩, c⟩ => + pure ((stmt, computeT stmt wit, c), computeW stmt wit) + +variable [SampleableType F] + +/-- **Hachi Lemma 9 (skeleton): CWSS of the HMZ25 lift at `k = 2d`.** + +**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; +* 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`. + +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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hd : 0 < Φ.φ.natDegree) : + (liftVerifier (oSpec := oSpec) Φ bound ρBound K).coordinateWiseSpecialSound init impl + (scalarStructure (2 * Φ.φ.natDegree) (by omega)) + (relRlinE Φ (n := n) (μ := μ) K.esc) + (relLiftE Φ bound ρBound K φF) := by + sorry + +/-- **The HMZ25 lift as a `CWSSPackage`** (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)) + (hd : 0 < Φ.φ.natDegree) : + CWSSPackage init impl + (RlinStatement Φ n μ) (PolyVec (Rq Φ) μ ⊕ E) + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + (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 + 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 + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean new file mode 100644 index 0000000000..cd9e30ac80 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean @@ -0,0 +1,158 @@ +/- +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 + +/-! + # Eq. (20) → `R^lin` adapter — skeleton (Hachi §4.3 entry; sumcheck-track milestone F2) + + Hachi §4.3 proves knowledge of a short solution of an **unstructured linear relation** + + `R^lin := {(z, (M, y)) : M z = y ∧ ‖z‖∞ ≤ bound}` ([NOZ26] §4.3, `R^lin_{q,d,n,μ,b}`) + + and Eq. (20) — the output of the `QuadEval` reduction — *is* such an instance: stacking the + response `ζ := (ŵ, flatten t̂, ẑ)` as one column and the five verification rows as one block + matrix: + + ``` + ŵ 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 | (cᵀ ⊗ G_{n_A}) | −(A J) ] = 0 + ``` + + (c6, the `S_b` range checks, becomes the `‖ζ‖∞ ≤ bound` conjunct.) This file is the zero-round + `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 + (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`. + + ## 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 + +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} + +/-- Column count `μ` of the Eq. (20) block system: the stacked witness +`ζ = ŵ ++ (flatten t̂ ++ ẑ)`. Associativity fixed once, here (F2.2 convention pin). -/ +abbrev rlinCols (innerRows messageDigits innerDigits zDigits m r : Nat) : Nat := + 2 ^ r * messageDigits + (2 ^ r * (innerRows * innerDigits) + 2 ^ m * messageDigits * zDigits) + +/-- Row count `n` of the Eq. (20) block system: the stacked rows `c1 ++ (c2 ++ (c3 ++ (c4 ++ +c5)))`. Associativity fixed once, here (F2.2 convention pin). -/ +abbrev rlinRows (innerRows outerRows dRows : Nat) : Nat := + dRows + (outerRows + (1 + (1 + innerRows))) + +/-- The `R^lin` statement ([NOZ26] §4.3): a public matrix `M`, a public right-hand side `yvec`, +and a public `ℓ∞`-norm bound on the witness. -/ +structure RlinStatement (Φ : CyclotomicModulus (ZMod q)) (n μ : ℕ) where + /-- The public matrix `M ∈ Rq^{n×μ}`. -/ + M : PolyMatrix (Rq Φ) n μ + /-- The public right-hand side `y ∈ Rq^n`. -/ + yvec : PolyVec (Rq Φ) n + /-- The public `ℓ∞`-norm bound on the witness (`b − 1` in the paper's `R^lin`; `γ` in this + chain, inherited from Eq. (20)'s c6). -/ + bound : ℕ + +/-- **The `R^lin` relation** ([NOZ26] §4.3): `M ζ = y` and `‖ζ‖∞ ≤ bound`. -/ +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 +`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) (ω γ : ℕ) + (X : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows × + CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) : + RlinStatement Φ (rlinRows innerRows outerRows dRows) + (rlinCols innerRows messageDigits innerDigits zDigits m r) := + sorry + +/-- **Witness unstacking** (the bridge's `mapWitInv`): split a stacked `ζ ∈ Rq^μ` back into the +`QuadEvalResponse` triple `(ŵ, t̂, ẑ)` (`Fin.addCases` splits + `finProdFinEquiv` un-flatten, +inverse to the stacking convention pinned at `rlinCols`). + +**Sorried (F2.2).** -/ +def unstack + (ζ : PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r)) : + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits := + sorry + +/-- **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. + +**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) + (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 + 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) : + 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) + (RlinStatement Φ (rlinRows innerRows outerRows dRows) + (rlinCols innerRows messageDigits innerDigits zDigits m r)) + (PolyVec (Rq Φ) (rlinCols innerRows messageDigits innerDigits zDigits m r) ⊕ E) + (!p[] : ProtocolSpec 0) where + verifier := ReduceClaim.verifier oSpec (rlinStmt (zDigits := zDigits) Φ 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) + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck.lean new file mode 100644 index 0000000000..bd12af36ee --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck.lean @@ -0,0 +1,54 @@ +/- +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.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 +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 +recursion (`Recursion/`). It operates on the shared batched-constraint encoding of +`ZeroCheck/Constraints.lean` (the sumcheck polynomials `F_{0,τ₀}`/`F_{α,τ₁}` and `roundRel`). + +## TODO — reuse the existing structured sum-check + +This subprotocol should be **rebased onto `ArkLib/ProofSystem/Sumcheck/Structured`** rather than +carrying bespoke round machinery. Concretely: the round polynomials `F_{0,τ₀}`/`F_{α,τ₁}` are +instances of `Sumcheck.Structured.computeRoundPoly` via `SumcheckMultiplierParam` (identity +combinator for the degree-2 linear check `F_α`; the range combinator `∏ⱼ (X − j)` of degree `2b` +for `F₀` — the multiplier docstring in `Structured.lean` already anticipates exactly this Hachi +case); the round consistency is `Sumcheck.Structured.sumcheckConsistencyProp` over +`SumcheckDomain.boolDomain`; the per-round data is `Structured.Statement`/`SumcheckWitness`. The +**exact CWSS round verifier is left `sorry` for now** (`Sumcheck/Rounds.lean`): wiring the +structured round into the CWSS chain first needs the wire format (`!v[...]`) and the +record-then-bridge convention reconciled with the structured round's `![...]` RBR verifier +(`Structured/SingleRound.lean`'s `roundOracleVerifier`), which is deferred. + +## Folder structure + +* `Sumcheck/Bridge.lean` — the zero-round **entry bridge**: from the zero-check's point-evaluation + claims to the *initial* sumcheck hypercube-sum claims (`∑ F_{0,τ₀} = 0`, `∑ F_{α,τ_α} = a` with + the linear target `a` computed by the verifier); the paper's "finish the proof using sumcheck + 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**). +* `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`. + +## References + +* [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/Sumcheck/Bridge.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean new file mode 100644 index 0000000000..dfe2b52cf6 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean @@ -0,0 +1,87 @@ +/- +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.ZeroCheck.Reduction + +/-! + # Sumcheck bridge — point claims to hypercube sums — skeleton (zero-round) + + 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 + points; + * `relOut = roundRelE 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. + + The statement map installs the empty challenge prefix and the initial target pair + `(0, zcTargetAlpha)`. The bridge is pure reshaping — the two directions are the algebraic + identities `∑ F_{0,τ₀} = H₀(τ₀)` and `∑ F_{α,τ_α} = H_α(τ_α) + zcTargetAlpha` + (`sum_sumcheckPolyZero` / `sum_sumcheckPolyAlpha`, `ZeroCheck/Constraints.lean`) — so the + only sorried piece is the pull-back through those (themselves sorried F5) identities. + + ## 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 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 (m₀ m₁ : ℕ) (bound ρBound : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The bridge's statement map: install the empty challenge prefix and the initial target pair +`(0, zcTargetAlpha)` on the zero-check statement. -/ +noncomputable def toRoundStatement {TCom : Type} (φF : ZMod q →+* F) + (s : ZeroCheckStatement Φ TCom F n μ) : RoundStatement Φ TCom F n μ 0 := + ⟨s, fun j => j.elim0, 0, zcTargetAlpha Φ m₁ φF s.rlin s.α (kroneckerPoint m₁ s.seedα)⟩ + +/-- **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. + +**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)) + (φ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 + 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. -/ +noncomputable def sumcheckBridgePackage (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (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) + (!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 + 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) + (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) + (mem_relZeroCheckE_of_roundRelE Φ m₀ m₁ bound ρBound K φF b) + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean new file mode 100644 index 0000000000..b1b41506ec --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean @@ -0,0 +1,178 @@ +/- +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.Sumcheck.Rounds + +/-! + # Final evaluation — Hachi Figure 7 tail — skeleton (milestone F8) + + The step closing the sumcheck loop ([NOZ26] Figure 7, "Open `t` to evaluate + `w̃(a₁, …, a_ℓ)`; evaluate `M̃_α(a₁, …, a_ℓ)`; check the correctness of the sumcheck"): + + * **message (P→V)** — the claimed evaluation `y′ := w̃(a₁, …, a_{m₀}) ∈ F`, sent in the + clear; + * **check (guarded)** — the verifier evaluates the *public* factors at the challenge point — + `eq̃(τ₀, a)`, the range product at `y′`, `α̃`, and `∑ᵢ eq̃(τ_α, i)·M̃_α(i, a)` (the paper's + expensive `Õ(√(2^ℓ)·λ)` step) — and checks both final sumcheck targets: + `eq̃(τ₀,a)·P_b(y′)·… = target₀` and `y′·α̃(a)·(∑ᵢ eq̃(τ_α,i)M̃_α(i,a)) = target_α`, plus the + bound-sanity conjunct. The verifier must be **guarded** (design D6): the check reads the + final targets, which the output statement drops — it can live neither downstream nor in a + pull-back. + * **output** — the *evaluation claim* `WEvalStatement`: the commitment `t`, the sumcheck point + `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 + 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. + + ## 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 OracleComp OracleSpec ProtocolSpec CoordinateWise + +/-- The final-evaluation wire format: one prover message carrying the claimed evaluation +`y′ ∈ F`. -/ +@[reducible] def pSpecFinalEval (F : Type) : ProtocolSpec 1 := + ⟨!v[.P_to_V], !v[F]⟩ + +instance {F : Type} : IsEmpty (pSpecFinalEval F).ChallengeIdx := + ⟨fun ⟨0, h⟩ => nomatch h⟩ + +instance {F : Type} [SampleableType F] : + ∀ i, SampleableType ((pSpecFinalEval F).Challenge i) := + fun i => isEmptyElim i + +/-- **The evaluation-claim statement** — the recursion currency after one full §4.3 pass: the +`w̃`-commitment `t`, the sumcheck point `a ∈ F^{m₀}`, and the claimed multilinear evaluation +`y′`. Everything else (the `R^lin` data, `α`, the seeds, the targets) is dropped — which is +exactly why the final check must be a runtime guard. -/ +structure WEvalStatement (TCom F : Type) (m₀ : ℕ) where + /-- The `w̃`-commitment from the lift stage. -/ + t : TCom + /-- The sumcheck challenge point `a = (a₁, …, a_{m₀})`. -/ + point : Fin m₀ → F + /-- The claimed evaluation `y′ = mle[w̃](a)`. -/ + value : F + +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 (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The final check ([NOZ26] Figure 7 tail): both final sumcheck targets against the public +factors evaluated at the point, with the claimed `y′` in place of `w̃(a)`, plus the bound-sanity +conjunct `bound ≤ rlin.bound`. All parameters the future implementation reads are pinned +explicitly. **Sorried (F8)** — the verifier's expensive public-evaluation step (`M̃_α` via +dynamic programming). -/ +def finalCheck {TCom : Type} (m₁ bound b : ℕ) (φF : ZMod q →+* F) + (stmt : RoundStatement Φ TCom F n μ m₀) (y' : F) : Bool := + sorry + +/-- The final-evaluation verifier: **guarded** on `finalCheck`, outputting the evaluation claim +`⟨t, a, y′⟩`. -/ +def finalEvalVerifier {TCom : Type} (φF : ZMod q →+* F) : + Verifier oSpec (RoundStatement Φ TCom F n μ m₀) (WEvalStatement TCom F m₀) + (pSpecFinalEval F) where + verify := fun stmt tr => + if finalCheck Φ m₀ m₁ bound b φF stmt (tr 0) then + pure ⟨stmt.zc.t, stmt.challenges, tr 0⟩ + else failure + +omit [NeZero q] [IsCyclotomic Φ] in +/-- The final-evaluation verifier is guarded — definitionally, by `finalCheck`. -/ +theorem finalEvalVerifier_isGuarded {TCom : Type} (φF : ZMod q →+* F) : + (finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (n := n) (μ := μ) (TCom := TCom) + φF).IsGuarded := + ⟨fun stmt tr => finalCheck Φ m₀ m₁ bound b φF stmt (tr 0), + fun stmt tr => ⟨stmt.zc.t, stmt.challenges, tr 0⟩, + fun _ _ => rfl⟩ + +/-- The honest final-evaluation prover skeleton: sends `y′ := mle[w̃](a)` (the parameter +`computeY`, honestly `wTableMleEval`) and carries `w̃` forward as the output witness. -/ +def finalEvalProver {TCom : Type} + (computeY : RoundStatement Φ TCom F n μ m₀ → LiftedWitness Φ μ n → F) : + Prover oSpec (RoundStatement Φ TCom F n μ m₀) (LiftedWitness Φ μ n) + (WEvalStatement TCom F m₀) (LiftedWitness Φ μ n) (pSpecFinalEval F) where + PrvState + | 0 => RoundStatement Φ TCom F n μ m₀ × LiftedWitness Φ μ n + | 1 => RoundStatement Φ TCom F n μ m₀ × LiftedWitness Φ μ n + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeY st.1 st.2, st) + receiveChallenge + | ⟨0, h⟩ => nomatch h + output := fun ⟨stmt, wit⟩ => + pure (⟨stmt.zc.t, stmt.challenges, computeY stmt wit⟩, wit) + +/-- **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)) + (φ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).** + +**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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) : + (finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (TCom := K.TCom) + φF).coordinateWiseSpecialSound init impl + CWSSStructure.ofIsEmpty + (roundRelE Φ m₀ m₁ bound ρBound K φF b m₀) + (relWEvalClaimE Φ m₀ bound ρBound b 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)) + (φF : ZMod q →+* F) : + GCWSSPackage init impl + (RoundStatement Φ K.TCom F n μ m₀) (LiftedWitness Φ μ n ⊕ E) + (WEvalStatement K.TCom F m₀) (LiftedWitness Φ μ n ⊕ E) + (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 + isGuarded := finalEvalVerifier_isGuarded Φ m₀ m₁ bound b φF + isCWSS := finalEval_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound b init impl K φF + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean new file mode 100644 index 0000000000..cc181915c8 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean @@ -0,0 +1,274 @@ +/- +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.Sumcheck.Bridge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded + +/-! + # Paired sumcheck rounds — Hachi Figure 6 / Lemma 11 — skeleton (milestone F7) + + The sumcheck loop of Hachi §4.3: `m₀` rounds, each reducing the pair of + partial-hypercube-sum claims (`roundRel i`, `ZeroCheck/Constraints.lean`) by one variable. + + ## Paired rounds (design D9) + + Figure 7 runs the `H₀`- and `H_α`-sumchecks with **shared challenges**: each round's message + is the *pair* of univariate round polynomials `(g_i^{(0)}, g_i^{(α)})` (degrees + `roundDegZero b = 2b` resp. `roundDegAlpha = 2`), followed by one scalar challenge + `a_i ← F` — the `pSpecScalar (RoundMsg F b) F` wire format. + + ## Guarded round verifiers (designs D6/R10) + + The round check `g_i(0) + g_i(1) = target_{i−1}` (for both components) reads the *previous* + target, which the next round's statement **drops** — so it cannot live in the output relation, + and a pure-with-dummy convention destroys extractability (all siblings of a tree node share + the message `g_i`, so a failed check would collapse every branch onto the same dummy — plan + risk R10). The round verifier is therefore **guarded**: `failure` on a failed check, which is + exactly the paper's "valid transcripts" premise for Lemma 11. + + ## 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 + `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 + package), so the only composition machinery it consumes is `Guarded.lean`'s B4 skeleton. + + **Sorried**: the per-round CWSS theorem `round_coordinateWiseSpecialSound` (Lemma 11). + + ## 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 OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.ScalarRound + +section Wire + +variable (F : Type) [Field F] (b : ℕ) + +/-- A round message: the pair of univariate round polynomials `(g_i^{(0)}, g_i^{(α)})`, degree- +bounded by the R8 pins (`roundDegZero b = 2b`, `roundDegAlpha = 2`). -/ +@[reducible] def RoundMsg : Type := + ↥(Polynomial.degreeLE F (roundDegZero b : ℕ)) × ↥(Polynomial.degreeLE F (roundDegAlpha : ℕ)) + +/-- The concatenated wire format of `count` paired sumcheck rounds (each round is +`pSpecScalar (RoundMsg F b) F`: one message pair, one scalar challenge). -/ +def roundsSpec : (count : ℕ) → ProtocolSpec (2 * count) + | 0 => !p[] + | count + 1 => roundsSpec count ++ₚ pSpecScalar (RoundMsg F b) F + +/-- Challenges of the concatenated rounds are sampleable (by recursion over the append +instance, applied by name — the append instance does not fire automatically on the +equation-compiled `roundsSpec`). -/ +instance roundsSpecSampleable [SampleableType F] : + ∀ (count : ℕ) (i : (roundsSpec F b count).ChallengeIdx), + SampleableType ((roundsSpec F b count).Challenge i) + | 0, i => Fin.elim0 i.1 + | count + 1, i => + letI := roundsSpecSampleable count + ProtocolSpec.instSampleableTypeChallengeAppend + (pSpec₁ := roundsSpec F b count) (pSpec₂ := pSpecScalar (RoundMsg F b) F) i + +end Wire + +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 (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The round check ([NOZ26] Figure 6): both round polynomials sum to the current targets over +`{0, 1}`. `Bool`-valued (design G3) so the guarded-verifier witness is definitional. -/ +def roundCheck {TCom : Type} {i : ℕ} (stmt : RoundStatement Φ TCom F n μ i) + (g : RoundMsg F b) : Bool := + decide (g.1.1.eval 0 + g.1.1.eval 1 = stmt.target₀) && + decide (g.2.1.eval 0 + g.2.1.eval 1 = stmt.targetα) + +/-- The `i`-th round's **guarded** verifier ([NOZ26] Figure 6): on a passing check, extend the +challenge prefix by `a_i` and replace the targets by `(g_i^{(0)}(a_i), g_i^{(α)}(a_i))`; +otherwise `failure` (see the module docstring for why the check cannot live in the output +relation). -/ +def roundVerifier {TCom : Type} (i : ℕ) : + Verifier oSpec (RoundStatement Φ TCom F n μ i) (RoundStatement Φ TCom F n μ (i + 1)) + (pSpecScalar (RoundMsg F b) F) where + verify := fun stmt tr => + if roundCheck Φ b stmt (tr.messages ⟨0, rfl⟩) then + pure ⟨stmt.zc, Fin.snoc stmt.challenges (tr.challenges ⟨1, rfl⟩), + (tr.messages ⟨0, rfl⟩).1.1.eval (tr.challenges ⟨1, rfl⟩), + (tr.messages ⟨0, rfl⟩).2.1.eval (tr.challenges ⟨1, rfl⟩)⟩ + else failure + +omit [NeZero q] [IsCyclotomic Φ] in +/-- The round verifier is guarded — definitionally, by `roundCheck`. -/ +theorem roundVerifier_isGuarded {TCom : Type} (i : ℕ) : + (roundVerifier (oSpec := oSpec) Φ b (n := n) (μ := μ) (TCom := TCom) + (F := F) i).IsGuarded := + ⟨fun stmt tr => roundCheck Φ b stmt (tr.messages ⟨0, rfl⟩), + fun stmt tr => + ⟨stmt.zc, Fin.snoc stmt.challenges (tr.challenges ⟨1, rfl⟩), + (tr.messages ⟨0, rfl⟩).1.1.eval (tr.challenges ⟨1, rfl⟩), + (tr.messages ⟨0, rfl⟩).2.1.eval (tr.challenges ⟨1, rfl⟩)⟩, + fun _ _ => rfl⟩ + +/-- The `i`-th round's honest prover skeleton ([NOZ26] Figure 6; completeness out of scope): the +round-polynomial pair is the parameter `computeG` (honestly: the partial hypercube sums of the +two sumcheck polynomials in the free variable), and the output witness carries `w̃` forward. -/ +def roundProver {TCom : Type} (i : ℕ) + (computeG : RoundStatement Φ TCom F n μ i → LiftedWitness Φ μ n → RoundMsg F b) : + Prover oSpec (RoundStatement Φ TCom F n μ i) (LiftedWitness Φ μ n) + (RoundStatement Φ TCom F n μ (i + 1)) (LiftedWitness Φ μ n) + (pSpecScalar (RoundMsg F b) F) where + PrvState + | 0 => RoundStatement Φ TCom F n μ i × LiftedWitness Φ μ n + | 1 => RoundStatement Φ TCom F n μ i × LiftedWitness Φ μ n + | 2 => (RoundStatement Φ TCom F n μ i × LiftedWitness Φ μ n) × F + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeG st.1 st.2, st) + | ⟨1, h⟩ => nomatch h + receiveChallenge + | ⟨0, h⟩ => nomatch h + | ⟨1, _⟩ => fun st => pure fun c => (st, c) + output := fun ⟨⟨stmt, wit⟩, c⟩ => + pure (⟨stmt.zc, Fin.snoc stmt.challenges c, + (computeG stmt wit).1.1.eval c, (computeG stmt wit).2.1.eval c⟩, wit) + +variable [SampleableType F] + +/-- **Hachi Lemma 11 (skeleton): per-round CWSS of the paired sumcheck round at +`k = max (2b) 2 + 1`.** + +**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 +`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` +claims. Assembled via a guarded variant of the scalar-round machinery (F4.1 + B4.1's +`check_eq_true_of_guarded_accepting`). + +**TODO (reuse `Sumcheck/Structured`):** this round should be the existing structured sum-check +round (`ArkLib/ProofSystem/Sumcheck/Structured`) rather than the bespoke `roundVerifier` above — +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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (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 + 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)) + (φ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) + (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) + isGuarded := roundVerifier_isGuarded Φ b i + isCWSS := round_coordinateWiseSpecialSound Φ 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 +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. -/ +noncomputable def roundsChainAux (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (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) + (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 } + | 0 => + ⟨CWSSPackage.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 + 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) }, + rfl, rfl⟩ + | count + 1 => + let prev := roundsChainAux init impl K φF count + ⟨prev.1.append (roundPackage Φ m₀ m₁ bound ρBound b init impl K φF count) prev.2.2, + prev.2.1, rfl⟩ + +/-- **The composed sumcheck loop** (Hachi Figure 7's round phase), from the round-`0` seam +(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)) + (φ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) + (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)) + (φ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 := + (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)) + (φ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 := + (roundsChainAux Φ m₀ m₁ bound ρBound b init impl K φF count).2.2 + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean new file mode 100644 index 0000000000..e293809517 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean @@ -0,0 +1,48 @@ +/- +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.ZeroCheck.Reduction + +/-! +# Hachi Zero-Check (Figure 5 / corrected Lemma 10) + +Umbrella 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. + +## ⚠ The paper's Lemma 10 is repaired here + +Hachi's Lemma 10 (uniform-vector-challenge extraction) is **not provable as stated**: a +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`. + +## Folder structure + +* `ZeroCheck/Constraints.lean` — the **shared constraint encoding** (Eqs. (21)–(23)): the table + `w̃`, the batched polynomials `H₀`/`H_α`, the sumcheck polynomials `F_{0,τ₀}`/`F_{α,τ₁}` with + their degree pins, the Kronecker point `kroneckerPoint`, `hypercubeSum`, and `roundRel`. Consumed + by both this zero-check *and* the sumcheck round loop (`Sumcheck/`), so it sits at the shared + base of the batched-sumcheck machinery. Definitions only (**sorried**), with characterizing + lemmas stated alongside. +* `ZeroCheck/Batch.lean` — the zero-round **batching bridge** (entry head): reinterprets the lift's + per-row/per-entry residual claims as the two `MvPolynomial` identities `H₀ ≡ 0 ∧ H_α ≡ 0` + (`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 + `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`. + +## References + +* [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/ZeroCheck/Batch.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean new file mode 100644 index 0000000000..baa10c5633 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -0,0 +1,106 @@ +/- +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.ZeroCheck.Constraints + +/-! + # Batching bridge — Hachi Eqs. (22)–(23) — skeleton (zero-round, part of milestone F6) + + 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 + ranges (`RingSwitch/Reduction.lean`); + * `relOut = relBatchedE` — 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 + *reading* of the claims changes. This isolates the batching algebra away from the zero-check's + Kronecker interpolation: + + * **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`): + `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 + `IsDomain F` — a field here — and `2b − 1 < q` to read the field roots back as centered + `Zq`-representatives), which yields `liftShort` under the norm-parameter hypotheses. + + ## 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 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 (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)) + (φF : ZMod q →+* F) (b : ℕ) : + Set (LiftStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := + {p | + K.com p.2 = p.1.2.1 ∧ + hZero Φ m₀ φF b p.2 = 0 ∧ + 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. + +**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` +(faithfulness of the sorried `M̃_α`/table encodings, F5); `H₀ ≡ 0` ⇒ each table entry is a root +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)) + (φ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 + 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)) + (φ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) + (!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 + 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) + (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) + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean new file mode 100644 index 0000000000..afcf94aed9 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -0,0 +1,244 @@ +/- +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.RingSwitch.Reduction +import Mathlib.Algebra.MvPolynomial.Basic + +/-! + # Constraint encoding — Hachi Eqs. (21)–(23) — skeleton (sumcheck-track milestone F5) + + Definitions only (no protocol): the shared constraint-encoding layer consumed by the batching + bridge, the zero-check, the sumcheck rounds, and the final-evaluation step. + + ## The table `w̃` (Eq. (21)) + + The committed lifted witness `(z, ρ)` is re-read as a `Zq`-valued table `w̃` indexed by the + `m₀`-cube: rows are the `Zq`-coefficient vectors of the `zⱼ ∈ Rq` followed by the base-`b` + gadget digits of the quotients `ρᵢ`, columns are the `d` coefficient positions. **Arity pin + (F5)**: `2 ^ m₀` = (number of `z`-rows + number of `ρ`-digit rows) · `d`, padded to a power of + two; the paper's `τ₀ ← F^{log μ + log d}` undercounts its own index space `[μ+n] × [d]` — the + formalization fixes `m₀` as *the table's* log-size and `m₁` as the row-batching arity + (`2 ^ m₁ ≥ n` rows of the lifted system). Little-endian cube indexing throughout + (`CMlPolynomial`/`EvalSplit` convention). + + ## The batched constraint polynomials (Eqs. (22)–(23)) + + * `hAlpha` (Eq. (22)): the `eq̃`-batched *linear* constraint polynomial + `H_α(τ) := ∑ᵢ eq̃(τ, i)·(∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ) − ŷᵢ(α))`, multilinear in `m₁` + variables; `H_α ≡ 0` iff every lifted row vanishes at `α`. + * `hZero` (Eq. (23)): the `eq̃`-batched *range* polynomial + `H₀(τ) := ∑_{u,ℓ} eq̃(τ, (u,ℓ))·w̃(u,ℓ)·∏_{j=1}^{b−1}(w̃(u,ℓ) − j)(w̃(u,ℓ) + j)`, + multilinear in `m₀` variables; `H₀ ≡ 0` iff every table entry lies in `[−(b−1), b−1]` + (needs `2b − 1 < q` to read field-roots as centered representatives). + + ## The sumcheck polynomials and degree pins (design R8) + + `F_{0,τ₀} := eq̃(τ₀,·)·(range product)(w̃(·))` has per-variable degree `2b` (range product + `2b − 1` on the multilinear `w̃`, times the multilinear `eq̃`) — hence `k = 2b + 1` transcripts + per round; `F_{α,τ₁} := w̃(·)·α̃(·)·(∑ᵢ eq̃(τ₁,i)·M̃_α(i,·))` has per-variable degree `≤ 2`. + The repo docstring's `2b+1` round degree and the paper's `b+1` coefficient count are both + 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`) + + `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** + (binary expansion of exponents), so univariate root counting is information-complete — the + engine of the corrected zero-check (`ZeroCheck/Reduction.lean`). + + Everything protocol-shaped built on these defs lives in the subsequent files; the defs here are + **sorried** (their content is index bookkeeping over the F2.1 conventions plus `eqPolynomial`/ + `LinearMvExtension` algebra), with their characterizing lemmas stated sorried alongside. + + ## 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 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 (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. -/ +def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F := + fun j => ρ ^ (2 ^ (j : ℕ)) + +/-- Per-round univariate degree of the range sumcheck (`F_{0,τ₀}`): the `2b − 1`-factor range +product over the multilinear `w̃`, times the multilinear `eq̃` — degree `2b` (pin R8). -/ +def roundDegZero (b : ℕ) : ℕ := 2 * b + +/-- Per-round univariate degree of the linear sumcheck (`F_{α,τ₁}`): `w̃ · α̃ · (∑ eq̃·M̃_α)` — +at most two multilinear factors per variable, degree `≤ 2` (pin R8). -/ +def roundDegAlpha : ℕ := 2 + +/-! ## The table and the batched constraint polynomials (sorried F5 content) -/ + +/-- **The Eq. (21) table**: the committed `(z, ρ)` re-read as a `Zq`-valued function on the +`m₀`-cube — `Zq`-coefficient rows of the `zⱼ`, then base-`b` gadget-digit rows of the `ρᵢ`, +zero-padded to `2 ^ m₀`. **Sorried (F5)**: index bookkeeping over the F2.1 conventions plus the +`ρ`-digit decomposition (F3.4). -/ +def wTable (b : ℕ) (w : LiftedWitness Φ μ n) : Fin (2 ^ m₀) → ZMod q := + sorry + +/-- Evaluation of the multilinear extension of the table `w̃` at a point `a ∈ F^{m₀}` (through +the embedding `φF`): `mle[w̃](a) = ∑ᵢ w̃(i)·eq̃(i, a)`. **Sorried (F5).** -/ +def wTableMleEval (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) + (a : Fin m₀ → F) : F := + sorry + +/-- **Eq. (23)**: the `eq̃`-batched range polynomial +`H₀(τ) := ∑ᵢ eq̃(τ, i)·w̃(i)·∏_{j=1}^{b−1}(w̃(i) − j)(w̃(i) + j)` — multilinear in `τ ∈ F^{m₀}`; +`H₀ ≡ 0` iff every table entry is a root of the range product, i.e. lies in `[−(b−1), b−1]` +(under `2b − 1 < q`). **Sorried (F5).** -/ +def hZero (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : + MvPolynomial (Fin m₀) F := + sorry + +/-- **Eq. (22)**: the `eq̃`-batched linear constraint polynomial +`H_α(τ) := ∑ᵢ eq̃(τ, i)·(∑_u M̃_α(i,u)·(∑_ℓ w̃(u,ℓ)·α̃(ℓ)) − ŷᵢ(α))` — multilinear in +`τ ∈ F^{m₁}`, built from the public `M̃_α` (the multilinear extension of the `α`-evaluated +lifted matrix, including the `−(α^d + 1)` quotient columns) and the table `w̃`; `H_α ≡ 0` iff +every lifted row of `relLift` vanishes at `α`. **Sorried (F5).** -/ +def hAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₁) F := + sorry + +/-- `H₀` is multilinear (degree `≤ 1` in each batching variable — only `eq̃` depends on `τ`). +Load-bearing for the Kronecker pullback degree bound `< 2 ^ m₀`. **Sorried (F5).** -/ +theorem hZero_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) + (j : Fin m₀) : (hZero Φ m₀ φF b w).degreeOf j ≤ 1 := by + sorry + +/-- `H_α` is multilinear (degree `≤ 1` in each batching variable). Load-bearing for the +Kronecker pullback degree bound `< 2 ^ m₁`. **Sorried (F5).** -/ +theorem hAlpha_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (w : LiftedWitness Φ μ n) (j : Fin m₁) : + (hAlpha Φ m₁ φF b s a w).degreeOf j ≤ 1 := by + sorry + +/-! ## The sumcheck polynomials (sorried F5 content) + +**TODO (reuse `Sumcheck/Structured`):** `sumcheckPolyZero`/`sumcheckPolyAlpha` should be defined as +`Sumcheck.Structured.computeRoundPoly` instances rather than from scratch — `F_α` via the identity +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. -/ + +/-- **`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 +read through its multilinear extension. Satisfies `∑_{x ∈ {0,1}^{m₀}} F_{0,τ₀}(x) = H₀(τ₀)` +(`sum_sumcheckPolyZero`). Per-variable degree `roundDegZero b = 2b`. **Sorried (F5).** -/ +def sumcheckPolyZero (φF : ZMod q →+* F) (b : ℕ) (τ₀ : Fin m₀ → F) + (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₀) F := + sorry + +/-- **`F_{α,τ₁}`** (the linear sumcheck summand): +`F_{α,τ₁}(x) := w̃(x)·α̃(x)·(∑ᵢ eq̃(τ₁, i)·M̃_α(i, x))`. Satisfies +`∑_{x ∈ {0,1}^{m₀}} F_{α,τ₁}(x) = H_α(τ₁) + zcTargetAlpha` (`sum_sumcheckPolyAlpha`). +Per-variable degree `roundDegAlpha = 2`. **Sorried (F5).** -/ +def sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (τ₁ : Fin m₁ → F) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₀) F := + sorry + +/-- The public initial target of the linear sumcheck: `a := ∑ᵢ eq̃(τ₁, i)·ŷᵢ(α)` — computable +by the verifier from the statement alone ([NOZ26] §4.3). **Sorried (F5).** -/ +def zcTargetAlpha (φF : ZMod q →+* F) (s : RlinStatement Φ n μ) (a : F) + (τ₁ : Fin m₁ → F) : F := + sorry + +/-- Partial hypercube sum: `hypercubeSum H i cs = ∑_{x ∈ {0,1}^{m₀ − i}} H(cs, x)` — the claim +currency of the `i`-th sumcheck round (round `0` is the full sum; round `m₀` is the point +evaluation `H(cs)`). **Sorried (F5).** -/ +def hypercubeSum (H : MvPolynomial (Fin m₀) F) (i : ℕ) (cs : Fin i → F) : F := + sorry + +/-- The sum of `F_{0,τ₀}` over the cube is `H₀(τ₀)` — the algebraic identity behind the +sumcheck bridge (`Sumcheck/Bridge.lean`). **Sorried (F5).** -/ +theorem sum_sumcheckPolyZero (φF : ZMod q →+* F) (b : ℕ) (τ₀ : Fin m₀ → F) + (w : LiftedWitness Φ μ n) : + hypercubeSum m₀ (sumcheckPolyZero Φ m₀ φF b τ₀ w) 0 (fun j => j.elim0) = + MvPolynomial.eval τ₀ (hZero Φ m₀ φF b w) := by + sorry + +/-- The sum of `F_{α,τ₁}` over the cube is `H_α(τ₁) + zcTargetAlpha` — the algebraic identity +behind the sumcheck bridge. **Sorried (F5).** -/ +theorem sum_sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (τ₁ : Fin m₁ → F) (w : LiftedWitness Φ μ n) : + hypercubeSum m₀ (sumcheckPolyAlpha Φ m₀ m₁ φF b s a τ₁ w) 0 (fun j => j.elim0) = + MvPolynomial.eval τ₁ (hAlpha Φ m₁ φF b s a w) + zcTargetAlpha Φ m₁ φF s a τ₁ := by + sorry + +/-! ## 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). -/ +structure ZeroCheckStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) where + /-- The `R^lin` statement (carrying the public `M`, `yvec`, `bound`). -/ + rlin : RlinStatement Φ n μ + /-- The `w̃`-commitment from the lift stage. -/ + t : TCom + /-- The HMZ25 evaluation challenge `α` from the lift stage. -/ + α : F + /-- The Kronecker seed `ρ₀` of the range zero-check. -/ + seed₀ : F + /-- The Kronecker seed `ρ_α` of the linear zero-check. -/ + seedα : F + +/-- The statement after `i` (paired) sumcheck rounds: the zero-check statement, the challenges +`a₁, …, aᵢ` so far, and the current pair of sumcheck targets `(z_i^{(0)}, z_i^{(α)})` +([NOZ26] Figure 6's `z_{i−1} ↦ z_i := g_i(a_i)`, for both parallel sumchecks). -/ +structure RoundStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) + (i : ℕ) where + /-- The zero-check statement (public data, commitment, `α`, Kronecker seeds). -/ + zc : ZeroCheckStatement Φ TCom F n μ + /-- The sumcheck challenges so far. -/ + challenges : Fin i → F + /-- The current target of the range sumcheck. -/ + target₀ : F + /-- The current target of the linear sumcheck. -/ + targetα : F + +variable (bound ρBound : ℕ) + +/-- **The per-round seam relation** of the paired sumcheck (the CWSS currency of [NOZ26] +Lemma 11): `w̃` opens `t`, and both partial-hypercube-sum claims at the current challenge prefix +equal the current targets. Round `0` (full sums) is produced by the sumcheck bridge; round `m₀` +(point evaluations) is consumed by the final-evaluation step. The public sanity conjunct +`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)) + (φF : ZMod q →+* F) (b : ℕ) (i : ℕ) : + Set (RoundStatement Φ K.TCom F n μ i × LiftedWitness Φ μ n) := + {p | + K.com p.2 = p.1.zc.t ∧ + hypercubeSum m₀ (sumcheckPolyZero Φ m₀ φF b (kroneckerPoint m₀ p.1.zc.seed₀) p.2) i + p.1.challenges = p.1.target₀ ∧ + hypercubeSum m₀ + (sumcheckPolyAlpha Φ m₀ m₁ φF b p.1.zc.rlin p.1.zc.α (kroneckerPoint m₁ p.1.zc.seedα) + 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 new file mode 100644 index 0000000000..c8574942b3 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -0,0 +1,196 @@ +/- +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.ZeroCheck.Batch + +/-! + # Zero-check — Hachi Figure 5 / **corrected** Lemma 10 — skeleton (milestone F6) + + One challenge round reducing the batched polynomial identities `H₀ ≡ 0 ∧ H_α ≡ 0` to their + evaluations at random points: the verifier samples the points, and the (never-sent) committed + `w̃` must make both evaluations zero. + + ## ⚠ The paper's Lemma 10 is not provable as stated — this file implements the repair + + Hachi's Lemma 10 claims CWSS of this round from an `SS(F, 2, max(2d, 2b−1))` star of + transcripts with **uniform vector challenges** `(τ₀, τ₁) ∈ F^{m₀} × F^{m₁}`. That claim is + **false**: a coordinate-wise star certifies only that the multilinear `H` vanishes on the + *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). + + **Adopted repair (one round, Kronecker curve):** sample two independent scalar **seeds** + `(ρ₀, ρ_α) ← F²` and derive the evaluation points on the Kronecker curves + + `τ₀ := κ_{m₀}(ρ₀) = (ρ₀, ρ₀², ρ₀⁴, …)`, `τ_α := κ_{m₁}(ρ_α)`. + + The pullback of an `m`-variate multilinear polynomial along `κ_m` is univariate of degree + `< 2^m` and the pullback is **injective** (binary expansion of exponents: + `LinearMvExtension.powAlgHom`), so ordinary univariate root counting becomes + information-complete. The challenge is modeled as the **seed pair** `F × F` with an + `(ℓ, k) = (2, D)` CWSS structure, `D := max 2 (max 2^{m₀} 2^{m₁})`: an `SS(F, 2, D)` star has + `2D − 1` members — `D` distinct seeds on each arm — and each arm interpolates one pullback. + The checked equations, `H₀`/`H_α`, and all downstream sumcheck formulas are unchanged; what + changes is the challenge *distribution* (curve-supported rather than uniform) and the error + scale (`D/|F|` rather than `m/|F|` — requires `D ≤ |F|`, i.e. a large enough extension field). + **This is the one place the formalization deliberately changes the paper's protocol to repair + its proof.** + + ## Protocol shape + + A single `V_to_P` round carrying `(ρ₀, ρ_α) : F × F`; no prover message. The verifier is a + pure pass-through extending the lift statement by the seeds (`ZeroCheckStatement`); the + evaluation claims constrain the never-sent `w̃` and live in `relZeroCheck`. This block runs at + the *fixed* `α` produced by the lift — the lift's `α`-fork remains a separate, earlier CWSS + 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). + + ## 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 OracleComp OracleSpec ProtocolSpec CoordinateWise + +/-- The zero-check's wire format: one verifier challenge carrying the **Kronecker seed pair** +`(ρ₀, ρ_α) ∈ F × F` (corrected Lemma 10; the batching points are derived on the curves). -/ +@[reducible] def pSpecZeroCheck (F : Type) : ProtocolSpec 1 := + ⟨!v[.V_to_P], !v[F × F]⟩ + +section Instances + +variable {F : Type} [SampleableType F] + +instance : ∀ i, SampleableType ((pSpecZeroCheck F).Challenge i) + | ⟨0, _⟩ => (inferInstance : SampleableType (F × F)) + +end Instances + +/-- **The corrected Lemma 10 CWSS structure**: the seed-pair challenge decomposes into `ℓ = 2` +scalar coordinates over the alphabet `F`, with soundness parameter +`k = D := max 2 (max 2^{m₀} 2^{m₁})` — the maximum padded constraint-table size (NOT the +paper's `max(2d, 2b−1)`, whose provenance is the Lemma 9 interpolation resp. the Lemma 11 round +degree). Star arity `2·(D−1)+1 = 2D−1`. -/ +def zeroCheckStructure (F : Type) (m₀ m₁ : ℕ) : CWSSStructure (pSpecZeroCheck F) where + coordIndex := fun _ => ⟨2, by omega⟩ + alphabet := fun _ => F + decompose := fun i => match i with + | ⟨0, _⟩ => (piFinTwoEquiv fun _ => F).symm + soundnessParam := fun _ => ⟨max 2 (max (2 ^ m₀) (2 ^ m₁)), le_max_left _ _⟩ + arity := fun _ => 2 * (max 2 (max (2 ^ m₀) (2 ^ m₁)) - 1) + 1 + arity_eq := rfl + +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 (m₀ m₁ : ℕ) (bound ρBound : ℕ) +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- The zero-check verifier (corrected Figure 5): a pure pass-through extending the lift +statement by the two Kronecker seeds. The evaluation claims constrain the never-sent `w̃` and +live in `relZeroCheck`. -/ +def zeroCheckVerifier {TCom : Type} : + Verifier oSpec (LiftStatement Φ TCom F n μ) (ZeroCheckStatement Φ TCom F n μ) + (pSpecZeroCheck F) where + verify := fun stmt tr => + pure ⟨stmt.1, stmt.2.1, stmt.2.2, + (tr.challenges ⟨0, rfl⟩).1, (tr.challenges ⟨0, rfl⟩).2⟩ + +/-- The zero-check prover (trivial: the round is challenge-only; the honest prover just absorbs +the seeds and carries its lifted witness forward as the output witness). -/ +def zeroCheckProver {TCom : Type} : + Prover oSpec (LiftStatement Φ TCom F n μ) (LiftedWitness Φ μ n) + (ZeroCheckStatement Φ TCom F n μ) (LiftedWitness Φ μ n) (pSpecZeroCheck F) where + PrvState + | 0 => LiftStatement Φ TCom F n μ × LiftedWitness Φ μ n + | 1 => (LiftStatement Φ TCom F n μ × LiftedWitness Φ μ n) × (F × F) + input := id + sendMessage + | ⟨0, h⟩ => nomatch h + receiveChallenge + | ⟨0, _⟩ => fun st => pure fun c => (st, c) + output := fun ⟨⟨stmt, wit⟩, c⟩ => + pure (⟨stmt.1, stmt.2.1, stmt.2.2, c.1, c.2⟩, wit) + +/-- **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)) + (φF : ZMod q →+* F) (b : ℕ) : + Set (ZeroCheckStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := + {p | + K.com p.2 = p.1.t ∧ + MvPolynomial.eval (kroneckerPoint m₀ p.1.seed₀) (hZero Φ m₀ φF b p.2) = 0 ∧ + MvPolynomial.eval (kroneckerPoint m₁ p.1.seedα) + (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)) + (φ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 + +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 + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (φF : ZMod q →+* F) (b : ℕ) : + (zeroCheckVerifier (oSpec := oSpec) Φ (n := n) (μ := μ) (F := F) + (TCom := K.TCom)).coordinateWiseSpecialSound init impl + (zeroCheckStructure F m₀ m₁) + (relBatchedE Φ m₀ m₁ bound ρBound K φF b) + (relZeroCheckE Φ 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)) + (φF : ZMod q →+* F) (b : ℕ) : + CWSSPackage init impl + (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) + (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 + 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 + +end Protocol + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html new file mode 100644 index 0000000000..d1a93e59cb --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html @@ -0,0 +1,799 @@ +Hachi — the evaluation opening, seam by seam + + + + +
+ +
+
+ + ArkLib · Commitments / Functional / Hachi · Lean 4 +
+

The Hachi evaluation opening, seam by seam

+

+ Hachi [NOZ26] is a lattice-based multilinear polynomial commitment over the + power-of-two cyclotomic ring Z_q[X]/(X^{2^α}+1). The opening is a + single line of nine reductions — starting at the quadratic-form evaluation + (QuadEval) and ending at a bare multilinear-evaluation claim on the committed table. + This map places every protocol-bearing file on that line, marks the bridges and + subfolders, and shows exactly what is proven or in progress. +

+
+ 28 files + 22 with protocol content + 9-link opening chain + rows 1–2 sorry-free + ≈24 open sorries +
+
+
+ +
+ +
+ + +
+
+ At a glance +

Where the formalization stands

+

Counts are over the 22 files carrying definitions or theorems (the six folder umbrella + re-export files are excluded). Status reflects real sorry proof terms, not the word + appearing in a docstring.

+
+
+
+ +
+ Proven — sorry-free & complete + WIP — theorems stated, proofs skeletal +
+
+
+ +
+ + +
+
+ Reading the line +

Node types & status keys

+
+
+
+

On the reduction line

+
+ 2 + Subprotocol step — a numbered link that runs a real interactive round (a message and/or a challenge) and carries a coordinate-wise-special-soundness (CWSS) extractor. +
+
+ + Bridge / adapter bridge — a zero-round link that only reshapes the statement to glue one subprotocol's output relation to the next one's input. Sound for any structure. +
+
+ 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 ▷ᵍ. +
+
+ A / B + Subfolder — the accented segment of each file path is its Hachi/ subfolder; every subfolder is one paper subprotocol. +
+
+
+

Proof status

+
+ proven + Sorry-free and complete. The QuadEval subprotocol (Lemma 8) is even axiom-clean — only propext / Classical.choice / Quot.sound. +
+
+ wip + A real skeleton: types, relations, verifier and package are defined and typecheck; the CWSS proof (and sometimes the encoding defs) are sorry. +
+
+ planned + Named in the composition roadmap but not yet a file — future heads, tails, and the completeness layer. +
+
+
+
+ +
+ + +
+
+ The composed opening +

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.

+
+
+
+ +
+ + +
+
+ Off the line +

The substrate & the cross-cutting machinery

+

These files are not links in the chain, but the chain cannot stand without them: the proven mathematical + base the line opens against, and the wiring that composes and threads it.

+
+
Proven substrate — the commitment & algebra the line opens against
+
+
Cross-cutting — threading, shared encoding, composition & the scheme interface
+
+
+ +
+ + +
+
+ Complete accounting +

Every file in Hachi/

+

All 28 files, including the six folder umbrellas. Filter by status; the row column is the + position on the reduction line above.

+
+
+ + + + +
+
+ + + + + +
FileRolePaperRowStatussorry
+
+
+ +
+ + +
+
+ Still to do +

Beyond the current line

+

Work named in the Composition.lean roadmap that has no file yet — the outer interface, + the completeness layer, and the one piece of generic machinery the guarded seams wait on.

+
+
+
+ +
+ + + + diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean new file mode 100644 index 0000000000..9e6f69695d --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean @@ -0,0 +1,77 @@ +/- +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 + +/-! + # Escape-threaded relations (`Set.withEscape`) + + Protocol-agnostic plumbing for **escape threading** in composed special-soundness chains + (Hachi [NOZ26] §4.3+; design decision G1 of the sumcheck-track plan). + + 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. + + 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`. + + ## References + + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + 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 diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean new file mode 100644 index 0000000000..6d3959f4d7 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean @@ -0,0 +1,221 @@ +/- +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.Package + +/-! + # 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**. + + ## Why guarded verifiers + + The existing composition machinery (`Verifier.append_coordinateWiseSpecialSound`, + `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 + statement (the `QuadEval` pattern). It fails exactly where a runtime check reads *sent or input* + data that the downstream statement type **drops** — in Hachi: + + 1. each sumcheck round's check `gᵢ(0) + gᵢ(1) = target` (the old target is dropped by the next + round's statement) — [NOZ26] Figure 6; + 2. the final-evaluation check against the last sumcheck targets — [NOZ26] Figure 7 tail; + 3. the §4.5 recursion handoff's trace check (the next-iteration statement type is pinned to + `QuadEvalStatement`, which cannot retain it). + + A **guarded** verifier `if check stmt tr then pure (out stmt tr) else failure` is the faithful + model (`failure` is native: the verifier monad is `OptionT (OracleComp _)`). Its acceptance + probability is `0` on the `failure` branch, so on an *accepting* tree every leaf has + `check = true` — which is exactly the paper's "valid transcripts" premise, and which the + guarded composition theorem below feeds to the left extraction. + + ## Contents + + * `Verifier.IsGuardedWith` / `Verifier.IsGuarded` — the guard predicate (`Bool`-valued check, + design decision G3); purity is the `check := fun _ _ => true` special case + (`IsGuarded.of_isPure`). + * `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`). + * `GCWSSPackage` — the guarded analogue of `CWSSPackage` (`isPure` ↝ `isGuarded`), with + `CWSSPackage.toGuarded` and the composition `GCWSSPackage.append` = infix `▷ᵍ`. + + 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. + + ## References + + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +noncomputable section + +open OracleComp OracleSpec ProtocolSpec + +namespace Verifier + +variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn StmtOut : Type} + {n : ℕ} {pSpec : ProtocolSpec n} + +/-- A verifier is **guarded with** a `Bool`-valued `check` and a deterministic output map `out` if +its verdict is `pure (out stmt tr)` when the check passes and `failure` otherwise. This is the +faithful model of a verifier that rejects at runtime (design decision G3 of the sumcheck-track +plan: `Bool`-valued checks; decidable-`Prop` consumers use `decide`). -/ +def IsGuardedWith (V : Verifier oSpec StmtIn StmtOut pSpec) + (check : StmtIn → FullTranscript pSpec → Bool) + (out : StmtIn → FullTranscript pSpec → StmtOut) : Prop := + ∀ stmt tr, V.verify stmt tr = if check stmt tr then pure (out stmt tr) else failure + +/-- A verifier is **guarded** if it is guarded with *some* check and output map. Purity is the +special case `check := fun _ _ => true` (`IsGuarded.of_isPure`). -/ +class IsGuarded (V : Verifier oSpec StmtIn StmtOut pSpec) : Prop where + is_guarded : ∃ check out, V.IsGuardedWith check out + +/-- Every pure verifier is guarded, with the trivially-true check. -/ +theorem IsGuarded.of_isPure (V : Verifier oSpec StmtIn StmtOut pSpec) (h : V.IsPure) : + V.IsGuarded := by + obtain ⟨f, hf⟩ := h.is_pure + exact ⟨fun _ _ => true, f, fun stmt tr => by simp [hf stmt tr]⟩ + +instance (V : Verifier oSpec StmtIn StmtOut pSpec) [h : V.IsPure] : V.IsGuarded := + IsGuarded.of_isPure V h + +section Append + +variable {Stmt₁ Wit₁ Stmt₂ Wit₂ Stmt₃ Wit₃ : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + {rel₁ : Set (Stmt₁ × Wit₁)} {rel₂ : Set (Stmt₂ × Wit₂)} {rel₃ : Set (Stmt₃ × Wit₃)} + +/-- Guardedness is closed under `Verifier.append`: the composite check runs the left check on the +transcript prefix and, if it passes, the right check on the suffix from the left output. + +**Sorried (B4.4).** Proof plan: mirror `Verifier.IsPure.append` +(`OracleReduction/Composition/Sequential/IsPure.lean`) — destructure both guard witnesses, take +`check := fun s tr => check₁ s tr.fst && check₂ (out₁ s tr.fst) tr.snd` and +`out := fun s tr => out₂ (out₁ s tr.fst) tr.snd`, and normalize +`Verifier.append`'s bind with `failure_bind`/`pure_bind` under the two `if`-splits. -/ +theorem IsGuarded.append (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) + (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (h₁ : V₁.IsGuarded) (h₂ : V₂.IsGuarded) : + (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). + +**Sorried.** Proof plan (transplant of `Verifier.append_coordinateWiseSpecialSound`, +`Composition.lean`, 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 + (`Pr = 1`), the `check₁ = false` branch contradicts `failure`'s acceptance probability `0` + (rejection lemma B4.1), so 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 its guarded analogue fed by the `check₁ = true` fact from delta 1. + (Each left leaf learns `check₁ = true` from *some* suffix transcript — the same nonemptiness + the pure proof already extracts via `LeafPath.exists_of_mem_fullTranscripts`.) + +The tree machinery (`appendSplit` and friends) is untouched. -/ +theorem append_coordinateWiseSpecialSound_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₂) + (h₂ : V₂.coordinateWiseSpecialSound init impl D₂ rel₂ rel₃) : + (V₁.append V₂).coordinateWiseSpecialSound init impl + (CWSSStructure.append D₁ D₂) rel₁ rel₃ := by + sorry + +end Append + +end Verifier + +namespace CoordinateWise + +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`. -/ +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). -/ + 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 verifier is guarded: its verdict is a deterministic function of statement and + 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 + +namespace GCWSSPackage + +variable {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + +/-- Forget purity: every (pure) `CWSSPackage` is a `GCWSSPackage` with the trivially-true +check. -/ +def _root_.CoordinateWise.CWSSPackage.toGuarded + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + (L : CWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec) : + GCWSSPackage init impl StmtIn WitIn StmtOut WitOut pSpec where + verifier := L.verifier + struct := L.struct + relIn := L.relIn + relOut := L.relOut + isGuarded := Verifier.IsGuarded.of_isPure L.verifier L.isPure + 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₂`. -/ +def append {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₂ : GCWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hseam : L₁.relOut = L₂.relIn := by rfl) : + GCWSSPackage 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 + isGuarded := Verifier.IsGuarded.append L₁.verifier L₂.verifier L₁.isGuarded L₂.isGuarded + isCWSS := by + have h₂ := L₂.isCWSS + 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₂ + +end GCWSSPackage + +@[inherit_doc GCWSSPackage.append] +scoped infixr:65 " ▷ᵍ " => GCWSSPackage.append + +end CoordinateWise + +end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean new file mode 100644 index 0000000000..e8d2d27860 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean @@ -0,0 +1,106 @@ +/- +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.SingleRound + +/-! + # 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`). + + 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`: + + * the HMZ25 lift (Figure 4 / Lemma 9): message `t = Com(w̃)`, challenge `α ← F`, `k = 2d`; + * 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 + 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. + + 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 + `Function.Injective fam` instead of `StarAt`. + + ## References + + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +open OracleComp OracleSpec ProtocolSpec CoordinateWise + +namespace CoordinateWise.ScalarRound + +/-- The two-round scalar-challenge protocol: the prover sends a message `Msg` (round 0, +`P_to_V`), the verifier replies with a single scalar challenge `C` (round 1, `V_to_P`). -/ +@[reducible] def pSpecScalar (Msg C : Type) : ProtocolSpec 2 := + ⟨!v[.P_to_V, .V_to_P], !v[Msg, C]⟩ + +variable {Msg C : Type} + +/-- 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 +Lemmas 9 and 11. Arity `1·(k−1)+1 = k`. -/ +@[reducible] def scalarStructure (k : ℕ) (hk : 2 ≤ k) : + CWSSStructure (pSpecScalar Msg C) := + CWSSStructure.ofSpecialSound (fun _ => k) (fun _ => hk) + +section Instances + +variable [SampleableType C] [OracleInterface Msg] + +/-- Hand-written 2-round instances (not auto-derived for `ProtocolSpec 2`). -/ +instance : ∀ i, SampleableType ((pSpecScalar Msg C).Challenge i) + | ⟨0, h⟩ => nomatch h + | ⟨1, _⟩ => (inferInstance : SampleableType C) + +instance : ∀ i, OracleInterface ((pSpecScalar Msg C).Message i) + | ⟨0, _⟩ => (inferInstance : OracleInterface Msg) + | ⟨1, h⟩ => nomatch h + +end Instances + +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 + (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) + (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 + sorry + +end Assembly + +end CoordinateWise.ScalarRound diff --git a/HACHI_GENERIC_RING_SWITCH_PLAN.md b/HACHI_GENERIC_RING_SWITCH_PLAN.md new file mode 100644 index 0000000000..b842b63097 --- /dev/null +++ b/HACHI_GENERIC_RING_SWITCH_PLAN.md @@ -0,0 +1,1079 @@ +# 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 new file mode 100644 index 0000000000..67b49d6686 --- /dev/null +++ b/HACHI_LEMMA10_GAP.md @@ -0,0 +1,852 @@ +# 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 new file mode 100644 index 0000000000..fa4f2f21b9 --- /dev/null +++ b/HACHI_RECURSION_GAP.md @@ -0,0 +1,357 @@ +# 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/docs/skills/README.md b/docs/skills/README.md index 759a49c876..1cd3ddaaea 100644 --- a/docs/skills/README.md +++ b/docs/skills/README.md @@ -21,6 +21,8 @@ After using a skill, review whether it should be updated: - [`discharge-lemmas.md`](discharge-lemmas.md) - workflow for triaging, placing, stating, and proving `sorry`s and open proof obligations, then summarizing what is proved vs. deferred. +- [`prove-milestone.md`](prove-milestone.md) - four-stage, paper-audited workflow for freezing and + constructively proving one Hachi milestone, then improving the workflow from run evidence. - [`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 diff --git a/docs/skills/prove-milestone.md b/docs/skills/prove-milestone.md new file mode 100644 index 0000000000..89baadaa95 --- /dev/null +++ b/docs/skills/prove-milestone.md @@ -0,0 +1,426 @@ +# /prove-milestone + +Use this workflow to discharge one Hachi milestone named by a row, subprotocol, milestone code, or +file in +[`hachi-overview.html`](../../ArkLib/Commitments/Functional/Hachi/hachi-overview.html). The goal is +not merely to make Lean accept existing statements. First establish that the statements encode the +intended protocol and theorem; then make every proof obligation tractable; only then fill the proof +bodies without changing what they mean. + +This workflow is deliberately stricter than [`discharge-lemmas.md`](discharge-lemmas.md). A Hachi +milestone is complete only when its paper contract, definitions, public theorem, package, and +composition seam agree and its entire proof-obligation closure is constructive and `sorry`-free. + +## Invocation contract + +Require an unambiguous target such as “row 8 / paired sumcheck rounds”, “F7”, or +`Sumcheck/Rounds.lean`. If the requested name selects several dashboard entries, resolve the +smallest semantic subprotocol that has its own input relation, output relation, verifier, and CWSS +certificate; ask only if two materially different scopes remain. + +Treat the dashboard as an index and status report, not as the specification. Establish the target +from all of these sources: + +- the exact version of the primary paper cited by the code, especially the referenced definition, + 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; +- 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 +paper faithfulness by themselves. Record the paper version and page/figure/equation references. If +the primary source is unavailable, do not declare Stage 1 complete. + +## Global invariants + +Maintain these invariants throughout the task: + +1. Preserve unrelated and pre-existing work. Start with `git status --short`, record the initial + diff and untracked files in scope, and never reset, overwrite, or silently absorb user changes. +2. Keep a target-closure ledger. Include the milestone's semantic definitions, proof declarations, + helper lemmas, exported package, and the immediate composition seam. Do not count unrelated + Hachi sorries as part of the target, but do expose any target theorem that transitively depends + on them. +3. Use the exact primary-source claim as the comparison point. Label every deliberate deviation as + a strengthening, weakening, repair, generalization, or implementation-only representation + choice, and justify the direction needed for soundness and completeness. +4. Never make a theorem easier by silently strengthening an input relation, weakening an output + relation, shrinking the challenge space, adding a false/unmotivated hypothesis, or moving a + verifier check into an assumption. +5. Make the remaining `sorry` count in the target closure monotonically decrease after the Stage 2 + freeze. Do not move, rename, hide, or replace a gap with another trust mechanism. +6. Do not use `native_decide`, `classical`, any `Classical.*` declaration, `axiom`, `admit`, + `Lean.ofReduceBool`, an unsafe/opaque proof surrogate, or a theorem whose transitive axiom set + contains `sorryAx`. Do not introduce project axioms. Treat `Classical.choice` as forbidden even + when it enters indirectly through a reused theorem. Record unavoidable foundational use of + `propext` or `Quot.sound`; do not call a result “axiom-free” if either appears. +7. Use a fresh independent reviewer when subagents are available. Give reviewers the raw paper + section, Lean files, and current artifacts, not the intended verdict or a proposed fix. + +Keep scratch manifests and experiments under `/tmp`, not as new root-level planning files. Promote +new, durable paper findings to `docs/kb/` when they materially change ArkLib's understanding of the +paper. + +## Required artifacts + +Maintain these artifacts during the run, in the conversation or in temporary files: + +- **scope manifest** — target, source files, declarations, imports, consumers, and initial dirty + state; +- **milestone contract** — paper-to-Lean comparison with exact source references; +- **audit ledger** — findings, severity, resolution, and evidence; +- **proof DAG** — every `sorry`, its dependencies, plan, difficulty, and status; +- **semantic-freeze manifest** — the Stage 2 definitions, declaration signatures, attributes, + instances, imports, package wiring, and allowed proof-body locations; +- **verification log** — per-proof builds, axiom output, scoped searches, final build, and semantic + review. +- **skill-improvement log** — end-of-run evidence, candidate edits, decisions, applied changes, and + validation of the revised skill. + +Do not use a prose plan as a substitute for typechecking. Each stage has an explicit exit gate. + +## Stage 1 — establish the right formalization + +This stage may change definitions, theorem statements, abstractions, hypotheses, and file +boundaries. It must end with the intended API fully stated and typechecking, with intentional proof +holes marking all remaining work. + +### 1. Reconstruct the paper contract independently + +Before accepting the current Lean design, write a compact contract covering: + +| Contract item | Questions to answer | +| --- | --- | +| Claim | What exact correctness, soundness, CWSS, or reduction statement is claimed? | +| Data | What are the public statement, witness, messages, challenges, responses, and commitment data? | +| Protocol | What is sent or sampled in each round, from which space and distribution, and in what order? | +| Acceptance | Which checks run at runtime, and which facts belong to the input or output relation? | +| Extraction | What accepting transcript shape is assumed, what witness/escape is returned, and with what arity/error? | +| Parameters | What degree, norm, cardinality, non-emptiness, distinctness, and field/ring assumptions are required? | +| Composition | What exact relation enters this seam, what relation leaves it, and what data is retained or dropped? | + +Translate every row into the corresponding Lean declaration or mark it missing. For relation +changes, state an equality, equivalence, or containment and verify that its direction is the one the +security argument needs. “Looks analogous” is not evidence. + +### 2. Run an adversarial formalization audit + +Audit from several angles: + +- **Faithfulness:** compare quantifier order, indices, dimensions, challenge type and distribution, + transcript shape, verifier checks, bounds, exceptional/escape cases, and conclusion. Check the + exact paper verifier, not just an easier relation with a similar name. +- **Security notion:** unfold enough of `coordinateWiseSpecialSound`, guarded verification, + challenge-tree structure, and package composition to confirm that the formal theorem expresses + the claimed extraction guarantee. +- **Abstraction quality:** determine whether one declaration mixes distinct mathematical seams, + whether a paper concept has been encoded twice, or whether a large target should be split at a + semantic boundary. Keep bridges as statement reshaping; do not conceal protocol work inside one. +- **Hypothesis minimality:** classify every explicit and typeclass hypothesis as a paper premise, + a representation requirement, a downstream composition requirement, or a proof artifact. Trace + its use and, in a scratch example, try removing or weakening it. An important paper assumption + that Lean never uses is also a warning that the theorem may be too weak. +- **Vacuity and countermodels:** try boundary and small cases. Check that challenge spaces and + relations are inhabited where intended, the verifier is not definitionally always rejecting, + witnesses carry the claimed information, an impossible hypothesis is not proving everything, + and the extractor conclusion is not true for an irrelevant reason. +- **Connectivity:** confirm that the audited theorem is the theorem stored in the exported + `CWSSPackage`/`GCWSSPackage`, that the package exposes the audited relations and verifier, and that + `Composition.lean` consumes that package at the advertised seam. A correct unused theorem does + not discharge a milestone. + +Apply these Hachi-specific attacks whenever the target can reach the relevant seam: + +- Recompute the transitive proof closure through generic CWSS/scalar/guarded composition code and + planned prerequisites. A row with one local `sorry` can still depend on missing or sorried + generic machinery. +- For every zero-round adapter, require the exact forward/completeness direction as well as the + CWSS pullback direction, or document the precise asymmetric containment that is intended. Prove + stack/unstack and encoding round trips; an empty or overly strong output relation can make the + pullback theorem meaningless. +- For every `Bool` guard such as a round, final, or trace check, establish `check = true ↔` the + advertised paper equations and an honest-acceptance lemma before proving soundness. An + always-false sorried check makes accepting-tree obligations vacuous. +- When extraction uses `LiftCom.collision_mem`, trace the required shortness proof for both + colliding openings back through every relation. A point-evaluation check does not by itself imply + coefficient/range shortness. +- For table and polynomial encodings, require explicit capacity inequalities, index equivalences, + padding behavior, reconstruction laws, degree bounds, and the inequalities connecting digit + bounds to public shortness parameters. +- Audit every division or derived omitted value for a nonzero denominator. In particular, a fixed + partial-evaluation pivot must not be assumed nonzero for all evaluation points. +- Do not quantify over an arbitrary basis table, packing map, commitment reinterpretation, or + escape conversion and then use unstated algebraic laws. State and justify independence, + round-trip, commutation, bound, and escape-validity laws at the right abstraction boundary. +- Treat corrected Lemma 10 as an explicit protocol repair, not a proof of the paper's printed + uniform-vector protocol. Treat the row-11 `Z`-packing pullback as a known open soundness gap until + an authorized repair replaces it. +- Re-audit a “proven” dependency when its documented relation or bound is only a containment or a + modeling generalization of the paper and becomes load-bearing for the target. + +Produce a concrete non-vacuity certificate: at least one honest symbolic instance/transcript or +small Lean example, plus a trace showing which verifier checks constrain which output-relation +facts. Use explicit mathematical counterexamples when useful, but do not use `native_decide`, even +for experiments. Independently search ArkLib and Mathlib before adding abstractions or hypotheses. + +### 3. Correct the skeleton + +Resolve every material audit finding now: + +- repair definitions and theorem statements rather than compensating in proofs; +- split the subprotocol only at mathematically meaningful interfaces with explicit relations; +- remove unused or unjustified hypotheses, and add a missing premise only when it belongs to the + paper or an explicitly approved repair; +- state all useful representation/equivalence/containment and sanity lemmas; +- wire the exact public theorem into the package and its immediate composition consumer; +- give every major definition and theorem a docstring with precise paper references and disclose + every deliberate divergence. + +Keep already-correct proofs. Use `sorry` only for genuine remaining proof bodies. Build each +affected file after statement changes so the skeleton, package, and seam elaborate together. +Every data-bearing definition—encoding, relation data, verifier, protocol spec, extractor, +statement conversion, or witness construction—must be implemented. Never use `sorry` to fabricate +data and then prove propositions about that fabricated value. + +If the paper claim appears false or under-specified, do not silently repair it. Isolate the failing +claim, produce the smallest rigorous counterexample or missing obligation possible, distinguish a +faithful model from candidate repairs, and obtain a repair decision unless the target already names +an approved corrected variant. A dashboard entry marked as an open gap is not discharged by +assuming the missing implication. + +### 4. Repeat until stable + +Alternate a builder pass with an adversarial reviewer pass. After each pass, update the contract +and ledger, fix findings, rebuild, and restart the clean-pass count. Exit Stage 1 only after two +consecutive passes find no new material error or actionable improvement: one paper/cryptographic +semantics pass and one Lean abstraction/API/composition pass. + +Stage 1 is complete only when: + +1. every contract item maps to final Lean declarations with a justified correspondence; +2. no known vacuity, false seam, silent paper deviation, unnecessary hypothesis, or unjustified + abstraction remains; +3. all intended definitions, theorem statements, helper statements, packages, and seam wiring + elaborate together; +4. every remaining proof obligation is an explicit, inventoried `sorry` in a proof body. + +## Stage 2 — develop a feasible proof architecture + +This stage may add, generalize, split, relocate, or restate helper lemmas, but every such change must +remain faithful to the Stage 1 contract. Finish all proof-interface design before freezing. + +### 1. Inventory exact goals and dependencies + +Use `rg` to locate textual placeholders, then use Lean diagnostics/build warnings to distinguish +proof terms from comments and to inspect the elaborated goals. Include sorries in local helpers, +structure fields, package certificates, and imported target-specific prerequisites. Build a DAG and +plan leaf obligations first. + +For each obligation record: + +- declaration, file, exact type, local context, and hypotheses actually available; +- mathematical argument and the invariant or normal form that makes it work; +- existing ArkLib/Mathlib lemmas to reuse, with checked names and instantiated types; +- any intermediate lemma, generalization, induction motive, extensionality principle, or coercion + normalization required; +- likely Lean proof steps, expected fragile points, and a fallback route; +- dependencies and a 1–10 difficulty rating with evidence. + +Use the scale honestly: 1 is a direct one- or two-minute proof; 5 is involved but bounded work with +known mathematics and APIs; 6 or above means the current hole is not ready for the proof phase; 10 +is day-scale or research-level work. No hole may enter Stage 3 at 6 or above. + +### 2. Materialize the proof architecture + +Search before inventing. Check candidate lemmas with scratch `#check`/`example` declarations and use +`exact?`, `apply?`, `rw?`, or `simp?` as exploration aids. Add every necessary top-level +intermediate lemma now, in its natural module, with its final statement and a `sorry` proof. Prefer +small reusable mathematical facts over protocol-specific duplication, but do not generalize beyond +a clear use. + +Split a hard hole until each resulting obligation is independently below 6. Splitting is legitimate +only when the helpers express real intermediate facts and do not merely restate the original goal, +assume its difficult premise, or form a dependency cycle. If an obligation cannot be brought below +6 without changing protocol semantics, return to Stage 1; if the mathematics itself is missing or +false, report the blocker rather than manipulating the rating. + +### 3. Adversarially validate feasibility + +Give a fresh reviewer the current skeleton and proof DAG. Ask it to attack each rating by checking +theorem names and orientations, universes and typeclasses, finite-index casts, induction motives, +algebraic side conditions, hidden classical reasoning, circular dependencies, and whether the +proposed conclusion really follows. Require proof spikes for the riskiest step and any disputed +rating. A plausible paragraph without an elaborated critical step is not enough. + +Revise and repeat until every objection is either fixed or refuted with Lean/math evidence and two +consecutive feasibility passes introduce no new helper or rating of 6 or above. + +### 4. Freeze the semantics + +Build the complete sorried skeleton, then create a semantic-freeze manifest and copy the scoped +files to a fresh `/tmp/prove-milestone-/stage2-baseline/` directory. Record: + +- every declaration name and full type, binder order, attributes, instance priority, and namespace; +- all definitions, relations, verifiers, protocol specs, structures, notation, and imports; +- package fields and composition wiring; +- the exact allowed proof-body placeholders; +- captured `#check @fully.qualified.name` output for every target declaration and `#print` output + for each frozen data-bearing definition, relation, verifier, and package constructor; +- scoped file hashes/diffs, initial user changes, `sorry` locations, and a successful build log. + +Do not commit merely to create the freeze. From this point onward, only the bodies of inventoried +proof declarations may change. If anything else is needed, explicitly return to Stage 1 or 2, +re-run their exit gates, and create a new freeze. + +## Stage 3 — discharge the frozen proof obligations + +Work through the proof DAG from leaves to public certificates. For one `sorry` at a time: + +1. Re-open its exact goal and follow the reviewed plan. +2. Replace only that proof body. Proof-local `have` statements are allowed; new top-level helpers, + attributes, imports, instances, definitions, or statement changes are not. +3. Run `lake env lean path/to/File.lean` immediately. Do not accumulate unverified edits. +4. Recount target-closure proof placeholders and require a strict decrease. Check that no `admit`, + `constant`, axiom, or renamed surrogate appeared. +5. Diff against the Stage 2 baseline. Reject every change outside allowlisted proof bodies, including + “harmless” binder, hypothesis, relation, verifier, package, or import edits. +6. Run the forbidden-construct scan over the changed proof and run `#print axioms` for the proved + declaration. It must not contain `sorryAx`, `Classical.choice`, or a project-defined axiom. +7. Mark the DAG entry proved only after the local build, freeze check, and axiom check pass. + +Prefer explicit, maintainable kernel-checked arguments and existing library lemmas. Automation is +acceptable when it produces an ordinary auditable proof term and passes the axiom audit; it is not +evidence that the theorem has the intended meaning. + +If the plan fails, stop editing that proof. Diagnose whether the issue is a missing helper (return +to Stage 2), a wrong statement/definition (return to Stage 1), or merely a tactic/API detail (revise +the proof plan without altering the freeze). Never make a frozen theorem easier in place. + +After the last local hole is removed, audit `#print axioms` for every new or changed theorem, the +milestone's exported certificate, and its package-facing theorem. Also check imported lemmas on +which the public certificate depends: a locally sorry-free wrapper around `sorryAx` is not a proven +milestone. + +Stage 3 is complete only when the target closure contains no `sorry`/`admit`, every source diff is +freeze-compliant, every scoped file builds, and all axiom/construct checks pass. + +## Stage 4 — independently verify that the right result was proved + +Perform a clean-room review from the primary paper and the frozen contract, preferably with a fresh +reviewer who is not shown the implementation rationale. Re-check: + +- the exact claim, quantifier order, hypotheses, dimensions, bounds, transcript structure, and + extraction conclusion; +- message/challenge order, challenge space and distribution, verifier acceptance predicate, and + guarded-versus-relational checks; +- equality/equivalence/containment directions at both seams; +- non-vacuity and representative boundary cases; +- that the public theorem is installed in the advertised package and the package is the one used by + composition; +- that no stronger assumption or weaker conclusion entered after the contract was written; +- that the final theorem and all target-specific dependencies are free of `sorryAx`, classical + choice, forbidden constructs, and new axioms. + +Compare the final `#check` and frozen-definition `#print` outputs with the Stage 2 captures; require +exact matches. Source review remains necessary because elaborator output is a backstop, not a +license to modify semantically relevant syntax. + +Try to falsify the result, not merely explain why it seems reasonable. Require two consecutive +clean independent audits. If either audit finds a material mismatch or improvement, return to +Stage 1, update the audit ledger, rebuild the skeleton, redo the proof architecture and semantic +freeze, and re-prove the affected obligations. Do not patch around the finding in Stage 4. + +## Final validation and handoff + +Once the clean-room review is clean: + +1. Run each changed Lean file directly, then `./scripts/validate.sh`; run scoped style linting and + the relevant `ReadLints` checks, and use `./scripts/validate.sh --lint` when repo-wide lint debt + will not obscure the result. Add `--docs` when Lean docstrings or documentation changed, and run + `git diff --check`. +2. Re-run the target-closure placeholder scan and the complete axiom manifest. +3. Update `hachi-overview.html` only from verified source facts: file status, exact genuine `sorry` + count, milestone description, and “proven” claim. Do not erase a known paper deviation or open + gap. Re-run documentation integrity checks after documentation changes. +4. Update `Composition.lean` provenance, Hachi module docstrings, and `docs/kb/` when their factual + account changed. Never hand-edit generated `ArkLib.lean` or derived site output. +5. Run the self-improvement pass below before writing the final report. + +## Self-improvement pass + +Run this pass exactly once at the end of every invocation, after all milestone work and verification +that can be completed. Run it even when the milestone ends at a legitimate false-claim, missing- +authority, or external-blocker exception. This pass is part of the task: do not merely suggest +improvements for a future agent. Apply every accepted, in-scope improvement to this canonical skill +file before the final response. + +### 1. Derive candidates from evidence + +Review the scope manifest, audit ledger, stage restarts, invalidated proof ratings, reviewer +findings, verification failures, and user corrections. Identify places where this skill was +ambiguous, incomplete, stale, inefficient, too permissive, or missing a reusable Hachi-specific +check. Do not infer a workflow defect merely because the targeted mathematics was hard. + +For each candidate record: + +| Field | Required content | +| --- | --- | +| Evidence | The concrete event, failure, or repeated friction observed during this run | +| Generality | Why another Hachi milestone is likely to encounter it | +| Proposed edit | The exact instruction to add, remove, tighten, or reorganize | +| Risk | Possible overfitting, contradiction, excess cost, or loss of useful freedom | +| Decision | Apply, reject as one-off, or defer because it needs user authority | + +Accept a candidate only when it is supported by the run, likely to recur, materially improves +correctness or efficiency, and is consistent with `AGENTS.md`, the user's constraints, and the +maintenance rule in `docs/skills/README.md`. Prefer tightening or simplifying an existing +instruction over appending a duplicate. Do not add task-specific theorem names, transient line +numbers, a success story, or a changelog unless they encode a stable class of failure. + +### 2. Apply accepted improvements + +Patch `docs/skills/prove-milestone.md` in the same run. Preserve its name, four-stage architecture, +semantic-freeze guarantee, constructive proof policy, and completion gates unless the user +explicitly changes them. Keep the file below 500 lines and remove superseded wording so the skill +does not grow monotonically. Do not modify milestone source files during this meta-pass. + +If no candidate meets the acceptance rule, make no cosmetic edit. Explicitly report that no +durable improvement was found and give the evidence considered. “No change” is preferable to an +unsupported self-modification. + +### 3. Validate the revised skill + +Re-read the complete skill rather than only the edited hunk. Check for contradictory stage gates, +broken links, duplicated rules, weakened safety requirements, line-count growth, and instructions +that would cause an infinite loop. Run documentation integrity and `git diff --check`. When the +edit materially changes behavior and subagents are available, give a fresh agent the revised skill +and a realistic Hachi target as a forward test; revise again if that test exposes a defect. + +Do not recursively run a new self-improvement pass because this pass edited the skill. Fix direct +validation defects within this same pass, then stop after the revised skill validates. + +### 4. Report the self-improvement + +Include a **Skill self-improvement** section in the final response. List the evidence-backed +candidates, which edits were applied and where, which candidates were rejected or deferred and why, +and the validation run on the revised skill. Do not call the overall invocation complete before +this report and the accepted edits both exist. + +Report: + +- the target and exact paper contract conclusion; +- definitions/statements changed before the freeze and why; +- the final proof DAG with ratings and statuses; +- hypotheses removed, retained, or added, with justification; +- semantic-freeze compliance; +- `#print axioms` results for the public declarations; +- validation commands and results; +- dashboard/documentation updates; +- skill-improvement candidates, applied edits, rejected/deferred candidates, and skill validation; +- any remaining out-of-scope sorries or upstream trust assumptions, clearly separated from the + discharged milestone. + +Do not call the milestone complete unless all four stage gates pass. The only legitimate terminal +exception is a demonstrated false/under-specified paper claim or a required repair decision outside +the authorized scope; report that as a formalization finding, not as a successful proof. diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index ef264c4470..1d12b949a5 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -103,10 +103,42 @@ home_page/ site assets and assembled website root (`toQuadEvalStatement`), the pulled-back input relation `relPolyEval`, and its CWSS `bridge_coordinateWiseSpecialSound`. `QuadEval.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`. + - `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. + (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** + encoding (Eqs. (21)–(23): the table `w̃`, `H₀`/`H_α`, the sumcheck polynomials, degree pins, + 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. + - `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/` (§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) + — 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). Each further §3/§4.3+/§4.5 subprotocol lands as one more `CWSSPackage` - `▷`-appended here. + (`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. - `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+ @@ -149,8 +181,14 @@ home_page/ site assets and assembled website root (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). The umbrella `CoordinateWiseSpecialSoundness.lean` - re-exports the core files. + 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 + `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/...`. - Ring switching is a **generic, instantiable compiler** under `ProofSystem/RingSwitching/`, not a