From 6a607154b1e30698a02219f38767bc01d0df7a59 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Wed, 1 Jul 2026 16:30:37 +0200 Subject: [PATCH 01/21] CWSS protocol infrastructure --- ArkLib.lean | 4 + .../Composition/Sequential/IsPure.lean | 58 +++ .../CoordinateWiseSpecialSoundness.lean | 9 + .../NoChallenge.lean | 138 ++++++ .../SeqCompose.lean | 405 ++++++++++++++++++ ArkLib/ProofSystem/Component/CheckClaim.lean | 100 +++-- ArkLib/ProofSystem/Component/ReduceClaim.lean | 73 ++++ .../ProofSystem/Component/SendChallenge.lean | 115 +++++ ArkLib/ProofSystem/Component/SendClaim.lean | 349 ++++++--------- ArkLib/ProofSystem/Component/SendWitness.lean | 117 ++++- 10 files changed, 1100 insertions(+), 268 deletions(-) create mode 100644 ArkLib/OracleReduction/Composition/Sequential/IsPure.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean create mode 100644 ArkLib/ProofSystem/Component/SendChallenge.lean diff --git a/ArkLib.lean b/ArkLib.lean index e82af39234..8bfaef1bb6 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -164,6 +164,7 @@ import ArkLib.OracleReduction.Cast import ArkLib.OracleReduction.Composition.Parallel.Basic import ArkLib.OracleReduction.Composition.Sequential.Append import ArkLib.OracleReduction.Composition.Sequential.General +import ArkLib.OracleReduction.Composition.Sequential.IsPure import ArkLib.OracleReduction.Equiv import ArkLib.OracleReduction.Execution import ArkLib.OracleReduction.FiatShamir.Basic @@ -191,6 +192,8 @@ 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.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose import ArkLib.OracleReduction.Security.Implications import ArkLib.OracleReduction.Security.Rewinding import ArkLib.OracleReduction.Security.RoundByRound @@ -218,6 +221,7 @@ import ArkLib.ProofSystem.Component.DoNothing import ArkLib.ProofSystem.Component.NoInteraction import ArkLib.ProofSystem.Component.RandomQuery import ArkLib.ProofSystem.Component.ReduceClaim +import ArkLib.ProofSystem.Component.SendChallenge import ArkLib.ProofSystem.Component.SendClaim import ArkLib.ProofSystem.Component.SendWitness import ArkLib.ProofSystem.ConstraintSystem.Lookup diff --git a/ArkLib/OracleReduction/Composition/Sequential/IsPure.lean b/ArkLib/OracleReduction/Composition/Sequential/IsPure.lean new file mode 100644 index 0000000000..f37b2b27c8 --- /dev/null +++ b/ArkLib/OracleReduction/Composition/Sequential/IsPure.lean @@ -0,0 +1,58 @@ +/- +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.Composition.Sequential.General + +/-! + # Purity of composed verifiers + + A verifier is `Verifier.IsPure` when its `verify` is a deterministic (`pure`) function of the + statement and transcript. This is exactly the deterministic-left hypothesis `hV₁` of the + CWSS / tree-soundness binary append (`Verifier.append_treeSpecialSound`, + `Verifier.append_coordinateWiseSpecialSound`), so propagating `IsPure` through composition lets + an `n`-ary CWSS composition discharge that hypothesis from per-factor purity. + + We show that the identity verifier is pure (`instIsPureId`), and that purity is preserved by + binary `append` (`IsPure.append`) and `n`-ary `seqCompose` (`IsPure.seqCompose`). +-/ + +open OracleComp OracleSpec ProtocolSpec + +namespace Verifier + +variable {ι : Type} {oSpec : OracleSpec ι} + +/-- The identity verifier is pure: `verify = fun stmt _ => pure stmt`. -/ +instance instIsPureId {Statement : Type} : + (Verifier.id (oSpec := oSpec) (Statement := Statement)).IsPure := + ⟨fun stmt _ => stmt, fun _ _ => rfl⟩ + +variable {Stmt₁ Stmt₂ Stmt₃ : Type} {m k : ℕ} + {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec k} + +/-- Purity is preserved by binary sequential composition of verifiers: the composed `verify` is the + composition of the two deterministic outputs. -/ +theorem IsPure.append (V₁ : Verifier oSpec Stmt₁ Stmt₂ pSpec₁) + (V₂ : Verifier oSpec Stmt₂ Stmt₃ pSpec₂) (h₁ : V₁.IsPure) (h₂ : V₂.IsPure) : + (V₁.append V₂).IsPure := by + obtain ⟨f₁, hf₁⟩ := h₁.is_pure + obtain ⟨f₂, hf₂⟩ := h₂.is_pure + refine ⟨fun stmt tr => f₂ (f₁ stmt tr.fst) tr.snd, fun stmt tr => ?_⟩ + simp only [Verifier.append, hf₁, hf₂, pure_bind, bind_pure] + +/-- Purity is preserved by `n`-ary sequential composition of verifiers. The base case is the + identity verifier (`Verifier.seqCompose` reduces to `Verifier.id` at `m = 0`); the step case is + `IsPure.append` of the head with the recursively-composed tail. -/ +theorem IsPure.seqCompose : + {m : ℕ} → (Stmt : Fin (m + 1) → Type) → {n : Fin m → ℕ} → + {pSpec : ∀ i, ProtocolSpec (n i)} → + (V : (i : Fin m) → Verifier oSpec (Stmt i.castSucc) (Stmt i.succ) (pSpec i)) → + (hV : ∀ i, (V i).IsPure) → (Verifier.seqCompose Stmt V).IsPure + | 0, _, _, _, _, _ => ⟨fun stmt _ => stmt, fun _ _ => rfl⟩ + | _ + 1, Stmt, _, _, V, hV => + IsPure.append (V 0) _ (hV 0) + (IsPure.seqCompose (Stmt ∘ Fin.succ) (fun i => V (Fin.succ i)) (fun i => hV (Fin.succ i))) + +end Verifier diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness.lean index ae103d208c..6ed189d568 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness.lean @@ -6,6 +6,8 @@ Authors: Tobias Rothmann import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose /-! # Coordinate-Wise Special Soundness (CWSS) @@ -24,6 +26,13 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Compositio (`toShape_append` / `toShape_seqCompose`), and preservation of CWSS under binary verifier append (`Verifier.append_coordinateWiseSpecialSound`) as a thin wrapper over the generic `Verifier.append_treeSpecialSound`. + * `NoChallenge` — the degenerate bridge for protocols with no challenge rounds + (`IsEmpty pSpec.ChallengeIdx`): tree special soundness collapses to a transcript-level extractor + (`Verifier.treeSpecialSound_of_isEmpty_challengeIdx`). + * `SeqCompose` — the `n`-ary sequential composition of (coordinate-wise) tree special soundness: + the identity base case (`Verifier.id_treeSpecialSound`), the shape unfolding + `ChallengeTreeShape.seqCompose_succ`, and the compositions + `Verifier.seqCompose_treeSpecialSound` / `Verifier.seqCompose_coordinateWiseSpecialSound`. Plain `(k)`-special soundness is the `ℓᵢ = 1` instance (`CWSSStructure.ofSpecialSound`); see also `Security.SpecialSoundness`. diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean new file mode 100644 index 0000000000..b02406b7c0 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean @@ -0,0 +1,138 @@ +/- +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.Basic + +/-! + # (Coordinate-wise) special soundness for protocols with no challenge rounds + + When a protocol has no challenge rounds (`IsEmpty pSpec.ChallengeIdx`) its challenge tree cannot + contain a `chalNode`, so a tree rooted at round `0` is a single chain of message nodes with a + unique full transcript, and `IsStructured S` holds vacuously. Hence tree special soundness + collapses to a *transcript-level* extraction obligation: provide a function `e` from the input + statement and the (unique) transcript to a witness, and show that whenever the verifier accepts + the transcript into `relOut.language` the extracted witness lies in `relIn`. + + This is the reusable bridge that makes the coordinate-wise special soundness of the zero-round / + send / check components (`SendClaim`, `SendWitness`, `CheckClaim`, `ReduceClaim`) cheap: each is + proved by supplying `e` and discharging the (degenerate, probability-free in the pure-verifier + case) acceptance obligation. + + ## Main results + + * `ProtocolSpec.ChallengeTree.transcripts_eq_singleton` / `fullTranscripts_eq_singleton` — + a no-challenge tree lists exactly one transcript. + * `ProtocolSpec.ChallengeTree.onlyTranscript` (+ `onlyTranscript_mem`) — that unique transcript. + * `Verifier.treeSpecialSound_of_isEmpty_challengeIdx` — the bridge. + * `Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx` and its `OracleVerifier` analogue. +-/ + +noncomputable section + +open OracleComp OracleSpec ProtocolSpec +open scoped NNReal + +namespace ProtocolSpec.ChallengeTree + +variable {n : ℕ} {pSpec : ProtocolSpec n} {arity : pSpec.ChallengeIdx → ℕ} + +/-- With no challenge rounds, every challenge (sub)tree lists exactly one transcript: there are no + branch points, only a chain of message nodes ending in a leaf. -/ +theorem transcripts_eq_singleton [IsEmpty pSpec.ChallengeIdx] : + {m : Fin (n + 1)} → (tree : ChallengeTree pSpec arity m) → (pre : Transcript m pSpec) → + ∃ tr, tree.transcripts pre = [tr] + | _, .leaf, pre => ⟨pre, rfl⟩ + | _, .msgNode _ _ msg child, pre => + show ∃ tr, child.transcripts (pre.concat msg) = [tr] from + transcripts_eq_singleton child (pre.concat msg) + | _, .chalNode m h _ _, _ => isEmptyElim (⟨m, h⟩ : pSpec.ChallengeIdx) + +/-- With no challenge rounds, a full tree (rooted at round `0`) has exactly one transcript. -/ +theorem fullTranscripts_eq_singleton [IsEmpty pSpec.ChallengeIdx] + (tree : ChallengeTree pSpec arity 0) : ∃ tr, tree.fullTranscripts = [tr] := + show ∃ tr, tree.transcripts default = [tr] from transcripts_eq_singleton tree default + +/-- The unique full transcript of a no-challenge tree. -/ +def onlyTranscript [IsEmpty pSpec.ChallengeIdx] + (tree : ChallengeTree pSpec arity 0) : FullTranscript pSpec := + (fullTranscripts_eq_singleton tree).choose + +theorem onlyTranscript_mem [IsEmpty pSpec.ChallengeIdx] + (tree : ChallengeTree pSpec arity 0) : + tree.onlyTranscript ∈ tree.fullTranscripts := by + have h : tree.fullTranscripts = [tree.onlyTranscript] := + (fullTranscripts_eq_singleton tree).choose_spec + rw [h] + exact List.mem_singleton_self _ + +end ProtocolSpec.ChallengeTree + +namespace Verifier + +open ProtocolSpec ProtocolSpec.ChallengeTree + +variable {ι : Type} {oSpec : OracleSpec ι} + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + +/-- **Degenerate tree special soundness.** For a protocol with no challenge rounds, the tree is a + single message-chain, so tree special soundness reduces to a transcript-level extractor: any `e` + such that "the verifier accepts the (unique) transcript into `relOut.language`" implies the + extracted witness lies in `relIn`. The shape `S` is irrelevant (`IsStructured` is vacuous). -/ +theorem treeSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] + (S : ChallengeTreeShape pSpec) (V : Verifier oSpec StmtIn StmtOut pSpec) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (e : StmtIn → FullTranscript pSpec → WitIn) + (h : ∀ stmtIn tr, + Pr[ (· ∈ relOut.language) | + OptionT.mk do (simulateQ impl (V.run stmtIn tr)).run' (← init)] = 1 → + (stmtIn, e stmtIn tr) ∈ relIn) : + V.treeSpecialSound init impl S relIn relOut := + ⟨fun stmtIn tree => e stmtIn tree.onlyTranscript, + fun stmtIn tree _ hAcc => h stmtIn _ (hAcc _ tree.onlyTranscript_mem)⟩ + +/-- CWSS corollary of `treeSpecialSound_of_isEmpty_challengeIdx`: any coordinate-wise structure `D` + works, since `IsStructured` is vacuous with no challenge rounds. -/ +theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] + (D : CWSSStructure pSpec) (V : Verifier oSpec StmtIn StmtOut pSpec) + (relIn : Set (StmtIn × WitIn)) (relOut : Set (StmtOut × WitOut)) + (e : StmtIn → FullTranscript pSpec → WitIn) + (h : ∀ stmtIn tr, + Pr[ (· ∈ relOut.language) | + OptionT.mk do (simulateQ impl (V.run stmtIn tr)).run' (← init)] = 1 → + (stmtIn, e stmtIn tr) ∈ relIn) : + V.coordinateWiseSpecialSound init impl D relIn relOut := + treeSpecialSound_of_isEmpty_challengeIdx init impl D.toShape V relIn relOut e h + +end Verifier + +namespace OracleVerifier + +open ProtocolSpec ProtocolSpec.ChallengeTree + +variable {ι : Type} {oSpec : OracleSpec ι} + {StmtIn WitIn StmtOut WitOut : Type} + {ιₛᵢ : Type} {OStmtIn : ιₛᵢ → Type} [∀ i, OracleInterface (OStmtIn i)] + {ιₛₒ : Type} {OStmtOut : ιₛₒ → Type} + {n : ℕ} {pSpec : ProtocolSpec n} + [∀ i, OracleInterface (pSpec.Message i)] + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + +/-- Oracle-reduction analogue of `coordinateWiseSpecialSound_of_isEmpty_challengeIdx`, on the + combined `(StmtIn × ∀ i, OStmtIn i)` statement. -/ +theorem coordinateWiseSpecialSound_of_isEmpty_challengeIdx [IsEmpty pSpec.ChallengeIdx] + (D : CWSSStructure pSpec) + (V : OracleVerifier oSpec StmtIn OStmtIn StmtOut OStmtOut pSpec) + (relIn : Set ((StmtIn × ∀ i, OStmtIn i) × WitIn)) + (relOut : Set ((StmtOut × ∀ i, OStmtOut i) × WitOut)) + (e : (StmtIn × ∀ i, OStmtIn i) → FullTranscript pSpec → WitIn) + (h : ∀ stmtIn tr, + Pr[ (· ∈ relOut.language) | + OptionT.mk do (simulateQ impl (V.toVerifier.run stmtIn tr)).run' (← init)] = 1 → + (stmtIn, e stmtIn tr) ∈ relIn) : + V.coordinateWiseSpecialSound init impl D relIn relOut := + V.toVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D relIn relOut e h + +end OracleVerifier diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean new file mode 100644 index 0000000000..fa69c80d9e --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean @@ -0,0 +1,405 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Composition.Sequential.IsPure + +/-! + # `n`-ary sequential composition for (coordinate-wise) special soundness + + This file lifts the binary append theory of + `CoordinateWiseSpecialSoundness.Composition` to the finite sequential composition + `Verifier.seqCompose`. The two ingredients are: + + * the **base case** `Verifier.id_treeSpecialSound`: the identity verifier (over the empty + protocol `!p[]`, which has no challenge rounds) is tree-special-sound for any shape with + `relIn = relOut`, via the no-challenge bridge `treeSpecialSound_of_isEmpty_challengeIdx`; and + * the **step case**, threading per-factor purity (`Verifier.IsPure`, from + `Composition.Sequential.IsPure`) into the deterministic-left hypothesis of + `Verifier.append_treeSpecialSound`, plus the shape identity + `ChallengeTreeShape.seqCompose_succ` that exposes the head/tail append structure of the + sequentially-composed shape. + + ## Main results + + * `Verifier.mem_of_pure_accepting` — converse of `Verifier.pure_accepting_of_mem`: a pure + verifier whose run is accepted with probability one has its output in the language. + * `Verifier.id_treeSpecialSound` — the n-ary base case. + * `ChallengeTreeShape.seqCompose_succ` — `seqCompose` of shapes unfolds to `append` of head/tail. + * `Verifier.seqCompose_treeSpecialSound` — generic n-ary tree-soundness composition. + * `Verifier.seqCompose_coordinateWiseSpecialSound` — the CWSS-specific wrapper. +-/ + +noncomputable section + +open OracleComp OracleSpec ProtocolSpec +open scoped NNReal + +namespace Verifier + +open ProtocolSpec ProtocolSpec.ChallengeTree + +variable {ι : Type} {oSpec : OracleSpec ι} + {StmtIn StmtOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + [∀ i, SampleableType (pSpec.Challenge i)] + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + +omit [∀ i, SampleableType (pSpec.Challenge i)] in +/-- Converse of `pure_accepting_of_mem`: if a verifier deterministically outputs `out` on +`(stmt, tr)` and its run is accepted into `lang` with probability one, then `out ∈ lang`. -/ +theorem mem_of_pure_accepting + (V : Verifier oSpec StmtIn StmtOut pSpec) + (stmt : StmtIn) (tr : pSpec.FullTranscript) + (lang : Set StmtOut) (out : StmtOut) + (hV : V.verify stmt tr = pure out) + (hAcc : Pr[ (· ∈ lang) | + OptionT.mk do (simulateQ impl (V.run stmt tr)).run' (← init)] = 1) : + out ∈ lang := by + rw [probEvent_eq_one_iff] at hAcc + obtain ⟨hFail, hmem⟩ := hAcc + -- The underlying probabilistic computation is `init >>= fun _ => pure (some out)`. + have hrun : (do (simulateQ impl (V.run stmt tr)).run' (← init) : + ProbComp (Option StmtOut)) = (init >>= fun _ => pure (some out)) := by + simp only [Verifier.run, hV] + congr 1 + refine hmem out ?_ + -- `init` has nonempty support, else the whole computation would fail with probability one. + have hne : (support init).Nonempty := by + by_contra hempty + rw [Set.not_nonempty_iff_eq_empty] at hempty + have hcfail : Pr[⊥ | + (init >>= fun _ => pure (some out) : ProbComp (Option StmtOut))] = 0 := by + have h2 := hFail + rw [OptionT.probFailure_eq, OptionT.run_mk, hrun] at h2 + exact (add_eq_zero.mp h2).1 + have hcsupp : + support (init >>= fun _ => pure (some out) : ProbComp (Option StmtOut)) = ∅ := by + rw [support_bind_const, support_pure]; simp [hempty] + rw [probFailure_eq_one hcsupp] at hcfail + exact one_ne_zero hcfail + rw [OptionT.mem_support_iff, OptionT.run_mk, hrun, support_bind_const, support_pure] + exact ⟨Set.mem_singleton _, hne⟩ + +end Verifier + +namespace Verifier + +open ProtocolSpec ProtocolSpec.ChallengeTree + +variable {ι : Type} {oSpec : OracleSpec ι} + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + +/-- **n-ary base case.** The identity verifier is tree-special-sound for any shape `S`, with input +relation equal to output relation: the empty protocol `!p[]` has no challenge rounds, so this is the +no-challenge bridge with the extractor that picks (classically) a witness of `stmtIn` whenever one +exists. -/ +theorem id_treeSpecialSound {Statement Witness : Type} [Nonempty Witness] + (S : ChallengeTreeShape (!p[] : ProtocolSpec 0)) + (rel : Set (Statement × Witness)) : + (Verifier.id (oSpec := oSpec) (Statement := Statement)).treeSpecialSound + init impl S rel rel := by + classical + refine treeSpecialSound_of_isEmpty_challengeIdx init impl S Verifier.id rel rel + (fun stmt _ => if h : ∃ w, (stmt, w) ∈ rel then h.choose else Classical.ofNonempty) ?_ + intro stmtIn tr hAcc + have hlang : stmtIn ∈ rel.language := + mem_of_pure_accepting init impl Verifier.id stmtIn tr rel.language stmtIn rfl hAcc + have hex : ∃ w, (stmtIn, w) ∈ rel := (Set.mem_language_iff rel stmtIn).1 hlang + simp only [dif_pos hex] + exact hex.choose_spec + +end Verifier + +namespace ChallengeTreeShape + +variable {m : ℕ} {len : Fin (m + 1) → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} + +/-- A sigma over `Fin` whose fibers are subtypes of `Fin` is determined by the underlying +`Fin`-level data: equal first components and equal underlying second values force equality. -/ +private theorem sigmaSubtype_ext {M : ℕ} {N : Fin M → ℕ} {P : (i : Fin M) → Fin (N i) → Prop} + {a a' : Fin M} {v : Fin (N a)} {v' : Fin (N a')} {p : P a v} {p' : P a' v'} + (ha : a = a') (hv : (v : ℕ) = (v' : ℕ)) : + (⟨a, ⟨v, p⟩⟩ : (i : Fin M) × {x : Fin (N i) // P i x}) = ⟨a', ⟨v', p'⟩⟩ := by + subst ha + have : v = v' := Fin.ext hv + subst this + rfl + +/-- Heterogeneous congruence for dependent function application. -/ +private theorem heq_app.{u, v} {α α' : Sort u} {β : α → Sort v} {β' : α' → Sort v} + (hα : α = α') (hβ : HEq β β') {f : (a : α) → β a} {f' : (a : α') → β' a} + (hf : HEq f f') {a : α} {a' : α'} (ha : HEq a a') : HEq (f a) (f' a') := by + subst hα + obtain rfl := eq_of_heq hβ + obtain rfl := eq_of_heq hf + obtain rfl := eq_of_heq ha + rfl + +/-- Heterogeneous congruence for `nodeOk`: a `ChallengeTreeShape`'s node predicate transports +across an equality of the underlying protocol, challenge index, arity, and a heterogeneous equality +of the sibling-challenge function. -/ +private theorem heq_nodeOk {n n' : ℕ} (hn : n = n') {p : ProtocolSpec n} {p' : ProtocolSpec n'} + (hp : HEq p p') {T : ChallengeTreeShape p} {T' : ChallengeTreeShape p'} (hT : HEq T T') + {i : p.ChallengeIdx} {i' : p'.ChallengeIdx} (hi : HEq i i') + {f : Fin (T.arity i) → p.Challenge i} {f' : Fin (T'.arity i') → p'.Challenge i'} + (hf : HEq f f') : + HEq (T.nodeOk i f) (T'.nodeOk i' f') := by + subst hn + obtain rfl := eq_of_heq hp + obtain rfl := eq_of_heq hT + obtain rfl := eq_of_heq hi + obtain rfl := eq_of_heq hf + exact HEq.rfl + +variable {a b : ℕ} {p₁ : ProtocolSpec a} {p₂ : ProtocolSpec b} + +/-- The append node predicate at a left-embedded index reduces to the left shape's predicate. -/ +theorem append_nodeOk_inl (S₁ : ChallengeTreeShape p₁) (S₂ : ChallengeTreeShape p₂) + (i₁ : p₁.ChallengeIdx) + (challenges : Fin ((S₁.append S₂).arity (ChallengeIdx.inl i₁)) → + (p₁ ++ₚ p₂).Challenge (ChallengeIdx.inl i₁)) : + (S₁.append S₂).nodeOk (ChallengeIdx.inl i₁) challenges + = S₁.nodeOk i₁ (fun j => cast (by simp [ProtocolSpec.append, ChallengeIdx.inl]) + (challenges (Fin.cast (by + change S₁.arity i₁ = ChallengeTree.appendArity S₁.arity S₂.arity (ChallengeIdx.inl i₁) + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl]) j))) := by + simp only [ChallengeTreeShape.append] + split + · rename_i i₁' heq + rw [ChallengeIdx.sumEquiv_symm_inl] at heq + obtain rfl : i₁' = i₁ := by simpa using heq.symm + rfl + · rename_i i₂' heq + rw [ChallengeIdx.sumEquiv_symm_inl] at heq + simp at heq + +/-- The append node predicate at a right-embedded index reduces to the right shape's predicate. -/ +theorem append_nodeOk_inr (S₁ : ChallengeTreeShape p₁) (S₂ : ChallengeTreeShape p₂) + (i₂ : p₂.ChallengeIdx) + (challenges : Fin ((S₁.append S₂).arity (ChallengeIdx.inr i₂)) → + (p₁ ++ₚ p₂).Challenge (ChallengeIdx.inr i₂)) : + (S₁.append S₂).nodeOk (ChallengeIdx.inr i₂) challenges + = S₂.nodeOk i₂ (fun j => cast (by simp [ProtocolSpec.append, ChallengeIdx.inr]) + (challenges (Fin.cast (by + change S₂.arity i₂ = ChallengeTree.appendArity S₁.arity S₂.arity (ChallengeIdx.inr i₂) + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr]) j))) := by + simp only [ChallengeTreeShape.append] + split + · rename_i i₁' heq + rw [ChallengeIdx.sumEquiv_symm_inr] at heq + simp at heq + · rename_i i₂' heq + rw [ChallengeIdx.sumEquiv_symm_inr] at heq + obtain rfl : i₂' = i₂ := by simpa using heq.symm + rfl + +/-- The `seqCompose` node predicate unfolds, by definition, to the decoded component's predicate +applied to the cast-in sibling challenges. -/ +theorem seqCompose_nodeOk_eq {r : ℕ} {ln : Fin r → ℕ} {ps : ∀ i, ProtocolSpec (ln i)} + (S : ∀ i, ChallengeTreeShape (ps i)) (ci : (ProtocolSpec.seqCompose ps).ChallengeIdx) + (f : Fin ((ChallengeTreeShape.seqCompose S).arity ci) → + (ProtocolSpec.seqCompose ps).Challenge ci) : + (ChallengeTreeShape.seqCompose S).nodeOk ci f + = (S (seqComposeChallengeIdxToSigma ci).1).nodeOk (seqComposeChallengeIdxToSigma ci).2 + (fun j => cast (seqCompose_challenge_eq ci) (f j)) := rfl + +/-- The decoded sigma of a left-embedded composed challenge index is `⟨0, i₁⟩`. -/ +private theorem toSigma_inl (i₁ : (pSpec 0).ChallengeIdx) : + seqComposeChallengeIdxToSigma + (pSpec := pSpec) + (ChallengeIdx.inl (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁) + = ⟨0, i₁⟩ := by + unfold seqComposeChallengeIdxToSigma + dsimp only + have hcoe : (ChallengeIdx.inl + (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁).1 + = Fin.embedSum (0 : Fin (m + 1)) i₁.1 := rfl + refine sigmaSubtype_ext (P := fun i x => (pSpec i).dir x = .V_to_P) + (a' := 0) (v' := i₁.1) (p' := i₁.2) ?_ ?_ + · rw [hcoe, Fin.splitSum_embedSum] + · rw [hcoe, Fin.splitSum_embedSum] + +/-- The decoded sigma of a right-embedded composed challenge index shifts by one round. -/ +private theorem toSigma_inr + (i₂ : (ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))).ChallengeIdx) : + seqComposeChallengeIdxToSigma (pSpec := pSpec) (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂) + = ⟨(seqComposeChallengeIdxToSigma i₂).1.succ, (seqComposeChallengeIdxToSigma i₂).2⟩ := by + have hcoe : (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂).1 + = Fin.natAdd (len 0) i₂.1 := rfl + conv_lhs => unfold seqComposeChallengeIdxToSigma + conv_rhs => unfold seqComposeChallengeIdxToSigma + dsimp only + refine sigmaSubtype_ext (P := fun i x => (pSpec i).dir x = .V_to_P) ?_ ?_ + · rw [hcoe, Fin.splitSum_succ]; erw [Fin.dappend_right] + · rw [hcoe, Fin.splitSum_succ]; erw [Fin.dappend_right] + +/-- **Successor unfolding of the sequentially-composed shape.** `ChallengeTreeShape.seqCompose` of +a family over `m + 1` factors is the binary `append` of the head shape with the sequential +composition of the tail. This is the shape-level analogue of +`ProtocolSpec.seqCompose_succ_eq_append`, and is what lets the `n`-ary tree-soundness induction +reduce its step to the binary `Verifier.append_treeSpecialSound`. -/ +theorem seqCompose_succ (S : ∀ i, ChallengeTreeShape (pSpec i)) : + ChallengeTreeShape.seqCompose S = + (S 0).append (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))) := by + have harity : (ChallengeTreeShape.seqCompose S).arity + = ((S 0).append (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i)))).arity := by + funext i + change (S (seqComposeChallengeIdxToSigma i).1).arity (seqComposeChallengeIdxToSigma i).2 + = ChallengeTree.appendArity (S 0).arity + (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i + rcases hsplit : (ChallengeIdx.sumEquiv (pSpec₁ := pSpec 0) + (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i)))).symm i with i₁ | i₂ + · obtain rfl : i = (ChallengeIdx.inl (pSpec₂ := + ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁ : + (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by + have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit + simpa [ChallengeIdx.sumEquiv_apply] using this + rw [toSigma_inl] + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl] + · obtain rfl : i = (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂ : + (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by + have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit + simpa [ChallengeIdx.sumEquiv_apply] using this + rw [toSigma_inr] + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr] + rfl + refine ChallengeTreeShape.ext harity ?_ + refine Function.hfunext rfl (fun i i' hi => ?_) + obtain rfl : i = i' := eq_of_heq hi + refine Function.hfunext (by rw [harity]) (fun challenges challenges' hch => ?_) + rcases hsplit : (ChallengeIdx.sumEquiv (pSpec₁ := pSpec 0) + (pSpec₂ := ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i)))).symm i with i₁ | i₂ + · obtain rfl : i = (ChallengeIdx.inl (pSpec₂ := + ProtocolSpec.seqCompose (fun i => pSpec (Fin.succ i))) i₁ : + (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by + have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit + simpa [ChallengeIdx.sumEquiv_apply] using this + apply heq_of_eq + rw [seqCompose_nodeOk_eq, append_nodeOk_inl] + have hsig := toSigma_inl (pSpec := pSpec) i₁ + have hfst := congrArg Sigma.fst hsig + have hsnd := (Sigma.ext_iff.mp hsig).2 + refine eq_of_heq (heq_nodeOk (congrArg len hfst) ?_ ?_ hsnd ?_) + · rw [hfst] + · rw [hfst] + · refine Function.hfunext (congrArg Fin ?hdom) (fun j j' hj => ?_) + case hdom => + change (ChallengeTreeShape.seqCompose S).arity _ = (S 0).arity i₁ + rw [harity] + change ChallengeTree.appendArity (S 0).arity + (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inl i₁) + = (S 0).arity i₁ + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl] + refine HEq.trans (cast_heq _ _) (HEq.trans ?_ (cast_heq _ _).symm) + refine heq_app (by rw [harity]) ?_ hch ?_ + · rw [harity] + · refine HEq.trans hj ?_ + exact (Fin.heq_ext_iff (by + change (S 0).arity i₁ = ChallengeTree.appendArity (S 0).arity + (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inl i₁) + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inl, Sum.elim_inl])).mpr rfl + · obtain rfl : i = (ChallengeIdx.inr (pSpec₁ := pSpec 0) i₂ : + (ProtocolSpec.seqCompose pSpec).ChallengeIdx) := by + have := (Equiv.symm_apply_eq ChallengeIdx.sumEquiv).mp hsplit + simpa [ChallengeIdx.sumEquiv_apply] using this + apply heq_of_eq + rw [seqCompose_nodeOk_eq, append_nodeOk_inr, seqCompose_nodeOk_eq] + have hsig := toSigma_inr (pSpec := pSpec) i₂ + have hfst := congrArg Sigma.fst hsig + have hsnd := (Sigma.ext_iff.mp hsig).2 + refine eq_of_heq (heq_nodeOk (congrArg len hfst) ?_ ?_ hsnd ?_) + · rw [hfst] + · rw [hfst] + · refine Function.hfunext (congrArg Fin ?hdomr) (fun j j' hj => ?_) + case hdomr => + change (ChallengeTreeShape.seqCompose S).arity _ + = (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ + rw [harity] + change ChallengeTree.appendArity (S 0).arity + (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inr i₂) + = (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr] + refine HEq.trans (cast_heq _ _) ?_ + refine HEq.trans ?_ (HEq.trans (cast_heq _ _) (cast_heq _ _)).symm + refine heq_app (by rw [harity]) ?_ hch ?_ + · rw [harity] + · refine HEq.trans hj ?_ + exact (Fin.heq_ext_iff (by + change (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity i₂ + = ChallengeTree.appendArity (S 0).arity + (ChallengeTreeShape.seqCompose (fun i => S (Fin.succ i))).arity (ChallengeIdx.inr i₂) + simp only [ChallengeTree.appendArity, Function.comp_apply, + ChallengeIdx.sumEquiv_symm_inr, Sum.elim_inr])).mpr rfl + +end ChallengeTreeShape + +section NaryCompose + +variable {ι : Type} {oSpec : OracleSpec ι} + {m : ℕ} {Stmt : Fin (m + 1) → Type} {Wit : Fin (m + 1) → Type} + {len : Fin m → ℕ} {pSpec : ∀ i, ProtocolSpec (len i)} + [∀ i, ∀ j, SampleableType ((pSpec i).Challenge j)] + {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + +/-- **`n`-ary generic tree-soundness composition.** If each factor verifier is pure (`IsPure`) and +tree-special-sound for the seam relations `rel i.castSucc ↦ rel i.succ`, then the sequential +composition `Verifier.seqCompose` is tree-special-sound for the sequentially-composed shape from +`rel 0` to `rel (Fin.last m)`. The induction's base case is `Verifier.id_treeSpecialSound` and its +step is `Verifier.append_treeSpecialSound`, with the head's purity discharging the +deterministic-left hypothesis and `ChallengeTreeShape.seqCompose_succ` exposing the appended +shape. -/ +theorem Verifier.seqCompose_treeSpecialSound + (S : ∀ i, ChallengeTreeShape (pSpec i)) + (rel : ∀ i, Set (Stmt i × Wit i)) + (hWit : Nonempty (Wit (Fin.last m))) + (V : ∀ i, Verifier oSpec (Stmt i.castSucc) (Stmt i.succ) (pSpec i)) + (hV : ∀ i, (V i).IsPure) + (h : ∀ i, (V i).treeSpecialSound init impl (S i) (rel i.castSucc) (rel i.succ)) : + (Verifier.seqCompose Stmt V).treeSpecialSound init impl + (ChallengeTreeShape.seqCompose S) (rel 0) (rel (Fin.last m)) := by + induction m with + | zero => + haveI : Nonempty (Wit 0) := hWit + rw [Verifier.seqCompose_zero] + exact Verifier.id_treeSpecialSound init impl (ChallengeTreeShape.seqCompose S) (rel 0) + | succ m ih => + rw [Verifier.seqCompose_succ, ChallengeTreeShape.seqCompose_succ] + obtain ⟨f₀, hf₀⟩ := (hV 0).is_pure + have htail := ih (fun i => S i.succ) (fun i => rel i.succ) hWit + (fun i => V i.succ) (fun i => hV i.succ) (fun i => h i.succ) + refine Verifier.append_treeSpecialSound init impl (V 0) + (Verifier.seqCompose (Stmt ∘ Fin.succ) (fun i => V i.succ)) + (S 0) (ChallengeTreeShape.seqCompose (fun i => S i.succ)) f₀ hf₀ (h 0) ?_ + simpa using htail + +/-- **`n`-ary CWSS composition.** The coordinate-wise special-soundness wrapper of +`seqCompose_treeSpecialSound`, obtained by unfolding `coordinateWiseSpecialSound` to tree-soundness +of the induced shape and rewriting with `CWSSStructure.toShape_seqCompose`. -/ +theorem Verifier.seqCompose_coordinateWiseSpecialSound + (D : ∀ i, CWSSStructure (pSpec i)) + (rel : ∀ i, Set (Stmt i × Wit i)) + (hWit : Nonempty (Wit (Fin.last m))) + (V : ∀ i, Verifier oSpec (Stmt i.castSucc) (Stmt i.succ) (pSpec i)) + (hV : ∀ i, (V i).IsPure) + (h : ∀ i, (V i).coordinateWiseSpecialSound init impl (D i) (rel i.castSucc) (rel i.succ)) : + (Verifier.seqCompose Stmt V).coordinateWiseSpecialSound init impl + (CWSSStructure.seqCompose D) (rel 0) (rel (Fin.last m)) := by + change (Verifier.seqCompose Stmt V).treeSpecialSound init impl + (CWSSStructure.toShape (CWSSStructure.seqCompose D)) (rel 0) (rel (Fin.last m)) + rw [CWSSStructure.toShape_seqCompose] + exact Verifier.seqCompose_treeSpecialSound init impl + (fun i => CWSSStructure.toShape (D i)) rel hWit V hV h + +end NaryCompose + +end diff --git a/ArkLib/ProofSystem/Component/CheckClaim.lean b/ArkLib/ProofSystem/Component/CheckClaim.lean index 9ecc2c23d4..3df8a3c95c 100644 --- a/ArkLib/ProofSystem/Component/CheckClaim.lean +++ b/ArkLib/ProofSystem/Component/CheckClaim.lean @@ -5,6 +5,7 @@ Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose /-! # Simple (Oracle) Reduction: Check if a predicate / claim on a statement is satisfied @@ -168,7 +169,8 @@ section OracleReduction variable {ιₛ : Type} (OStatement : ιₛ → Type) [∀ i, OracleInterface (OStatement i)] -/-- The oracle prover for the `CheckClaim` oracle reduction. -/ +/-- The oracle prover for the `CheckClaim` oracle reduction: it forwards the statement and all +oracle statements unchanged (there is no message and no witness). -/ @[inline, specialize] def oracleProver : OracleProver oSpec Statement OStatement Unit Statement OStatement Unit !p[] where @@ -178,58 +180,74 @@ def oracleProver : OracleProver oSpec receiveChallenge := fun i => nomatch i output := fun stmt => pure (stmt, ()) -variable (pred : ReaderT Statement (OracleComp [OStatement]ₒ) Prop) - -- (hPred : ∀ stmt, NeverFail (pred stmt)) - -/-- The oracle verifier for the `CheckClaim` oracle reduction. -/ +/-- The oracle verifier for the `CheckClaim` oracle reduction is a **pure pass-through** (per §1.2 +of the Hachi CWSS plan): it returns the statement and all oracle statements unchanged. The predicate +being checked is *not* run as an effectful `guard`/oracle computation here; instead it lives in the +output relation `oracleRelOut`. This keeps the verifier `IsPure` (so it can be a left factor in a +CWSS composition) and sidesteps the unfinished no-failure `OracleComp` refactor. (The `guard`-based +plain-reduction variant above is retained as a rightmost-only factor.) -/ @[inline, specialize] def oracleVerifier : OracleVerifier oSpec Statement OStatement Statement OStatement !p[] where - verify := fun stmt _ => do let _ ← pred stmt; return stmt - embed := Embedding.inl - hEq := by intro i; simp + verify := fun stmt _ => pure stmt + embed := Function.Embedding.inl + hEq := fun _ => rfl /-- The oracle reduction for the `CheckClaim` oracle reduction. -/ @[inline, specialize] def oracleReduction : OracleReduction oSpec Statement OStatement Unit Statement OStatement Unit !p[] where prover := oracleProver oSpec Statement OStatement - verifier := oracleVerifier oSpec Statement OStatement pred + verifier := oracleVerifier oSpec Statement OStatement variable {Statement} {OStatement} --- @[reducible, simp] --- def toRelInput : Set ((Statement × (∀ i, OStatement i)) × Unit) := --- { ⟨⟨stmt, oStmt⟩, _⟩ | simulateQ' (toOracleImpl OStatement oStmt) (pred stmt) (hPred stmt) } - --- -- theorem oracleProver_run - --- variable {σ : Type} {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} - --- /-- The `CheckClaim` reduction satisfies perfect completeness. -/ --- @[simp] --- theorem oracleReduction_completeness (h : init.neverFails) : --- (oracleReduction oSpec Statement OStatement pred).perfectCompleteness init impl --- (toRelInput pred hPred) Set.univ := by --- -- TODO: fix this proof once `OracleComp` no longer has failure --- simp only [OracleReduction.perfectCompleteness, toRelInput, OracleReduction.toReduction, --- oracleReduction, oracleProver, Nat.reduceAdd, Fin.isValue, MessageIdx, Message, ChallengeIdx, --- Challenge, Fin.reduceLast, oracleVerifier, bind_pure_comp, OracleVerifier.toVerifier, --- simulateQ_map, Embedding.inl_apply, eq_mpr_eq_cast, cast_eq, Functor.map_map, --- Reduction.perfectCompleteness_eq_prob_one, Set.mem_setOf_eq, StateT.run'_eq, Set.mem_univ, --- true_and, probEvent_eq_one_iff, probFailure_eq_zero_iff, neverFails_bind_iff, h, --- neverFails_map_iff, support_bind, support_map, Set.mem_iUnion, Set.mem_image, Prod.exists, --- exists_and_right, exists_eq_right, exists_prop, forall_exists_index, and_imp, Prod.forall, --- Fin.forall_fin_zero_pi, Prod.mk.injEq] --- simp only [Reduction.run, Prover.run, Verifier.run, toOracleImpl, simulateQ'] --- simp only [ChallengeIdx, Fin.reduceLast, Prover.runToRound_zero_of_prover_first, Fin.isValue, --- bind_pure_comp, liftM_eq_liftComp, liftComp_map, Functor.map_map, pure_bind] --- intro stmt oStmt _ --- sorry --- -- simp [Reduction.run, Prover.run, Verifier.run, simOracle2] --- -- aesop - --- theorem oracleReduction_rbr_knowledge_soundness : True := sorry +/-- The pure pass-through oracle verifier's underlying non-oracle verifier returns the combined +input statement unchanged. -/ +theorem oracleVerifier_toVerifier_run {stmt : Statement} {oStmt : ∀ i, OStatement i} + {tr : (!p[] : ProtocolSpec 0).FullTranscript} : + (oracleVerifier oSpec Statement OStatement).toVerifier.run ⟨stmt, oStmt⟩ tr = + pure ⟨stmt, oStmt⟩ := by + simp only [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] + rw [show simulateQ (OracleInterface.simOracle2 oSpec oStmt tr.messages) + (pure stmt : OptionT (OracleComp _) Statement) + = (pure stmt : OptionT (OracleComp oSpec) Statement) from rfl, pure_bind] + congr 1 + +/-- The `CheckClaim` oracle verifier is pure: its underlying verifier deterministically returns the +combined statement, which discharges the deterministic-left hypothesis of the CWSS binary append. -/ +instance instIsPure : (oracleVerifier oSpec Statement OStatement).toVerifier.IsPure := + ⟨fun p _ => p, fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ + +variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (P : Statement → (∀ i, OStatement i) → Prop) + (relIn : Set ((Statement × ∀ i, OStatement i) × Unit)) + +/-- The output relation of the pure-pass-through `CheckClaim`: the input relation intersected with +the checked predicate `P` on the combined statement. Because the verifier is a pure pass-through, +"acceptance" is exactly membership in `oracleRelOut.language`, i.e. `P` holding — so the check is +enforced by the relation rather than by a runtime `guard`. -/ +@[reducible, simp] +def oracleRelOut : Set ((Statement × ∀ i, OStatement i) × Unit) := + relIn ∩ {x | P x.1.1 x.1.2} + +/-- **Coordinate-wise special soundness of `CheckClaim`.** The verifier is a pure pass-through with +no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge +`coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to a transcript-level obligation. The +extractor is trivial (`e := fun _ _ => ()`, there is no witness); since the pass-through output +equals the input and `oracleRelOut P relIn ⊆ relIn`, accepting into `oracleRelOut.language` forces +the input into `relIn`. Holds for any coordinate-wise structure `D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (!p[] : ProtocolSpec 0)) : + (oracleVerifier oSpec Statement OStatement).coordinateWiseSpecialSound init impl D relIn + (oracleRelOut P relIn) := by + refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec Statement OStatement) relIn (oracleRelOut P relIn) (fun _ _ => ()) ?_ + rintro ⟨stmt, oStmt⟩ tr hAcc + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement).toVerifier ⟨stmt, oStmt⟩ tr + (oracleRelOut P relIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu.1 end OracleReduction diff --git a/ArkLib/ProofSystem/Component/ReduceClaim.lean b/ArkLib/ProofSystem/Component/ReduceClaim.lean index c421b0bd73..5408cd7038 100644 --- a/ArkLib/ProofSystem/Component/ReduceClaim.lean +++ b/ArkLib/ProofSystem/Component/ReduceClaim.lean @@ -5,6 +5,7 @@ Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose /-! # Simple (Oracle) Reduction: Locally / non-interactively reduce a claim @@ -171,6 +172,34 @@ theorem verifier_rbrKnowledgeSoundness (hRel : ∀ stmtIn witOut, simp only [ProtocolSpec.ChallengeIdx] exact fun _ _ _ i => Fin.elim0 i.1 +/-- The `ReduceClaim` verifier is pure: it deterministically returns `mapStmt stmt`. This discharges +the deterministic-left hypothesis of the CWSS binary append. -/ +instance instIsPure : (verifier oSpec mapStmt).IsPure := + ⟨fun stmt _ => mapStmt stmt, fun _ _ => rfl⟩ + +/-- **Coordinate-wise special soundness of `ReduceClaim`.** The verifier is pure with no challenge +rounds, so CWSS collapses (via the no-challenge bridge) to a transcript-level obligation. Given the +witness pull-back `mapWitInv` and the compatibility `hRel` (the same hypothesis as for RBR knowledge +soundness), the extractor `e stmtIn := mapWitInv stmtIn witOut` — where `witOut` is any output +witness making `mapStmt stmtIn` accepted — lands in `relIn`. Holds for any `D`. -/ +theorem verifier_coordinateWiseSpecialSound [Nonempty WitIn] + (D : CWSSStructure (!p[] : ProtocolSpec 0)) + (hRel : ∀ stmtIn witOut, + (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : + (verifier oSpec mapStmt).coordinateWiseSpecialSound init impl D relIn relOut := by + classical + refine Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (verifier oSpec mapStmt) relIn relOut + (fun stmtIn _ => if h : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut + then mapWitInv stmtIn h.choose else Classical.ofNonempty) ?_ + intro stmtIn tr hAcc + have hlang : mapStmt stmtIn ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl (verifier oSpec mapStmt) stmtIn tr + relOut.language (mapStmt stmtIn) rfl hAcc + have hex : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut := (Set.mem_language_iff _ _).1 hlang + simp only [dif_pos hex] + exact hRel stmtIn hex.choose hex.choose_spec + end Reduction section OracleReduction @@ -338,6 +367,50 @@ theorem oracleVerifier_rbrKnowledgeSoundness (hRel : ∀ stmtIn oStmtIn witOut, intro stmtIn witIn prover i exact Fin.elim0 i.1 +/-- The `ReduceClaim` oracle verifier's underlying non-oracle verifier deterministically returns the +mapped statement together with the reshaped oracle statements (`mapOStmt`). -/ +theorem oracleVerifier_toVerifier_run {stmt : StmtIn} {oStmt : ∀ i, OStmtIn i} + {tr : (!p[] : ProtocolSpec 0).FullTranscript} : + (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier.run ⟨stmt, oStmt⟩ tr = + pure ⟨mapStmt stmt, mapOStmt embedIdx hEq oStmt⟩ := by + simp only [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] + rfl + +/-- The `ReduceClaim` oracle verifier is pure, discharging the deterministic-left hypothesis of the +CWSS binary append. -/ +instance instIsPureOracle : + (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier.IsPure := + ⟨fun p _ => ⟨mapStmt p.1, mapOStmt embedIdx hEq p.2⟩, + fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ + +/-- **Coordinate-wise special soundness of the `ReduceClaim` oracle reduction.** As in the +non-oracle case, the verifier is pure with no challenge rounds, so CWSS collapses to a +transcript-level obligation discharged by the witness pull-back `mapWitInv` and the compatibility +`hRel` (identical to the RBR knowledge soundness hypothesis, `mapStmt` replaced by `mapStmt ⊗ +mapOStmt`). -/ +theorem oracleVerifier_coordinateWiseSpecialSound [Nonempty WitIn] + (D : CWSSStructure (!p[] : ProtocolSpec 0)) + (hRel : ∀ stmtIn oStmtIn witOut, + ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → + ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : + (oracleVerifier oSpec mapStmt embedIdx hEq).coordinateWiseSpecialSound init impl D + relIn relOut := by + classical + refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec mapStmt embedIdx hEq) relIn relOut + (fun s _ => if h : ∃ witOut, + ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut + then mapWitInv s h.choose else Classical.ofNonempty) ?_ + rintro ⟨stmt, oStmt⟩ tr hAcc + have hlang : (mapStmt stmt, mapOStmt embedIdx hEq oStmt) ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier ⟨stmt, oStmt⟩ tr relOut.language _ + (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + have hex : ∃ witOut, ((mapStmt stmt, mapOStmt embedIdx hEq oStmt), witOut) ∈ relOut := + (Set.mem_language_iff _ _).1 hlang + simp only [dif_pos hex] + exact hRel stmt oStmt hex.choose hex.choose_spec + end OracleReduction end ReduceClaim diff --git a/ArkLib/ProofSystem/Component/SendChallenge.lean b/ArkLib/ProofSystem/Component/SendChallenge.lean new file mode 100644 index 0000000000..f184827e67 --- /dev/null +++ b/ArkLib/ProofSystem/Component/SendChallenge.lean @@ -0,0 +1,115 @@ +/- +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.RoundByRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic + +/-! + # Simple Oracle Reduction - SendChallenge (the fold challenge round) + + A one-round, verifier-first (`V_to_P`) oracle reduction: the verifier samples a **challenge + vector** `c : Fin ℓ → C`, sends it to the prover, and appends it to the output statement. There is + no witness and **no check** — this is the definitional challenge-round building block of the + Hachi/Greyhound fold (Figure 3), where `ℓ = 2ʳ` and `C ⊆ Rq`. + + Per §1.4 of the Hachi CWSS plan, a lone challenge round has no relation to extract into, so it is + *not* coordinate-wise special sound on its own; its CWSS is established only as part of the + surrounding fold block (Lemma 8, out of scope here). What this file provides is: + + - the component itself (`oracleProver` / `oracleVerifier` / `oracleReduction`); + - `instIsPure`: the verifier is pure — it reads the challenge off the transcript and appends it, + with no runtime check (§1.2) — so it can be a left factor in a CWSS `append`; + - `foldBlockStructure`: the `CWSSStructure` this round contributes to the block — one challenge + round with `coordIndex = ℓ`, `alphabet = C`, `soundnessParam = 2` (so `arity = ℓ·(2−1)+1 = ℓ+1` + and `nodeOk = IsSpecialSoundFamily ℓ 2`), matching Hachi Lemma 4 / Def. 3 exactly. + + To *run* the reduction (completeness / soundness) one additionally needs + `[SampleableType (Fin ℓ → C)]` (available from `[SampleableType C]` via the derived `Fin`-domain + product instance); it is not required for the definitions, `IsPure`, or the structure above. +-/ + +open OracleSpec OracleComp OracleQuery OracleInterface ProtocolSpec Function + +namespace SendChallenge + +variable {ι : Type} (oSpec : OracleSpec ι) (Statement : Type) + {ιₛ : Type} (OStatement : ιₛ → Type) [∀ i, OracleInterface (OStatement i)] + (C : Type) (ℓ : ℕ) + +/-- One `V_to_P` challenge round carrying the fold challenge vector `c : Fin ℓ → C`. -/ +@[reducible] +def pSpec : ProtocolSpec 1 := ⟨!v[.V_to_P], !v[Fin ℓ → C]⟩ + +/-- The oracle prover receives the challenge `c` and appends it to the statement (the oracle +statements pass through). It has no message to send. -/ +@[inline, specialize] +def oracleProver : OracleProver oSpec + Statement OStatement Unit + (Statement × (Fin ℓ → C)) OStatement Unit (pSpec C ℓ) where + PrvState + | 0 => Statement × (∀ i, OStatement i) + | 1 => (Statement × (∀ i, OStatement i)) × (Fin ℓ → C) + input := Prod.fst + sendMessage | ⟨0, h⟩ => nomatch h + receiveChallenge | ⟨0, _⟩ => fun st => pure fun c => (st, c) + output := fun ⟨⟨stmt, oStmt⟩, c⟩ => pure (((stmt, c), oStmt), ()) + +/-- The oracle verifier samples the challenge `c` (as the `V_to_P` round), reads it off the +transcript, and appends it to the output statement — no check. This keeps it pure. -/ +@[inline, specialize] +def oracleVerifier : OracleVerifier oSpec + Statement OStatement (Statement × (Fin ℓ → C)) OStatement (pSpec C ℓ) where + verify := fun stmt chal => pure (stmt, chal ⟨0, rfl⟩) + embed := Function.Embedding.inl + hEq := fun _ => rfl + +/-- The oracle reduction for `SendChallenge`. -/ +@[inline, specialize] +def oracleReduction : OracleReduction oSpec + Statement OStatement Unit + (Statement × (Fin ℓ → C)) OStatement Unit (pSpec C ℓ) where + prover := oracleProver oSpec Statement OStatement C ℓ + verifier := oracleVerifier oSpec Statement OStatement C ℓ + +instance : VerifierOnly (pSpec C ℓ) where + verifier_first' := by simp + +variable {Statement} {OStatement} {C} {ℓ} + +/-- The pure verifier's underlying non-oracle verifier returns the statement together with the +sampled challenge (read off the transcript), with the oracle statements passed through. -/ +theorem oracleVerifier_toVerifier_run {stmt : Statement} {oStmt : ∀ i, OStatement i} + {tr : (pSpec C ℓ).FullTranscript} : + (oracleVerifier oSpec Statement OStatement C ℓ).toVerifier.run ⟨stmt, oStmt⟩ tr = + pure ⟨(stmt, tr.challenges ⟨0, rfl⟩), oStmt⟩ := by + simp only [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] + rw [show simulateQ (OracleInterface.simOracle2 oSpec oStmt tr.messages) + (pure (stmt, tr.challenges ⟨0, rfl⟩) : + OptionT (OracleComp _) (Statement × (Fin ℓ → C))) + = (pure (stmt, tr.challenges ⟨0, rfl⟩) : + OptionT (OracleComp oSpec) (Statement × (Fin ℓ → C))) from rfl, pure_bind] + congr 1 + +/-- The `SendChallenge` oracle verifier is pure: it deterministically appends the (transcript-read) +challenge to the statement. This discharges the deterministic-left hypothesis of the CWSS append, +letting the challenge round sit as a left factor in the fold block. -/ +instance instIsPure : (oracleVerifier oSpec Statement OStatement C ℓ).toVerifier.IsPure := + ⟨fun p tr => ⟨(p.1, tr.challenges ⟨0, rfl⟩), p.2⟩, + fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ + +/-- The **fold-block coordinate-wise structure**: the single challenge round of `SendChallenge` +carries `ℓ` coordinates over the alphabet `C`, decomposed by the identity (`Challenge = Fin ℓ → C` +already), with soundness parameter `k = 2`. Hence `arity = ℓ·(2−1)+1 = ℓ+1` and the node predicate +is `IsSpecialSoundFamily ℓ 2` — exactly the branching required by Hachi Lemma 4 / Def. 3 (with +`ℓ = 2ʳ`). This is the shape the fold block's CWSS (Lemma 8) is proven against. -/ +def foldBlockStructure (hℓ : 0 < ℓ) : CWSSStructure (pSpec C ℓ) where + coordIndex := fun _ => ⟨ℓ, hℓ⟩ + alphabet := fun _ => C + decompose := fun i => Equiv.cast (by rcases i with ⟨j, hj⟩; fin_cases j; rfl) + soundnessParam := fun _ => ⟨2, le_refl 2⟩ + arity := fun _ => ℓ * (2 - 1) + 1 + arity_eq := rfl + +end SendChallenge diff --git a/ArkLib/ProofSystem/Component/SendClaim.lean b/ArkLib/ProofSystem/Component/SendClaim.lean index e1e9d4971e..6c1a32e694 100644 --- a/ArkLib/ProofSystem/Component/SendClaim.lean +++ b/ArkLib/ProofSystem/Component/SendClaim.lean @@ -1,240 +1,157 @@ /- Copyright (c) 2024-2025 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Quang Dao +Authors: Quang Dao, Tobias Rothmann -/ import ArkLib.OracleReduction.Security.RoundByRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose /-! # Simple Oracle Reduction - SendClaim - The prover sends a claim to the verifier. - - - There is no witness (e.g. `Witness = Unit`), and there is a single `OStatement`. - - The prover sends a message of the same type as `OStatement` to the verifier. - - The verifier performs the check for `rel`, which can be expressed as an oracle computation. - - The output data has no `Statement`, only two `OStatement`s: one from the beginning data, - and the other is the message from the prover. - - The output relation checks whether the two `OStatement`s are the same. - - TODO: Generalize + The prover sends a **claim** (a single oracle message) to the verifier, computed from the input + (combined) statement by a function `f`. This is the "prover-computed message" building block, e.g. + the Hachi/Greyhound first message `v := D ŵ` or the Sumcheck round polynomial `q`. + + - There is no witness (`Witness = Unit`). + - The prover sends a message of type `Message` (with an `OracleInterface`), namely `f stmt oStmt`. + - The **verifier is a pure pass-through** (`verify := fun stmt _ => pure stmt`): per §1.2 of the + Hachi CWSS plan, the claim is *not* checked by a runtime `guard`; the check lives in the output + relation `toORelOut` (the predicate `P` over statement / oracle statements / message). + - The output oracle statements are the input oracle statements together with the sent message, + `OStatement ⊕ᵥ (fun _ : Fin 1 => Message)`. + + ## Security + + The verifier is pure and has no challenge rounds, hence **coordinate-wise special sound** + (`oracleVerifier_coordinateWiseSpecialSound`) for any `CWSSStructure`, via the no-challenge bridge + `OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx`. The extractor is trivial + (`e := fun _ _ => ()`, there is no witness) and the output relation `toORelOut relIn P` refines + the input relation by the claim predicate `P`, so accepting into its language forces the input + into `relIn`. These results are `sorryAx`-free. This mirrors `SendSingleWitness` (the special + case `Message := Witness`) on the verifier side. + + Perfect completeness — that an honest prover's claim `f stmt oStmt` lands in `toORelOut` whenever + the input is in `relIn` and `f` respects `P` — is deferred: it needs the same all-pure + oracle-reduction completeness reasoning as `SendSingleWitness.oracleReduction_completeness`, and + is orthogonal to the CWSS target here. This design supersedes the previous effectful-verifier one + (whose completeness proof no longer applies). -/ -open OracleSpec OracleComp OracleQuery OracleInterface ProtocolSpec +open OracleSpec OracleComp OracleQuery OracleInterface ProtocolSpec Function Equiv namespace SendClaim variable {ι : Type} (oSpec : OracleSpec ι) (Statement : Type) - {ιₛᵢ : Type} [Unique ιₛᵢ] (OStatement : ιₛᵢ → Type) [inst : ∀ i, OracleInterface (OStatement i)] - -@[reducible] -def pSpec : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[OStatement default]⟩ + {ιₛᵢ : Type} (OStatement : ιₛᵢ → Type) [∀ i, OracleInterface (OStatement i)] + (Message : Type) [OracleInterface Message] -/-- -The prover takes in the old oracle statement as input, and sends it as the protocol message. --/ -def oracleProver : OracleProver oSpec - Statement OStatement Unit - Unit (OStatement ⊕ᵥ OStatement) Unit - (pSpec OStatement) where - PrvState := fun _ => OStatement default +/-- One prover→verifier message carrying the claim of type `Message`. -/ +@[reducible, simp] +def pSpec : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[Message]⟩ - input := fun ⟨⟨_, oStmt⟩, _⟩ => oStmt default +/-- `SendClaim` is a single `P_to_V` message, so it has no challenge rounds. This makes its +coordinate-wise special soundness reduce to the no-challenge bridge. -/ +instance instIsEmptyChallengeIdx : IsEmpty (pSpec Message).ChallengeIdx := + ⟨fun ⟨0, h⟩ => nomatch h⟩ - sendMessage | ⟨0, _⟩ => fun st => pure (st, st) +variable (f : Statement → (∀ i, OStatement i) → Message) +/-- The oracle prover for `SendClaim`: it computes the claim `f stmt oStmt` and sends it as the only +oracle message, exposing it (together with the input oracle statements) as the output oracles. -/ +@[inline, specialize] +def oracleProver : OracleProver oSpec + Statement OStatement Unit + Statement (OStatement ⊕ᵥ (fun _ : Fin 1 => Message)) Unit + (pSpec Message) where + PrvState := fun _ => Statement × (∀ i, OStatement i) + input := Prod.fst + sendMessage | ⟨0, _⟩ => fun ⟨stmt, oStmt⟩ => pure (f stmt oStmt, ⟨stmt, oStmt⟩) receiveChallenge | ⟨0, h⟩ => nomatch h - - output := fun st => pure - (⟨(), fun x => match x with - | .inl _ => by simpa [Unique.uniq] using st - | .inr default => by simpa [Unique.uniq] using st⟩, - ()) - -variable (relIn : Set ((Statement × (∀ i, OStatement i)) × Unit)) - (relComp : Statement → OracleComp [OStatement]ₒ Unit) - -- (rel_eq : ∀ stmt oStmt, rel stmt oStmt ↔ - -- (OracleInterface.simOracle []ₒ (OracleInterface.oracle oStmt)).run = oStmt) - -/-- -The verifier checks that the relationship `rel oldStmt newStmt` holds. -It has access to the original and new `OStatement` via their oracle indices. --/ -def oracleVerifier : OracleVerifier oSpec Statement OStatement Unit (OStatement ⊕ᵥ OStatement) - (pSpec OStatement) where - - verify := fun stmt _ => relComp stmt - - embed := { - toFun := fun - | .inl i => .inl i - | .inr _ => .inr ⟨0, by simp⟩ - inj' := by - intro a b h - match a, b with - | .inl _, .inl _ => simpa using h - | .inl _, .inr _ => simp at h - | .inr _, .inl _ => simp at h - | .inr _, .inr _ => congr 1; exact Subsingleton.elim _ _ - } - + output := fun ⟨stmt, oStmt⟩ => pure (⟨stmt, Sum.rec oStmt (fun _ => f stmt oStmt)⟩, ()) + +/-- The oracle verifier for `SendClaim` is a **pure pass-through**: it returns the statement and +exposes the input oracle statements together with the prover's message as the output oracles. The +claim predicate is enforced in `toORelOut`, not at runtime, keeping the verifier `IsPure`. -/ +@[inline, specialize] +def oracleVerifier : OracleVerifier oSpec + Statement OStatement Statement (OStatement ⊕ᵥ (fun _ : Fin 1 => Message)) + (pSpec Message) where + verify := fun stmt _ => pure stmt + embed := .sumMap (.refl _) + <| Equiv.toEmbedding + <| .symm (subtypeUnivEquiv (by aesop)) hEq := by - intro i - match i with - | .inl _ => rfl - | .inr j => simp [ProtocolSpec.Message]; exact congrArg OStatement (Unique.uniq _ j) - -/-- -Combine the prover and verifier into an oracle reduction. -The input has no statement or witness, but one `OStatement`. -The output is also no statement or witness, but two `OStatement`s. --/ -def oracleReduction : OracleReduction oSpec - Statement OStatement Unit - Unit (OStatement ⊕ᵥ OStatement) Unit (pSpec OStatement) where - prover := oracleProver oSpec Statement OStatement - verifier := oracleVerifier oSpec Statement OStatement relComp + intro i; rcases i with j | j + · rfl + · fin_cases j; rfl -def relOut : Set ((Unit × (∀ i, (Sum.elim OStatement OStatement) i)) × Unit) := - setOf (fun ⟨⟨(), oracles⟩, _⟩ => oracles (.inl default) = oracles (.inr default)) - -variable {σ : Type} {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} - -/-- -Proof of perfect completeness: if `rel old new` holds in the real setting, -it also holds in the ideal setting, etc. --/ -instance : ProverOnly (pSpec OStatement) where - prover_first' := by simp - -theorem completeness [Nonempty σ] : - (oracleReduction oSpec Statement OStatement relComp).perfectCompleteness - init impl relIn (relOut OStatement) := by - simp only [OracleReduction.perfectCompleteness, oracleReduction, relOut] - simp only [Reduction.perfectCompleteness_eq_prob_one] - -- `relIn` membership is unused: SendClaim is a deterministic forwarding component - -- whose computation succeeds unconditionally, independent of the input relation. - intro ⟨stmt, oStmt⟩ wit _ - -- 1. Unfold (run_of_prover_first absorbs Verifier.run for P_to_V) - simp only [OracleReduction.toReduction, Reduction.run_of_prover_first, - oracleProver, oracleVerifier, OracleVerifier.toVerifier] - -- 2. Bridge OptionT.pure → OracleComp.pure (some x) so simulateQ_pure fires - simp_rw [show (pure : _ → OptionT (OracleComp _) _) = fun x => (pure (some x) : - OracleComp _ _) from rfl] - -- 3. Peel prover binds (all pure — erw matches through definitional eq) - erw [simulateQ_bind]; erw [simulateQ_pure]; simp only [pure_bind] - erw [simulateQ_bind]; erw [simulateQ_pure]; simp only [pure_bind] - -- 4. Peel verifier bind - erw [simulateQ_bind] - -- 5. Probability decomposition via probEvent_eq_one_iff - rw [probEvent_eq_one_iff] - simp only [OptionT.probFailure_eq, OptionT.mem_support_iff, OptionT.run_mk] - simp only [support_bind, Set.mem_iUnion] - exact ⟨by { - simp only [probFailure_eq_zero, zero_add, probOutput_eq_zero_iff] - intro h - rw [mem_support_bind_iff] at h - obtain ⟨s, -, hs⟩ := h - simp only [StateT.run'_eq, support_map, Set.mem_image] at hs - obtain ⟨⟨_, s'⟩, hs, rfl⟩ := hs - simp only [StateT.run_bind] at hs - rw [mem_support_bind_iff] at hs - obtain ⟨⟨x, s''⟩, hx, hs⟩ := hs - -- Unfold SubSpec liftM + OptionT in hx - simp only [MonadLift.monadLift, liftM, monadLift, MonadLiftT.monadLift, - OptionT.run_mk, OptionT.run_bind, OptionT.run_lift] at hx - -- simulateQ_map rewrites simulateQ impl (some <$> _) → some <$> simulateQ impl _ - erw [simulateQ_map] at hx - -- Peel outer simulateQ layer (OptionT.mk is definitionally transparent) - erw [simulateQ_map] at hx - -- Bridge: StateT.run_map converts (some <$> m : StateT).run s to map at ProbComp level - rw [StateT.run_map] at hx - simp only [support_map, Set.mem_image] at hx - obtain ⟨⟨val, s₀⟩, hval, heq⟩ := hx - obtain ⟨rfl, rfl⟩ := Prod.mk.inj heq - -- x = some val, s'' = s₀; hs depends on val : Option (...) - dsimp only [] at hs - rcases val with _ | ⟨a⟩ - · exfalso - simp only [bind_pure_comp] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [Option.elimM_map] at hval - simp only [Option.elim_some] at hval - dsimp only [OptionT.run] at hval - simp only [bind_pure_comp] at hval - rw [simulateQ_map, simulateQ_map] at hval - rw [StateT.run_map] at hval - simp only [support_map, Set.mem_image] at hval - obtain ⟨⟨_, _⟩, _, h⟩ := hval - simp [Prod.mk.injEq] at h - · simp only [Option.getM, pure_bind] at hs - erw [simulateQ_pure] at hs - simp only [StateT.run_pure, support_pure, Set.mem_singleton_iff] at hs - exact absurd (congr_arg Prod.fst hs) (by simp) - }, by { - -- Part 2: ∀ x ∈ support computation, event x - intro x hx - obtain ⟨s, -, hx⟩ := hx - simp only [StateT.run'_eq, support_map, Set.mem_image] at hx - obtain ⟨⟨xval, s'⟩, hx, rfl⟩ := hx - simp only [StateT.run_bind] at hx - rw [mem_support_bind_iff] at hx - obtain ⟨⟨y, s''⟩, hy, hx⟩ := hx - -- Unfold SubSpec liftM + OptionT in hy - simp only [MonadLift.monadLift, liftM, monadLift, MonadLiftT.monadLift, - OptionT.run_mk, OptionT.run_bind, OptionT.run_lift] at hy - -- simulateQ_map: y is always some _ - erw [simulateQ_map] at hy - -- Peel outer simulateQ layer - erw [simulateQ_map] at hy - -- Bridge: StateT.run_map converts to ProbComp level map - rw [StateT.run_map] at hy - simp only [support_map, Set.mem_image] at hy - obtain ⟨⟨val, s₀⟩, hval, heq⟩ := hy - obtain ⟨rfl, rfl⟩ := Prod.mk.inj heq - -- y = some val; hx depends on val : Option (...) - dsimp only [] at hx - rcases val with _ | ⟨a⟩ - · exfalso - simp only [bind_pure_comp] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [Option.elimM_map] at hval - simp only [Option.elim_some] at hval - dsimp only [OptionT.run] at hval - simp only [bind_pure_comp] at hval - rw [simulateQ_map, simulateQ_map] at hval - rw [StateT.run_map] at hval - simp only [support_map, Set.mem_image] at hval - obtain ⟨⟨_, _⟩, _, h⟩ := hval - simp [Prod.mk.injEq] at h - · simp only [Option.getM, pure_bind] at hx - erw [simulateQ_pure] at hx - simp only [StateT.run_pure, support_pure, Set.mem_singleton_iff, Prod.mk.injEq, - Option.some.injEq] at hx - obtain ⟨rfl, -⟩ := hx - simp only [bind_pure_comp] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [simulateQ_map] at hval - erw [Option.elimM_map] at hval - simp only [Option.elim_some] at hval - dsimp only [OptionT.run] at hval - simp only [bind_pure_comp] at hval - rw [simulateQ_map, simulateQ_map] at hval - rw [StateT.run_map] at hval - simp only [support_map, Set.mem_image, Prod.mk.injEq, Option.some.injEq] at hval - obtain ⟨⟨_, _⟩, _, rfl, rfl⟩ := hval - simp only [Set.mem_setOf_eq] - refine ⟨trivial, Prod.ext (Subsingleton.elim _ _) ?_⟩ - funext i - rcases i with j | j <;> { - have hj : j = default := Unique.uniq _ j - subst hj; rfl - } - }⟩ +/-- The oracle reduction for `SendClaim`. -/ +@[inline, specialize] +def oracleReduction : OracleReduction oSpec + Statement OStatement Unit + Statement (OStatement ⊕ᵥ (fun _ : Fin 1 => Message)) Unit + (pSpec Message) where + prover := oracleProver oSpec Statement OStatement Message f + verifier := oracleVerifier oSpec Statement OStatement Message + +variable {Statement} {OStatement} {Message} + +/-- The pure pass-through oracle verifier's underlying non-oracle verifier returns the statement +together with the output oracle statements (input oracles at `inl`, the message at `inr 0`). -/ +theorem oracleVerifier_toVerifier_run {stmt : Statement} {oStmt : ∀ i, OStatement i} + {tr : (pSpec Message).FullTranscript} : + (oracleVerifier oSpec Statement OStatement Message).toVerifier.run ⟨stmt, oStmt⟩ tr = + pure ⟨stmt, Sum.rec oStmt (fun i => match i with | 0 => tr 0)⟩ := by + simp only [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] + rw [show simulateQ (OracleInterface.simOracle2 oSpec oStmt tr.messages) + (pure stmt : OptionT (OracleComp _) Statement) + = (pure stmt : OptionT (OracleComp oSpec) Statement) from rfl, pure_bind] + congr 1 + congr 1 + funext idx + rcases idx with j | j + · rfl + · fin_cases j; rfl + +/-- The `SendClaim` oracle verifier is pure, discharging the deterministic-left hypothesis of the +CWSS binary append. -/ +instance instIsPure : + (oracleVerifier oSpec Statement OStatement Message).toVerifier.IsPure := + ⟨fun p tr => ⟨p.1, Sum.rec p.2 (fun i => match i with | 0 => tr 0)⟩, + fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ + +variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (relIn : Set ((Statement × ∀ i, OStatement i) × Unit)) + (P : Statement → (∀ i, OStatement i) → Message → Prop) + +/-- The output relation of `SendClaim`: the input relation (read off the pass-through oracles at +`inl`) together with the claim predicate `P` applied to the statement, the input oracles, and the +sent message (the oracle at `inr 0`). Because the verifier is a pure pass-through, "acceptance" is +membership in `toORelOut.language`; the `P` check is enforced here rather than at runtime. -/ +@[reducible, simp] +def toORelOut : + Set ((Statement × (∀ i, (Sum.elim OStatement fun _ : Fin 1 => Message) i)) × Unit) := + setOf (fun ⟨⟨stmt, oStmtAndMsg⟩, _⟩ => + (⟨⟨stmt, fun i => oStmtAndMsg (Sum.inl i)⟩, ()⟩ ∈ relIn) ∧ + P stmt (fun i => oStmtAndMsg (Sum.inl i)) (oStmtAndMsg (Sum.inr 0))) + +/-- **Coordinate-wise special soundness of `SendClaim`.** The verifier is a pure pass-through with +no challenge rounds, so CWSS collapses (via the oracle no-challenge bridge) to a transcript-level +obligation. The extractor is trivial (`e := fun _ _ => ()`, there is no witness); since the output +oracle statements at `inl` are the input oracles unchanged and `toORelOut relIn P` refines `relIn`, +accepting into `toORelOut.language` forces the input into `relIn`. Holds for any `D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (pSpec Message)) : + (oracleVerifier oSpec Statement OStatement Message).coordinateWiseSpecialSound init impl D + relIn (toORelOut relIn P) := by + refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec Statement OStatement Message) relIn (toORelOut relIn P) + (fun _ _ => ()) ?_ + rintro ⟨stmt, oStmt⟩ tr hAcc + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement Message).toVerifier ⟨stmt, oStmt⟩ tr + (toORelOut relIn P).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu.1 end SendClaim diff --git a/ArkLib/ProofSystem/Component/SendWitness.lean b/ArkLib/ProofSystem/Component/SendWitness.lean index 1e8ca71313..159e6ab7c3 100644 --- a/ArkLib/ProofSystem/Component/SendWitness.lean +++ b/ArkLib/ProofSystem/Component/SendWitness.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Quang Dao -/ import ArkLib.OracleReduction.Security.RoundByRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose import Mathlib.Data.FinEnum /-! @@ -15,10 +16,22 @@ sends the (entire) witness to the verifier. There are two variants: 1. For oracle reduction: the witness is an indexed family of types, and sent in a single oracle message to the verifier (using the derived indexed product instance for oracle interface). - We also define a simpler variant where one sends a single witness (converted to be indexed by - `Fin 1`). + We also define a simpler variant, `SendSingleWitness`, where one sends a single witness (converted + to be indexed by `Fin 1`). -2. For reduction: the witness is a type, and sent as a statement to the verifier. +2. For reduction (`SendWitness`, no oracle statements): the witness is a type, and sent as a + statement to the verifier. + +## Security + +The verifier of each variant is **pure** (`Verifier.IsPure` / `OracleVerifier.toVerifier.IsPure`) +and has no challenge rounds, so it is **coordinate-wise special sound** for any `CWSSStructure` +(`verifier_coordinateWiseSpecialSound` and, for the oracle variant, +`SendSingleWitness.oracleVerifier_coordinateWiseSpecialSound`), via the no-challenge bridge +`Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx`. The extractor takes the witness to be +the prover's single message (`e := fun _ tr => tr 0`) — the canonical "open in the clear" base case. +These results are `sorryAx`-free. The indexed-family oracle variant (`section OracleReduction`) is +deferred; see the note there. -/ open OracleSpec OracleComp OracleQuery ProtocolSpec Function Equiv @@ -40,6 +53,10 @@ def pSpec : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[Witness]⟩ instance : ∀ i, VCVCompatible ((pSpec Witness).Challenge i) | ⟨0, h⟩ => nomatch h +/-- The `SendWitness` protocol is a single `P_to_V` message, so it has no challenge rounds. This is +what makes its (coordinate-wise) special soundness reduce to the no-challenge bridge. -/ +instance instIsEmptyChallengeIdx : IsEmpty (pSpec Witness).ChallengeIdx := ⟨fun ⟨0, h⟩ => nomatch h⟩ + @[inline, specialize] def prover : Prover oSpec Statement Witness (Statement × Witness) Unit (pSpec Witness) where PrvState @@ -67,6 +84,12 @@ variable {Statement} {Witness} def toRelOut : Set ((Statement × Witness) × Unit) := Prod.fst ⁻¹' relIn +/-- The `SendWitness` verifier is pure: it deterministically returns `⟨stmt, transcript 0⟩`. This +discharges the deterministic-left hypothesis of the CWSS/tree-soundness binary append, so the +component can appear as a left factor in a sequential composition. -/ +instance instIsPure : (verifier oSpec Statement Witness).IsPure := + ⟨fun stmt tr => ⟨stmt, tr 0⟩, fun _ _ => rfl⟩ + open Classical in /-- The `SendWitness` reduction satisfies perfect completeness. -/ @[simp] @@ -76,12 +99,43 @@ theorem reduction_completeness : intro stmtIn witIn hIn sorry -theorem reduction_rbr_knowledge_soundness : True := trivial +/-- **Coordinate-wise special soundness of `SendWitness`.** The verifier has no challenge rounds, so +CWSS collapses (via the no-challenge bridge `coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to +a transcript-level extraction obligation. The extractor is `e := fun _ tr => tr 0`: the witness *is* +the (single) prover message. Since the verifier is pure with output `⟨stmt, tr 0⟩` and +`relOut = Prod.fst ⁻¹' relIn`, acceptance into `relOut.language` forces `⟨stmt, tr 0⟩ ∈ relIn`, +which is exactly the extracted witness. This is the canonical "open in the clear" CWSS base case, +and holds for *any* coordinate-wise structure `D`. -/ +theorem verifier_coordinateWiseSpecialSound (D : CWSSStructure (pSpec Witness)) : + (verifier oSpec Statement Witness).coordinateWiseSpecialSound init impl D relIn + (toRelOut relIn) := by + refine Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (verifier oSpec Statement Witness) relIn (toRelOut relIn) (fun _ tr => tr 0) ?_ + intro stmtIn tr hAcc + have hmem : (⟨stmtIn, tr 0⟩ : Statement × Witness) ∈ (toRelOut relIn).language := + Verifier.mem_of_pure_accepting init impl (verifier oSpec Statement Witness) stmtIn tr + (toRelOut relIn).language ⟨stmtIn, tr 0⟩ rfl hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu end Reduction /-! - Now, the oracle reduction version + Now, the oracle reduction version. + + **Status: deferred.** This indexed-family variant is currently only a prover skeleton (the oracle + verifier and reduction below are left commented out). Finishing it *as sketched* is blocked by the + current `OracleVerifier` interface: the prover sends the whole family as a **single** product + message `∀ i, Witness i` (`oraclePSpec` has one round), yet the intended output oracle statements + `OStatement ⊕ᵥ Witness` and the commented `embed` (via `FinEnum.equiv`) expect **per-index** + oracles. Under `embed`/`hEq` an output oracle can only *select* an existing source oracle, not + decompose a product; this is exactly the `simOStmt` refactor noted in `OracleReduction/Basic`. + Two coherent designs resolve it — (a) keep the single product message and output it as one product + oracle (which is `SendSingleWitness` at `Witness := ∀ i, Witness i`), or (b) rewrite `oraclePSpec` + as a `FinEnum.card ιw`-round protocol so each witness is its own message (per-index oracles then + come from per-message sources). Both are out of scope for the CWSS work; the pure-verifier ⟹ CWSS + pattern is already validated end-to-end by the reduction version above and by `SendSingleWitness` + below (each with `IsPure` + `coordinateWiseSpecialSound`, all `sorryAx`-free). -/ section OracleReduction @@ -188,6 +242,11 @@ variable {ιₛ : Type} (OStatement : ιₛ → Type) [∀ i, OracleInterface (O @[reducible, simp] def oraclePSpec : ProtocolSpec 1 := ⟨!v[.P_to_V], !v[Witness]⟩ +/-- The `SendSingleWitness` protocol is a single `P_to_V` message, so it has no challenge rounds. +This is what makes its coordinate-wise special soundness reduce to the no-challenge bridge. -/ +instance instIsEmptyChallengeIdx : IsEmpty (oraclePSpec Witness).ChallengeIdx := + ⟨fun ⟨0, h⟩ => nomatch h⟩ + /-- The oracle prover for the `SendSingleWitness` oracle reduction. The prover sends the witness `wit` to the verifier as the only oracle message. @@ -244,11 +303,28 @@ theorem oracleVerifier_toVerifier_run {stmt : Statement} {oStmt : ∀ i, OStatem {tr : (oraclePSpec Witness).FullTranscript} : (oracleVerifier oSpec Statement OStatement Witness).toVerifier.run ⟨stmt, oStmt⟩ tr = pure ⟨stmt, Sum.rec oStmt (fun i => match i with | 0 => tr 0)⟩ := by - simp [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] - sorry - -- stop - -- ext i; rcases i <;> simp - -- split; simp + -- The oracle verifier's `verify` is `pure stmt`, so after `simulateQ_pure` reduces the simulated + -- pure and `pure_bind` collapses the bind, `toVerifier.run` is `pure` of the pair + -- `⟨stmt, oStmtOut⟩`, where `oStmtOut` reads the output oracle statements off `embed`. It remains + -- to identify `oStmtOut` with the explicit `Sum.rec` form, which we do coordinate-by-coordinate. + simp only [Verifier.run, OracleVerifier.toVerifier, oracleVerifier] + rw [show simulateQ (OracleInterface.simOracle2 oSpec oStmt tr.messages) + (pure stmt : OptionT (OracleComp _) Statement) + = (pure stmt : OptionT (OracleComp oSpec) Statement) from rfl, pure_bind] + congr 1 + congr 1 + funext idx + rcases idx with j | j + · rfl + · fin_cases j; rfl + +/-- The `SendSingleWitness` oracle verifier is pure: its underlying (non-oracle) verifier +deterministically returns the statement together with the output oracle statements read off the +transcript. This discharges the deterministic-left hypothesis of the CWSS binary append. -/ +instance instIsPure : + (oracleVerifier oSpec Statement OStatement Witness).toVerifier.IsPure := + ⟨fun p tr => ⟨p.1, Sum.rec p.2 (fun i => match i with | 0 => tr 0)⟩, + fun ⟨_, _⟩ _ => oracleVerifier_toVerifier_run (oSpec := oSpec)⟩ variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (oRelIn : Set ((Statement × (∀ i, OStatement i)) × Witness)) @@ -279,6 +355,25 @@ theorem oracleReduction_completeness (h : NeverFail init) : -- and_true, Fin.isValue, and_imp, forall_const, true_and] -- aesop -theorem oracleReduction_rbr_knowledge_soundness : True := trivial +/-- **Coordinate-wise special soundness of `SendSingleWitness`.** The oracle verifier has no +challenge rounds, so CWSS collapses (via the oracle no-challenge bridge +`coordinateWiseSpecialSound_of_isEmpty_challengeIdx`) to a transcript-level extraction obligation on +the combined statement `Statement × (∀ i, OStatement i)`. The extractor is `e := fun _ tr => tr 0`: +the extracted witness *is* the single oracle message. Since the verifier is pure with output +`⟨stmt, oStmtOut⟩` (where `oStmtOut` exposes the old oracle statements together with the message), +acceptance into `(toORelOut oRelIn).language` unfolds to exactly `⟨⟨stmt, oStmt⟩, tr 0⟩ ∈ oRelIn`. +Holds for *any* coordinate-wise structure `D`. -/ +theorem oracleVerifier_coordinateWiseSpecialSound (D : CWSSStructure (oraclePSpec Witness)) : + (oracleVerifier oSpec Statement OStatement Witness).coordinateWiseSpecialSound init impl D + oRelIn (toORelOut oRelIn) := by + refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D + (oracleVerifier oSpec Statement OStatement Witness) oRelIn (toORelOut oRelIn) + (fun _ tr => tr 0) ?_ + rintro ⟨stmt, oStmt⟩ tr hAcc + have hmem := Verifier.mem_of_pure_accepting init impl + (oracleVerifier oSpec Statement OStatement Witness).toVerifier ⟨stmt, oStmt⟩ tr + (toORelOut oRelIn).language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc + obtain ⟨_, hu⟩ := (Set.mem_language_iff _ _).1 hmem + exact hu end SendSingleWitness From 14acadffec1537343231ca27fd9c12cda63908ff Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Mon, 6 Jul 2026 17:55:24 +0200 Subject: [PATCH 02/21] qudaratic eq messy --- ArkLib.lean | 5 + .../Commitments/Functional/Hachi/Basic.lean | 219 +++++ .../Functional/Hachi/GadgetNorms.lean | 142 +++ .../Functional/Hachi/PolynomialEvalSplit.lean | 49 + .../PolyEvalReduction.lean | 203 ++++ .../Hachi/PolynomialQuadraticEq/QuadEval.lean | 865 ++++++++++++++++++ .../QuadEvalGadgets.lean | 271 ++++++ .../CyclotomicRing/NormBounds/Basic.lean | 104 +++ .../NoChallenge.lean | 19 + .../SingleRound.lean | 463 ++++++++++ docs/wiki/repo-map.md | 40 +- 11 files changed, 2379 insertions(+), 1 deletion(-) create mode 100644 ArkLib/Commitments/Functional/Hachi/Basic.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean diff --git a/ArkLib.lean b/ArkLib.lean index 8bfaef1bb6..1b286b27d2 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -1,5 +1,6 @@ import ArkLib.AGM.Basic import ArkLib.Commitments.Functional.Basic +import ArkLib.Commitments.Functional.Hachi.Basic import ArkLib.Commitments.Functional.Hachi.Gadget import ArkLib.Commitments.Functional.Hachi.GadgetNorms import ArkLib.Commitments.Functional.Hachi.InnerOuter @@ -8,6 +9,9 @@ import ArkLib.Commitments.Functional.Hachi.InnerOuter.Correctness import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security import ArkLib.Commitments.Functional.Hachi.PolynomialEvalSplit +import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.PolyEvalReduction +import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEval +import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEvalGadgets import ArkLib.Commitments.Functional.KZG.Algebra import ArkLib.Commitments.Functional.KZG.Basic import ArkLib.Commitments.Functional.KZG.Binding @@ -194,6 +198,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound import ArkLib.OracleReduction.Security.Implications import ArkLib.OracleReduction.Security.Rewinding import ArkLib.OracleReduction.Security.RoundByRound diff --git a/ArkLib/Commitments/Functional/Hachi/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Basic.lean new file mode 100644 index 0000000000..81fb32c6a0 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Basic.lean @@ -0,0 +1,219 @@ +/- +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.PolynomialQuadraticEq.PolyEvalReduction +import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme +import ArkLib.Commitments.Functional.Basic +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge + +/-! +# Hachi as a Functional Commitment — the composition home + +This is the designated home of Hachi [NOZ26] as a **functional commitment** and of the growing +n-ary composition of its subprotocols. Right now the composed verifier chains exactly two factors: + +* the zero-round **bridge** (`PolyEvalReduction.bridgeVerifier`), which reinterprets a + `CMlPolynomial`-level statement as a `QuadEvalStatement` (monomial-basis instantiation of the + Eq. (12) bases), and +* Hachi's two-round **`QuadEval`** reduction (Lemma 8). + +`evalVerifier` is their `Verifier.append`; `hachi_eval_coordinateWiseSpecialSound` is the +composed coordinate-wise special soundness — sorry-free — obtained from +`Verifier.append_coordinateWiseSpecialSound` (the left factor is `IsPure`, so `hV₁` is `rfl`; +statement chaining is definitional). The composed structure is +`CWSSStructure.ofIsEmpty.append foldStructure`; the input relation is the polynomial-level +`relPolyEval` and the output relation is Hachi Eq. (20) (`relOut`). + +## Growing the composition + +Each further §4.3+ subprotocol is appended with its own CWSS proof; any shape mismatch between +`relOut_i` and `relIn_{i+1}` gets its own zero-round `ReduceClaim` adapter (same recipe as the +bridge). Once ≥3 factors exist, upgrade the binary `Verifier.append` to `Verifier.seqCompose` + +`seqCompose_coordinateWiseSpecialSound`: every factor is `IsPure` and `seqCompose_succ_eq_append` +is `rfl`, so no rework of the existing proof is needed. The `𝔽_{q^k}` ring-switch entry point +(§4.1) is a future zero-round head adapter placed in front of `relPolyEval`, built by the same +recipe as the bridge. + +## 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] +-/ + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound + +section Composition + +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 ι} {ω : ℕ} + +/-- The **composed evaluation verifier**: the zero-round polynomial-level bridge +(`bridgeVerifier`) followed by Hachi's two-round `QuadEval` reduction (`verifier`). Stated over the +appended protocol spec `!p[] ++ₚ pSpec …` (its length index `0 + 2` is defeq to `2`, but the +vappend contents are not syntactically the bare two-round spec, so we keep the appended form). -/ +def evalVerifier : + Verifier oSpec + (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + ((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := + (bridgeVerifier (oSpec := oSpec) 𝓜(q, α)).append (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α)) + +/-- **Hachi evaluation reduction — coordinate-wise special soundness of the composed protocol +(Hachi §4.2/Figure 3, `Rq`-level), sorry-free.** The composed `evalVerifier` (bridge ⧺ QuadEval) is +coordinate-wise special sound for the appended structure `ofIsEmpty.append foldStructure`, reducing +the polynomial-level input relation `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or +MSIS(D)) to Hachi Eq. (20) (`relOut`). Pinned to `𝓜(q, α)` with the [LS18] hypotheses exactly as +`quadEval_coordinateWiseSpecialSound`. Assembled by `Verifier.append_coordinateWiseSpecialSound`: +the bridge's CWSS (`bridge_coordinateWiseSpecialSound`, any `D`) composes with QuadEval's Lemma 8 +(`quadEval_coordinateWiseSpecialSound'`); the left factor is pure so `hV₁` is `rfl` and the middle +relation is QuadEval's `relIn`. -/ +theorem hachi_eval_coordinateWiseSpecialSound {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + (evalVerifier (oSpec := oSpec) (ω := ω) (q := q) (α := α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) (innerDigits := innerDigits) + (dRows := dRows) (m := m) (r := r)).coordinateWiseSpecialSound init impl + (CWSSStructure.ofIsEmpty.append + (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r))) + (relPolyEval 𝓜(q, α) ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := by + unfold evalVerifier + exact Verifier.append_coordinateWiseSpecialSound init impl _ _ _ _ + (fun s _ => toQuadEvalStatement 𝓜(q, α) s) (fun _ _ => rfl) + (bridge_coordinateWiseSpecialSound 𝓜(q, α) init impl CWSSStructure.ofIsEmpty ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (quadEval_coordinateWiseSpecialSound' init impl hq5 hκ hτ) + +end Composition + +/-! ## Functional-commitment scaffolding + +Hachi as a `Commitment.Scheme` (`ArkLib.Commitments.Functional.Basic`) over the multilinear data +`CMlPolynomial (Rq 𝓜(q,α)) (r + m)`. The eval-oracle interface and the honest committer operations +(`hachiKeygen` / `hachiCommit`) are real; the opening `Proof` is deferred (see the `TODO` block). -/ + +section FunctionalCommitment + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} +variable {innerRows messageDigits outerRows innerDigits dRows m r : Nat} {ω : ℕ} + +/-- The **evaluation oracle** on a committed multilinear polynomial: a query is a split evaluation +point `(xl, xh)` (the split matches `PolyEvalStatement`; the unsplit view `xl ++ xh` is a later +cosmetic lens), and the answer is `f(xl ++ xh)`. This is the `OracleInterface` that makes +`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` the `Data` of a functional commitment (`Commitment.Scheme`); +`toOC` follows `OracleContext.ofFunction`. -/ +instance evalOracleInterface : + OracleInterface (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) where + Query := Vector (Rq 𝓜(q, α)) r × Vector (Rq 𝓜(q, α)) m + toOC := + { spec := (Vector (Rq 𝓜(q, α)) r × Vector (Rq 𝓜(q, α)) m) →ₒ Rq 𝓜(q, α) + impl := fun p => do return CMlPolynomial.eval (← read) (p.1 ++ p.2) } + +variable + [SampleableType (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * messageDigits))] + [SampleableType (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * innerDigits)))] + [SampleableType (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * messageDigits))] + +/-- Honest **key generation**: sample the inner/outer/short Ajtai matrices `(A, B, D)` uniformly +(matching `InnerOuter.commitmentScheme.setup`, extended with the Hachi short-commitment matrix `D`, +Eq. (16)) and return the resulting `PublicParamsD` as both the committer and the verifier key. -/ +def hachiKeygen : + ProbComp + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows × + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) := do + let A ← $ᵗ (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * messageDigits)) + let B ← $ᵗ (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * innerDigits))) + let D ← $ᵗ (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * messageDigits)) + let pp : + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows := + { innerMatrix := A, outerMatrix := B, dMatrix := D } + pure (pp, pp) + +/-- Honest **commitment**: reshape the polynomial into its `2^r × 2^m` coefficient matrix +(`Hachi.toMatrix`, which is definitionally a `Message 𝓜(q,α) (2^m) (2^r)`), gadget-decompose it into +the per-block messages/inner decompositions (`generateDecomps` with `Decomposition.ofDigits`), and +outer-commit (`commitWithDecomps`). Deterministic; the decommitment is the `Decomp` data. -/ +def hachiCommit [DecidableEq (ZMod q)] {base : ZMod q} + (ddMsg : DigitDecomposition base messageDigits) + (ddInner : DigitDecomposition base innerDigits) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (p : CMlPolynomial (Rq 𝓜(q, α)) (r + m)) : + Commitment 𝓜(q, α) outerRows × + Decomp 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := + let decomps := generateDecomps 𝓜(q, α) (Decomposition.ofDigits 𝓜(q, α) ddMsg ddInner) + pp.toPublicParams (Hachi.toMatrix p) + (commitWithDecomps 𝓜(q, α) pp.toPublicParams decomps, decomps) + +/-- **Hachi as a functional commitment** (`Commitment.Scheme`) over the multilinear data +`CMlPolynomial (Rq 𝓜(q,α)) (r + m)`, with the eval oracle `evalOracleInterface`, honest +`hachiKeygen` / `hachiCommit`, committer and verifier key `PublicParamsD`, and decommitment +`Decomp`. + +The `opening` field — the complete opening `Proof` (a `Reduction … Bool Unit`) — is **provisional** +(`sorry`): its boolean verdict is Hachi Eq. (20) membership (`relOut`), which depends on the never- +sent triple `(ŵ, t̂, ẑ)`; it becomes verifier-computable only after the remaining §4.3+ subprotocols +(and their honest-prover layer, `QuadEval.computeV`/`computeResp`, §9.3) are formalized. Everything +else here is real. The stated `pSpec` is the composed evaluation protocol spec (`!p[] ++ₚ pSpec …`), +i.e. the shape the finished opening will run over — see the `TODO` block. -/ +noncomputable def hachiFC [DecidableEq (ZMod q)] {base : ZMod q} + (ddMsg : DigitDecomposition base messageDigits) + (ddInner : DigitDecomposition base innerDigits) : + _root_.Commitment.Scheme unifSpec + (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) + (Commitment 𝓜(q, α) outerRows) + (Decomp 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + ((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where + keygen := hachiKeygen + commit := fun pp p => pure (hachiCommit ddMsg ddInner pp p) + opening := sorry + +end FunctionalCommitment + +/-! ## TODO — remaining work toward the full Hachi functional commitment + +The composition (`evalVerifier` / `hachi_eval_coordinateWiseSpecialSound`) is the `Rq`-level +CWSS core; the FC scaffolding (`evalOracleInterface`, `hachiKeygen`, `hachiCommit`, `hachiFC`) is +the honest committer side. Still open, in rough dependency order: + +* **Remaining §4.3+ subprotocols.** Each is appended to `evalVerifier` with its own CWSS proof, + bridged by a zero-round `ReduceClaim` adapter when `relOut_i`/`relIn_{i+1}` disagree in shape. + Once ≥3 factors accumulate, migrate the binary `Verifier.append` to `Verifier.seqCompose` + + `seqCompose_coordinateWiseSpecialSound` (every factor is `IsPure`, `seqCompose_succ_eq_append` + is `rfl`). +* **Completeness / honest-prover layer** (§9.3): instantiate `QuadEval.prover`'s `computeV` / + `computeResp` from the `QuadEvalGadgets` carrier/decomposition definitions, discharging + `Commitment.perfectCorrectness` for `hachiFC` — this is what materializes `hachiFC.opening` + (currently `sorry`). +* **CWSS → knowledge-extraction bridge** and the cross-run step + (`outputToModuleSIS_valid_of_verified`): turn the coordinate-wise special soundness into the + `Commitment.extractability` / `functionBinding` statements. +* **`𝔽_{q^k}` ring-switch head** (§4.1): a zero-round head adapter in front of `relPolyEval` + lifting the `Rq`-level statement to the paper's headline multilinear-over-`𝔽_{q^k}` claim. +* **`ShortChallenge` instances.** Stating `Commitment.perfectCorrectness` / `extractability` needs + `[[pSpec.Challenge]ₒ.Fintype]` / `VCVCompatible` / `SampleableType` for `ShortChallenge 𝓜(q,α) ω` + (a subtype of `Rq`); the requisite `Fintype`/`DecidablePred` work does not yet exist. +-/ + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean b/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean index 1fedff18af..484a30096f 100644 --- a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean +++ b/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean @@ -133,4 +133,146 @@ theorem gadgetDecompose_zmod_vecL2NormSq_le {b digits rows : ℕ} (hb : 1 < b) ( end ZModGadgetNorms +/-! # Part II — the recomposition direction `G·ẑ` -/ + +section ZModGadgetRecomposeNorms + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] + +/-- **Core recomposition coefficient bound.** Each centered coefficient of an entry of the +gadget product `G_{b,rows} ·ᵥ v` is at most `(∑_{u constRq_mul_coeff Φ h1 _ _ k + -- the explicit integer representative of that coefficient + have hrep : ((∑ e : Fin digits, + (b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs : ℤ) : ZMod q) + = (gadgetMul Φ ((b : ZMod q)) v i).1.coeff k := by + rw [hcoeff, Int.cast_sum] + refine Finset.sum_congr rfl fun e _ => ?_ + rw [Int.cast_mul, Int.cast_pow, Int.cast_natCast, ZMod.coe_valMinAbs] + -- entrywise range bound from the ℓ∞ hypothesis + have hentry : ∀ e : Fin digits, + ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs.natAbs ≤ γ := fun e => + calc ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs.natAbs + ≤ Rq.lInftyNorm Φ (v (finProdFinEquiv (i, e))) := + Finset.le_sup (f := fun k => ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs.natAbs) + (Finset.mem_range.mpr hk) + _ ≤ vecLInftyNorm Φ v := + Finset.le_sup (f := fun j => Rq.lInftyNorm Φ (v j)) (Finset.mem_univ _) + _ ≤ γ := hv + -- minimality of the centered representative + triangle over the integer representative + calc ((gadgetMul Φ ((b : ZMod q)) v i).1.coeff k).valMinAbs.natAbs + ≤ (∑ e : Fin digits, + (b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs).natAbs := + valMinAbs_natAbs_le _ hrep + _ ≤ ∑ e : Fin digits, + ((b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs).natAbs := + Int.natAbs_sum_le _ _ + _ = ∑ e : Fin digits, + b ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs.natAbs := by + refine Finset.sum_congr rfl fun e _ => ?_ + rw [Int.natAbs_mul, Int.natAbs_pow, Int.natAbs_natCast] + _ ≤ ∑ e : Fin digits, b ^ (e : ℕ) * γ := + Finset.sum_le_sum fun e _ => Nat.mul_le_mul_left _ (hentry e) + _ = (∑ u ∈ Finset.range digits, b ^ u) * γ := by + rw [← Finset.sum_mul, Fin.sum_univ_eq_sum_range (fun u => b ^ u) digits] + +/-- Entrywise `ℓ∞` growth of the gadget recomposition: `‖(G·ᵥv)ᵢ‖∞ ≤ (∑_{u + gadgetMul_zmod_coeff_natAbs_le Φ hd h1 v hv i (Finset.mem_range.mp hkmem) + +/-- **Deliverable 3(a): `ℓ∞` growth of the gadget recomposition.** +`‖G_{b,rows} ·ᵥ v‖∞ ≤ (∑_{u gadgetMul_zmod_lInftyNorm_le Φ hd h1 v hv i + +/-- **Deliverable 3(c): the J-recomposition `ℓ₂²` chain.** From the range check `‖ẑ‖∞ ≤ γ` +(Eq. (20)'s `ẑ ∈ S_b`, symmetric model), the recomposed `z = J·ẑ` satisfies +`‖z‖₂² ≤ zRecomposeL2SqBound γ b τ (deg φ) rows`. This is the derivation that replaces v1's +primitive `‖z‖₂² ≤ B_z` verifier check (see the plan's change ledger, §10). -/ +theorem gadgetMul_zmod_vecL2NormSq_le {b rows digits : ℕ} (hd : 0 < digits) + (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v : PolyVec (Rq Φ) (rows * digits)) + (hv : vecLInftyNorm Φ v ≤ γ) : + ‖gadgetMul Φ ((b : ZMod q)) v‖₂² + ≤ zRecomposeL2SqBound γ b digits Φ.φ.natDegree rows := by + calc vecL2NormSq Φ (gadgetMul Φ ((b : ZMod q)) v) + ≤ rows * (Φ.φ.natDegree * (vecLInftyNorm Φ (gadgetMul Φ ((b : ZMod q)) v)) ^ 2) := + vecL2NormSq_le_card_mul_lInftyNorm_sq Φ _ + _ ≤ rows * (Φ.φ.natDegree * ((∑ u ∈ Finset.range digits, b ^ u) * γ) ^ 2) := + Nat.mul_le_mul_left _ (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left + (gadgetMul_zmod_vecLInftyNorm_le Φ hd h1 v hv) 2)) + _ = zRecomposeL2SqBound γ b digits Φ.φ.natDegree rows := rfl + +/-- End-to-end subtraction chain: two range-checked decompositions recompose to vectors whose +difference is `ℓ₂²`-bounded by `subL2NormSqBound (zRecomposeL2SqBound …) = 4·B_z` — exactly the +`βSq` needed for `VerifiedBlock.scaled_short` (`‖c̄ⱼ •ᵥ sⱼ‖₂² = ‖z_sib − z_cent‖₂² ≤ 4·B_z`). -/ +theorem gadgetMul_zmod_sub_l2NormSq_le {b rows digits : ℕ} (hd : 0 < digits) + (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v w : PolyVec (Rq Φ) (rows * digits)) + (hv : vecLInftyNorm Φ v ≤ γ) (hw : vecLInftyNorm Φ w ≤ γ) : + ‖gadgetMul Φ ((b : ZMod q)) v - gadgetMul Φ ((b : ZMod q)) w‖₂² + ≤ subL2NormSqBound (zRecomposeL2SqBound γ b digits Φ.φ.natDegree rows) := + sub_l2NormSq_le Φ _ _ (gadgetMul_zmod_vecL2NormSq_le Φ hd h1 v hv) + (gadgetMul_zmod_vecL2NormSq_le Φ hd h1 w hw) + +/-! ## Deliverable 4: the constants of Hachi's polynomial-evaluation reduction (`B_z`, `βSq`) -/ + +/-- **The reduction's derived `B_z`** (Hachi Lemma 8) — the `ℓ₂²` bound on `z = J_{2^m}·ẑ` that +follows from +Eq. (20)'s range check on `ẑ` alone (no extra verifier check): `z` has `2^m·δ` entries +(`cols = 2^m·δ`, `d = deg φ`, `τ = ⌈log_b β⌉` digits of the `J` gadget), so +`B_z = 2^m·δ · (d · ((∑_{u<τ} bᵘ)·γ)²)`. + +**Honest values (paper footnote):** `γ` plays the paper's `b` — Eq. (20) checks +`ẑ ∈ S_b` (centered coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`, magnitude `≤ b`), which the +symmetric model relaxes to `‖ẑ‖∞ ≤ γ` with `γ := b`. Then the entrywise `ℓ∞` bound +`(∑_{u<τ} bᵘ)·b = b·(b^τ-1)/(b-1) ≤ 2·b^τ` recovers (up to the constant 2) the paper's +derived `‖z⁽ʲ⁾‖∞ ≤ b^τ` (Lemma 8's `β̄ = 2b^τ` slack), and +`B_z ≈ 2^m·δ·d·b^{2τ}` up to small constants. -/ +def quadEvalZL2SqBound (γ b τ d m δ : ℕ) : ℕ := zRecomposeL2SqBound γ b τ d (2 ^ m * δ) + +/-- **The reduction's `βSq`** (Hachi Lemma 8) := `subL2NormSqBound B_z = 4·B_z` — the `ℓ₂²` bound +on the extracted `c̄ⱼ •ᵥ sⱼ = z_sib − z_central` fed to `VerifiedBlock.scaled_short`. -/ +def quadEvalBetaSq (γ b τ d m δ : ℕ) : ℕ := subL2NormSqBound (quadEvalZL2SqBound γ b τ d m δ) + +/-- `βSq = 4·B_z`, definitionally. -/ +theorem quadEvalBetaSq_eq_four_mul (γ b τ d m δ : ℕ) : + quadEvalBetaSq γ b τ d m δ = 4 * quadEvalZL2SqBound γ b τ d m δ := rfl + +/-- The plan-facing instantiation: a range-checked `ẑ : Rq^{(2^m·δ)·τ}` recomposes to +`z = J·ẑ` with `‖z‖₂² ≤ B_z = quadEvalZL2SqBound γ b τ (deg φ) m δ`. -/ +theorem quadEvalZ_l2NormSq_le {b m δ τ : ℕ} (hτ : 0 < τ) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} + (zhat : PolyVec (Rq Φ) ((2 ^ m * δ) * τ)) (hγ : vecLInftyNorm Φ zhat ≤ γ) : + ‖gadgetMul Φ ((b : ZMod q)) zhat‖₂² ≤ quadEvalZL2SqBound γ b τ Φ.φ.natDegree m δ := + gadgetMul_zmod_vecL2NormSq_le Φ hτ h1 zhat hγ + +end ZModGadgetRecomposeNorms + end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean index df4a9e347a..5b3a48aa3a 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean @@ -175,6 +175,55 @@ theorem evalSplit_eq_eval (p : CMlPolynomial R (nl + nh)) (xl : Vector R nl) (xh = toMatrix p y x * ((monomialBasis xl).get y * (monomialBasis xh).get x) ring +/-! ## The inverse reshape `toPolynomial` + +The reshape `toMatrix` has a definitional inverse `toPolynomial`: reading the matrix back into the +coefficient vector along `splitEquiv`. Being a bijection, it lets the Hachi bridge phrase "the +derived-message matrix evaluates to `y`" (`QuadEval.evalConsistency`) as the polynomial-level +statement "`eval (toPolynomial M) x = y`" — computable and extraction-friendly, yet interchangeable +with the matrix reading via the round-trip lemmas below. -/ + +/-- Inverse reshape of `toMatrix`: read the `2 ^ nl × 2 ^ nh` matrix back into the +`2 ^ (nl + nh)` coefficient vector along `splitEquiv` (entry `k` is `M row col` for +`(col, row) = splitEquiv.symm k`, i.e. row = low/first variables, column = high/last variables). -/ +def toPolynomial (M : PolyMatrix R (2 ^ nl) (2 ^ nh)) : CMlPolynomial R (nl + nh) := + Vector.ofFn fun k => M ((splitEquiv nl nh).symm k).2 ((splitEquiv nl nh).symm k).1 + +omit [CommSemiring R] in +/-- Coefficient `k` of `toPolynomial M` is the matrix entry at `splitEquiv.symm k`. -/ +@[simp] theorem toPolynomial_get (M : PolyMatrix R (2 ^ nl) (2 ^ nh)) + (k : Fin (2 ^ (nl + nh))) : + (toPolynomial M).get k = M ((splitEquiv nl nh).symm k).2 ((splitEquiv nl nh).symm k).1 := by + simp only [toPolynomial, Vector.get_ofFn] + +omit [CommSemiring R] in +/-- Round-trip: reshaping `toPolynomial M` back to a matrix recovers `M`. -/ +@[simp] theorem toMatrix_toPolynomial (M : PolyMatrix R (2 ^ nl) (2 ^ nh)) : + toMatrix (toPolynomial M) = M := by + funext i j + simp only [toMatrix, toPolynomial_get, Equiv.symm_apply_apply] + +omit [CommSemiring R] in +/-- Round-trip: reshaping `toMatrix p` back to a polynomial recovers `p`. -/ +@[simp] theorem toPolynomial_toMatrix (p : CMlPolynomial R (nl + nh)) : + toPolynomial (toMatrix p) = p := by + apply Vector.ext + intro k hk + simp only [toPolynomial, Vector.getElem_ofFn, toMatrix, Vector.get_eq_getElem, + Prod.mk.eta, Equiv.apply_symm_apply] + +/-- **The bridge lemma.** The split bilinear form of the reshaped matrix against the monomial bases +equals the multilinear evaluation of `toPolynomial M` at `xl ++ xh`. Phrased purely in this file's +vocabulary; the Hachi evaluation reduction consumes it to connect `QuadEval.evalConsistency` +(`splitForm (derivedMsgMatrix …) (mb xl) (mb xh) = y`) to the `CMlPolynomial`-level evaluation +claim. -/ +theorem splitForm_monomialBasis_eq_eval (M : PolyMatrix R (2 ^ nl) (2 ^ nh)) + (xl : Vector R nl) (xh : Vector R nh) : + splitForm M (monomialBasis xl).get (monomialBasis xh).get + = CMlPolynomial.eval (toPolynomial M) (xl ++ xh) := by + rw [← evalSplit_eq_eval (toPolynomial M) xl xh] + simp only [evalSplit, toMatrix_toPolynomial] + /-- Coefficientwise additivity of `CMlPolynomial` (the `+` is `Vector.zipWith (·+·)`). -/ private theorem coeff_add (p q : CMlPolynomial R (nl + nh)) (k : Fin (2 ^ (nl + nh))) : (p + q).get k = p.get k + q.get k := by diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean new file mode 100644 index 0000000000..b36cb325e9 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean @@ -0,0 +1,203 @@ +/- +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.PolynomialQuadraticEq.QuadEval +import ArkLib.Commitments.Functional.Hachi.PolynomialEvalSplit +import ArkLib.ProofSystem.Component.ReduceClaim + +/-! + # Polynomial-level bridge into Hachi's `QuadEval` reduction + + `PolynomialQuadraticEq/QuadEval.lean` (Hachi Lemma 8) states its evaluation claim over *opaque* + Eq. (12) basis vectors `avec`/`bvec` — it never mentions `CMlPolynomial`. + `PolynomialEvalSplit.lean` proves that a multilinear evaluation factors as the split bilinear form + of a reshaped coefficient matrix against the monomial tensor bases (`evalSplit_eq_eval`, + `splitForm_monomialBasis_eq_eval`), but + nothing connects the two. + + This module is the connective tissue: a zero-round **bridge** that reinterprets a + `CMlPolynomial`-level statement (`PolyEvalStatement`) as a `QuadEvalStatement` by taking the + Eq. (12) bases to be the monomial tensor bases `mb(xl)` / `mb(xh)` of the low/high halves of the + evaluation point (`toQuadEvalStatement`), realized as the `ReduceClaim` reduction. Because + `ReduceClaim`'s verifier is pure with no challenge rounds, its coordinate-wise special soundness + (`ReduceClaim.verifier_coordinateWiseSpecialSound`) collapses to the transcript-level pull-back + `mem_relPolyEval_of_relIn`, so the bridge is CWSS for **any** `D` + (`bridge_coordinateWiseSpecialSound`). + + The result is a polynomial-level input relation `relPolyEval` (a weak `VerifiedOpening` whose + *extracted polynomial* evaluates to `y` at `xl ++ xh`, or a Module-SIS solution for `B`/`D`) that + `QuadEval`'s two-round reduction refines to Hachi Eq. (20). `Basic.lean` composes the two + (`bridge.append QuadEval.verifier`) into the sorry-free + `hachi_eval_coordinateWiseSpecialSound`. + + ## Faithfulness note (Eq. (12) convention) + + The paper's `bᵀ = (x₁^{i₁}⋯x_r^{i_r})ᵢ` ranges over the **first** `r` variables and indexes the + matrix **rows**; `aᵀ` over the **last** `m` variables indexes the columns. `PolynomialEvalSplit` + fixes exactly this split (low/first = rows = `b`), and `QuadEval`'s `derivedMsgMatrix` has + rows = outer/`b` blocks. Hence `bvec := mb(xl)` (over `xl`, the first `r` variables) and + `avec := mb(xh)` (over `xh`, the last `m` variables) is the faithful instantiation, and + `evalConsistency` (`splitForm M b a`, argument order load-bearing) matches + `splitForm_monomialBasis_eq_eval` on the nose. + + This is the `Rq`-level protocol of Hachi §4.2/Figure 3 (`Data = CMlPolynomial (Rq Φ) (r + m)`); + the paper's headline multilinear-over-`𝔽_{q^k}` protocol (§4.1 ring switch/packing) is a later + zero-round head adapter in front of `relPolyEval`, built by the same recipe. + + Sits inside `namespace ArkLib.Lattices.Ajtai.InnerOuter` (activates the scoped + `PolyVec`/`*ᵥ`/`dot`/`splitForm`) with `open WeakBinding`; the split layer is reached as + `Hachi.toPolynomial` etc. Never `open ArkLib.Lattices` here (the `⬝ᵥ` token is ambiguous). + + ## 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] +-/ + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise.SingleRound + +/-! ## The polynomial-level statement and the bridge map (any coefficient field `R`) -/ + +section Defs + +variable {R : Type} [Field R] [BEq R] [LawfulBEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] +variable {innerRows messageDigits outerRows innerDigits dRows m r : Nat} +variable {ι : Type} {oSpec : OracleSpec ι} + +/-- Input statement of the composed Hachi evaluation protocol at the polynomial level (Hachi +§4.2/Figure 3, `Rq`-level): the public parameters `(A, B, D)`, the outer commitment `u`, the +evaluation point *split as a pair* `(xl, xh)` (low/first `r` variables and high/last `m` variables — +storing the split avoids `take`/`drop` casts; `xl ++ xh` recovers the paper's point), and the +claimed evaluation `y = f(xl ++ xh)`. -/ +structure PolyEvalStatement (Φ : CyclotomicModulus R) + (innerRows messageDigits outerRows innerDigits dRows m r : Nat) where + /-- Public matrices `(A, B, D)`. -/ + pp : Hachi.PublicParamsD Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + /-- The outer commitment `u`. -/ + u : Commitment Φ outerRows + /-- The outer/low point half `x₁ … x_r` (the first `r` variables; the matrix-row / `b` split). -/ + xl : Vector (Rq Φ) r + /-- The inner/high point half `x_{r+1} … x_l` (the last `m` variables; the column / `a` split). -/ + xh : Vector (Rq Φ) m + /-- The claimed evaluation `y = f(xl ++ xh)`. -/ + y : Rq Φ + +/-- Reinterpret the polynomial-level statement as a `QuadEvalStatement` by taking the two Hachi +Eq. (12) evaluation bases to be the monomial tensor bases of the point halves: +`bᵀ := mb(xl)` (outer, over the first `r` variables, indexing rows) and `aᵀ := mb(xh)` (inner, over +the last `m` variables, indexing columns). `.get : Fin (2^·) → Rq Φ` is definitionally the +`PolyVec` the `QuadEvalStatement` fields expect. -/ +def toQuadEvalStatement + (s : PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows where + pp := s.pp + u := s.u + avec := (CMlPolynomial.monomialBasis s.xh).get + bvec := (CMlPolynomial.monomialBasis s.xl).get + y := s.y + +/-- The zero-round **bridge verifier**: a `ReduceClaim` head that reinterprets the polynomial-level +statement as a `QuadEvalStatement` via `toQuadEvalStatement`. Pure with no challenge rounds, so its +CWSS holds for any `D` (`bridge_coordinateWiseSpecialSound`). -/ +def bridgeVerifier : + Verifier oSpec + (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + !p[] := + ReduceClaim.verifier oSpec (toQuadEvalStatement Φ) + +end Defs + +/-! ## The polynomial-level relation and the pull-back (over `ZMod q`) -/ + +section ZModDefs + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] +variable {innerRows messageDigits outerRows innerDigits dRows m r : Nat} +variable {ι : Type} {oSpec : OracleSpec ι} + +/-- The polynomial extracted from a weak opening: the inverse reshape (`Hachi.toPolynomial`) of the +Eq. (15) derived-message matrix `M`. A bijection, so +`toMatrix (extractedPoly …) = derivedMsgMatrix …` (`toMatrix_extractedPoly`), keeping the +polynomial reading interchangeable with the matrix reading for downstream binding arguments. -/ +def extractedPoly (base : ZMod q) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : + CMlPolynomial (Rq Φ) (r + m) := + Hachi.toPolynomial (derivedMsgMatrix Φ base o) + +omit [NeZero q] in +/-- Round-trip: the reshaped `extractedPoly` recovers the Eq. (15) derived-message matrix. -/ +@[simp] theorem toMatrix_extractedPoly (base : ZMod q) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : + Hachi.toMatrix (extractedPoly Φ base o) = derivedMsgMatrix Φ base o := by + simp only [extractedPoly, Hachi.toMatrix_toPolynomial] + +/-- **`relPolyEval` — the polynomial-level input relation** of the composed Hachi evaluation +protocol: either a weak `VerifiedOpening` for `u` whose *extracted polynomial* evaluates to `y` at +`xl ++ xh`, or a Module-SIS solution for the outer matrix `B`, or one for the short-commitment +matrix `D`. It pulls back `QuadEval`'s `relIn` (whose opening disjunct is the matrix-level +`evalConsistency`) through `toQuadEvalStatement`; the opening disjunct is the interface into a +`CMlPolynomial`-level functional commitment. -/ +def relPolyEval (base : ZMod q) (βSq γ κ : ℕ) : + Set (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r × + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := + { p | match p with + | (s, .opening o) => + VerifiedOpening Φ base βSq γ κ s.pp.toPublicParams s.u o ∧ + CMlPolynomial.eval (extractedPoly Φ base o) (s.xl ++ s.xh) = s.y + | (s, .msisB z) => ModuleSIS.relation Φ (outerShort Φ γ) s.pp.outerMatrix z = true + | (s, .msisD z) => ModuleSIS.relation Φ (dShort Φ γ) s.pp.dMatrix z = true } + +omit [NeZero q] in +/-- **Pull-back lemma** (the `hRel` for the bridge's CWSS): a `QuadEvalWitness` accepted by +`QuadEval`'s `relIn` at the reinterpreted statement `toQuadEvalStatement Φ s` is accepted by +`relPolyEval` at the polynomial-level statement `s`. The MSIS disjuncts are preserved verbatim +(`toQuadEvalStatement` keeps `pp`); the opening disjunct converts the matrix-level `evalConsistency` +(`splitForm (derivedMsgMatrix …) (mb xl) (mb xh) = y`) to the `CMlPolynomial.eval` claim via +`Hachi.splitForm_monomialBasis_eq_eval`. -/ +theorem mem_relPolyEval_of_relIn (base : ZMod q) (βSq γ κ : ℕ) + (s : PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) + (w : QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (h : (toQuadEvalStatement Φ s, w) ∈ relIn Φ base βSq γ κ) : + (s, w) ∈ relPolyEval Φ base βSq γ κ := by + cases w with + | opening o => + obtain ⟨hvo, hec⟩ := h + refine ⟨hvo, ?_⟩ + change CMlPolynomial.eval (Hachi.toPolynomial (derivedMsgMatrix Φ base o)) (s.xl ++ s.xh) + = s.y + rw [← Hachi.splitForm_monomialBasis_eq_eval (derivedMsgMatrix Φ base o) s.xl s.xh] + exact hec + | msisB z => exact h + | msisD z => exact h + +omit [NeZero q] in +/-- **CWSS of the bridge.** The zero-round `ReduceClaim` head is coordinate-wise special sound for +any `D`: with no challenge rounds, CWSS collapses (via the no-challenge bridge) to the +transcript-level pull-back `mem_relPolyEval_of_relIn`, reducing `QuadEval`'s `relIn` to the +polynomial-level `relPolyEval`. The witness type is unchanged (`QuadEvalWitness`), so the witness +pull-back is the identity. -/ +theorem bridge_coordinateWiseSpecialSound {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (D : CWSSStructure (!p[] : ProtocolSpec 0)) (base : ZMod q) (βSq γ κ : ℕ) : + (bridgeVerifier (oSpec := oSpec) Φ (innerRows := innerRows) (messageDigits := messageDigits) + (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) + (r := r)).coordinateWiseSpecialSound init impl D + (relPolyEval Φ base βSq γ κ) (relIn Φ base βSq γ κ) := by + refine ReduceClaim.verifier_coordinateWiseSpecialSound + (relIn := relPolyEval Φ base βSq γ κ) (relOut := relIn Φ base βSq γ κ) + (mapWitInv := fun _ w => w) (D := D) ?_ + intro s w h + exact mem_relPolyEval_of_relIn Φ base βSq γ κ s w h + +end ZModDefs + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean new file mode 100644 index 0000000000..4e8765d59d --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean @@ -0,0 +1,865 @@ +/- +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.PolynomialQuadraticEq.QuadEvalGadgets +import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound + +/-! + # Hachi polynomial-evaluation reduction (QuadEval) — coordinate-wise special soundness + (Hachi Lemma 8) + + Hachi's polynomial-evaluation reduction proves `f(x) = y` by rewriting the evaluation as the + quadratic form `bᵀ M a` and folding the `2ʳ` carrier blocks under the verifier's challenge + vector — hence the name `QuadEval`. It is Hachi's multilinear / inner-outer lift of Greyhound's + [NS24, §3.1] polynomial-evaluation protocol. + + **Lemma 8** (Hachi [NOZ26] §4.2, Figure 3, p. 17–18): this reduction is coordinate-wise special + sound. From `2ʳ+1` accepting transcripts with challenge vectors in `SS(C, 2ʳ, 2)`, the tree + extractor either reconstructs a valid weak `InnerOuter.Opening` by subtract-and-divide, or + outputs a Module-SIS solution for `B` or `D`. + + The reduction is modeled as the two-round + `pSpec ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin 2ʳ → C]⟩`: round 0 (P→V) sends the short + commitment `v = D ŵ`; round 1 (V→P) is the challenge vector. The triple `(ŵ, t̂, ẑ)` is the + **output witness** (`QuadEvalResponse`, never sent — §4.3 proves knowledge of it instead), so the + verifier is a pure pass-through (`IsPure` by `rfl`) and the extractor sources per-branch triples + from `relOut.language`. + + This module holds: + * the reduction's definitions and relations (`CarrierCom`, `QuadEvalStatement`, + `QuadEvalResponse`, `QuadEvalWitness`, `ShortChallenge`, `derivedMsgMatrix`/`evalConsistency`, + `dShort`, `relOut`, `relIn`); + * the two-transcript MSIS lemmas (`msisB_of_two_valid`, `msisD_of_two_valid`) and the + subtract-and-divide core (`inner_eq_of_chain`); + * the pure `verifier` + `instIsPure` and the `prover` skeleton; + * the extractor data (`extractedOpening`, `buildWitness`) and the extraction lemmas + (`verifiedOpening_of_star` = Sublemma 1, `evalConsistency_of_relOut_star` = Sublemma 2 — both + internal steps of Hachi Lemma 8's case (C), not paper lemmas — and `buildWitness_mem_relIn`), + and the top-level theorem `quadEval_coordinateWiseSpecialSound(')`. + + This module implements milestones **M3–M7** of + `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` (§9.5). It sits inside + `namespace ArkLib.Lattices.Ajtai.InnerOuter` (required: that namespace activates the scoped + `PolyVec`/`*ᵥ`/`•ᵥ`/`dot`/`splitForm`), with `open WeakBinding` (so `VerifiedOpening`/`outerShort` + resolve). Never `open ArkLib.Lattices` here (the `⬝ᵥ` token is ambiguous between + `Matrix.dotProduct` and `ArkLib.Lattices.dot`); spell `dot _ _`. + + ## 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] +-/ + +-- ================= PolynomialQuadraticEq/QuadEval.lean — definitions layer ================= +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise.SingleRound + +/-! ## Generic definitions (any coefficient field `R`, mimicking `Scheme.lean`'s `Defs`) -/ + +section Defs + +variable {R : Type} [Field R] [BEq R] [LawfulBEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] +variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits : Nat} + +/-- The carrier commitment space: `v = D ŵ` lives in the `D`-row space. -/ +abbrev CarrierCom (Φ : CyclotomicModulus R) (dRows : Nat) := Simple.Commitment Φ dRows + +/-- Input statement of Hachi's polynomial-evaluation reduction (Hachi §4.2, Figure 3): the public +parameters `(A, B, D)`, the outer commitment `u`, the two evaluation basis vectors +`a ∈ Rq^{2^m}` (`avec`) and `b ∈ Rq^{2^r}` (`bvec`) of Eq. (12), and the claimed evaluation +`y = u_eval`. -/ +structure QuadEvalStatement (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits outerRows blocks innerDigits dRows : Nat) where + /-- Public matrices `(A, B, D)`. -/ + pp : Hachi.PublicParamsD Φ innerRows messageRows messageDigits outerRows blocks innerDigits dRows + /-- The outer commitment `u`. -/ + u : Commitment Φ outerRows + /-- The inner evaluation basis `aᵀ = (x_{r+1}^{j₁} ⋯ x_l^{j_m})_j ∈ Rq^{2^m}` (Eq. 12). -/ + avec : PolyVec (Rq Φ) messageRows + /-- The outer evaluation basis `bᵀ = (x_1^{i₁} ⋯ x_r^{i_r})_i ∈ Rq^{2^r}` (Eq. 12). -/ + bvec : PolyVec (Rq Φ) blocks + /-- The claimed evaluation `y = u_eval = f(x)`. -/ + y : Rq Φ + +/-- The reduction's output witness `(ŵ, t̂, ẑ)` of Hachi Eq. (20) — Figure 3's final "message", +never sent in the composed protocol (§4.3 proves knowledge of it instead). Block-major layouts +(`finProdFinEquiv`, block = outer index). -/ +structure QuadEvalResponse (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits blocks innerDigits zDigits : Nat) where + /-- `ŵ := G⁻¹_{2^r}(w)`, the decomposed carrier (block-major, `blocks · messageDigits`). -/ + carrierDec : PolyVec (Rq Φ) (blocks * messageDigits) + /-- `t̂ = (t̂ᵢ)ᵢ`, the per-block inner decompositions. -/ + innerDec : PolyVec (PolyVec (Rq Φ) (innerRows * innerDigits)) blocks + /-- `ẑ := J⁻¹(z)`, the decomposed masked opening (`τ = zDigits` digits). -/ + zDec : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits) + +instance : Nonempty + (QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits) := + ⟨⟨fun _ => 0, fun _ _ => 0, fun _ => 0⟩⟩ + +/-- The extracted (input-side) witness of Hachi Lemma 8: either a weak `Opening` for `u`, or a +Module-SIS solution for the outer matrix `B`, or one for the short-commitment matrix `D`. -/ +inductive QuadEvalWitness (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits blocks innerDigits : Nat) where + /-- A weak opening `(sᵢ, t̂ᵢ, c̄ᵢ)ᵢ` for the outer commitment `u`. -/ + | opening (o : Opening Φ innerRows messageRows messageDigits blocks innerDigits) + /-- A Module-SIS solution for the outer matrix `B`. -/ + | msisB (z : ModuleSIS.Solution Φ (blocks * (innerRows * innerDigits))) + /-- A Module-SIS solution for the short-commitment matrix `D`. -/ + | msisD (z : ModuleSIS.Solution Φ (blocks * messageDigits)) + +instance : Nonempty (QuadEvalWitness Φ innerRows messageRows messageDigits blocks innerDigits) := + ⟨.msisB (fun _ => 0)⟩ + +end Defs + +/-! ## The challenge space (over `ZMod q`, where the norms live) -/ + +section ShortChallenge + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + +/-- **The reduction's challenge space, carried by the type** (Hachi §4.2): the paper's +`C ⊆ {c ∈ Rq : ‖c‖₁ ≤ ω}` is rendered as the subtype of `ℓ₁`-short ring elements. The +verifier's relation `relOut` therefore needs NO challenge-norm checks (faithful to Eq. (20), +which never checks the challenge); extraction recovers `‖cᵢ‖₁ ≤ ω` from the subtype property. -/ +def ShortChallenge (Φ : CyclotomicModulus (ZMod q)) (ω : ℕ) : Type := + {c : Rq Φ // Rq.l1Norm Φ c ≤ ω} + +variable {Φ : CyclotomicModulus (ZMod q)} [IsCyclotomic Φ] {ω : ℕ} + +namespace ShortChallenge + +/-- The underlying ring element `c ∈ Rq` of a short challenge (Hachi §4.2's challenge `cᵢ`). -/ +def val (c : ShortChallenge Φ ω) : Rq Φ := Subtype.val c + +omit [NeZero q] [IsCyclotomic Φ] in +/-- The subtype bound: every challenge is `ℓ₁`-short. -/ +theorem l1Norm_le (c : ShortChallenge Φ ω) : ‖c.val‖₁ ≤ ω := Subtype.prop c + +/-- Coordinate difference of two short challenges is `ℓ₁`-bounded by `2ω` — the extractor's +`hshort` for the slack `c̄ⱼ = c_{j,j} - c_{0,j}` (Hachi Lemma 8), for free from the subtype. -/ +theorem l1Norm_val_sub_le (c c' : ShortChallenge Φ ω) : ‖c.val - c'.val‖₁ ≤ 2 * ω := + calc ‖c.val - c'.val‖₁ ≤ ‖c.val‖₁ + ‖c'.val‖₁ := Rq.l1Norm_sub_le Φ c.val c'.val + _ ≤ ω + ω := Nat.add_le_add c.l1Norm_le c'.l1Norm_le + _ = 2 * ω := (Nat.two_mul ω).symm + +omit [NeZero q] [IsCyclotomic Φ] in +/-- `≠` on the subtype transfers to `≠` on the underlying ring elements — the extractor's +nonzero-slack input (`CoordEq` gives subtype-`≠` at the differing coordinate). -/ +theorem val_ne_of_ne {c c' : ShortChallenge Φ ω} (h : c ≠ c') : c.val ≠ c'.val := + fun hval => h (Subtype.ext hval) + +omit [NeZero q] [IsCyclotomic Φ] in +/-- `val` is injective (distinct Hachi §4.2 challenges have distinct underlying ring elements). -/ +theorem val_injective : Function.Injective (val : ShortChallenge Φ ω → Rq Φ) := + fun _ _ h => Subtype.ext h + +end ShortChallenge + +end ShortChallenge + +/-! ## Eval consistency (Eq. 15) — probe-compiled §9.3 block, kept verbatim -/ + +section ZModDefs + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] +variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits + m r : Nat} + +/-- The matrix `M` of Hachi Eq. (15): row `i` = derived message block `G_{2^m} · sᵢ`; rows are +indexed by the outer basis `b`, columns by the inner basis `a`. -/ +def derivedMsgMatrix (base : ZMod q) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : + PolyMatrix (Rq Φ) (2 ^ r) (2 ^ m) := fun i k => derivedMessage Φ base o.toDecomp i k + +omit [NeZero q] in +/-- Row `i` of the Hachi Eq. (15) matrix `M` is the derived message block (definitional). -/ +@[simp] theorem derivedMsgMatrix_apply {base : ZMod q} + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) (i : Fin (2 ^ r)) : + derivedMsgMatrix Φ base o i = derivedMessage Φ base o.toDecomp i := rfl + +/-- Eq. (15): the derived messages of the weak opening evaluate to `y` under the split +bilinear form (`splitForm`, argument order `b a` load-bearing). -/ +def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec (Rq Φ) (2 ^ r)) + (y : Rq Φ) (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : Prop := + splitForm (derivedMsgMatrix Φ base o) b a = y + +omit [NeZero q] in +/-- The Hachi Eq. (15) quadratic form `bᵀ M a` expands to the block sum +`∑ᵢ bᵢ · ⟨a, G_{2^m} sᵢ⟩` (the per-block reading used by `evalConsistency_of_star`). -/ +theorem splitForm_derivedMsgMatrix_eq_sum (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) + (b : PolyVec (Rq Φ) (2 ^ r)) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : + splitForm (derivedMsgMatrix Φ base o) b a = + ∑ i, b i * dot a (derivedMessage Φ base o.toDecomp i) := by + rw [splitForm, dot_eq_sum]; refine Finset.sum_congr rfl (fun i _ => ?_) + rw [matVecMul_apply, derivedMsgMatrix_apply, dot_comm] + +omit [NeZero q] in +/-- **Sublemma 2 of the extraction** (an internal step of Hachi Lemma 8, case (C), establishing +Eq. (15)): from c3 (`dot b w = y`) and the c4-subtractions +(`wⱼ = dot a (derivedMessage o j)`), the extracted opening is eval-consistent. -/ +theorem evalConsistency_of_star (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) + (b : PolyVec (Rq Φ) (2 ^ r)) (y : Rq Φ) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (w : PolyVec (Rq Φ) (2 ^ r)) (c3 : dot b w = y) + (c4 : ∀ j, w j = dot a (derivedMessage Φ base o.toDecomp j)) : + evalConsistency Φ base a b y o := by + unfold evalConsistency; rw [splitForm_derivedMsgMatrix_eq_sum, ← c3, dot_eq_sum] + refine Finset.sum_congr rfl (fun j _ => ?_); rw [c4 j] + +/-! ## `dShort` and the output/input relations -/ + +/-- Shortness predicate for the extracted `D`-kernel witness (Hachi Lemma 8, case (B)): the +`D`-matrix analogue of `outerShort`, at `subLInftyNormBound γ = 2·γ`. -/ +def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool := + fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ) + +/-- **`relOut` — exactly Hachi Eq. (20) plus the `S_b` range checks** on +`((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: + +* c1: `D ŵ = v` +* c2: `B (flatten t̂) = u` +* c3: `bᵀ (G_{2^r} ŵ) = y` (row 3 of Eq. (20), `u_eval`) +* c4: `(cᵀ ⊗ G₁) ŵ = aᵀ G_{2^m} J ẑ` (row 4; challenges coerced from the subtype) +* c5: `(cᵀ ⊗ G_{n_A}) t̂ = A J ẑ` (row 5) +* c6: the `S_b` range checks, as symmetric `ℓ∞` balls `≤ γ`. + +**`S_b` modeling**: Eq. (20) checks `(ŵ, t̂, ẑ) ∈ S_b^…`, whose elements have centered +coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 uses the symmetric ball `‖·‖∞ ≤ γ`, which with `γ ≥ +⌈b/2⌉` **contains** the paper's box — so every Eq.-(20)-valid transcript is `relOut`-valid and +the CWSS theorem covers the paper's verifier. No challenge-norm checks appear (the challenge +TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from +c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `GadgetNorms.lean`) — both exactly as in the +paper. -/ +def relOut (base : ZMod q) (ω γ : ℕ) : + Set ((QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × + CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) × + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) := + { p | match p with + | ((stmt, v, chals), resp) => + let c : PolyVec (Rq Φ) (2 ^ r) := fun i => (chals i).val + let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := + Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec + -- c1: `D ŵ = v` + Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ + -- c2: `B (flatten t̂) = u` + Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ + -- c3: `bᵀ (G_{2^r} ŵ) = y` + dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ + -- c4: `(cᵀ ⊗ G₁) ŵ = aᵀ (G_{2^m} z)` + Hachi.tensorG1 Φ base messageDigits c resp.carrierDec = + dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ + -- c5: `(cᵀ ⊗ G_{n_A}) t̂ = A z` + Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = + stmt.pp.innerMatrix *ᵥ z ∧ + -- c6: the `S_b` range checks (as `ℓ∞` balls) + vecLInftyNorm Φ resp.carrierDec ≤ γ ∧ + vecLInftyNorm Φ (PolyVec.flattenBlocks resp.innerDec) ≤ γ ∧ + vecLInftyNorm Φ resp.zDec ≤ γ } + +/-- **`relIn` — Hachi Lemma 8's extraction disjunction**: a weak `VerifiedOpening` for `u` that is +also eval-consistent (Eq. 15), or a Module-SIS solution for `B`, or one for `D`. The `.opening` +disjunct is the interface into `outputToModuleSIS_valid_of_verified` for the downstream +cross-run knowledge-soundness step. -/ +def relIn (base : ZMod q) (βSq γ κ : ℕ) : + Set (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := + { p | match p with + | (stmt, .opening o) => + VerifiedOpening Φ base βSq γ κ stmt.pp.toPublicParams stmt.u o ∧ + evalConsistency Φ base stmt.avec stmt.bvec stmt.y o + | (stmt, .msisB z) => + ModuleSIS.relation Φ (outerShort Φ γ) stmt.pp.outerMatrix z = true + | (stmt, .msisD z) => + ModuleSIS.relation Φ (dShort Φ γ) stmt.pp.dMatrix z = true } + +/-! ## The two-transcript MSIS extraction lemmas (Hachi Lemma 8, cases (A)/(B)) -/ + +/-- **Hachi Lemma 8, case (A)**: two Eq.-(20)-valid responses (c2 + c6 facts) with different inner +decompositions yield a `B`-kernel Module-SIS solution. Clone of +`outer_relation_of_verified`'s tail. -/ +theorem msisB_of_two_valid {γ : ℕ} + {B : Simple.PublicParams Φ outerRows (blocks * (innerRows * innerDigits))} + {u : Commitment Φ outerRows} + {resp₁ resp₂ : + QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits} + (hu₁ : Simple.commit Φ B (PolyVec.flattenBlocks resp₁.innerDec) = u) + (hu₂ : Simple.commit Φ B (PolyVec.flattenBlocks resp₂.innerDec) = u) + (hγ₁ : vecLInftyNorm Φ (PolyVec.flattenBlocks resp₁.innerDec) ≤ γ) + (hγ₂ : vecLInftyNorm Φ (PolyVec.flattenBlocks resp₂.innerDec) ≤ γ) + (hne : PolyVec.flattenBlocks resp₁.innerDec ≠ PolyVec.flattenBlocks resp₂.innerDec) : + ModuleSIS.relation Φ (outerShort Φ γ) B + (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) = true := by + have hne0 : PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec ≠ 0 := + sub_ne_zero.mpr hne + have hshort : vecLInftyNorm Φ + (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) ≤ + subLInftyNormBound γ := + sub_lInftyNorm_le Φ _ _ hγ₁ hγ₂ + have heq : B *ᵥ PolyVec.flattenBlocks resp₁.innerDec = + B *ᵥ PolyVec.flattenBlocks resp₂.innerDec := by + simpa [Simple.commit] using hu₁.trans hu₂.symm + have hker : B *ᵥ + (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) = 0 := by + rw [matVecMul_sub]; exact sub_eq_zero.mpr heq + simp [ModuleSIS.relation, outerShort, hne0, hshort, hker] + +/-- **Hachi Lemma 8, case (B)**: two Eq.-(20)-valid responses (c1 + c6 facts, shared `v`) with +different carrier decompositions yield a `D`-kernel Module-SIS solution. -/ +theorem msisD_of_two_valid {γ : ℕ} + {D : Simple.PublicParams Φ dRows (blocks * messageDigits)} + {v : CarrierCom Φ dRows} + {resp₁ resp₂ : + QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits} + (hv₁ : Simple.commit Φ D resp₁.carrierDec = v) + (hv₂ : Simple.commit Φ D resp₂.carrierDec = v) + (hγ₁ : vecLInftyNorm Φ resp₁.carrierDec ≤ γ) + (hγ₂ : vecLInftyNorm Φ resp₂.carrierDec ≤ γ) + (hne : resp₁.carrierDec ≠ resp₂.carrierDec) : + ModuleSIS.relation Φ (dShort Φ γ) D (resp₁.carrierDec - resp₂.carrierDec) = true := by + have hne0 : resp₁.carrierDec - resp₂.carrierDec ≠ 0 := sub_ne_zero.mpr hne + have hshort : vecLInftyNorm Φ (resp₁.carrierDec - resp₂.carrierDec) ≤ subLInftyNormBound γ := + sub_lInftyNorm_le Φ _ _ hγ₁ hγ₂ + have heq : D *ᵥ resp₁.carrierDec = D *ᵥ resp₂.carrierDec := by + simpa [Simple.commit] using hv₁.trans hv₂.symm + have hker : D *ᵥ (resp₁.carrierDec - resp₂.carrierDec) = 0 := by + rw [matVecMul_sub]; exact sub_eq_zero.mpr heq + simp [ModuleSIS.relation, dShort, hne0, hshort, hker] + +/-! ## The subtract-and-divide core step (`inner_eq` via `Ring.inverse`) -/ + +omit [NeZero q] in +/-- The c5-side unit-cancellation of the subtract-and-divide extraction (Hachi Lemma 8, case (C)): +from the c5-subtract chain `c̄ᵢ •ᵥ (G_{n_A} t̂ᵢ) = A *ᵥ Δz` and `IsUnit c̄ᵢ`, the extracted +message block +`sᵢ := Ring.inverse c̄ᵢ •ᵥ Δz` satisfies the weak-opening inner gadget relation +(`VerifiedBlock.inner_eq`). Total at the definition site — no `IsUnit` needed to *define* `sᵢ`. -/ +theorem inner_eq_of_chain {base : ZMod q} {cols : Nat} + (A : Simple.PublicParams Φ innerRows cols) + (that : Simple.Message Φ (innerRows * innerDigits)) (zdiff : PolyVec (Rq Φ) cols) + (c : Rq Φ) (hc : IsUnit c) + (hchain : c •ᵥ Simple.commit Φ (gadgetMatrix Φ base innerRows innerDigits) that = + A *ᵥ zdiff) : + Simple.commit Φ (gadgetMatrix Φ base innerRows innerDigits) that + = Simple.commit Φ A (Ring.inverse c •ᵥ zdiff) := by + have hAs : Simple.commit Φ A (Ring.inverse c •ᵥ zdiff) = Ring.inverse c •ᵥ (A *ᵥ zdiff) := by + simp only [Simple.commit]; rw [matVecMul_scalarVecMul] + rw [hAs, ← hchain]; funext i + simp only [scalarVecMul_apply, ← mul_assoc, Ring.inverse_mul_cancel c hc, one_mul] + +/-! ## The protocol: pure verifier, `IsPure`, prover skeleton -/ + +section Protocol + +variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} + +/-- The reduction's verifier (Hachi §4.2, Figure 3) is a **pure pass-through**: it re-emits the +statement, the round-0 carrier commitment `v`, and the round-1 challenge vector. The Eq.-(20) +checks live in `relOut` (the `(ŵ, t̂, ẑ)` triple is never sent — §4.3 proves knowledge of it), so +there is no runtime `guard` and the verifier is `IsPure`. -/ +def verifier : + Verifier oSpec + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × + CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) + (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where + verify := fun stmt tr => pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) + +omit [NeZero q] [IsCyclotomic Φ] in +/-- The `hpure` shape consumed by `branch_relOut_language` (`SingleRound.lean`). -/ +theorem verifier_verify_pure + (stmt : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + (tr : (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r).FullTranscript) : + (verifier (oSpec := oSpec) (ω := ω) Φ).verify stmt tr = + pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) := rfl + +/-- The Hachi Figure 3 verifier is pure: its output is a total function of `(stmt, tr)`. -/ +instance instIsPure : (verifier (oSpec := oSpec) (ω := ω) Φ + (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)).IsPure := + ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ + +/-- Prover skeleton (Hachi §4.2, Figure 3; completeness is out of scope for Lemma 8): round 0 sends +the carrier commitment `v` (cf. `SendClaim.oracleProver`), round 1 receives the challenge vector +(cf. `SendChallenge.oracleProver`), and the output witness is the `QuadEvalResponse` `(ŵ, t̂, ẑ)` +of Eq. (20). The honest computations (`v = D ŵ` with `ŵ = G⁻¹(w)`, `ẑ = J⁻¹(Σᵢ cᵢ sᵢ)`, …) are the +parameters `computeV` / `computeResp`, to be instantiated by the completeness layer from the +`QuadEvalGadgets` carrier/decomposition definitions (§9.3). -/ +def prover (WitIn : Type) + (computeV : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → + WitIn → CarrierCom Φ dRows) + (computeResp : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → + WitIn → (Fin (2 ^ r) → ShortChallenge Φ ω) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : + Prover oSpec + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + WitIn + (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) + (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where + PrvState + | 0 => + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn + | 1 => + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn + | 2 => + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn) × (Fin (2 ^ r) → ShortChallenge Φ ω) + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeV 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, computeV stmt wit, c), computeResp stmt wit c) + +end Protocol + +end ZModDefs + +end ArkLib.Lattices.Ajtai.InnerOuter + +-- ================= PolynomialQuadraticEq/QuadEval.lean — extraction assembly ================= + +section QuadEvalExtraction + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound + +/-! ## The extracted opening and the witness assembler (generic `Φ`) -/ + +section BuildWitness + +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} + +/-- The subtract-and-divide weak opening extracted from a star of accepting branches +(Hachi Lemma 8, case (C)): per coordinate `j`, the message is +`sⱼ := c̄ⱼ⁻¹ •ᵥ (z^{(sib j)} − z^{(central)})` with `z^{(i)} := J ẑ^{(i)}`, the inner +decomposition is the shared central `t̂`, and the challenge is the slack +`c̄ⱼ := c_{sib j, j} − c_{central, j}`. Total — no `IsUnit`/star hypotheses at the definition +(`Ring.inverse` is total); correctness lives in `verifiedOpening_of_star`. -/ +noncomputable def extractedOpening (base : ZMod q) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : + Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := + let z : Fin (2 ^ r + 1) → PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := fun j => + Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ (resp j).zDec + { message := fun j => + Ring.inverse ((fam (sib fam j) j).val - (fam (central fam) j).val) •ᵥ + (z (sib fam j) - z (central fam)) + innerDecomp := (resp (central fam)).innerDec + challenge := fun j => (fam (sib fam j) j).val - (fam (central fam) j).val } + +open Classical in +/-- The reduction's witness assembler (Hachi Lemma 8's three-case extraction) — the `mkWitness` +argument of the generic extractor `E`: + +* some branch's (flattened) inner decomposition `t̂` differs from the central one → a + `B`-kernel Module-SIS solution (the `msisB_of_two_valid` data); +* else some branch's carrier decomposition `ŵ` differs from the central one → a `D`-kernel + Module-SIS solution (the `msisD_of_two_valid` data); +* else all branches share `t̂` and `ŵ`, and the star's subtract-and-divide yields the weak + opening `extractedOpening`. + +("Two branches differ" is equivalent to "some branch differs from the central one": if two +branches disagree, at least one of them disagrees with the central branch.) Fully defined — +the classical `dite`s always produce a term; that each case lands in `relIn` is +`buildWitness_mem_relIn`. -/ +noncomputable def buildWitness (base : ZMod q) + (_stmt : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + (_v : CarrierCom Φ dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge Φ ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := + if hB : ∃ j, PolyVec.flattenBlocks (resp j).innerDec + ≠ PolyVec.flattenBlocks (resp (central fam)).innerDec then + .msisB (PolyVec.flattenBlocks (resp hB.choose).innerDec - + PolyVec.flattenBlocks (resp (central fam)).innerDec) + else if hD : ∃ j, (resp j).carrierDec ≠ (resp (central fam)).carrierDec then + .msisD ((resp hD.choose).carrierDec - (resp (central fam)).carrierDec) + else + .opening (extractedOpening Φ base fam resp) + +omit [NeZero q] [IsCyclotomic Φ] in +/-- Coordinate difference transfers from the `ShortChallenge` subtype to the underlying ring +vectors — the bridge from `sib_coordEq` (subtype-level `CoordEq`) to the ring-level +coordinate-isolation lemmas `tensorG_coordDiff`/`tensorG1_coordDiff` (`QuadEvalGadgets.lean`). -/ +theorem ShortChallenge.coordEq_val {ℓ : ℕ} {i : Fin ℓ} {x y : Fin ℓ → ShortChallenge Φ ω} + (h : CoordEq i x y) : + CoordEq i (fun j => (x j).val) (fun j => (y j).val) := + ⟨ShortChallenge.val_ne_of_ne h.1, fun j hj => congrArg ShortChallenge.val (h.2 j hj)⟩ + +end BuildWitness + +/-! ## The extraction lemmas and the top-level theorem (pinned to `𝓜(q, α)`) + +Only here does the Lyubashevsky–Seiler invertibility enter (`isUnit_of_l1Norm_le` is pinned +to the power-of-two modulus), so — mirroring `InnerOuter/Security.lean` — the statements are +pinned to `𝓜(q, α)` and carry the [LS18] hypotheses `q ≡ 5 (mod 8)`, `(2ω)² < q`. -/ + +section Pinned + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} +variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} + +/-- **Sublemma 1 (an internal step of Hachi Lemma 8, case (C), weak-opening part).** At a +star-shaped family of `2^r + 1` `relOut`-accepting branches sharing the carrier commitment `v` +and (cases (A)/(B) excluded) sharing `t̂` and `ŵ`, the subtract-and-divide `extractedOpening` is a +`VerifiedOpening` at + +* `βSq := quadEvalBetaSq γ b zDigits (deg φ) m messageDigits = 4·B_z`, the `GadgetNorms`-derived + `J`-recomposition bound (`deg φ = 2^α`; no primitive `‖z‖₂²` verifier check anywhere); +* `γ' := γ` — **not** `2γ`: `outer_short` constrains the extracted opening's `innerDecomp`, + which is the CENTRAL branch's `t̂` verbatim, and relOut c6 bounds it by `γ` directly (the + `2γ` slack of `subLInftyNormBound` is only for the *difference* witnesses of cases (A)/(B)); +* `κ := 2ω`, the slack bound for `c̄ⱼ = c_{sib j, j} − c_{central, j}` from two `‖·‖₁ ≤ ω` + subtype challenges (`ShortChallenge.l1Norm_val_sub_le`). -/ +theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) + (hτ : 0 < zDigits) + (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (v : CarrierCom 𝓜(q, α) dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hstar : ∃ e, StarAt fam e) + (ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec) + (_hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : + VerifiedOpening 𝓜(q, α) ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) + stmt.pp.toPublicParams stmt.u + (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) := by + -- PROOF PLAN (rank 7). Notation: e := central fam, sibᵢ := sib fam i, + -- c̄ᵢ := (fam sibᵢ i).val − (fam e i).val, zⱼ := jMatrix 𝓜 b _ zDigits *ᵥ (resp j).zDec, + -- Δzᵢ := z sibᵢ − z e. Destructure each hrel j by plain + -- `obtain ⟨c1, c2, c3, c4, c5, c6w, c6t, c6z⟩ := hrel j` (smoke test above: the match + -- iota-reduces on the literal tuple). + -- * outer_eq := c2 of the central branch (relOut c2 is verbatim + -- `Simple.commit 𝓜 B (flattenBlocks t̂) = stmt.u`). + -- * outer_short := c6t of the central branch — the γ (NOT 2γ) bound; the extracted + -- `innerDecomp` IS `(resp e).innerDec`, syntactically. + -- * block i, fieldwise: + -- - hne : c̄ᵢ ≠ 0 — from sib_coordEq fam hstar i : CoordEq i (fam e) (fam sibᵢ) (SingleRound), + -- sib_coordEq_ne + ShortChallenge.val_ne_of_ne + sub_ne_zero_of_ne. + -- - hpos := Rq.l1Norm_pos_of_ne_zero 𝓜(q,α) hne (NormBounds/Basic.lean). + -- - challenge_short := ShortChallenge.l1Norm_val_sub_le _ _ : ‖c̄ᵢ‖₁ ≤ 2ω (via + -- Rq.l1Norm_sub_le). + -- - unit := isUnit_of_l1Norm_le α hq5 hpos challenge_short hκ (LS18, 𝓜-pinned; κ = 2ω). + -- - scaled_short : challenge i •ᵥ message i = c̄ᵢ •ᵥ (Ring.inverse c̄ᵢ •ᵥ Δzᵢ) = Δzᵢ + -- (scalarVecMul associativity + Ring.mul_inverse_cancel with unit); then + -- ‖Δzᵢ‖₂² ≤ subL2NormSqBound (quadEvalZL2SqBound γ b zDigits (deg φ) m messageDigits) + -- = quadEvalBetaSq … (rfl) + -- by gadgetMul_zmod_sub_l2NormSq_le 𝓜 hτ h1 (resp sibᵢ).zDec (resp e).zDec c6z c6z + -- (GadgetNorms.lean; `jMatrix … *ᵥ ·` is `gadgetMul` by rfl), with + -- h1 : 1 ≤ (𝓜(q,α)).φ.natDegree from hachiModulus_natDegree ▸ Nat.one_le_two_pow. + -- - inner_eq : the c5-subtract chain. Rewrite c5 of branch sibᵢ along ht sibᵢ to the + -- shared t̂ := (resp e).innerDec; subtract c5 of the central branch: + -- tensorG_sub_challenge + matVecMul_sub give + -- tensorG 𝓜 b innerRows innerDigits (c_sib − c_e) t̂ = A *ᵥ Δzᵢ; + -- coordinate-isolate with tensorG_coordDiff at + -- ShortChallenge.coordEq_val (coordEq_symm (sib_coordEq fam hstar i)) + -- to get c̄ᵢ •ᵥ (gadgetMatrix 𝓜 b innerRows innerDigits *ᵥ t̂ i) = A *ᵥ Δzᵢ; close by + -- inner_eq_of_chain 𝓜 stmt.pp.innerMatrix (t̂ i) Δzᵢ c̄ᵢ unit (above) + -- (`Simple.commit 𝓜 (gadgetMatrix …)` is `gadgetMatrix … *ᵥ ·` by rfl). + -- outer_eq / outer_short := c2 / c6t of the central branch. + obtain ⟨-, hc2e, -, -, -, -, hc6te, -⟩ := hrel (central fam) + refine ⟨hc2e, hc6te, fun i => ?_⟩ + -- Block `i`: the extracted challenge `c̄ᵢ := c_{sib i, i} − c_{e, i}` is nonzero, `2ω`-short, + -- hence a unit (Lyubashevsky–Seiler). + have hne : (fam (sib fam i) i).val - (fam (central fam) i).val ≠ 0 := + sub_ne_zero_of_ne (ShortChallenge.val_ne_of_ne (sib_coordEq_ne fam hstar i)) + have hshort : ‖(fam (sib fam i) i).val - (fam (central fam) i).val‖₁ ≤ 2 * ω := + ShortChallenge.l1Norm_val_sub_le _ _ + have hunit : IsUnit ((fam (sib fam i) i).val - (fam (central fam) i).val) := + isUnit_of_l1Norm_le α hq5 (Rq.l1Norm_pos_of_ne_zero 𝓜(q, α) hne) hshort hκ + refine ⟨hunit, hshort, ?_, ?_⟩ + · -- scaled_short : `c̄ᵢ •ᵥ (c̄ᵢ⁻¹ •ᵥ Δzᵢ) = Δzᵢ = J ẑ^{(sib i)} − J ẑ^{(e)}`, then the + -- `J`-recomposition ℓ₂² bound from the two branches' c6z (`GadgetNorms.lean`). + have h1 : 1 ≤ (𝓜(q, α)).φ.natDegree := by + rw [hachiModulus_natDegree]; exact Nat.one_le_two_pow + obtain ⟨-, -, -, -, -, -, -, hc6zs⟩ := hrel (sib fam i) + obtain ⟨-, -, -, -, -, -, -, hc6ze⟩ := hrel (central fam) + have hcancel : (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp).challenge i •ᵥ + (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp).message i + = Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (sib fam i)).zDec + - Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (central fam)).zDec := by + simp only [extractedOpening] + funext k + simp only [scalarVecMul_apply, ← mul_assoc, Ring.mul_inverse_cancel _ hunit, one_mul] + rw [hcancel] + exact gadgetMul_zmod_sub_l2NormSq_le 𝓜(q, α) hτ h1 + (resp (sib fam i)).zDec (resp (central fam)).zDec hc6zs hc6ze + · -- inner_eq : the c5-subtract chain, shared `t̂ := (resp e).innerDec`, coordinate-isolated + -- and unit-divided. + have hcoord : CoordEq i (fun k => (fam (sib fam i) k).val) + (fun k => (fam (central fam) k).val) := + ShortChallenge.coordEq_val 𝓜(q, α) (coordEq_symm (sib_coordEq fam hstar i)) + obtain ⟨-, -, -, -, hc5s, -, -, -⟩ := hrel (sib fam i) + obtain ⟨-, -, -, -, hc5e, -, -, -⟩ := hrel (central fam) + rw [ht (sib fam i)] at hc5s + have hchain : ((fam (sib fam i) i).val - (fam (central fam) i).val) •ᵥ + (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) innerRows innerDigits *ᵥ + (resp (central fam)).innerDec i) + = stmt.pp.innerMatrix *ᵥ + (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (sib fam i)).zDec + - Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (central fam)).zDec) := by + rw [matVecMul_sub, ← hc5s, ← hc5e, ← Hachi.tensorG_sub_challenge, + Hachi.tensorG_coordDiff 𝓜(q, α) ((b : ZMod q)) innerRows innerDigits hcoord] + simp only [extractedOpening] + exact inner_eq_of_chain 𝓜(q, α) stmt.pp.innerMatrix + ((resp (central fam)).innerDec i) _ + ((fam (sib fam i) i).val - (fam (central fam) i).val) hunit hchain + +/-- **Sublemma 2 wiring (Eq. (15) for the extracted opening).** The shared-`ŵ` c3 row plus the +coordinate-isolated, unit-divided c4 rows give eval-consistency of `extractedOpening`. The +core computation `evalConsistency_of_star` is PROVED above (Part 4 / A4); this statement +discharges its `w`/`c3`/`c4` hypotheses from the star. -/ +theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) + (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (v : CarrierCom 𝓜(q, α) dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hstar : ∃ e, StarAt fam e) + (hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : + evalConsistency 𝓜(q, α) ((b : ZMod q)) stmt.avec stmt.bvec stmt.y + (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) := by + -- Apply `evalConsistency_of_star` at the shared recomposed carrier + -- `w := G_{2^r} *ᵥ (resp (central fam)).carrierDec`. + refine evalConsistency_of_star 𝓜(q, α) ((b : ZMod q)) stmt.avec stmt.bvec stmt.y + (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) + (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ (resp (central fam)).carrierDec) + ?_ ?_ + · -- c3: verbatim c3 row of the central branch. + obtain ⟨-, -, hc3, -, -, -, -, -⟩ := hrel (central fam) + exact hc3 + · -- c4 j: the coordinate-isolated, unit-divided c4-subtract chain. + intro j + obtain ⟨-, -, -, hc4s, -, -, -, -⟩ := hrel (sib fam j) + obtain ⟨-, -, -, hc4e, -, -, -, -⟩ := hrel (central fam) + -- Move the sibling's c4 onto the shared carrier `ŵ := (resp (central fam)).carrierDec`. + rw [hw (sib fam j)] at hc4s + -- The difference challenge `c̄ⱼ = c_{sib j, j} − c_{central, j}` differs from `0`, hence is a + -- unit (Lyubashevsky–Seiler, `‖c̄ⱼ‖₁ ≤ 2ω < √q`). + have hne : (fam (sib fam j) j).val - (fam (central fam) j).val ≠ 0 := + sub_ne_zero_of_ne (ShortChallenge.val_ne_of_ne (sib_coordEq_ne fam hstar j)) + have hunit : IsUnit ((fam (sib fam j) j).val - (fam (central fam) j).val) := + isUnit_of_l1Norm_le α hq5 (Rq.l1Norm_pos_of_ne_zero 𝓜(q, α) hne) + (ShortChallenge.l1Norm_val_sub_le _ _) hκ + -- Subtract-and-isolate: the two branches' c4 rows, sharing `ŵ`, give `c̄ⱼ · wⱼ = aᵀ G Δzⱼ`. + have hcoord : CoordEq j (fun i => (fam (sib fam j) i).val) + (fun i => (fam (central fam) i).val) := + ShortChallenge.coordEq_val 𝓜(q, α) (coordEq_symm (sib_coordEq fam hstar j)) + have hchain : dot stmt.avec (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ m) messageDigits *ᵥ + ((Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (sib fam j)).zDec) + - (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (central fam)).zDec))) + = ((fam (sib fam j) j).val - (fam (central fam) j).val) * + (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ + (resp (central fam)).carrierDec) j := by + rw [matVecMul_sub, dot_sub, ← hc4s, ← hc4e, ← Hachi.tensorG1_sub_challenge, + Hachi.tensorG1_coordDiff 𝓜(q, α) ((b : ZMod q)) messageDigits hcoord] + -- Divide by `c̄ⱼ`: the extracted message `sⱼ := c̄ⱼ⁻¹ •ᵥ Δzⱼ` recovers `wⱼ` under `aᵀ G`. + change (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ + (resp (central fam)).carrierDec) j + = dot stmt.avec (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ m) messageDigits *ᵥ + (Ring.inverse ((fam (sib fam j) j).val - (fam (central fam) j).val) •ᵥ + ((Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (sib fam j)).zDec) + - (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (resp (central fam)).zDec)))) + rw [matVecMul_scalarVecMul, dot_scalarVecMul, hchain, ← mul_assoc, + Ring.inverse_mul_cancel _ hunit, one_mul] + +/-- **The witness assembler is correct** — the `hmk` input to the generic assembly +`coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`): at every star-shaped family of +`relOut`-accepting branches, `buildWitness` lands in `relIn`. This is the ONLY remaining +mathematical obligation of the top-level theorem (see +`quadEval_coordinateWiseSpecialSound'`). -/ +theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) + (hτ : 0 < zDigits) + (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (v : CarrierCom 𝓜(q, α) dRows) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (resp : Fin (2 ^ r + 1) → + QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hstar : ∃ e, StarAt fam e) : + (stmt, buildWitness 𝓜(q, α) ((b : ZMod q)) stmt v fam resp) ∈ + relIn 𝓜(q, α) ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) := by + -- PROOF PLAN (rank 6) — Hachi Lemma 8's three-case split. `unfold buildWitness` and split + -- the two classical `dite`s (`rw [dif_pos hB]` / `rw [dif_neg hB, dif_pos hD]` / both neg). + -- 1. Case hB (∃ j, flattenBlocks (resp j).innerDec ≠ flattenBlocks (resp e).innerDec): + -- relIn membership at the literal `.msisB _` iota-reduces to the bare MSIS disjunct + -- (smoke test above); close with msisB_of_two_valid (PROVED above) at + -- B := stmt.pp.outerMatrix, + -- hu₁/hu₂ := c2 of branches hB.choose / e (both equal stmt.u), hγ₁/hγ₂ := their c6t, + -- hne := hB.choose_spec. + -- 2. Case hD (¬hB, ∃ j, (resp j).carrierDec ≠ (resp e).carrierDec): close with + -- msisD_of_two_valid (PROVED above) at D := stmt.pp.dMatrix, hv₁/hv₂ := c1 of branches + -- hD.choose / e — both commit to the SHARED round-0 message `v` (the tree sharing of `v` + -- across branches is exactly what fires this case), hγ₁/hγ₂ := their c6w, + -- hne := hD.choose_spec. + -- 3. Case ¬hB ∧ ¬hD: push_neg gives ∀-equalities; hw is direct; ht at family level via + -- PolyVec.block_eq_of_flattenBlocks_eq (Vectors.lean:59) + funext from the flattened + -- equality. relIn membership at the literal `.opening _` iota-reduces to + -- `VerifiedOpening … ∧ evalConsistency …` (smoke test above); close with + -- ⟨verifiedOpening_of_star hq5 hκ hτ … hrel hstar ht hw, + -- evalConsistency_of_relOut_star hq5 hκ … hrel hstar hw⟩. + unfold buildWitness + by_cases hB : ∃ j, PolyVec.flattenBlocks (resp j).innerDec + ≠ PolyVec.flattenBlocks (resp (central fam)).innerDec + · -- Case (A): some branch's inner decomposition differs → `B`-kernel MSIS solution. + rw [dif_pos hB] + obtain ⟨-, hu₁, -, -, -, -, hγ₁, -⟩ := hrel hB.choose + obtain ⟨-, hu₂, -, -, -, -, hγ₂, -⟩ := hrel (central fam) + exact msisB_of_two_valid 𝓜(q, α) hu₁ hu₂ hγ₁ hγ₂ hB.choose_spec + · by_cases hD : ∃ j, (resp j).carrierDec ≠ (resp (central fam)).carrierDec + · -- Case (B): shared `t̂` but some carrier decomposition differs → `D`-kernel MSIS solution + -- (the shared round-0 message `v` is what makes both branches commit to the same `v`). + rw [dif_neg hB, dif_pos hD] + obtain ⟨hv₁, -, -, -, -, hγ₁, -, -⟩ := hrel hD.choose + obtain ⟨hv₂, -, -, -, -, hγ₂, -, -⟩ := hrel (central fam) + exact msisD_of_two_valid 𝓜(q, α) hv₁ hv₂ hγ₁ hγ₂ hD.choose_spec + · -- Case (C): shared `t̂` and `ŵ` → the subtract-and-divide weak opening (Sublemmas 1 & 2). + rw [dif_neg hB, dif_neg hD] + push Not at hB hD + have ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec := + fun j => funext fun i => PolyVec.block_eq_of_flattenBlocks_eq (hB j) i + exact ⟨verifiedOpening_of_star hq5 hκ hτ stmt v fam resp hrel hstar ht hD, + evalConsistency_of_relOut_star hq5 hκ stmt v fam resp hrel hstar hD⟩ + +/-- **Hachi Lemma 8 (CWSS of Hachi's polynomial-evaluation reduction, Figure 3; originally +Greyhound's [NS24, §3.1] folding protocol), top-level statement.** +The reduction's verifier is coordinate-wise special sound for the `(ℓ, k) = (2^r, 2)` +structure, with `relOut` = Eq. (20) + the `S_b` range checks and `relIn` = weak opening +(eval-consistent) ∨ MSIS(B) ∨ MSIS(D), at the derived constants +`βSq = quadEvalBetaSq γ b zDigits (deg φ) m messageDigits` and `κ = 2ω`. + +The first proof step below is the load-bearing WitIn/WitOut wiring whose absence +invalidated v1 of this plan: the generic extractor `E` at `WitOut := QuadEvalResponse` +(relOut's witness) and `WitIn := QuadEvalWitness` (relIn's witness), assembled by `buildWitness` — +verified here to +elaborate. -/ +theorem quadEval_coordinateWiseSpecialSound {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) + (r := r)).coordinateWiseSpecialSound init impl + (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r)) + (relIn 𝓜(q, α) ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := by + -- THE WIRING STEP (v1's blocker, now verified to typecheck): + refine ⟨E (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) + (buildWitness 𝓜(q, α) ((b : ZMod q))), ?_⟩ + -- The direct route mirrors the generic assembly `coordinateWiseSpecialSound_of_mkWitness` + -- (`SingleRound.lean`), documenting the tree mechanics inline: shape-recover the tree, fire + -- each branch's `relOut.language` guard, obtain the star center, then close with the sole + -- math lemma `buildWitness_mem_relIn`. + classical + intro stmtIn tree hStruct hAcc + -- 1. shape recovery: every accepting tree of `pSpec` is `tree2 v challenges`. + obtain ⟨v, challenges, rfl⟩ := tree_shape tree + have harity := (foldStructure_arity (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r)).symm + -- 2. per-branch language membership (the extractor's guards fire on accepting trees). + have hmem : ∀ j : Fin (2 ^ r + 1), + ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ + relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ := by + intro j + have h := branch_relOut_language init impl _ (fun _ _ => rfl) + (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) stmtIn v challenges hAcc + (Fin.cast harity j) + exact (Set.mem_language_iff _ _).1 h + -- 3. the sibling family is special sound, hence has a star center. + have hfam := (nodeOk_iff_family challenges).1 hStruct.1 + have hstar : ∃ e, StarAt + (fun j : Fin (2 ^ r + 1) => challenges (Fin.cast harity j)) e := + exists_starAt (le_refl 2) (by omega) _ hfam + -- 4. each chosen response satisfies `relOut` — `E` computes definitionally on `tree2`. + have hbranch : ∀ j : Fin (2 ^ r + 1), + ((stmtIn, v, challenges (Fin.cast harity j)), + if h : ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ + relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ + then h.choose else Classical.ofNonempty) ∈ + relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ := by + intro j + rw [dif_pos (hmem j)] + exact (hmem j).choose_spec + -- 5. close with the sole math lemma. + exact buildWitness_mem_relIn hq5 hκ hτ stmtIn v _ _ hbranch hstar + +/-- The same statement, proved in ONE LINE through A1's generic assembly +`coordinateWiseSpecialSound_of_mkWitness`: every tree/extractor/guard obligation is discharged +generically, and the whole of Hachi Lemma 8 reduces to the single math lemma +`buildWitness_mem_relIn`. Kept alongside `quadEval_coordinateWiseSpecialSound` so BOTH proof +routes' wiring is probe-verified. -/ +theorem quadEval_coordinateWiseSpecialSound' {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) + (r := r)).coordinateWiseSpecialSound init impl + (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r)) + (relIn 𝓜(q, α) ((b : ZMod q)) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := + coordinateWiseSpecialSound_of_mkWitness init impl _ (fun _ _ => rfl) _ _ + (buildWitness 𝓜(q, α) ((b : ZMod q))) + (fun stmtIn v fam resp hbranch hstar => + buildWitness_mem_relIn hq5 hκ hτ stmtIn v fam resp hbranch hstar) + +-- NOTE (oracleVerifier wrapper): deliberately NOT included. An +-- `OracleVerifier.coordinateWiseSpecialSound` wrapper needs (i) an actual `OracleVerifier` +-- for the reduction, whose round-0 message `v : CarrierCom 𝓜(q,α) dRows` requires an +-- `OracleInterface (Simple.Commitment 𝓜(q,α) dRows)` instance that does not exist in the repo +-- (a query-model design decision), and (ii) the repo's oracle-level append theorem is itself +-- still sorried — so the plain-`Verifier` statement above is the +-- right interface for Lemma 8 now; the oracle wrapper is future work at the composition step. + +end Pinned + +end ArkLib.Lattices.Ajtai.InnerOuter + +end QuadEvalExtraction diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean new file mode 100644 index 0000000000..219d2d9b46 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean @@ -0,0 +1,271 @@ +/- +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.InnerOuter.Scheme +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic + +/-! + # Hachi polynomial-evaluation reduction — gadget algebra (Hachi §4.2, Figure 3) + + Gadget algebra supporting **Hachi Lemma 8** (coordinate-wise special soundness of Hachi's + polynomial-evaluation reduction — proving `f(x) = y` via the quadratic form `bᵀ M a`, Hachi + [NOZ26] §4.2, Figure 3): the short-commitment matrix `D` and its commitment `v`, the carrier + `w`/`ŵ`, the `J` gadget and `ẑ`, and the block-weighted `tensorG` / `tensorG1` sums together + with their algebra (`tensorG_sub`, `tensorG_sub_challenge`, `tensorG_coordDiff` — the + coordinate-isolation crux). + + Hachi's reduction (§4.2) is the multilinear / inner-outer lift of Greyhound's [NS24, §3.1] + folding-based polynomial-evaluation protocol; this file is its gadget layer. The naming + `QuadEval` reflects the content — an evaluation claim expressed as a quadratic equation — while + "fold" below refers to Greyhound's mechanism of folding the `2ʳ` carrier blocks under the + verifier's challenge vector. + + Implements §9.3 of `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` (milestone M1). + + ## References + + * [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +open CompPoly ArkLib.Lattices ArkLib.Lattices.CyclotomicModulus ArkLib.Lattices.Ajtai +open scoped BigOperators + +namespace ArkLib.Lattices.Hachi + +variable {R : Type} [Field R] [BEq R] [LawfulBEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] + +/-- Inner-outer params `(A, B)` extended with the Hachi short-commitment matrix `D` +(Hachi [NOZ26] Eq. (16); the analogue of Greyhound's [NS24, §3.1] carrier commitment): `D` +commits to the block-major carrier decomposition `ŵ ∈ Rq^{blocks·messageDigits}`. -/ +structure PublicParamsD (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits outerRows blocks innerDigits dRows : Nat) extends + InnerOuter.PublicParams Φ innerRows messageRows messageDigits outerRows blocks innerDigits where + /-- The Hachi short-commitment matrix `D` (Hachi [NOZ26] Eq. (16)). -/ + dMatrix : Simple.PublicParams Φ dRows (blocks * messageDigits) + +/-! ## The carrier `w`, its decomposition `ŵ`, and the short commitment `v = D ŵ` -/ + +section Carrier +variable {messageRows messageDigits blocks : Nat} (base : R) + +/-- The carrier entry `wᵢ := aᵀ · G_{2^m} · sᵢ` (Hachi Eq. (16)/(17); only `gadgetMatrix`, no +`DecidableEq R`). -/ +def carrierEntry (a : PolyVec (Rq Φ) messageRows) + (s : PolyVec (Rq Φ) (messageRows * messageDigits)) : Rq Φ := + ArkLib.Lattices.splitForm (gadgetMatrix Φ base messageRows messageDigits) a s + +/-- The carrier `w := (w₁, …, w_{2ʳ})`, `wᵢ = aᵀ G_{2^m} sᵢ` (Hachi [NOZ26] Eq. (16)). -/ +def carrier (a : PolyVec (Rq Φ) messageRows) + (s : PolyVec (PolyVec (Rq Φ) (messageRows * messageDigits)) blocks) : PolyVec (Rq Φ) blocks := + fun i => carrierEntry Φ base a (s i) + +variable [DecidableEq R] -- ← introduced AFTER the pure defs (else unusedSectionVars) + +/-- The carrier decomposition `ŵ := G⁻¹_{blocks}(w)` (Hachi [NOZ26] Eq. (16)/(17)), block-major, +length `blocks * messageDigits`. `base` is IMPLICIT (pinned by `ddCarrier`). -/ +def carrierDecomp {base : R} (ddCarrier : DigitDecomposition base messageDigits) + (a : PolyVec (Rq Φ) messageRows) + (s : PolyVec (PolyVec (Rq Φ) (messageRows * messageDigits)) blocks) : + PolyVec (Rq Φ) (blocks * messageDigits) := + gadgetDecompose Φ ddCarrier (carrier Φ base a s) + +/-- Roundtrip `w = G_{blocks} *ᵥ ŵ` (Hachi Eq. (17)). -/ +theorem carrier_eq_gadget {base : R} (hd : 0 < messageDigits) (h1 : 1 ≤ Φ.φ.natDegree) + (ddCarrier : DigitDecomposition base messageDigits) (a : PolyVec (Rq Φ) messageRows) + (s : PolyVec (PolyVec (Rq Φ) (messageRows * messageDigits)) blocks) : + carrier Φ base a s + = gadgetMatrix Φ base blocks messageDigits *ᵥ carrierDecomp Φ ddCarrier a s := by + rw [carrierDecomp]; exact (gadgetDecompose_lawful Φ hd h1 ddCarrier (carrier Φ base a s)).symm + +/-- The honest short carrier commitment `v := D ŵ` (Hachi Eq. (16), Figure 3 round 0). -/ +def carrierCommit {dRows : Nat} (D : Simple.PublicParams Φ dRows (blocks * messageDigits)) + {base : R} (ddCarrier : DigitDecomposition base messageDigits) + (a : PolyVec (Rq Φ) messageRows) + (s : PolyVec (PolyVec (Rq Φ) (messageRows * messageDigits)) blocks) : + Simple.Commitment Φ dRows := + Simple.commit Φ D (carrierDecomp Φ ddCarrier a s) + +end Carrier + +/-! ## The `J` gadget and the response decomposition `ẑ` -/ + +section JGadget + +/-- The `J` gadget `J_n := I_n ⊗ [1, base, …, base^(zDigits-1)]` (Hachi Eq. (18)–(20)); in this +reduction `n = messageRows * messageDigits` and `zDigits = τ`. -/ +def jMatrix (base : R) (n zDigits : Nat) : PolyMatrix (Rq Φ) n (n * zDigits) := + gadgetMatrix Φ base n zDigits + +variable [DecidableEq R] + +/-- The response decomposition `ẑ := J⁻¹(z)` (Hachi Eq. (20), the `ẑ` the prover commits to +implicitly), block-major, length `n * zDigits`. `base` is IMPLICIT (pinned by `ddZ`). -/ +def zDecomp {n zDigits : Nat} {base : R} (ddZ : DigitDecomposition base zDigits) + (z : PolyVec (Rq Φ) n) : PolyVec (Rq Φ) (n * zDigits) := + gadgetDecompose Φ ddZ z + +/-- Roundtrip `z = J *ᵥ ẑ` (the verifier's reconstruction of `z` from `ẑ`, Eq. (20)). -/ +theorem z_eq_jMatrix {n zDigits : Nat} {base : R} (hd : 0 < zDigits) + (h1 : 1 ≤ Φ.φ.natDegree) (ddZ : DigitDecomposition base zDigits) (z : PolyVec (Rq Φ) n) : + z = jMatrix Φ base n zDigits *ᵥ zDecomp Φ ddZ z := by + rw [zDecomp, jMatrix]; exact (gadgetDecompose_lawful Φ hd h1 ddZ z).symm + +end JGadget + +/-! ## The block-weighted gadget sums `tensorG` (c5) and `tensorG1` (c4) -/ + +section TensorG +variable {k digits blocks : Nat} + +/-- `tensorG_k c x := Σᵢ cᵢ •ᵥ (G_k *ᵥ xᵢ) : PolyVec (Rq Φ) k` — the Lean rendering of +`(cᵀ ⊗ G_{k}) x̂` on a block family `x` (Eq. (19)/(20) row 5, with `k = n_A`). -/ +def tensorG (base : R) (k digits : Nat) (c : PolyVec (Rq Φ) blocks) + (x : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : PolyVec (Rq Φ) k := + ∑ i : Fin blocks, (c i) •ᵥ (gadgetMatrix Φ base k digits *ᵥ x i) + +/-- `tensorG` is subtractive in the block family (algebra for Hachi Lemma 8's two-transcript +subtraction of Eq. (20) row 5). -/ +theorem tensorG_sub (base : R) (k digits : Nat) (c : PolyVec (Rq Φ) blocks) + (x y : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : + tensorG Φ base k digits c (x - y) + = tensorG Φ base k digits c x - tensorG Φ base k digits c y := by + simp only [tensorG, ← Finset.sum_sub_distrib] + refine Finset.sum_congr rfl fun i _ => ?_ + have hxy : (x - y) i = x i - y i := rfl + rw [hxy, matVecMul_sub] + funext r + simp only [scalarVecMul_apply, Pi.sub_apply, mul_sub] + +/-- `tensorG` is subtractive in the challenge vector (Hachi Lemma 8's two-transcript subtraction +of Eq. (20) row 5). -/ +theorem tensorG_sub_challenge (base : R) (k digits : Nat) (c c' : PolyVec (Rq Φ) blocks) + (x : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : + tensorG Φ base k digits (c - c') x + = tensorG Φ base k digits c x - tensorG Φ base k digits c' x := by + simp only [tensorG, ← Finset.sum_sub_distrib] + refine Finset.sum_congr rfl fun i _ => ?_ + funext r + simp only [Pi.sub_apply, scalarVecMul_apply, sub_mul] + +/-- Coordinate isolation (Hachi Lemma 8, case (C), the c5 subtract-and-divide crux): if +`c ≡ⱼ c'`, the challenge-difference sum collapses to the `j`-th block, +`tensorG (c − c') x = (cⱼ − c'ⱼ) •ᵥ (G_k *ᵥ xⱼ)`. -/ +theorem tensorG_coordDiff (base : R) (k digits : Nat) + {c c' : PolyVec (Rq Φ) blocks} {j : Fin blocks} (h : CoordinateWise.CoordEq j c c') + (x : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : + tensorG Φ base k digits (c - c') x = (c j - c' j) •ᵥ (gadgetMatrix Φ base k digits *ᵥ x j) := by + simp only [tensorG]; rw [Finset.sum_eq_single j] + · funext r; simp only [scalarVecMul_apply, Pi.sub_apply] + · intro i _ hij + have hzero : (c - c') i = 0 := by simp only [Pi.sub_apply]; rw [h.2 i hij, sub_self] + funext r; simp only [scalarVecMul_apply, hzero, Pi.zero_apply, zero_mul] + · intro hj; exact absurd (Finset.mem_univ j) hj + +end TensorG + +section TensorG1 +variable {blocks : Nat} + +/-- The scalar `(cᵀ ⊗ G₁) x` of Eq. (18)/(20) row 4, on the block-major FLAT carrier +decomposition `x = ŵ ∈ Rq^{blocks·digits}`: since `cᵀ ⊗ G₁ = cᵀ · (I_blocks ⊗ G₁)`, this is +`⟨c, G_blocks *ᵥ x⟩` — the challenge-weighted sum of the recomposed carrier `w = G_blocks ŵ`. -/ +def tensorG1 (base : R) (digits : Nat) (c : PolyVec (Rq Φ) blocks) + (x : PolyVec (Rq Φ) (blocks * digits)) : Rq Φ := + dot c (gadgetMatrix Φ base blocks digits *ᵥ x) + +/-- `tensorG1` is literally the per-block `G₁`-row form `Σᵢ cᵢ · (Σₑ baseᵉ · x_{(i,e)})` — the +Kronecker reading `(cᵀ ⊗ G₁) ŵ` of Hachi Eq. (18)/(20) row 4. -/ +theorem tensorG1_eq_sum_blocks (base : R) (digits : Nat) (hd : 0 < digits) + (c : PolyVec (Rq Φ) blocks) (x : PolyVec (Rq Φ) (blocks * digits)) : + tensorG1 Φ base digits c x + = ∑ i : Fin blocks, c i + * ∑ e : Fin digits, constRq Φ (base ^ (e : ℕ)) * x (finProdFinEquiv (i, e)) := by + simp only [tensorG1, dot_eq_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [show (gadgetMatrix Φ base blocks digits *ᵥ x) i = gadgetMul Φ base x i from rfl, + gadgetMul_apply Φ base hd] + +/-- `tensorG1` is subtractive in the challenge vector (Hachi Lemma 8's two-transcript subtraction +of Eq. (20) row 4). -/ +theorem tensorG1_sub_challenge (base : R) (digits : Nat) (c c' : PolyVec (Rq Φ) blocks) + (x : PolyVec (Rq Φ) (blocks * digits)) : + tensorG1 Φ base digits (c - c') x + = tensorG1 Φ base digits c x - tensorG1 Φ base digits c' x := by + simp only [tensorG1, dot_eq_sum, Pi.sub_apply, sub_mul, Finset.sum_sub_distrib] + +/-- Coordinate isolation at `k = 1` (Hachi Lemma 8, case (C), the c4 subtract-and-divide crux): +if `c ≡ⱼ c'`, then `tensorG1 (c − c') ŵ = (cⱼ − c'ⱼ) · wⱼ` where `w := G_blocks *ᵥ ŵ` is the +recomposed carrier. -/ +theorem tensorG1_coordDiff (base : R) (digits : Nat) + {c c' : PolyVec (Rq Φ) blocks} {j : Fin blocks} (h : CoordinateWise.CoordEq j c c') + (x : PolyVec (Rq Φ) (blocks * digits)) : + tensorG1 Φ base digits (c - c') x + = (c j - c' j) * (gadgetMatrix Φ base blocks digits *ᵥ x) j := by + simp only [tensorG1, dot_eq_sum]; rw [Finset.sum_eq_single j] + · simp only [Pi.sub_apply] + · intro i _ hij + have hzero : (c - c') i = 0 := by simp only [Pi.sub_apply]; rw [h.2 i hij, sub_self] + rw [hzero, zero_mul] + · intro hj; exact absurd (Finset.mem_univ j) hj + +end TensorG1 + +/-- `splitForm` is subtractive in the inner (right) basis vector (companion to Vectors.lean's +`splitForm_add_right`; Hachi Lemma 8's c4-side two-transcript subtraction `aᵀG(z⁽ʲ⁾) − aᵀG(z⁽⁰⁾)` +of Eq. (20) row 4). -/ +theorem splitForm_sub_right {P : Type} [CommRing P] {n m : ℕ} (M : PolyMatrix P n m) + (u : PolyVec P n) (v w : PolyVec P m) : + ArkLib.Lattices.splitForm M u (v - w) + = ArkLib.Lattices.splitForm M u v - ArkLib.Lattices.splitForm M u w := by + simp only [ArkLib.Lattices.splitForm, matVecMul_sub, dot_sub] + +/-! ## Interface checks: the c3/c4/c5 verifier rows of Eq. (20) read off these defs -/ + +section InterfaceChecks +variable {messageRows messageDigits zDigits blocks innerRows innerDigits : Nat} + +-- c3 (Eq. 20 row 3): `bᵀ (G_{2ʳ} ŵ) = 𝓎` on the flat carrier decomposition `ŵ`. +example (base : R) (b : PolyVec (Rq Φ) blocks) (what : PolyVec (Rq Φ) (blocks * messageDigits)) + (y : Rq Φ) : Prop := + dot b (gadgetMatrix Φ base blocks messageDigits *ᵥ what) = y + +-- c4 (Eq. 20 row 4): `(cᵀ ⊗ G₁) ŵ = aᵀ G_{2^m} (J ẑ)` reads off `tensorG1`/`jMatrix`. +example (base : R) (c : PolyVec (Rq Φ) blocks) (what : PolyVec (Rq Φ) (blocks * messageDigits)) + (zhat : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits)) + (a : PolyVec (Rq Φ) messageRows) : Prop := + tensorG1 Φ base messageDigits c what + = ArkLib.Lattices.splitForm (gadgetMatrix Φ base messageRows messageDigits) a + (jMatrix Φ base (messageRows * messageDigits) zDigits *ᵥ zhat) + +-- c5 (Eq. 20 row 5): `(cᵀ ⊗ G_{n_A}) t̂ = A (J ẑ)` reads off `tensorG`/`jMatrix`. +example (base : R) (c : PolyVec (Rq Φ) blocks) + (that : PolyVec (PolyVec (Rq Φ) (innerRows * innerDigits)) blocks) + (A : Simple.PublicParams Φ innerRows (messageRows * messageDigits)) + (zhat : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits)) : Prop := + tensorG Φ base innerRows innerDigits c that + = A *ᵥ (jMatrix Φ base (messageRows * messageDigits) zDigits *ᵥ zhat) + +-- c4-subtract chain (Lemma 8, extraction step 2): from the two branches' c4 rows sharing `ŵ` +-- and `CoordEq j c_s c_e`, the challenge difference isolates `(c_sⱼ − c_eⱼ) · wⱼ`, where +-- `w := G_blocks *ᵥ ŵ`. (`z_s`/`z_e` stand for the J-recompositions `J ẑ⁽ʲ⁾`/`J ẑ⁽⁰⁾`.) +example (base : R) {c_s c_e : PolyVec (Rq Φ) blocks} {j : Fin blocks} + (h : CoordinateWise.CoordEq j c_s c_e) + (what : PolyVec (Rq Φ) (blocks * messageDigits)) (a : PolyVec (Rq Φ) messageRows) + (z_s z_e : PolyVec (Rq Φ) (messageRows * messageDigits)) + (hc4s : tensorG1 Φ base messageDigits c_s what + = splitForm (gadgetMatrix Φ base messageRows messageDigits) a z_s) + (hc4e : tensorG1 Φ base messageDigits c_e what + = splitForm (gadgetMatrix Φ base messageRows messageDigits) a z_e) : + (c_s j - c_e j) * (gadgetMatrix Φ base blocks messageDigits *ᵥ what) j + = splitForm (gadgetMatrix Φ base messageRows messageDigits) a (z_s - z_e) := by + rw [splitForm_sub_right, ← hc4s, ← hc4e, ← tensorG1_sub_challenge, + tensorG1_coordDiff Φ base messageDigits h] + +end InterfaceChecks + + +end ArkLib.Lattices.Hachi diff --git a/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean b/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean index 7f8ea8f7bf..75291c71d4 100644 --- a/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean +++ b/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean @@ -240,4 +240,108 @@ theorem sub_lInftyNorm_le {cols : ℕ} (v w : PolyVec (Rq Φ) cols) {bound : ℕ unfold subLInftyNormBound omega +/-! ## Deliverable 1: ℓ₁ triangle inequality for subtraction -/ + +/-- **`ℓ₁` subtraction triangle inequality** (ring element): `‖a - b‖₁ ≤ ‖a‖₁ + ‖b‖₁`. -/ +theorem Rq.l1Norm_sub_le (a b : Rq Φ) : ‖a - b‖₁ ≤ ‖a‖₁ + ‖b‖₁ := by + unfold Rq.l1Norm + rw [← Finset.sum_add_distrib] + refine Finset.sum_le_sum (fun k _ => ?_) + have hcoeff : (a - b).1.coeff k = a.1.coeff k - b.1.coeff k := by + rw [Rq.sub_val, CompPoly.CPolynomial.coeff_sub] + rw [hcoeff] + exact valMinAbs_sub_natAbs_le _ _ + +/-! ## Deliverable 2: ℓ₁ positivity (the `hpos` bridge for `isUnit_of_l1Norm_le`) -/ + +omit [NeZero q] in +/-- The centered `ℓ₁` norm of `0` is `0`. -/ +theorem Rq.l1Norm_zero : ‖(0 : Rq Φ)‖₁ = 0 := by + unfold Rq.l1Norm + refine Finset.sum_eq_zero fun k _ => ?_ + rw [Rq.zero_val, CompPoly.CPolynomial.coeff_zero, ZMod.valMinAbs_zero, Int.natAbs_zero] + +omit [NeZero q] in +/-- A ring element with zero centered `ℓ₁` norm is `0`: every centered coefficient +representative below `deg φ` vanishes (`ZMod.valMinAbs_eq_zero`), and the representative is +reduced (degree `< deg φ`), so the underlying polynomial is `0`. -/ +theorem Rq.eq_zero_of_l1Norm_eq_zero {x : Rq Φ} (h : ‖x‖₁ = 0) : x = 0 := by + unfold Rq.l1Norm at h + -- Each centered coefficient below `deg φ` is zero. + have hlt : ∀ k, k < Φ.φ.natDegree → x.1.coeff k = 0 := by + intro k hk + have hz0 : (x.1.coeff k).valMinAbs.natAbs = 0 := + (Finset.sum_eq_zero_iff.mp h) k (Finset.mem_range.mpr hk) + rw [← ZMod.valMinAbs_eq_zero, ← Int.natAbs_eq_zero] + exact hz0 + -- Hence the underlying polynomial is `0` (coeffs below `deg φ` by the above, coeffs at or + -- above `deg φ` by reducedness). + have htoP : x.1.toPoly = 0 := by + apply Polynomial.ext + intro k + rw [Polynomial.coeff_zero] + by_cases hk : k < Φ.φ.natDegree + · rw [← CompPoly.CPolynomial.coeff_toPoly]; exact hlt k hk + · rw [not_lt] at hk + have hdeg : x.1.toPoly.degree < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced x.2 + have hφne : Φ.φ.toPoly ≠ 0 := (IsCyclotomic.monic (Φ := Φ)).ne_zero + have hdegφ : Φ.φ.toPoly.degree = (Φ.φ.natDegree : WithBot ℕ) := by + rw [Polynomial.degree_eq_natDegree hφne, CompPoly.CPolynomial.natDegree_toPoly] + have hle' : Φ.φ.toPoly.degree ≤ (k : WithBot ℕ) := by + rw [hdegφ]; exact_mod_cast hk + exact Polynomial.coeff_eq_zero_of_degree_lt (lt_of_lt_of_le hdeg hle') + have hx1 : x.1 = 0 := (CompPoly.CPolynomial.toPoly_eq_zero_iff x.1).mp htoP + exact Subtype.ext (by rw [Rq.zero_val]; exact hx1) + +omit [NeZero q] in +/-- The centered `ℓ₁` norm vanishes exactly on `0`. -/ +theorem Rq.l1Norm_eq_zero_iff (x : Rq Φ) : ‖x‖₁ = 0 ↔ x = 0 := + ⟨fun h => Rq.eq_zero_of_l1Norm_eq_zero Φ h, fun h => h ▸ Rq.l1Norm_zero Φ⟩ + +omit [NeZero q] in +/-- **`ℓ₁` positivity.** A nonzero ring element has positive centered `ℓ₁` norm — the `hpos` +input to `isUnit_of_l1Norm_le` for the extracted difference challenge `c̄ⱼ ≠ 0`. -/ +theorem Rq.l1Norm_pos_of_ne_zero {x : Rq Φ} (hx : x ≠ 0) : 0 < ‖x‖₁ := + Nat.pos_of_ne_zero fun h0 => hx (Rq.eq_zero_of_l1Norm_eq_zero Φ h0) + +/-! ## Deliverable 3(b): ℓ∞ → ℓ₂² aggregation bridge -/ + +omit [NeZero q] [IsCyclotomic Φ] in +/-- **`ℓ∞ → ℓ₂²` bridge** (ring element): `‖x‖₂² ≤ deg φ · ‖x‖∞²` — each of the `deg φ` +centered coefficients contributes at most `‖x‖∞²`. -/ +theorem Rq.l2NormSq_le_natDegree_mul_lInftyNorm_sq (x : Rq Φ) : + ‖x‖₂² ≤ Φ.φ.natDegree * (Rq.lInftyNorm Φ x) ^ 2 := by + unfold Rq.l2NormSq + calc ∑ k ∈ Finset.range Φ.φ.natDegree, (x.1.coeff k).valMinAbs.natAbs ^ 2 + ≤ ∑ _k ∈ Finset.range Φ.φ.natDegree, (Rq.lInftyNorm Φ x) ^ 2 := + Finset.sum_le_sum fun k hk => + Nat.pow_le_pow_left + (Finset.le_sup (f := fun k => (x.1.coeff k).valMinAbs.natAbs) hk) 2 + _ = Φ.φ.natDegree * (Rq.lInftyNorm Φ x) ^ 2 := by + rw [Finset.sum_const, Finset.card_range, smul_eq_mul] + +omit [NeZero q] [IsCyclotomic Φ] in +/-- **`ℓ∞ → ℓ₂²` bridge** (vector): `‖v‖₂² ≤ cols · (deg φ · ‖v‖∞²)`. -/ +theorem vecL2NormSq_le_card_mul_lInftyNorm_sq {cols : ℕ} (v : PolyVec (Rq Φ) cols) : + ‖v‖₂² ≤ cols * (Φ.φ.natDegree * (vecLInftyNorm Φ v) ^ 2) := by + unfold vecL2NormSq + calc ∑ i : Fin cols, Rq.l2NormSq Φ (v i) + ≤ ∑ _i : Fin cols, Φ.φ.natDegree * (vecLInftyNorm Φ v) ^ 2 := + Finset.sum_le_sum fun i _ => + le_trans (Rq.l2NormSq_le_natDegree_mul_lInftyNorm_sq Φ (v i)) + (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left + (Finset.le_sup (f := fun i => Rq.lInftyNorm Φ (v i)) (Finset.mem_univ i)) 2)) + _ = cols * (Φ.φ.natDegree * (vecLInftyNorm Φ v) ^ 2) := by + rw [Finset.sum_const, Finset.card_univ, Fintype.card_fin, smul_eq_mul] + +/-! ## Deliverable 3(c), constant: the recomposition growth bound -/ + +/-- Squared-`ℓ₂` bound for a base-`b` gadget **recomposition** `z = J·ẑ` of an +`ℓ∞`-range-checked decomposed vector (`‖ẑ‖∞ ≤ γ`): each of the `cols` entries of `z` is a +`τ`-digit base-`b` weighted sum, so its centered coefficients are at most `(∑_{u<τ} bᵘ)·γ`, +and squaring/summing over `d = deg φ` coefficients and `cols` entries gives +`cols · (d · ((∑_{u<τ} bᵘ)·γ)²)`. -/ +def zRecomposeL2SqBound (γ b τ d cols : ℕ) : ℕ := + cols * (d * ((∑ u ∈ Finset.range τ, b ^ u) * γ) ^ 2) + end ArkLib.Lattices.CyclotomicModulus diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean index b02406b7c0..9b4ee26243 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean @@ -34,6 +34,25 @@ noncomputable section open OracleComp OracleSpec ProtocolSpec open scoped NNReal +namespace CWSSStructure + +/-- The canonical coordinate-wise structure on a **challenge-free** protocol + (`IsEmpty pSpec.ChallengeIdx`): every field is the empty eliminator, since there are no challenge + rounds to describe. `coordinateWiseSpecialSound_of_isEmpty_challengeIdx` proves CWSS for *any* + `D` on such a protocol, but the binary-append composition theorem + (`Verifier.append_coordinateWiseSpecialSound`) needs a *concrete* structure for the zero-round + left factor (a `ReduceClaim`/`CheckClaim` head); this is that structure. -/ +def ofIsEmpty {n : ℕ} {pSpec : ProtocolSpec n} [IsEmpty pSpec.ChallengeIdx] : + CWSSStructure pSpec where + coordIndex := fun i => isEmptyElim i + alphabet := fun i => isEmptyElim i + decompose := fun i => isEmptyElim i + soundnessParam := fun i => isEmptyElim i + arity := fun i => isEmptyElim i + arity_eq := funext fun i => isEmptyElim i + +end CWSSStructure + namespace ProtocolSpec.ChallengeTree variable {n : ℕ} {pSpec : ProtocolSpec n} {arity : pSpec.ChallengeIdx → ℕ} diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean new file mode 100644 index 0000000000..0a433da285 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean @@ -0,0 +1,463 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose + +/-! + # Single-challenge-round tree navigation (generic CWSS building block) + + Reusable navigation for a challenge tree of shape `msgNode(pre) → chalNode → leaves`: exactly + one pre-challenge message, one challenge round, and nothing after. This is the generic core + shared by any coordinate-wise special sound (CWSS) protocol with a single challenge round over + a `2^r`-coordinate challenge vector — in particular Hachi's polynomial-evaluation reduction + (`QuadEval`, Hachi [NOZ26] Lemma 8; originally Greyhound's [NS24, §3.1] folding protocol). + + It provides: + + - the two-round `pSpec` (`P_to_V` carrier commitment, then `V_to_P` challenge vector); + - the index-generic round readers `topMsgAux`/`readPre` (round-0 message) and + `chalsAux`/`readChallenges` (the round-1 sibling-challenge family, *plainly typed* as + `Fin (arity ⟨1, rfl⟩) → Challenge ⟨1, rfl⟩` — the arity is baked into the tree's type, so no + `Σ'`-packaging or arity guard is needed); + - the star tree `tree2` with the reader computation rules (`readPre_tree2`, + `readChallenges_tree2`, both `rfl`); + - **shape recovery** (`tree_shape`, `tree_eq_tree2`): *every* tree of this `pSpec` rooted at + round `0` is a `tree2` — in fact `tree = tree2 (readPre tree) (readChallenges tree)`. This + is what lets the top-level CWSS proof rewrite an arbitrary structured accepting tree into + the synthetic star shape that all branch lemmas below are pinned to; + - the per-branch transcripts `branchPath`/`branchTr` and the navigation lemmas `branch_pre`, + `branch_challenge`, `branch_mem`; + - the CWSS structure `foldStructure` (`ℓ = 2^r`, `k = 2`, arity `2^r + 1`) with + `foldStructure_arity` and `nodeOk_iff_family`; + - the star-center machinery `StarAt`/`central`/`sib`, `exists_starAt`, `sib_coordEq`, and the + orientation/pointwise bridges `coordEq_symm`, `sib_coordEq_ne`, `sib_coordEq_apply_off` + (consumers get `challenges (sib i) i ≠ challenges (central …) i`, hence + `challenges (sib i) i - challenges (central …) i ≠ 0` via `sub_ne_zero_of_ne`, and + off-coordinate agreement for the subtract-and-cancel step); + - the pure-acceptance bridge `branch_relOut_language`; + - the tree extractor `E`, generic over *separate* relation-witness (`WitOut`) and extracted + input-witness (`WitIn`) types: it reads `(v, challenge family)` off the tree, sources one + `WitOut` per branch from `relOut` by classical choice, and hands everything to a caller- + supplied `mkWitness`; and + - the **generic assembly** `coordinateWiseSpecialSound_of_mkWitness`: any pure statement- + extending verifier of this `pSpec` is CWSS for `foldStructure` given only the protocol- + specific witness assembler `hmk` — all tree navigation, shape recovery, guard-firing, and + star-center plumbing is discharged here once. + + This module is milestone **M2** of `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` + (§9.4). +-/ + +open OracleComp OracleSpec ProtocolSpec ProtocolSpec.ChallengeTree CoordinateWise + +-- NB: `central`/`sib`/`E` need classical choice; the `linter.style.openClassical` linter (active +-- via `mathlibStandardSet`) forbids a file-level `open Classical`, so we use `open Classical in` +-- per definition instead. + +namespace CoordinateWise.SingleRound + +/-- The two-round single-challenge-round protocol (instantiated by Hachi's `QuadEval` reduction): +the prover sends a carrier commitment `CarrierCom` (round 0, `P_to_V`), the verifier replies with +a challenge vector `Fin (2^r) → C` (round 1, `V_to_P`). -/ +@[reducible] def pSpec (CarrierCom C : Type) (r : ℕ) : ProtocolSpec 2 := + ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin (2^r) → C]⟩ + +variable {CarrierCom C : Type} {r : ℕ} + {arity : (pSpec CarrierCom C r).ChallengeIdx → ℕ} + +/-! ## Round readers + +Naive `match tree` on a `ChallengeTree … 0` fails ("dependent elimination failed"), so each +reader is index-generic: it matches at an arbitrary round index `a` and carries the proof +`a = 0` (resp. `a = 1`), discharged per constructor via `congrArg Fin.val` + +`Direction.noConfusion`. Since the arity function is a parameter of `ChallengeTree`'s *type* +(and challenge-index proofs are definitionally irrelevant), the sibling family stored in the +round-1 `chalNode` already has the plain type `Fin (arity ⟨1, rfl⟩) → Challenge ⟨1, rfl⟩` — no +`Σ' K, Fin K → _` packaging is needed. -/ + +/-- Index-generic round-0 message reader: peel the top `msgNode` of a tree at any index `a` +together with a proof `a = 0`. -/ +def topMsgAux : {a : Fin 3} → ChallengeTree (pSpec CarrierCom C r) arity a → a = (0 : Fin 3) → + (pSpec CarrierCom C r).Message ⟨0, rfl⟩ + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m _ msg _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact msg + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-- Read the round-0 message (the pre-challenge carrier commitment) off a full tree. -/ +def readPre (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : + (pSpec CarrierCom C r).Message ⟨0, rfl⟩ := + topMsgAux tree rfl + +/-- Index-generic round-1 reader: peel the sibling-challenge family off a `chalNode` at any +index `a` together with a proof `a = 1`. The family is returned plainly typed — the child count +of the round-1 `chalNode` is definitionally `arity ⟨1, rfl⟩`. -/ +def chalsAux : {a : Fin 3} → ChallengeTree (pSpec CarrierCom C r) arity a → a = (1 : Fin 3) → + (Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + | _, .chalNode m h chals _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact chals + +/-- Read the round-1 sibling-challenge family off a full tree: a two-level peel — the round-0 +helper strips the top `msgNode` and hands its child (which sits at round 1) to `chalsAux`. -/ +def readChallenges (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : + Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩ := + aux tree rfl +where + /-- Round-0 helper for `readChallenges`: strip the top `msgNode`, delegate to `chalsAux`. -/ + aux : {a : Fin 3} → ChallengeTree (pSpec CarrierCom C r) arity a → a = (0 : Fin 3) → + (Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m _ _ child, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact chalsAux child rfl + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-! ## The star tree and shape recovery -/ + +/-- The star tree: one message node carrying `v`, one challenge node carrying the sibling +family, leaves below. Every tree of this `pSpec` has this shape (`tree_eq_tree2`). -/ +def tree2 (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : + ChallengeTree (pSpec CarrierCom C r) arity 0 := + .msgNode 0 rfl v (.chalNode 1 rfl challenges (fun _ => .leaf)) + +/-- The round-0 reader computes on the star tree. -/ +@[simp] theorem readPre_tree2 (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : + readPre (tree2 v challenges) = v := rfl + +/-- The round-1 reader computes on the star tree. -/ +@[simp] theorem readChallenges_tree2 (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : + readChallenges (tree2 v challenges) = challenges := rfl + +/-- Shape recovery, level 2: every subtree at the last round is a leaf. -/ +theorem eq_leaf : {a : Fin 3} → (t : ChallengeTree (pSpec CarrierCom C r) arity a) → + (ha : a = Fin.last 2) → + HEq t (ChallengeTree.leaf : ChallengeTree (pSpec CarrierCom C r) arity (Fin.last 2)) + | _, .leaf, _ => HEq.rfl + | _, .msgNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simpa using m.isLt.ne) + | _, .chalNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simpa using m.isLt.ne) + +/-- Shape recovery, level 1: every subtree at round 1 is a `chalNode` over leaves. -/ +theorem chal_shape : {a : Fin 3} → (t : ChallengeTree (pSpec CarrierCom C r) arity a) → + (ha : a = 1) → + ∃ challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩, + HEq t (ChallengeTree.chalNode (pSpec := pSpec CarrierCom C r) (arity := arity) + 1 rfl challenges (fun _ => .leaf)) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + | _, .chalNode m h chals children, ha => by + obtain rfl : m = 1 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + refine ⟨chals, ?_⟩ + have hch : children = fun _ => .leaf := by + funext j + exact eq_of_heq (eq_leaf (children j) rfl) + rw [hch] + +/-- Shape recovery, level 0: every tree at round 0 is a `tree2`. -/ +theorem tree_shape_aux : {a : Fin 3} → (t : ChallengeTree (pSpec CarrierCom C r) arity a) → + (ha : a = 0) → + ∃ v challenges, HEq t (tree2 (arity := arity) v challenges) + | _, .leaf, ha => by simp [Fin.ext_iff] at ha + | _, .msgNode m h msg child, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + obtain ⟨challenges, hchild⟩ := chal_shape child rfl + refine ⟨msg, challenges, ?_⟩ + rw [eq_of_heq hchild] + exact HEq.rfl + | _, .chalNode m h _ _, ha => by + obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) + exact absurd h Direction.noConfusion + +/-- **Shape recovery (existential form).** Every full tree of the two-round `pSpec` is a star +tree. -/ +theorem tree_shape (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : + ∃ v challenges, tree = tree2 (arity := arity) v challenges := by + obtain ⟨v, challenges, h⟩ := tree_shape_aux tree rfl + exact ⟨v, challenges, eq_of_heq h⟩ + +/-- **Shape recovery.** Every full tree of the two-round `pSpec` *is* the star tree of its +reads: `tree = tree2 (readPre tree) (readChallenges tree)`. This is the load-bearing rewrite +that turns an arbitrary structured accepting tree into the synthetic `tree2` that the branch +lemmas (`branch_pre`/`branch_challenge`/`branch_mem`/`branch_relOut_language`) are pinned to. -/ +theorem tree_eq_tree2 (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : + tree = tree2 (readPre tree) (readChallenges tree) := by + obtain ⟨v, challenges, rfl⟩ := tree_shape tree + rfl + +/-! ## Per-branch transcripts -/ + +/-- The root-to-leaf path through `tree2` selecting branch `j` of the challenge node. Defined +separately from `branchTr` so the path's tree index is pinned to `tree2 v challenges` (a bare +`LeafPath.msg (.chal j .leaf)` leaves the tree — hence `v`, `challenges` — undetermined). -/ +def branchPath (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (j : Fin (arity ⟨1, rfl⟩)) : LeafPath (tree2 v challenges) := + .msg (.chal j .leaf) + +/-- The full transcript of branch `j` of the star tree: message `v`, challenge +`challenges j`. -/ +def branchTr (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (j : Fin (arity ⟨1, rfl⟩)) : (pSpec CarrierCom C r).FullTranscript := + (branchPath v challenges j).fullTranscript + +/-- Branch `j`'s transcript carries challenge `challenges j` at round 1. -/ +theorem branch_challenge (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (j : Fin (arity ⟨1, rfl⟩)) : + (branchTr v challenges j).challenges ⟨1, rfl⟩ = challenges j := by + simp only [branchTr, branchPath, LeafPath.fullTranscript, LeafPath.transcript, + FullTranscript.challenges, Transcript.concat] + simp [Fin.snoc] + +/-- Branch `j`'s transcript carries the shared message `v` at round 0. -/ +theorem branch_pre (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (j : Fin (arity ⟨1, rfl⟩)) : + (branchTr v challenges j).messages ⟨0, rfl⟩ = v := by + simp only [branchTr, branchPath, LeafPath.fullTranscript, LeafPath.transcript, + FullTranscript.messages, Transcript.concat] + simp [Fin.snoc] + +/-- Branch `j`'s transcript is one of the star tree's leaf transcripts. -/ +theorem branch_mem (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (j : Fin (arity ⟨1, rfl⟩)) : + branchTr v challenges j ∈ (tree2 v challenges).fullTranscripts := + LeafPath.mem_fullTranscripts _ + +/-! ## The CWSS structure -/ + +/-- The single-round CWSS structure (Hachi's `QuadEval` reduction, Hachi Lemma 8): the single +challenge round carries `ℓ = 2^r` coordinates over +the alphabet `C`, decomposed by the identity (`Challenge ⟨1, rfl⟩ = (Fin (2^r) → C)` already), +with soundness parameter `k = 2`; hence arity `2^r·(2−1)+1 = 2^r+1` and +`nodeOk = IsSpecialSoundFamily (2^r) 2` — the branching of Hachi Lemma 8 / Def. 3. -/ +def foldStructure : CWSSStructure (pSpec CarrierCom C r) where + coordIndex := fun _ => ⟨2^r, Nat.two_pow_pos r⟩ + alphabet := fun _ => C + decompose := fun i => Equiv.cast (by rcases i with ⟨j, hj⟩; fin_cases j + · exact (Direction.noConfusion hj : _) + · rfl) + soundnessParam := fun _ => ⟨2, le_refl 2⟩ + arity := fun _ => 2^r * (2 - 1) + 1 + arity_eq := rfl + +/-- The single-round arity is `2^r + 1` (propositionally — `2^r * (2 - 1) + 1` is not `rfl`-equal +to `2^r + 1`; this is the bridge the extractor's `Fin.cast` uses). -/ +theorem foldStructure_arity : + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity ⟨1, rfl⟩ = 2^r + 1 := by + simp [foldStructure] + +/-- The single-round node predicate is exactly the `SS(C, 2^r, 2)` condition on the sibling +family (Hachi Lemma 8 / Def. 3). -/ +theorem nodeOk_iff_family + (challenges : Fin ((foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity + ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).nodeOk ⟨1, rfl⟩ challenges ↔ + IsSpecialSoundFamily (2^r) 2 + (fun j => (challenges (Fin.cast (by simp [foldStructure]) j))) := by + unfold CWSSStructure.nodeOk + simp only [foldStructure, CWSSStructure.ell, CWSSStructure.k] + rfl + +/-! ## Star-center machinery -/ + +/-- `e` is a star center of the sibling family: every coordinate has a sibling differing from +`challenges e` exactly there. -/ +def StarAt {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) (e : Fin K) : Prop := + ∀ i, ∃ j, CoordEq i (challenges e) (challenges j) + +open Classical in +/-- A star center of the family, chosen classically (arbitrary if none exists). No +`IsSpecialSoundFamily` hypothesis at the definition — `exists_starAt` supplies existence. -/ +noncomputable def central {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] : + Fin K := + if h : ∃ e, StarAt challenges e then h.choose else Classical.arbitrary _ + +open Classical in +/-- The coordinate-`i` sibling of the star center, chosen classically. -/ +noncomputable def sib {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] + (i : Fin ℓ) : Fin K := + if h : ∃ j, CoordEq i (challenges (central challenges)) (challenges j) then h.choose + else Classical.arbitrary _ + +/-- A special-sound family has a star center (promotes the family's central index; needs +`2 ≤ k` so each coordinate's sibling set is nonempty). -/ +theorem exists_starAt {ℓ k K : ℕ} (hk : 2 ≤ k) (hK : K = ℓ*(k-1)+1) + (challenges : Fin K → (Fin ℓ → C)) + (hfam : IsSpecialSoundFamily ℓ k (fun j => challenges (Fin.cast hK.symm j))) : + ∃ e, StarAt challenges e := by + obtain ⟨_, e, he⟩ := hfam; refine ⟨Fin.cast hK.symm e, fun i => ?_⟩ + obtain ⟨J, _, hJcard, hJ⟩ := he i + obtain ⟨j, hj⟩ : J.Nonempty := by rw [← Finset.card_pos, hJcard]; omega + exact ⟨Fin.cast hK.symm j, hJ j hj⟩ + +/-- The chosen sibling differs from the chosen center exactly at coordinate `i`. -/ +theorem sib_coordEq {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] + (hstar : ∃ e, StarAt challenges e) (i : Fin ℓ) : + CoordEq i (challenges (central challenges)) (challenges (sib challenges i)) := by + have hc : StarAt challenges (central challenges) := by + unfold central; rw [dif_pos hstar]; exact hstar.choose_spec + unfold sib; rw [dif_pos (hc i)]; exact (hc i).choose_spec + +/-- `CoordEq` is symmetric (orientation bridge: `sib_coordEq` is oriented center-first, the +extraction's difference challenge `c̄ᵢ := c_{sib,i} − c_{central,i}` is oriented sibling-first). -/ +theorem coordEq_symm {S : Type*} {ℓ : ℕ} {i : Fin ℓ} {x y : Fin ℓ → S} + (h : CoordEq i x y) : CoordEq i y x := + ⟨h.1.symm, fun j hj => (h.2 j hj).symm⟩ + +/-- Sibling-first pointwise disagreement at coordinate `i`: with a ring-valued alphabet, +`sub_ne_zero_of_ne` turns this into `challenges (sib …) i - challenges (central …) i ≠ 0` — +the nonzeroness of the extracted difference challenge `c̄ᵢ`. -/ +theorem sib_coordEq_ne {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] + (hstar : ∃ e, StarAt challenges e) (i : Fin ℓ) : + challenges (sib challenges i) i ≠ challenges (central challenges) i := + (coordEq_symm (sib_coordEq challenges hstar i)).1 + +/-- Sibling-first pointwise agreement off coordinate `i` (the off-coordinate cancellation of the +subtract-and-divide step). -/ +theorem sib_coordEq_apply_off {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] + (hstar : ∃ e, StarAt challenges e) (i : Fin ℓ) {j : Fin ℓ} (hj : j ≠ i) : + challenges (sib challenges i) j = challenges (central challenges) j := + ((sib_coordEq challenges hstar i).2 j hj).symm + +/-! ## Pure-acceptance bridge and extractor -/ + +section Bridge + +variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitOut : Type} {σ : Type} + +/-- Acceptance of the star tree specializes, per branch `j`, to membership of the branch's +verifier output `(stmtIn, v, challenges j)` in `relOut.language` — for any pure verifier that +outputs the statement extended by the transcript's message and challenge. -/ +theorem branch_relOut_language (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) + (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2^r) → C)) (pSpec CarrierCom C r)) + (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) + (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) + (stmtIn : StmtIn) (v : CarrierCom) + (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) + (hAcc : (tree2 v challenges).IsAccepting init impl V stmtIn relOut.language) + (j : Fin (arity ⟨1, rfl⟩)) : + (stmtIn, v, challenges j) ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl V stmtIn (branchTr v challenges j) relOut.language + (stmtIn, v, challenges j) (by rw [hpure]; rw [branch_pre, branch_challenge]; rfl) + (hAcc _ (branch_mem v challenges j)) + +end Bridge + +open Classical in +/-- The tree extractor, generic over separate witness types: `relOut` relates the extended +statement to a per-branch response `WitOut`; `mkWitness` assembles the extracted input witness +`WitIn` from the shared message, the `2^r + 1` sibling challenge vectors, and one classically +chosen `WitOut` per branch (`Classical.ofNonempty` where none exists — on structured accepting +trees `branch_relOut_language` fires every guard). Hypothesis-free: all correctness is proven +downstream. -/ +noncomputable def E {StmtIn WitOut WitIn : Type} [Nonempty WitOut] + (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) + (mkWitness : StmtIn → CarrierCom → (Fin (2^r+1) → (Fin (2^r) → C)) → + (Fin (2^r+1) → WitOut) → WitIn) : + Extractor.TreeBased StmtIn WitIn (pSpec CarrierCom C r) + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity := + fun stmtIn tree => + let v := readPre tree + let fam : Fin (2^r+1) → (Fin (2^r) → C) := fun j => + readChallenges tree (Fin.cast foldStructure_arity.symm j) + let resp : Fin (2^r+1) → WitOut := fun j => + if h : ∃ w, ((stmtIn, v, fam j), w) ∈ relOut then h.choose else Classical.ofNonempty + mkWitness stmtIn v fam resp + +section Assembly + +variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitOut WitIn : Type} [Nonempty WitOut] + {σ : Type} + +/-- **Generic single-round CWSS assembly.** Any pure statement-extending verifier of the +two-round `pSpec` is coordinate-wise special sound for `foldStructure`, provided a witness +assembler `mkWitness` that turns per-branch `relOut`-witnesses at star-shaped challenge +families into a `relIn`-witness. This discharges all tree/extractor plumbing once; the +protocol-specific work (Hachi Lemma 8's case split and subtract-divide) lives entirely in +`hmk`. -/ +theorem coordinateWiseSpecialSound_of_mkWitness + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2^r) → C)) (pSpec CarrierCom C r)) + (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) + (relIn : Set (StmtIn × WitIn)) + (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) + (mkWitness : StmtIn → CarrierCom → (Fin (2^r+1) → (Fin (2^r) → C)) → + (Fin (2^r+1) → WitOut) → WitIn) + (hmk : ∀ stmtIn v (fam : Fin (2^r+1) → (Fin (2^r) → C)) (resp : Fin (2^r+1) → WitOut), + (∀ j, ((stmtIn, v, fam j), resp j) ∈ relOut) → + (∃ e, StarAt fam e) → + (stmtIn, mkWitness stmtIn v fam resp) ∈ relIn) : + V.coordinateWiseSpecialSound init impl + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)) relIn relOut := by + classical + refine ⟨E relOut mkWitness, ?_⟩ + intro stmtIn tree hStruct hAcc + obtain ⟨v, challenges, rfl⟩ := tree_shape tree + have harity := (foldStructure_arity (CarrierCom := CarrierCom) (C := C) (r := r)).symm + -- each branch's guard fires: per-branch membership in `relOut.language` + have hmem : ∀ j : Fin (2^r+1), + ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut := by + intro j + have h := branch_relOut_language init impl V hpure relOut stmtIn v challenges hAcc + (Fin.cast harity j) + exact (Set.mem_language_iff relOut _).1 h + -- the sibling family is special sound, hence has a star center + have hfam := (nodeOk_iff_family challenges).1 hStruct.1 + have hstar : ∃ e, StarAt + (fun j : Fin (2^r+1) => challenges (Fin.cast harity j)) e := + exists_starAt (le_refl 2) (by omega) _ hfam + -- each chosen response satisfies the relation (the extractor's guards fire) + have hbranch : ∀ j : Fin (2^r+1), + ((stmtIn, v, challenges (Fin.cast harity j)), + if h : ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut + then h.choose else Classical.ofNonempty) ∈ relOut := by + intro j + rw [dif_pos (hmem j)] + exact (hmem j).choose_spec + -- the extractor computes definitionally on the recovered star tree; `exact` closes by defeq + exact hmk stmtIn v _ _ hbranch hstar + +end Assembly + +/-! ## The 2-round protocol instances (NOT auto-derived for `ProtocolSpec 2`) -/ + +section Instances + +variable {CarrierCom C : Type} {r : ℕ} [SampleableType C] [OracleInterface CarrierCom] + +/-- Hand-written 2-round instances (NOT auto-derived for `ProtocolSpec 2`). +Generic in `C`; with `C := ShortChallenge Φ ω` they discharge from the instance ARGUMENT +`[SampleableType (ShortChallenge Φ ω)]` (see the `example`s in `QuadEval.lean`). -/ +instance : ∀ i, SampleableType ((pSpec CarrierCom C r).Challenge i) + | ⟨0, h⟩ => nomatch h + | ⟨1, _⟩ => (inferInstance : SampleableType (Fin (2^r) → C)) + +instance : ∀ i, OracleInterface ((pSpec CarrierCom C r).Message i) + | ⟨0, _⟩ => (inferInstance : OracleInterface CarrierCom) + | ⟨1, h⟩ => nomatch h + +end Instances + +end CoordinateWise.SingleRound diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index 2286f1f669..c14e6b0e65 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -76,6 +76,36 @@ home_page/ site assets and assembled website root `X^{2^α}+1`, which the security proofs genuinely require). - `InnerOuter.lean` — top-level re-export of the scheme, its correctness, and its weak-binding reduction. + - `PolynomialQuadraticEq/` — Hachi's polynomial-evaluation reduction ([NOZ26, §4.2, Figure 3]), + which proves + `f(x) = y` by expressing the evaluation as the quadratic form `bᵀ M a` and folding the `2ʳ` + carrier blocks under the challenge vector (hence the name `QuadEval`); it is Hachi's + multilinear/inner-outer lift of Greyhound's [NS24, §3.1] folding protocol. `QuadEvalGadgets` + holds the gadget algebra (`PublicParamsD`, the carrier/short commitment `v = D ŵ`, the + `J`-decomposition of `z`, and the `tensorG`/`tensorG1` challenge combinations); `QuadEval` is + the 2-round protocol, its `relOut` (Eq. (20) + range balls) / `relIn` (weak opening + ∨ MSIS(B) ∨ MSIS(D)), the subtract-and-divide extractor `buildWitness`, and **Lemma 8** + (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound(')`, `sorryAx`-free. + The generic tree plumbing lives in `Security/CoordinateWiseSpecialSoundness/SingleRound`; the + supporting norm bounds are in `Data/Lattices/CyclotomicRing/NormBounds/Basic` and `GadgetNorms`. + `PolynomialQuadraticEq/PolyEvalReduction` adds the **polynomial-level bridge**: a zero-round + `ReduceClaim` head + (`bridgeVerifier`) that reinterprets a `CMlPolynomial`-level statement `PolyEvalStatement` as a + `QuadEvalStatement` via the monomial tensor bases (`toQuadEvalStatement`), the pulled-back input + relation `relPolyEval` (the extracted polynomial evaluates to `y`, or MSIS(B/D)), and its CWSS + `bridge_coordinateWiseSpecialSound` (any `D`, via the pull-back `mem_relPolyEval_of_relIn`). + - `PolynomialEvalSplit.lean` — the matrix split underlying the evaluation argument: multilinear + evaluation `eval p (xl ++ xh)` factors as the vector–matrix–vector product + `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))` (`evalSplit_eq_eval`), with the inverse reshape + `toPolynomial` and the bridge lemma `splitForm_monomialBasis_eq_eval` consumed by + `PolynomialQuadraticEq/PolyEvalReduction`. + - `Basic.lean` — the **designated home of Hachi as a functional commitment** and of the growing + n-ary composition. `evalVerifier` is `bridge.append QuadEval.verifier`; + `hachi_eval_coordinateWiseSpecialSound` is the composed CWSS (`sorryAx`-free), assembled by + `Verifier.append_coordinateWiseSpecialSound` from the bridge and Lemma 8. It also holds the FC + scaffolding: the eval `OracleInterface`, honest `hachiKeygen`/`hachiCommit`, and the + `Commitment.Scheme` value `hachiFC` (its opening `Proof` is a documented `sorry` pending the + remaining §4.3+ subprotocols and the completeness layer). - The Merkle tree implementations now live upstream in `VCVio`, so use `VCVio.CryptoFoundations.MerkleTree` or `VCVio.CryptoFoundations.InductiveMerkleTree` instead of the old ArkLib-local modules. @@ -108,7 +138,15 @@ home_page/ site assets and assembled website root (`CoordEq`, `IsSpecialSoundFamily`), `CWSSStructure`, `CWSSStructure.toShape`, and `Verifier.coordinateWiseSpecialSound`; `Composition` transports CWSS structures across sequential composition and proves binary append preservation via the generic transcript-tree - split. The umbrella `CoordinateWiseSpecialSoundness.lean` re-exports both files. + split; `NoChallenge` and `SeqCompose` supply the empty-challenge base case and the n-ary + sequential wrappers. `NoChallenge` also provides `CWSSStructure.ofIsEmpty`, the concrete + challenge-free structure used as the left factor when appending a zero-round `ReduceClaim` head + (e.g. Hachi's `bridgeVerifier`). `SingleRound` is the generic single-challenge-round navigation + layer + (tree shape recovery `tree_shape`/`tree_eq_tree2`, 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. - 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 From 63c92420731d93f6112d53b466359dca24f875af Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Mon, 6 Jul 2026 18:33:54 +0200 Subject: [PATCH 03/21] clean up --- ArkLib/ProofSystem/Component/CheckClaim.lean | 4 ++-- ArkLib/ProofSystem/Component/ReduceClaim.lean | 15 +++++++------ .../ProofSystem/Component/SendChallenge.lean | 22 ++++++++++++------- ArkLib/ProofSystem/Component/SendClaim.lean | 15 +++++++++---- ArkLib/ProofSystem/Component/SendWitness.lean | 8 +++---- blueprint/src/references.bib | 8 ------- docs/skills/make-pr-ready.md | 8 +++++++ 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/ArkLib/ProofSystem/Component/CheckClaim.lean b/ArkLib/ProofSystem/Component/CheckClaim.lean index 3df8a3c95c..0ca34e6e0d 100644 --- a/ArkLib/ProofSystem/Component/CheckClaim.lean +++ b/ArkLib/ProofSystem/Component/CheckClaim.lean @@ -180,8 +180,8 @@ def oracleProver : OracleProver oSpec receiveChallenge := fun i => nomatch i output := fun stmt => pure (stmt, ()) -/-- The oracle verifier for the `CheckClaim` oracle reduction is a **pure pass-through** (per §1.2 -of the Hachi CWSS plan): it returns the statement and all oracle statements unchanged. The predicate +/-- The oracle verifier for the `CheckClaim` oracle reduction is a **pure pass-through**: it +returns the statement and all oracle statements unchanged. The predicate being checked is *not* run as an effectful `guard`/oracle computation here; instead it lives in the output relation `oracleRelOut`. This keeps the verifier `IsPure` (so it can be a left factor in a CWSS composition) and sidesteps the unfinished no-failure `OracleComp` refactor. (The `guard`-based diff --git a/ArkLib/ProofSystem/Component/ReduceClaim.lean b/ArkLib/ProofSystem/Component/ReduceClaim.lean index 5408cd7038..a57256743c 100644 --- a/ArkLib/ProofSystem/Component/ReduceClaim.lean +++ b/ArkLib/ProofSystem/Component/ReduceClaim.lean @@ -133,7 +133,7 @@ lemma support_mk (m : Type _ → Type _) [Monad m] [MonadLiftT m SetM] /-- The knowledge state function for the `ReduceClaim` reduction. -/ def knowledgeStateFunction (hRel : ∀ stmtIn witOut, - (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : + (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : (verifier oSpec mapStmt).KnowledgeStateFunction init impl relIn relOut (extractor mapWitInv) where toFun | ⟨0, _⟩ => fun stmtIn _ witIn => ⟨stmtIn, witIn⟩ ∈ relIn @@ -148,7 +148,8 @@ def knowledgeStateFunction (hRel : ∀ stmtIn witOut, rw [OptionT.mem_support_iff] at hx simp only [OptionT.run_mk, support_bind, Set.mem_iUnion] at hx obtain ⟨s, _, hx⟩ := hx - have key : (simulateQ impl (pure (mapStmt stmtIn) : OptionT (OracleComp oSpec) StmtOut)).run' s = + have key : (simulateQ impl + (pure (mapStmt stmtIn) : OptionT (OracleComp oSpec) StmtOut)).run' s = pure (some (mapStmt stmtIn)) := by change (simulateQ impl (pure (some (mapStmt stmtIn)) : OracleComp oSpec (Option StmtOut))).run' s = _ @@ -166,7 +167,7 @@ Note that since there is no challenge round, all the work is done in the definit knowledge state function. -/ @[simp] theorem verifier_rbrKnowledgeSoundness (hRel : ∀ stmtIn witOut, - (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : + (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : (verifier oSpec mapStmt).rbrKnowledgeSoundness init impl relIn relOut 0 := by refine ⟨_, _, knowledgeStateFunction relIn relOut hRel, ?_⟩ simp only [ProtocolSpec.ChallengeIdx] @@ -324,8 +325,8 @@ variable {mapWitInv : (StmtIn × (∀ i, OStmtIn i)) → WitOut → WitIn} /-- The knowledge state function for the `ReduceClaim` oracle reduction. -/ def oracleKnowledgeStateFunction (hRel : ∀ stmtIn oStmtIn witOut, - ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → - ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : + ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → + ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : (oracleVerifier oSpec mapStmt embedIdx hEq).KnowledgeStateFunction init impl relIn relOut (extractor mapWitInv) where toFun | ⟨0, _⟩ => fun ⟨stmtIn, oStmtIn⟩ _ witIn => ⟨⟨stmtIn, oStmtIn⟩, witIn⟩ ∈ relIn @@ -360,8 +361,8 @@ Note that since there is no challenge round, all the work is done in the definit knowledge state function. -/ @[simp] theorem oracleVerifier_rbrKnowledgeSoundness (hRel : ∀ stmtIn oStmtIn witOut, - ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → - ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : + ((mapStmt stmtIn, mapOStmt embedIdx hEq oStmtIn), witOut) ∈ relOut → + ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : (oracleVerifier oSpec mapStmt embedIdx hEq).rbrKnowledgeSoundness init impl relIn relOut 0 := by refine ⟨_, _, oracleKnowledgeStateFunction relIn relOut hRel, ?_⟩ intro stmtIn witIn prover i diff --git a/ArkLib/ProofSystem/Component/SendChallenge.lean b/ArkLib/ProofSystem/Component/SendChallenge.lean index f184827e67..df35eadd6d 100644 --- a/ArkLib/ProofSystem/Component/SendChallenge.lean +++ b/ArkLib/ProofSystem/Component/SendChallenge.lean @@ -12,22 +12,28 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic A one-round, verifier-first (`V_to_P`) oracle reduction: the verifier samples a **challenge vector** `c : Fin ℓ → C`, sends it to the prover, and appends it to the output statement. There is no witness and **no check** — this is the definitional challenge-round building block of the - Hachi/Greyhound fold (Figure 3), where `ℓ = 2ʳ` and `C ⊆ Rq`. + Greyhound [NS24] / Hachi [NOZ26] fold ([NOZ26, Figure 3]), where `ℓ = 2ʳ` and `C ⊆ Rq`. - Per §1.4 of the Hachi CWSS plan, a lone challenge round has no relation to extract into, so it is - *not* coordinate-wise special sound on its own; its CWSS is established only as part of the - surrounding fold block (Lemma 8, out of scope here). What this file provides is: + A lone challenge round has no relation to extract into, so it is *not* coordinate-wise special + sound on its own; its CWSS is established only as part of the surrounding fold block + ([NOZ26, Lemma 8], out of scope here). What this file provides is: - the component itself (`oracleProver` / `oracleVerifier` / `oracleReduction`); - `instIsPure`: the verifier is pure — it reads the challenge off the transcript and appends it, - with no runtime check (§1.2) — so it can be a left factor in a CWSS `append`; + with no runtime check — so it can be a left factor in a CWSS `append`; - `foldBlockStructure`: the `CWSSStructure` this round contributes to the block — one challenge round with `coordIndex = ℓ`, `alphabet = C`, `soundnessParam = 2` (so `arity = ℓ·(2−1)+1 = ℓ+1` - and `nodeOk = IsSpecialSoundFamily ℓ 2`), matching Hachi Lemma 4 / Def. 3 exactly. + and `nodeOk = IsSpecialSoundFamily ℓ 2`), matching [NOZ26, Lemma 4 / Definition 3] exactly. To *run* the reduction (completeness / soundness) one additionally needs `[SampleableType (Fin ℓ → C)]` (available from `[SampleableType C]` via the derived `Fin`-domain product instance); it is not required for the definitions, `IsPure`, or the structure above. + + ## References + + * [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] -/ open OracleSpec OracleComp OracleQuery OracleInterface ProtocolSpec Function @@ -102,8 +108,8 @@ instance instIsPure : (oracleVerifier oSpec Statement OStatement C ℓ).toVerifi /-- The **fold-block coordinate-wise structure**: the single challenge round of `SendChallenge` carries `ℓ` coordinates over the alphabet `C`, decomposed by the identity (`Challenge = Fin ℓ → C` already), with soundness parameter `k = 2`. Hence `arity = ℓ·(2−1)+1 = ℓ+1` and the node predicate -is `IsSpecialSoundFamily ℓ 2` — exactly the branching required by Hachi Lemma 4 / Def. 3 (with -`ℓ = 2ʳ`). This is the shape the fold block's CWSS (Lemma 8) is proven against. -/ +is `IsSpecialSoundFamily ℓ 2` — exactly the branching required by [NOZ26, Lemma 4 / Definition 3] +(with `ℓ = 2ʳ`). This is the shape the fold block's CWSS ([NOZ26, Lemma 8]) is proven against. -/ def foldBlockStructure (hℓ : 0 < ℓ) : CWSSStructure (pSpec C ℓ) where coordIndex := fun _ => ⟨ℓ, hℓ⟩ alphabet := fun _ => C diff --git a/ArkLib/ProofSystem/Component/SendClaim.lean b/ArkLib/ProofSystem/Component/SendClaim.lean index 6c1a32e694..ea6437dfdc 100644 --- a/ArkLib/ProofSystem/Component/SendClaim.lean +++ b/ArkLib/ProofSystem/Component/SendClaim.lean @@ -11,13 +11,14 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose The prover sends a **claim** (a single oracle message) to the verifier, computed from the input (combined) statement by a function `f`. This is the "prover-computed message" building block, e.g. - the Hachi/Greyhound first message `v := D ŵ` or the Sumcheck round polynomial `q`. + the Greyhound [NS24] / Hachi [NOZ26] first message `v := D ŵ` or the Sumcheck round polynomial + `q`. - There is no witness (`Witness = Unit`). - The prover sends a message of type `Message` (with an `OracleInterface`), namely `f stmt oStmt`. - - The **verifier is a pure pass-through** (`verify := fun stmt _ => pure stmt`): per §1.2 of the - Hachi CWSS plan, the claim is *not* checked by a runtime `guard`; the check lives in the output - relation `toORelOut` (the predicate `P` over statement / oracle statements / message). + - The **verifier is a pure pass-through** (`verify := fun stmt _ => pure stmt`): the claim is + *not* checked by a runtime `guard`; the check lives in the output relation `toORelOut` (the + predicate `P` over statement / oracle statements / message). - The output oracle statements are the input oracle statements together with the sent message, `OStatement ⊕ᵥ (fun _ : Fin 1 => Message)`. @@ -36,6 +37,12 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose oracle-reduction completeness reasoning as `SendSingleWitness.oracleReduction_completeness`, and is orthogonal to the CWSS target here. This design supersedes the previous effectful-verifier one (whose completeness proof no longer applies). + + ## References + + * [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] -/ open OracleSpec OracleComp OracleQuery OracleInterface ProtocolSpec Function Equiv diff --git a/ArkLib/ProofSystem/Component/SendWitness.lean b/ArkLib/ProofSystem/Component/SendWitness.lean index 159e6ab7c3..017d9b5fbf 100644 --- a/ArkLib/ProofSystem/Component/SendWitness.lean +++ b/ArkLib/ProofSystem/Component/SendWitness.lean @@ -344,10 +344,10 @@ theorem oracleReduction_completeness (h : NeverFail init) : -- TODO: clean up this proof -- simp only [OracleReduction.perfectCompleteness, oraclePSpec, toORelOut, Fin.isValue, -- OracleReduction.toReduction, MessageIdx, Reduction.perfectCompleteness_eq_prob_one, - -- ChallengeIdx, StateT.run'_eq, Set.mem_setOf_eq, probEvent_eq_one_iff, probFailure_eq_zero_iff, - -- neverFails_bind_iff, neverFails_map_iff, support_bind, support_map, Set.mem_iUnion, - -- Set.mem_image, Prod.exists, exists_and_right, exists_eq_right, exists_prop, forall_exists_index, - -- and_imp, Prod.forall, Prod.mk.injEq] + -- ChallengeIdx, StateT.run'_eq, Set.mem_setOf_eq, probEvent_eq_one_iff, + -- probFailure_eq_zero_iff, neverFails_bind_iff, neverFails_map_iff, support_bind, + -- support_map, Set.mem_iUnion, Set.mem_image, Prod.exists, exists_and_right, + -- exists_eq_right, exists_prop, forall_exists_index, and_imp, Prod.forall, Prod.mk.injEq] -- simp_rw [h, Reduction.run, oracleReduction, oracleVerifier_toVerifier_run, oracleProver_run] -- simp only [ChallengeIdx, oraclePSpec, id_eq, liftM_eq_liftComp, -- liftComp_pure, bind_pure_comp, map_pure, simulateQ_pure, StateT.run_pure, diff --git a/blueprint/src/references.bib b/blueprint/src/references.bib index 0454549ead..15c6d7061f 100644 --- a/blueprint/src/references.bib +++ b/blueprint/src/references.bib @@ -160,14 +160,6 @@ @article{DP24 year={2024} } -@misc{NOZ26, - author = {Ngoc Khanh Nguyen and George O'Rourke and Jiapeng Zhang}, - title = {Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over Extension Fields}, - howpublished = {Cryptology {ePrint} Archive, Paper 2026/156}, - year = {2026}, - url = {https://eprint.iacr.org/2026/156} -} - @inproceedings{DP25, author = {Diamond, Benjamin E. and Posen, Jim}, title = {Succinct Arguments over Towers of Binary Fields}, diff --git a/docs/skills/make-pr-ready.md b/docs/skills/make-pr-ready.md index 69838cd16b..61a3f42288 100644 --- a/docs/skills/make-pr-ready.md +++ b/docs/skills/make-pr-ready.md @@ -124,6 +124,14 @@ Work through these in order. Do not stop until every item is complete. keys yourself: grep each `[KEY]` used in docstrings against `blueprint/src/references.bib` and add any missing entry (then regenerate). A key can be "present-looking" but actually a different paper — confirm the entry's title/authors match the citation, not just that the key exists. +- Also check for **duplicate BibTeX keys**: `grep -oE '^@[a-z]+\{[^,]+' blueprint/src/references.bib + | sort | uniq -d`. Neither `kb/lint` nor the sync script flags a key defined twice (the JSON dict + silently collapses it), but it is real bib cruft a reviewer will hit. Keep the better-formatted + entry and delete the other. +- Docstrings must cite **papers, not internal planning documents**. Phrases like "per §1.2 of the + X plan" pointing at an out-of-repo design doc are dead references the day the PR merges — restate + the design rationale directly in the docstring and cite the underlying paper with a `[KEY]` + (e.g. `[NOZ26, Lemma 8]` for a specific result). - Also check the **reverse direction — orphan entries**: a BibTeX key (and/or a scaffolded `docs/kb/papers/.md` + `docs/kb/sources//` page) that **no** `[KEY]` in any `.lean` docstring or blueprint `.tex` actually cites. `kb/lint` passes on these (they are internally From c33ad9a9659b1bebbec8823564f0afe77c5f0c19 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Mon, 6 Jul 2026 18:51:21 +0200 Subject: [PATCH 04/21] remove classical --- .../SeqCompose.lean | 15 +++++--- ArkLib/ProofSystem/Component/ReduceClaim.lean | 37 +++++++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean index fa69c80d9e..aa92d6bbc9 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SeqCompose.lean @@ -94,22 +94,25 @@ variable {ι : Type} {oSpec : OracleSpec ι} /-- **n-ary base case.** The identity verifier is tree-special-sound for any shape `S`, with input relation equal to output relation: the empty protocol `!p[]` has no challenge rounds, so this is the -no-challenge bridge with the extractor that picks (classically) a witness of `stmtIn` whenever one +no-challenge bridge with the extractor that noncomputably picks a witness of `stmtIn` whenever one exists. -/ theorem id_treeSpecialSound {Statement Witness : Type} [Nonempty Witness] (S : ChallengeTreeShape (!p[] : ProtocolSpec 0)) (rel : Set (Statement × Witness)) : (Verifier.id (oSpec := oSpec) (Statement := Statement)).treeSpecialSound init impl S rel rel := by - classical + -- For each statement, some candidate witness works as soon as any witness exists. + have hpick : ∀ stmt : Statement, ∃ w : Witness, (∃ w', (stmt, w') ∈ rel) → (stmt, w) ∈ rel := by + intro stmt + rcases or_not (p := ∃ w', (stmt, w') ∈ rel) with ⟨w, hw⟩ | h + · exact ⟨w, fun _ => hw⟩ + · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ refine treeSpecialSound_of_isEmpty_challengeIdx init impl S Verifier.id rel rel - (fun stmt _ => if h : ∃ w, (stmt, w) ∈ rel then h.choose else Classical.ofNonempty) ?_ + (fun stmt _ => (hpick stmt).choose) ?_ intro stmtIn tr hAcc have hlang : stmtIn ∈ rel.language := mem_of_pure_accepting init impl Verifier.id stmtIn tr rel.language stmtIn rfl hAcc - have hex : ∃ w, (stmtIn, w) ∈ rel := (Set.mem_language_iff rel stmtIn).1 hlang - simp only [dif_pos hex] - exact hex.choose_spec + exact (hpick stmtIn).choose_spec ((Set.mem_language_iff rel stmtIn).1 hlang) end Verifier diff --git a/ArkLib/ProofSystem/Component/ReduceClaim.lean b/ArkLib/ProofSystem/Component/ReduceClaim.lean index a57256743c..4394cadbc5 100644 --- a/ArkLib/ProofSystem/Component/ReduceClaim.lean +++ b/ArkLib/ProofSystem/Component/ReduceClaim.lean @@ -188,18 +188,21 @@ theorem verifier_coordinateWiseSpecialSound [Nonempty WitIn] (hRel : ∀ stmtIn witOut, (mapStmt stmtIn, witOut) ∈ relOut → (stmtIn, mapWitInv stmtIn witOut) ∈ relIn) : (verifier oSpec mapStmt).coordinateWiseSpecialSound init impl D relIn relOut := by - classical + -- For each input statement, some candidate input witness works as soon as any output witness + -- makes the mapped statement accepted. + have hpick : ∀ stmtIn : StmtIn, ∃ witIn : WitIn, + (∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut) → (stmtIn, witIn) ∈ relIn := by + intro stmtIn + rcases or_not (p := ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut) with ⟨witOut, hw⟩ | h + · exact ⟨mapWitInv stmtIn witOut, fun _ => hRel stmtIn witOut hw⟩ + · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ refine Verifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D - (verifier oSpec mapStmt) relIn relOut - (fun stmtIn _ => if h : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut - then mapWitInv stmtIn h.choose else Classical.ofNonempty) ?_ + (verifier oSpec mapStmt) relIn relOut (fun stmtIn _ => (hpick stmtIn).choose) ?_ intro stmtIn tr hAcc have hlang : mapStmt stmtIn ∈ relOut.language := Verifier.mem_of_pure_accepting init impl (verifier oSpec mapStmt) stmtIn tr relOut.language (mapStmt stmtIn) rfl hAcc - have hex : ∃ witOut, (mapStmt stmtIn, witOut) ∈ relOut := (Set.mem_language_iff _ _).1 hlang - simp only [dif_pos hex] - exact hRel stmtIn hex.choose hex.choose_spec + exact (hpick stmtIn).choose_spec ((Set.mem_language_iff _ _).1 hlang) end Reduction @@ -396,21 +399,25 @@ theorem oracleVerifier_coordinateWiseSpecialSound [Nonempty WitIn] ((stmtIn, oStmtIn), mapWitInv (stmtIn, oStmtIn) witOut) ∈ relIn) : (oracleVerifier oSpec mapStmt embedIdx hEq).coordinateWiseSpecialSound init impl D relIn relOut := by - classical + -- For each combined input statement, some candidate input witness works as soon as any output + -- witness makes the mapped statement accepted. + have hpick : ∀ s : StmtIn × (∀ i, OStmtIn i), ∃ witIn : WitIn, + (∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut) → + (s, witIn) ∈ relIn := by + intro s + rcases or_not (p := ∃ witOut, ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut) + with ⟨witOut, hw⟩ | h + · exact ⟨mapWitInv s witOut, fun _ => hRel s.1 s.2 witOut hw⟩ + · exact ⟨Nonempty.some inferInstance, fun hex => absurd hex h⟩ refine OracleVerifier.coordinateWiseSpecialSound_of_isEmpty_challengeIdx init impl D (oracleVerifier oSpec mapStmt embedIdx hEq) relIn relOut - (fun s _ => if h : ∃ witOut, - ((mapStmt s.1, mapOStmt embedIdx hEq s.2), witOut) ∈ relOut - then mapWitInv s h.choose else Classical.ofNonempty) ?_ + (fun s _ => (hpick s).choose) ?_ rintro ⟨stmt, oStmt⟩ tr hAcc have hlang : (mapStmt stmt, mapOStmt embedIdx hEq oStmt) ∈ relOut.language := Verifier.mem_of_pure_accepting init impl (oracleVerifier oSpec mapStmt embedIdx hEq).toVerifier ⟨stmt, oStmt⟩ tr relOut.language _ (oracleVerifier_toVerifier_run (oSpec := oSpec)) hAcc - have hex : ∃ witOut, ((mapStmt stmt, mapOStmt embedIdx hEq oStmt), witOut) ∈ relOut := - (Set.mem_language_iff _ _).1 hlang - simp only [dif_pos hex] - exact hRel stmt oStmt hex.choose hex.choose_spec + exact (hpick (stmt, oStmt)).choose_spec ((Set.mem_language_iff _ _).1 hlang) end OracleReduction From f6fbf034f6599fb84783fd45734ab78df7dd2ad8 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Thu, 9 Jul 2026 20:14:44 +0200 Subject: [PATCH 05/21] CWSSPackage for (more) readble CWSS composition --- ArkLib.lean | 1 + .../Commitments/Functional/Hachi/Basic.lean | 293 ++++++---- .../Functional/Hachi/GadgetNorms.lean | 45 +- .../Functional/Hachi/PolynomialEvalSplit.lean | 1 + .../PolyEvalReduction.lean | 32 +- .../Hachi/PolynomialQuadraticEq/QuadEval.lean | 518 ++++++------------ .../QuadEvalGadgets.lean | 115 +--- .../CyclotomicRing/NormBounds/Basic.lean | 20 +- .../NoChallenge.lean | 1 + .../Package.lean | 106 ++++ .../SingleRound.lean | 168 +++--- docs/skills/make-pr-ready.md | 13 +- docs/wiki/repo-map.md | 10 +- 13 files changed, 601 insertions(+), 722 deletions(-) create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean diff --git a/ArkLib.lean b/ArkLib.lean index 1b286b27d2..8fe69ab35d 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -197,6 +197,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound import ArkLib.OracleReduction.Security.Implications diff --git a/ArkLib/Commitments/Functional/Hachi/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Basic.lean index 81fb32c6a0..928eddfe51 100644 --- a/ArkLib/Commitments/Functional/Hachi/Basic.lean +++ b/ArkLib/Commitments/Functional/Hachi/Basic.lean @@ -8,40 +8,88 @@ import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! # Hachi as a Functional Commitment — the composition home This is the designated home of Hachi [NOZ26] as a **functional commitment** and of the growing -n-ary composition of its subprotocols. Right now the composed verifier chains exactly two factors: +n-ary composition of its subprotocols. Each subprotocol is formalized in its own file and exported +as a coordinate-wise-special-sound (CWSS) `CWSSPackage`; 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 `▷`. -* the zero-round **bridge** (`PolyEvalReduction.bridgeVerifier`), which reinterprets a - `CMlPolynomial`-level statement as a `QuadEvalStatement` (monomial-basis instantiation of the - Eq. (12) bases), and -* Hachi's two-round **`QuadEval`** reduction (Lemma 8). +Right now the finished core chains exactly two links — the polynomial-level bridge and `QuadEval` +(`evalChain = bridgePackage ▷ quadEvalPackage`, certificate `eval_coordinateWiseSpecialSound`). The +remaining §3/§4.3+/§4.5 subprotocols are placeholders (see the `TODO` block); each will land as one +more `CWSSPackage` `▷`-appended into the chain. -`evalVerifier` is their `Verifier.append`; `hachi_eval_coordinateWiseSpecialSound` is the -composed coordinate-wise special soundness — sorry-free — obtained from -`Verifier.append_coordinateWiseSpecialSound` (the left factor is `IsPure`, so `hV₁` is `rfl`; -statement chaining is definitional). The composed structure is -`CWSSStructure.ofIsEmpty.append foldStructure`; the input relation is the polynomial-level -`relPolyEval` and the output relation is Hachi Eq. (20) (`relOut`). +## 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/§4.1) — `Gadget`, `GadgetNorms`. +* **Inner-outer Ajtai commitment** + weak binding (§4.1) — + `InnerOuter/{Scheme, Correctness, Security, Arithmetic}`. +* **Multilinear evaluation as a matrix–vector product** (§4) — `PolynomialEvalSplit`. +* **`QuadEval` gadget algebra** (§4.2, Figure 3) — `PolynomialQuadraticEq/QuadEvalGadgets`. +* **`QuadEval` reduction** (§4.2, Lemma 8) — `PolynomialQuadraticEq/QuadEval`, exported as + `quadEvalPackage`. +* **Polynomial-level bridge** (§4.2) — `PolynomialQuadraticEq/PolyEvalReduction`, exported as + `bridgePackage`. +* **Single-round CWSS tree navigation** *(generic)* — + `OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound`. +* **`CWSSPackage` + the `▷` chain operator** *(generic)* — + `OracleReduction/Security/CoordinateWiseSpecialSoundness/Package`. + +## The composed verifier chain + +Top-to-bottom is the composition order (each `▷` is one `CWSSPackage`). The `═══` band is the +finished core (`evalChain`); everything else is a placeholder for a future package: + +```text + §3.2/§4.5 partial-evaluations head ── planned (pure, 1 msg) + │ ▷ + ▼ + §3.1 ring-switch packing head ── planned (guarded, 1 msg) + │ ▷ + ▼ + σ₋₁ statement adapter ── planned (0-round ReduceClaim) + │ ▷ + ═══════════════════════ evalChain (finished core) ═══════════════════════ + ▼ + bridge PolyEvalStatement ⇒ QuadEval.relIn bridgePackage (§4.2, 0-round) + │ ▷ + ▼ + QuadEval QuadEval.relIn ⇒ Eq.(20) relOut quadEvalPackage (§4.2, Lemma 8) + ══════════════════════════════════════════════════════════════════════════ + │ ▷ + ▼ + §4.3 Eq.(20) ⇒ R^lin ⇒ HMZ25 lift ⇒ + zero-check rounds ⇒ sumcheck ⇒ final eval ── planned (Lemmas 9–11) + │ ▷ + ▼ + §4.5 recursion handoff ⇒ next iteration ── planned (guarded) +``` ## Growing the composition -Each further §4.3+ subprotocol is appended with its own CWSS proof; any shape mismatch between -`relOut_i` and `relIn_{i+1}` gets its own zero-round `ReduceClaim` adapter (same recipe as the -bridge). Once ≥3 factors exist, upgrade the binary `Verifier.append` to `Verifier.seqCompose` + -`seqCompose_coordinateWiseSpecialSound`: every factor is `IsPure` and `seqCompose_succ_eq_append` -is `rfl`, so no rework of the existing proof is needed. The `𝔽_{q^k}` ring-switch entry point -(§4.1) is a future zero-round head adapter placed in front of `relPolyEval`, built by the same -recipe as the bridge. +Each further subprotocol is exported from its file as a `CWSSPackage` and `▷`-appended here; 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`). Guarded subprotocols (the §3.1 head, the sumcheck +rounds, the final-eval and §4.5 handoff — those 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` (every finished factor is +`IsPure` and `seqCompose_succ_eq_append` is `rfl`, so no existing proof is reworked). ## 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] +* [Lyubashevsky, V., and Seiler, G., *Short, Invertible Elements in Partially Splitting + Cyclotomic Rings and Applications to Lattice-Based Zero-Knowledge Proofs*][LS18] -/ namespace ArkLib.Lattices.Ajtai.InnerOuter @@ -55,47 +103,50 @@ section Composition 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 ι} {ω : ℕ} +variable {σ : Type} -/-- The **composed evaluation verifier**: the zero-round polynomial-level bridge -(`bridgeVerifier`) followed by Hachi's two-round `QuadEval` reduction (`verifier`). Stated over the -appended protocol spec `!p[] ++ₚ pSpec …` (its length index `0 + 2` is defeq to `2`, but the -vappend contents are not syntactically the bare two-round spec, so we keep the appended form). -/ -def evalVerifier : - Verifier oSpec +/-- **The composed evaluation reduction** (Hachi [NOZ26, §4.2, Figure 3], `Rq`-level): the bridge +package (link 3) chained with the `QuadEval` package (link 4) via the `CWSSPackage` operator `▷`. +Both packages are defined next to their CWSS theorems in the component files (`bridgePackage` in +`PolyEvalReduction`, `quadEvalPackage` in `QuadEval`); 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 §3/§4.3+ +subprotocol is one more package `▷`-appended here (see the module header). -/ +def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + CWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows - × CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows × + CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) ((!p[] : ProtocolSpec 0) ++ₚ pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := - (bridgeVerifier (oSpec := oSpec) 𝓜(q, α)).append (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α)) - -/-- **Hachi evaluation reduction — coordinate-wise special soundness of the composed protocol -(Hachi §4.2/Figure 3, `Rq`-level), sorry-free.** The composed `evalVerifier` (bridge ⧺ QuadEval) is -coordinate-wise special sound for the appended structure `ofIsEmpty.append foldStructure`, reducing -the polynomial-level input relation `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or -MSIS(D)) to Hachi Eq. (20) (`relOut`). Pinned to `𝓜(q, α)` with the [LS18] hypotheses exactly as -`quadEval_coordinateWiseSpecialSound`. Assembled by `Verifier.append_coordinateWiseSpecialSound`: -the bridge's CWSS (`bridge_coordinateWiseSpecialSound`, any `D`) composes with QuadEval's Lemma 8 -(`quadEval_coordinateWiseSpecialSound'`); the left factor is pure so `hV₁` is `rfl` and the middle -relation is QuadEval's `relIn`. -/ -theorem hachi_eval_coordinateWiseSpecialSound {σ : Type} - (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - (evalVerifier (oSpec := oSpec) (ω := ω) (q := q) (α := α) (innerRows := innerRows) - (messageDigits := messageDigits) (outerRows := outerRows) (innerDigits := innerDigits) - (dRows := dRows) (m := m) (r := r)).coordinateWiseSpecialSound init impl + bridgePackage 𝓜(q, α) init impl (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) ▷ + quadEvalPackage init impl hq5 hκ hτ + +/-- **Hachi evaluation reduction — coordinate-wise special soundness (Hachi [NOZ26, §4.2, +Figure 3], `Rq`-level), `sorry`-free.** This is the certificate carried by `evalChain`: the composed +verifier (bridge ⧺ `QuadEval`) is CWSS for `ofIsEmpty.append foldStructure`, reducing the +polynomial-level `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or MSIS(D)) to Hachi +Eq. (20) (`relOut`), pinned to `𝓜(q, α)` with the [LS18] hypotheses of +`quadEval_coordinateWiseSpecialSound`. -/ +theorem eval_coordinateWiseSpecialSound (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} + (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + ((bridgeVerifier (oSpec := oSpec) (innerRows := innerRows) (messageDigits := messageDigits) + (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r) + 𝓜(q, α)).append + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α))).coordinateWiseSpecialSound init impl (CWSSStructure.ofIsEmpty.append (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r))) - (relPolyEval 𝓜(q, α) ((b : ZMod q)) + (relPolyEval 𝓜(q, α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := by - unfold evalVerifier - exact Verifier.append_coordinateWiseSpecialSound init impl _ _ _ _ - (fun s _ => toQuadEvalStatement 𝓜(q, α) s) (fun _ _ => rfl) - (bridge_coordinateWiseSpecialSound 𝓜(q, α) init impl CWSSStructure.ofIsEmpty ((b : ZMod q)) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (quadEval_coordinateWiseSpecialSound' init impl hq5 hκ hτ) + (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := + (evalChain init impl hq5 hκ hτ).isCWSS end Composition @@ -103,117 +154,121 @@ end Composition Hachi as a `Commitment.Scheme` (`ArkLib.Commitments.Functional.Basic`) over the multilinear data `CMlPolynomial (Rq 𝓜(q,α)) (r + m)`. The eval-oracle interface and the honest committer operations -(`hachiKeygen` / `hachiCommit`) are real; the opening `Proof` is deferred (see the `TODO` block). -/ +(`keygen` / `commit`) are real; the opening `Proof` is deferred (see the `TODO` block). -/ section FunctionalCommitment variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} -variable {innerRows messageDigits outerRows innerDigits dRows m r : Nat} {ω : ℕ} - -/-- The **evaluation oracle** on a committed multilinear polynomial: a query is a split evaluation -point `(xl, xh)` (the split matches `PolyEvalStatement`; the unsplit view `xl ++ xh` is a later -cosmetic lens), and the answer is `f(xl ++ xh)`. This is the `OracleInterface` that makes -`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` the `Data` of a functional commitment (`Commitment.Scheme`); -`toOC` follows `OracleContext.ofFunction`. -/ -instance evalOracleInterface : - OracleInterface (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) where - Query := Vector (Rq 𝓜(q, α)) r × Vector (Rq 𝓜(q, α)) m +variable {innerRows outerRows dRows m r : Nat} {ω : ℕ} + +/-- The **multilinear evaluation oracle** on a committed `n`-variable multilinear polynomial: a +query is an evaluation point `x : Vector (Rq 𝓜(q,α)) n` and the answer is `f x`. This is the +`OracleInterface` that makes `CMlPolynomial (Rq 𝓜(q,α)) n` the `Data` of a functional commitment +(`Commitment.Scheme`); `toOC` follows `OracleContext.ofFunction`. -/ +instance multilinearEvalOracleInterface {n : ℕ} : + OracleInterface (CMlPolynomial (Rq 𝓜(q, α)) n) where + Query := Vector (Rq 𝓜(q, α)) n toOC := - { spec := (Vector (Rq 𝓜(q, α)) r × Vector (Rq 𝓜(q, α)) m) →ₒ Rq 𝓜(q, α) - impl := fun p => do return CMlPolynomial.eval (← read) (p.1 ++ p.2) } + { spec := Vector (Rq 𝓜(q, α)) n →ₒ Rq 𝓜(q, α) + impl := fun p => do return CMlPolynomial.eval (← read) p } + +-- `b > 1` is the gadget base used for **all** decompositions. Faithful to Hachi [NOZ26] §2.1/§4.1, +-- every coefficient is written in `δ := ⌈log_b q⌉ = Nat.clog b q` base-`b` digits — a single `δ` +-- shared by the message gadget `G⁻¹_{2ᵐ}` and the inner gadget `G⁻¹_{n_A}` — so both digit counts +-- are `Nat.clog b q` (and `q ≤ bᵟ` holds by `Nat.le_pow_clog`). +variable (b : ℕ) variable - [SampleableType (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * messageDigits))] - [SampleableType (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * innerDigits)))] - [SampleableType (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * messageDigits))] + [SampleableType (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q))] + [SampleableType (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q)))] + [SampleableType (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q))] /-- Honest **key generation**: sample the inner/outer/short Ajtai matrices `(A, B, D)` uniformly (matching `InnerOuter.commitmentScheme.setup`, extended with the Hachi short-commitment matrix `D`, Eq. (16)) and return the resulting `PublicParamsD` as both the committer and the verifier key. -/ -def hachiKeygen : +def keygen : ProbComp - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) dRows × - Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows) := do - let A ← $ᵗ (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * messageDigits)) - let B ← $ᵗ (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * innerDigits))) - let D ← $ᵗ (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * messageDigits)) + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) + (Nat.clog b q) dRows) := do + let A ← $ᵗ (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q)) + let B ← $ᵗ (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q))) + let D ← $ᵗ (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q)) let pp : - Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) dRows := { innerMatrix := A, outerMatrix := B, dMatrix := D } pure (pp, pp) -/-- Honest **commitment**: reshape the polynomial into its `2^r × 2^m` coefficient matrix -(`Hachi.toMatrix`, which is definitionally a `Message 𝓜(q,α) (2^m) (2^r)`), gadget-decompose it into -the per-block messages/inner decompositions (`generateDecomps` with `Decomposition.ofDigits`), and -outer-commit (`commitWithDecomps`). Deterministic; the decommitment is the `Decomp` data. -/ -def hachiCommit [DecidableEq (ZMod q)] {base : ZMod q} - (ddMsg : DigitDecomposition base messageDigits) - (ddInner : DigitDecomposition base innerDigits) - (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows) +/-- Honest **commitment** to a multilinear polynomial `p`: reshape it into its `2^r × 2^m` +coefficient matrix (`Hachi.toMatrix`, definitionally a `Message 𝓜(q,α) (2^m) (2^r)`), +gadget-decompose it into the per-block messages/inner decompositions with the **canonical base-`b` +digit decomposition** `zmodDigitDecomposition` at the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` +(the `q ≤ bᵟ` obligation is `Nat.le_pow_clog`), and outer-commit (`commitWithDecomps`). +Deterministic; the decommitment is the `Decomp` data. -/ +def commit [DecidableEq (ZMod q)] (hb : 1 < b) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) + (Nat.clog b q) dRows) (p : CMlPolynomial (Rq 𝓜(q, α)) (r + m)) : Commitment 𝓜(q, α) outerRows × - Decomp 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits := - let decomps := generateDecomps 𝓜(q, α) (Decomposition.ofDigits 𝓜(q, α) ddMsg ddInner) + Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q) := + let decomps := generateDecomps 𝓜(q, α) + (Decomposition.ofDigits 𝓜(q, α) + (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q)) + (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q))) pp.toPublicParams (Hachi.toMatrix p) (commitWithDecomps 𝓜(q, α) pp.toPublicParams decomps, decomps) /-- **Hachi as a functional commitment** (`Commitment.Scheme`) over the multilinear data -`CMlPolynomial (Rq 𝓜(q,α)) (r + m)`, with the eval oracle `evalOracleInterface`, honest -`hachiKeygen` / `hachiCommit`, committer and verifier key `PublicParamsD`, and decommitment -`Decomp`. +`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable polynomial, with the `r`/`m` split +feeding the outer/inner gadgets. It commits a polynomial directly (no caller-supplied +decompositions): the honest `commit` uses the canonical base-`b` gadget decomposition at the +paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` (Hachi [NOZ26] §2.1/§4.1), shared by the message and +inner gadgets — so `messageDigits`/`innerDigits` are not free parameters. The only parameters are +the gadget base `b` and `1 < b`; the scheme carries the eval oracle +`multilinearEvalOracleInterface`, honest `keygen` / `commit`, committer and verifier key +`PublicParamsD`, and decommitment `Decomp`. The `opening` field — the complete opening `Proof` (a `Reduction … Bool Unit`) — is **provisional** (`sorry`): its boolean verdict is Hachi Eq. (20) membership (`relOut`), which depends on the never- sent triple `(ŵ, t̂, ẑ)`; it becomes verifier-computable only after the remaining §4.3+ subprotocols -(and their honest-prover layer, `QuadEval.computeV`/`computeResp`, §9.3) are formalized. Everything -else here is real. The stated `pSpec` is the composed evaluation protocol spec (`!p[] ++ₚ pSpec …`), -i.e. the shape the finished opening will run over — see the `TODO` block. -/ -noncomputable def hachiFC [DecidableEq (ZMod q)] {base : ZMod q} - (ddMsg : DigitDecomposition base messageDigits) - (ddInner : DigitDecomposition base innerDigits) : - _root_.Commitment.Scheme unifSpec +and their honest-prover layer (`QuadEval.prover`'s `computeV`/`computeResp`) are formalized. +Everything else here is real. The stated `pSpec` is the composed evaluation protocol spec +(`!p[] ++ₚ pSpec …`), i.e. the shape the finished opening will run over — see the `TODO` block. -/ +def hachi [DecidableEq (ZMod q)] (hb : 1 < b) : + Commitment.Scheme unifSpec (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) (Commitment 𝓜(q, α) outerRows) - (Decomp 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + (Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q)) + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) dRows) - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) dRows) ((!p[] : ProtocolSpec 0) ++ₚ pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where - keygen := hachiKeygen - commit := fun pp p => pure (hachiCommit ddMsg ddInner pp p) + keygen := keygen b + commit := fun pp p => pure (commit b hb pp p) opening := sorry end FunctionalCommitment /-! ## TODO — remaining work toward the full Hachi functional commitment -The composition (`evalVerifier` / `hachi_eval_coordinateWiseSpecialSound`) is the `Rq`-level -CWSS core; the FC scaffolding (`evalOracleInterface`, `hachiKeygen`, `hachiCommit`, `hachiFC`) is -the honest committer side. Still open, in rough dependency order: +The composition (`evalChain` / `eval_coordinateWiseSpecialSound`) is the `Rq`-level CWSS core; the +FC scaffolding (`multilinearEvalOracleInterface`, `keygen`, `commit`, `fc`) is the honest committer +side. Still open, in rough dependency order: -* **Remaining §4.3+ subprotocols.** Each is appended to `evalVerifier` with its own CWSS proof, - bridged by a zero-round `ReduceClaim` adapter when `relOut_i`/`relIn_{i+1}` disagree in shape. - Once ≥3 factors accumulate, migrate the binary `Verifier.append` to `Verifier.seqCompose` + +* **Remaining §3/§4.3+/§4.5 subprotocols.** Each is exported from its file as a `CWSSPackage` and + `▷`-appended into `evalChain`, bridged by 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`). -* **Completeness / honest-prover layer** (§9.3): instantiate `QuadEval.prover`'s `computeV` / - `computeResp` from the `QuadEvalGadgets` carrier/decomposition definitions, discharging - `Commitment.perfectCorrectness` for `hachiFC` — this is what materializes `hachiFC.opening` - (currently `sorry`). -* **CWSS → knowledge-extraction bridge** and the cross-run step - (`outputToModuleSIS_valid_of_verified`): turn the coordinate-wise special soundness into the - `Commitment.extractability` / `functionBinding` statements. -* **`𝔽_{q^k}` ring-switch head** (§4.1): a zero-round head adapter in front of `relPolyEval` - lifting the `Rq`-level statement to the paper's headline multilinear-over-`𝔽_{q^k}` claim. -* **`ShortChallenge` instances.** Stating `Commitment.perfectCorrectness` / `extractability` needs - `[[pSpec.Challenge]ₒ.Fintype]` / `VCVCompatible` / `SampleableType` for `ShortChallenge 𝓜(q,α) ω` - (a subtype of `Rq`); the requisite `Fintype`/`DecidablePred` work does not yet exist. +* **Completeness / honest-prover layer**: instantiate `QuadEval.prover`'s `computeV` / + `computeResp` from the `QuadEvalGadgets` carrier/decomposition definitions + (`carrierCommit`, `zDecomp`), discharging `Commitment.perfectCorrectness` for `fc` — + this is what materializes `fc.opening` (currently `sorry`). -/ end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean b/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean index 484a30096f..eb93906eb2 100644 --- a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean +++ b/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean @@ -151,18 +151,18 @@ digit decompositions. -/ theorem gadgetMul_zmod_coeff_natAbs_le {b rows digits : ℕ} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v : PolyVec (Rq Φ) (rows * digits)) (hv : vecLInftyNorm Φ v ≤ γ) (i : Fin rows) {k : ℕ} (hk : k < Φ.φ.natDegree) : - ((gadgetMul Φ ((b : ZMod q)) v i).1.coeff k).valMinAbs.natAbs + ((gadgetMul Φ (b : ZMod q) v i).1.coeff k).valMinAbs.natAbs ≤ (∑ u ∈ Finset.range digits, b ^ u) * γ := by -- the coefficient of the gadget product is the digit-weighted sum of block coefficients - have hcoeff : (gadgetMul Φ ((b : ZMod q)) v i).1.coeff k - = ∑ e : Fin digits, ((b : ZMod q)) ^ (e : ℕ) * (v (finProdFinEquiv (i, e))).1.coeff k := by - rw [gadgetMul_apply Φ ((b : ZMod q)) hd v i, ← Rq.coeffHom_apply Φ k, map_sum] + have hcoeff : (gadgetMul Φ (b : ZMod q) v i).1.coeff k + = ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * (v (finProdFinEquiv (i, e))).1.coeff k := by + rw [gadgetMul_apply Φ (b : ZMod q) hd v i, ← Rq.coeffHom_apply Φ k, map_sum] simp only [Rq.coeffHom_apply] exact Finset.sum_congr rfl fun e _ => constRq_mul_coeff Φ h1 _ _ k -- the explicit integer representative of that coefficient have hrep : ((∑ e : Fin digits, (b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs : ℤ) : ZMod q) - = (gadgetMul Φ ((b : ZMod q)) v i).1.coeff k := by + = (gadgetMul Φ (b : ZMod q) v i).1.coeff k := by rw [hcoeff, Int.cast_sum] refine Finset.sum_congr rfl fun e _ => ?_ rw [Int.cast_mul, Int.cast_pow, Int.cast_natCast, ZMod.coe_valMinAbs] @@ -177,7 +177,7 @@ theorem gadgetMul_zmod_coeff_natAbs_le {b rows digits : ℕ} (hd : 0 < digits) Finset.le_sup (f := fun j => Rq.lInftyNorm Φ (v j)) (Finset.mem_univ _) _ ≤ γ := hv -- minimality of the centered representative + triangle over the integer representative - calc ((gadgetMul Φ ((b : ZMod q)) v i).1.coeff k).valMinAbs.natAbs + calc ((gadgetMul Φ (b : ZMod q) v i).1.coeff k).valMinAbs.natAbs ≤ (∑ e : Fin digits, (b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs).natAbs := valMinAbs_natAbs_le _ hrep @@ -197,34 +197,34 @@ theorem gadgetMul_zmod_coeff_natAbs_le {b rows digits : ℕ} (hd : 0 < digits) theorem gadgetMul_zmod_lInftyNorm_le {b rows digits : ℕ} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v : PolyVec (Rq Φ) (rows * digits)) (hv : vecLInftyNorm Φ v ≤ γ) (i : Fin rows) : - Rq.lInftyNorm Φ (gadgetMul Φ ((b : ZMod q)) v i) + Rq.lInftyNorm Φ (gadgetMul Φ (b : ZMod q) v i) ≤ (∑ u ∈ Finset.range digits, b ^ u) * γ := by unfold Rq.lInftyNorm exact Finset.sup_le fun k hkmem => gadgetMul_zmod_coeff_natAbs_le Φ hd h1 v hv i (Finset.mem_range.mp hkmem) -/-- **Deliverable 3(a): `ℓ∞` growth of the gadget recomposition.** +/-- **`ℓ∞` growth of the gadget recomposition.** `‖G_{b,rows} ·ᵥ v‖∞ ≤ (∑_{u gadgetMul_zmod_lInftyNorm_le Φ hd h1 v hv i -/-- **Deliverable 3(c): the J-recomposition `ℓ₂²` chain.** From the range check `‖ẑ‖∞ ≤ γ` +/-- **The `J`-recomposition `ℓ₂²` chain.** From the range check `‖ẑ‖∞ ≤ γ` (Eq. (20)'s `ẑ ∈ S_b`, symmetric model), the recomposed `z = J·ẑ` satisfies -`‖z‖₂² ≤ zRecomposeL2SqBound γ b τ (deg φ) rows`. This is the derivation that replaces v1's -primitive `‖z‖₂² ≤ B_z` verifier check (see the plan's change ledger, §10). -/ +`‖z‖₂² ≤ zRecomposeL2SqBound γ b τ (deg φ) rows` — no primitive `‖z‖₂²` verifier check +is needed. -/ theorem gadgetMul_zmod_vecL2NormSq_le {b rows digits : ℕ} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v : PolyVec (Rq Φ) (rows * digits)) (hv : vecLInftyNorm Φ v ≤ γ) : - ‖gadgetMul Φ ((b : ZMod q)) v‖₂² + ‖gadgetMul Φ (b : ZMod q) v‖₂² ≤ zRecomposeL2SqBound γ b digits Φ.φ.natDegree rows := by - calc vecL2NormSq Φ (gadgetMul Φ ((b : ZMod q)) v) - ≤ rows * (Φ.φ.natDegree * (vecLInftyNorm Φ (gadgetMul Φ ((b : ZMod q)) v)) ^ 2) := + calc vecL2NormSq Φ (gadgetMul Φ (b : ZMod q) v) + ≤ rows * (Φ.φ.natDegree * (vecLInftyNorm Φ (gadgetMul Φ (b : ZMod q) v)) ^ 2) := vecL2NormSq_le_card_mul_lInftyNorm_sq Φ _ _ ≤ rows * (Φ.φ.natDegree * ((∑ u ∈ Finset.range digits, b ^ u) * γ) ^ 2) := Nat.mul_le_mul_left _ (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left @@ -237,12 +237,12 @@ difference is `ℓ₂²`-bounded by `subL2NormSqBound (zRecomposeL2SqBound …) theorem gadgetMul_zmod_sub_l2NormSq_le {b rows digits : ℕ} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} (v w : PolyVec (Rq Φ) (rows * digits)) (hv : vecLInftyNorm Φ v ≤ γ) (hw : vecLInftyNorm Φ w ≤ γ) : - ‖gadgetMul Φ ((b : ZMod q)) v - gadgetMul Φ ((b : ZMod q)) w‖₂² + ‖gadgetMul Φ (b : ZMod q) v - gadgetMul Φ (b : ZMod q) w‖₂² ≤ subL2NormSqBound (zRecomposeL2SqBound γ b digits Φ.φ.natDegree rows) := sub_l2NormSq_le Φ _ _ (gadgetMul_zmod_vecL2NormSq_le Φ hd h1 v hv) (gadgetMul_zmod_vecL2NormSq_le Φ hd h1 w hw) -/-! ## Deliverable 4: the constants of Hachi's polynomial-evaluation reduction (`B_z`, `βSq`) -/ +/-! ## The constants of Hachi's polynomial-evaluation reduction (`B_z`, `βSq`) -/ /-- **The reduction's derived `B_z`** (Hachi Lemma 8) — the `ℓ₂²` bound on `z = J_{2^m}·ẑ` that follows from @@ -262,17 +262,6 @@ def quadEvalZL2SqBound (γ b τ d m δ : ℕ) : ℕ := zRecomposeL2SqBound γ b on the extracted `c̄ⱼ •ᵥ sⱼ = z_sib − z_central` fed to `VerifiedBlock.scaled_short`. -/ def quadEvalBetaSq (γ b τ d m δ : ℕ) : ℕ := subL2NormSqBound (quadEvalZL2SqBound γ b τ d m δ) -/-- `βSq = 4·B_z`, definitionally. -/ -theorem quadEvalBetaSq_eq_four_mul (γ b τ d m δ : ℕ) : - quadEvalBetaSq γ b τ d m δ = 4 * quadEvalZL2SqBound γ b τ d m δ := rfl - -/-- The plan-facing instantiation: a range-checked `ẑ : Rq^{(2^m·δ)·τ}` recomposes to -`z = J·ẑ` with `‖z‖₂² ≤ B_z = quadEvalZL2SqBound γ b τ (deg φ) m δ`. -/ -theorem quadEvalZ_l2NormSq_le {b m δ τ : ℕ} (hτ : 0 < τ) (h1 : 1 ≤ Φ.φ.natDegree) {γ : ℕ} - (zhat : PolyVec (Rq Φ) ((2 ^ m * δ) * τ)) (hγ : vecLInftyNorm Φ zhat ≤ γ) : - ‖gadgetMul Φ ((b : ZMod q)) zhat‖₂² ≤ quadEvalZL2SqBound γ b τ Φ.φ.natDegree m δ := - gadgetMul_zmod_vecL2NormSq_le Φ hτ h1 zhat hγ - end ZModGadgetRecomposeNorms end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean index 5b3a48aa3a..60344f2f6c 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean @@ -64,6 +64,7 @@ variable {R : Type*} {nl nh : Nat} def splitEquiv (nl nh : Nat) : Fin (2 ^ nh) × Fin (2 ^ nl) ≃ Fin (2 ^ (nl + nh)) := finProdFinEquiv.trans (finCongr (by rw [Nat.mul_comm, ← pow_add])) +/-- The underlying value of `splitEquiv nl nh (x, y)` is `y + 2 ^ nl * x`. -/ @[simp] theorem splitEquiv_val (x : Fin (2 ^ nh)) (y : Fin (2 ^ nl)) : (splitEquiv nl nh (x, y)).val = y.val + 2 ^ nl * x.val := rfl diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean index b36cb325e9..210fbee64a 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/PolyEvalReduction.lean @@ -6,6 +6,8 @@ Authors: Tobias Rothmann import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEval import ArkLib.Commitments.Functional.Hachi.PolynomialEvalSplit import ArkLib.ProofSystem.Component.ReduceClaim +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge /-! # Polynomial-level bridge into Hachi's `QuadEval` reduction @@ -61,7 +63,7 @@ namespace ArkLib.Lattices.Ajtai.InnerOuter open CompPoly ArkLib.Lattices.CyclotomicModulus open WeakBinding -open OracleComp OracleSpec ProtocolSpec CoordinateWise.SingleRound +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound /-! ## The polynomial-level statement and the bridge map (any coefficient field `R`) -/ @@ -191,12 +193,30 @@ theorem bridge_coordinateWiseSpecialSound {σ : Type} (bridgeVerifier (oSpec := oSpec) Φ (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)).coordinateWiseSpecialSound init impl D - (relPolyEval Φ base βSq γ κ) (relIn Φ base βSq γ κ) := by - refine ReduceClaim.verifier_coordinateWiseSpecialSound + (relPolyEval Φ base βSq γ κ) (relIn Φ base βSq γ κ) := + ReduceClaim.verifier_coordinateWiseSpecialSound (relIn := relPolyEval Φ base βSq γ κ) (relOut := relIn Φ base βSq γ κ) - (mapWitInv := fun _ w => w) (D := D) ?_ - intro s w h - exact mem_relPolyEval_of_relIn Φ base βSq γ κ s w h + (mapWitInv := fun _ w => w) (D := D) + (mem_relPolyEval_of_relIn Φ base βSq γ κ) + +/-- **The polynomial-level bridge as a `CWSSPackage`** (Hachi [NOZ26, §4.2]): the zero-round +`ReduceClaim` head `bridgeVerifier` bundled with the empty challenge structure (`ofIsEmpty`) and its +CWSS certificate `bridge_coordinateWiseSpecialSound`, ready to be `▷`-composed before `QuadEval`. +Its `relOut` is `QuadEval`'s input relation `relIn`, so the seam with `quadEvalPackage` is `rfl`. -/ +def bridgePackage {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (base : ZMod q) (βSq γ κ : ℕ) : + CWSSPackage init impl + (PolyEvalStatement Φ innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + (QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (!p[] : ProtocolSpec 0) where + verifier := bridgeVerifier (oSpec := oSpec) Φ + struct := CWSSStructure.ofIsEmpty + relIn := relPolyEval Φ base βSq γ κ + relOut := relIn Φ base βSq γ κ + isPure := ⟨fun stmt _ => toQuadEvalStatement Φ stmt, fun _ _ => rfl⟩ + isCWSS := bridge_coordinateWiseSpecialSound Φ init impl CWSSStructure.ofIsEmpty base βSq γ κ end ZModDefs diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean index 4e8765d59d..ef4fef804a 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean @@ -6,6 +6,7 @@ Authors: Tobias Rothmann import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEvalGadgets import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! # Hachi polynomial-evaluation reduction (QuadEval) — coordinate-wise special soundness @@ -24,44 +25,37 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRoun The reduction is modeled as the two-round `pSpec ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin 2ʳ → C]⟩`: round 0 (P→V) sends the short commitment `v = D ŵ`; round 1 (V→P) is the challenge vector. The triple `(ŵ, t̂, ẑ)` is the - **output witness** (`QuadEvalResponse`, never sent — §4.3 proves knowledge of it instead), so the - verifier is a pure pass-through (`IsPure` by `rfl`) and the extractor sources per-branch triples - from `relOut.language`. - - This module holds: - * the reduction's definitions and relations (`CarrierCom`, `QuadEvalStatement`, - `QuadEvalResponse`, `QuadEvalWitness`, `ShortChallenge`, `derivedMsgMatrix`/`evalConsistency`, - `dShort`, `relOut`, `relIn`); - * the two-transcript MSIS lemmas (`msisB_of_two_valid`, `msisD_of_two_valid`) and the - subtract-and-divide core (`inner_eq_of_chain`); - * the pure `verifier` + `instIsPure` and the `prover` skeleton; - * the extractor data (`extractedOpening`, `buildWitness`) and the extraction lemmas - (`verifiedOpening_of_star` = Sublemma 1, `evalConsistency_of_relOut_star` = Sublemma 2 — both - internal steps of Hachi Lemma 8's case (C), not paper lemmas — and `buildWitness_mem_relIn`), - and the top-level theorem `quadEval_coordinateWiseSpecialSound(')`. - - This module implements milestones **M3–M7** of - `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` (§9.5). It sits inside - `namespace ArkLib.Lattices.Ajtai.InnerOuter` (required: that namespace activates the scoped - `PolyVec`/`*ᵥ`/`•ᵥ`/`dot`/`splitForm`), with `open WeakBinding` (so `VerifiedOpening`/`outerShort` - resolve). Never `open ArkLib.Lattices` here (the `⬝ᵥ` token is ambiguous between - `Matrix.dotProduct` and `ArkLib.Lattices.dot`); spell `dot _ _`. + **output witness** (`QuadEvalResponse`, never sent — §4.3 proves knowledge of it instead), so + the verifier is a pure pass-through and the extractor sources per-branch triples from + `relOut.language`. + + Contents: the reduction's types and relations (`QuadEvalStatement`, `QuadEvalResponse`, + `QuadEvalWitness`, `ShortChallenge`, `relOut` = Eq. (20) + range checks, `relIn` = weak opening + ∨ MSIS(B) ∨ MSIS(D)); the pure `verifier` and the honest `prover` skeleton; the extractor + (`extractedOpening`, `buildWitness`) with its correctness lemmas; and the top-level theorem + `quadEval_coordinateWiseSpecialSound`. + + The file sits inside `namespace ArkLib.Lattices.Ajtai.InnerOuter` (required: that namespace + activates the scoped `PolyVec`/`*ᵥ`/`•ᵥ`/`dot`/`splitForm`), with `open WeakBinding` (so + `VerifiedOpening`/`outerShort` resolve). Never `open ArkLib.Lattices` here (the `⬝ᵥ` token is + ambiguous between `Matrix.dotProduct` and `ArkLib.Lattices.dot`); spell `dot _ _`. ## 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] + * [Lyubashevsky, V., and Seiler, G., *Short, Invertible Elements in Partially Splitting + Cyclotomic Rings and Applications to Lattice-Based Zero-Knowledge Proofs*][LS18] -/ --- ================= PolynomialQuadraticEq/QuadEval.lean — definitions layer ================= namespace ArkLib.Lattices.Ajtai.InnerOuter open CompPoly ArkLib.Lattices.CyclotomicModulus open WeakBinding -open OracleComp OracleSpec ProtocolSpec CoordinateWise.SingleRound +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound -/-! ## Generic definitions (any coefficient field `R`, mimicking `Scheme.lean`'s `Defs`) -/ +/-! ## Generic definitions (any coefficient field `R`) -/ section Defs @@ -100,6 +94,7 @@ structure QuadEvalResponse (Φ : CyclotomicModulus R) /-- `ẑ := J⁻¹(z)`, the decomposed masked opening (`τ = zDigits` digits). -/ zDec : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits) +/-- `QuadEvalResponse` is inhabited (the all-zero triple). -/ instance : Nonempty (QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits) := ⟨⟨fun _ => 0, fun _ _ => 0, fun _ => 0⟩⟩ @@ -115,6 +110,7 @@ inductive QuadEvalWitness (Φ : CyclotomicModulus R) /-- A Module-SIS solution for the short-commitment matrix `D`. -/ | msisD (z : ModuleSIS.Solution Φ (blocks * messageDigits)) +/-- `QuadEvalWitness` is inhabited (a trivial `msisB` witness). -/ instance : Nonempty (QuadEvalWitness Φ innerRows messageRows messageDigits blocks innerDigits) := ⟨.msisB (fun _ => 0)⟩ @@ -157,16 +153,11 @@ nonzero-slack input (`CoordEq` gives subtype-`≠` at the differing coordinate). theorem val_ne_of_ne {c c' : ShortChallenge Φ ω} (h : c ≠ c') : c.val ≠ c'.val := fun hval => h (Subtype.ext hval) -omit [NeZero q] [IsCyclotomic Φ] in -/-- `val` is injective (distinct Hachi §4.2 challenges have distinct underlying ring elements). -/ -theorem val_injective : Function.Injective (val : ShortChallenge Φ ω → Rq Φ) := - fun _ _ h => Subtype.ext h - end ShortChallenge end ShortChallenge -/-! ## Eval consistency (Eq. 15) — probe-compiled §9.3 block, kept verbatim -/ +/-! ## Eval consistency (Eq. 15) and the relations -/ section ZModDefs @@ -181,12 +172,6 @@ def derivedMsgMatrix (base : ZMod q) (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : PolyMatrix (Rq Φ) (2 ^ r) (2 ^ m) := fun i k => derivedMessage Φ base o.toDecomp i k -omit [NeZero q] in -/-- Row `i` of the Hachi Eq. (15) matrix `M` is the derived message block (definitional). -/ -@[simp] theorem derivedMsgMatrix_apply {base : ZMod q} - (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) (i : Fin (2 ^ r)) : - derivedMsgMatrix Φ base o i = derivedMessage Φ base o.toDecomp i := rfl - /-- Eq. (15): the derived messages of the weak opening evaluate to `y` under the split bilinear form (`splitForm`, argument order `b a` load-bearing). -/ def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec (Rq Φ) (2 ^ r)) @@ -194,30 +179,20 @@ def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec ( splitForm (derivedMsgMatrix Φ base o) b a = y omit [NeZero q] in -/-- The Hachi Eq. (15) quadratic form `bᵀ M a` expands to the block sum -`∑ᵢ bᵢ · ⟨a, G_{2^m} sᵢ⟩` (the per-block reading used by `evalConsistency_of_star`). -/ -theorem splitForm_derivedMsgMatrix_eq_sum (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) - (b : PolyVec (Rq Φ) (2 ^ r)) - (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : - splitForm (derivedMsgMatrix Φ base o) b a = - ∑ i, b i * dot a (derivedMessage Φ base o.toDecomp i) := by - rw [splitForm, dot_eq_sum]; refine Finset.sum_congr rfl (fun i _ => ?_) - rw [matVecMul_apply, derivedMsgMatrix_apply, dot_comm] - -omit [NeZero q] in -/-- **Sublemma 2 of the extraction** (an internal step of Hachi Lemma 8, case (C), establishing -Eq. (15)): from c3 (`dot b w = y`) and the c4-subtractions -(`wⱼ = dot a (derivedMessage o j)`), the extracted opening is eval-consistent. -/ +/-- Eq. (15) for the extracted opening (an internal step of Hachi Lemma 8, case (C)): from c3 +(`dot b w = y`) and the c4-subtractions (`wⱼ = dot a (derivedMessage o j)`), the extracted +opening is eval-consistent. -/ theorem evalConsistency_of_star (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec (Rq Φ) (2 ^ r)) (y : Rq Φ) (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) (w : PolyVec (Rq Φ) (2 ^ r)) (c3 : dot b w = y) (c4 : ∀ j, w j = dot a (derivedMessage Φ base o.toDecomp j)) : evalConsistency Φ base a b y o := by - unfold evalConsistency; rw [splitForm_derivedMsgMatrix_eq_sum, ← c3, dot_eq_sum] - refine Finset.sum_congr rfl (fun j _ => ?_); rw [c4 j] - -/-! ## `dShort` and the output/input relations -/ + unfold evalConsistency + rw [splitForm, ← c3, dot_eq_sum, dot_eq_sum] + refine Finset.sum_congr rfl (fun j _ => ?_) + rw [c4 j, matVecMul_apply, dot_comm] + rfl /-- Shortness predicate for the extracted `D`-kernel witness (Hachi Lemma 8, case (B)): the `D`-matrix analogue of `outerShort`, at `subLInftyNormBound γ = 2·γ`. -/ @@ -283,67 +258,31 @@ def relIn (base : ZMod q) (βSq γ κ : ℕ) : | (stmt, .msisD z) => ModuleSIS.relation Φ (dShort Φ γ) stmt.pp.dMatrix z = true } -/-! ## The two-transcript MSIS extraction lemmas (Hachi Lemma 8, cases (A)/(B)) -/ - -/-- **Hachi Lemma 8, case (A)**: two Eq.-(20)-valid responses (c2 + c6 facts) with different inner -decompositions yield a `B`-kernel Module-SIS solution. Clone of -`outer_relation_of_verified`'s tail. -/ -theorem msisB_of_two_valid {γ : ℕ} - {B : Simple.PublicParams Φ outerRows (blocks * (innerRows * innerDigits))} - {u : Commitment Φ outerRows} - {resp₁ resp₂ : - QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits} - (hu₁ : Simple.commit Φ B (PolyVec.flattenBlocks resp₁.innerDec) = u) - (hu₂ : Simple.commit Φ B (PolyVec.flattenBlocks resp₂.innerDec) = u) - (hγ₁ : vecLInftyNorm Φ (PolyVec.flattenBlocks resp₁.innerDec) ≤ γ) - (hγ₂ : vecLInftyNorm Φ (PolyVec.flattenBlocks resp₂.innerDec) ≤ γ) - (hne : PolyVec.flattenBlocks resp₁.innerDec ≠ PolyVec.flattenBlocks resp₂.innerDec) : - ModuleSIS.relation Φ (outerShort Φ γ) B - (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) = true := by - have hne0 : PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec ≠ 0 := - sub_ne_zero.mpr hne - have hshort : vecLInftyNorm Φ - (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) ≤ - subLInftyNormBound γ := - sub_lInftyNorm_le Φ _ _ hγ₁ hγ₂ - have heq : B *ᵥ PolyVec.flattenBlocks resp₁.innerDec = - B *ᵥ PolyVec.flattenBlocks resp₂.innerDec := by - simpa [Simple.commit] using hu₁.trans hu₂.symm - have hker : B *ᵥ - (PolyVec.flattenBlocks resp₁.innerDec - PolyVec.flattenBlocks resp₂.innerDec) = 0 := by - rw [matVecMul_sub]; exact sub_eq_zero.mpr heq - simp [ModuleSIS.relation, outerShort, hne0, hshort, hker] - -/-- **Hachi Lemma 8, case (B)**: two Eq.-(20)-valid responses (c1 + c6 facts, shared `v`) with -different carrier decompositions yield a `D`-kernel Module-SIS solution. -/ -theorem msisD_of_two_valid {γ : ℕ} - {D : Simple.PublicParams Φ dRows (blocks * messageDigits)} - {v : CarrierCom Φ dRows} - {resp₁ resp₂ : - QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits} - (hv₁ : Simple.commit Φ D resp₁.carrierDec = v) - (hv₂ : Simple.commit Φ D resp₂.carrierDec = v) - (hγ₁ : vecLInftyNorm Φ resp₁.carrierDec ≤ γ) - (hγ₂ : vecLInftyNorm Φ resp₂.carrierDec ≤ γ) - (hne : resp₁.carrierDec ≠ resp₂.carrierDec) : - ModuleSIS.relation Φ (dShort Φ γ) D (resp₁.carrierDec - resp₂.carrierDec) = true := by - have hne0 : resp₁.carrierDec - resp₂.carrierDec ≠ 0 := sub_ne_zero.mpr hne - have hshort : vecLInftyNorm Φ (resp₁.carrierDec - resp₂.carrierDec) ≤ subLInftyNormBound γ := - sub_lInftyNorm_le Φ _ _ hγ₁ hγ₂ - have heq : D *ᵥ resp₁.carrierDec = D *ᵥ resp₂.carrierDec := by - simpa [Simple.commit] using hv₁.trans hv₂.symm - have hker : D *ᵥ (resp₁.carrierDec - resp₂.carrierDec) = 0 := by - rw [matVecMul_sub]; exact sub_eq_zero.mpr heq - simp [ModuleSIS.relation, dShort, hne0, hshort, hker] +/-! ## The two-transcript MSIS extraction step (Hachi Lemma 8, cases (A)/(B)) -/ + +/-- **Hachi Lemma 8, cases (A)/(B), two-transcript step**: two `γ`-short openings of the same +commitment under `M` differ by an `ℓ∞`-short kernel vector — a Module-SIS solution for `M` at +the bound `subLInftyNormBound γ = 2·γ`. Instantiated at `M = B` with `outerShort` (case (A)) +and at `M = D` with `dShort` (case (B)) in `buildWitness_mem_relIn`. -/ +theorem msis_of_commit_eq {rows cols γ : ℕ} + (M : Simple.PublicParams Φ rows cols) {u : Simple.Commitment Φ rows} + {x₁ x₂ : PolyVec (Rq Φ) cols} + (h₁ : Simple.commit Φ M x₁ = u) (h₂ : Simple.commit Φ M x₂ = u) + (hγ₁ : vecLInftyNorm Φ x₁ ≤ γ) (hγ₂ : vecLInftyNorm Φ x₂ ≤ γ) (hne : x₁ ≠ x₂) : + ModuleSIS.relation Φ (fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ)) M (x₁ - x₂) + = true := by + have hker : M *ᵥ (x₁ - x₂) = 0 := by + rw [matVecMul_sub] + exact sub_eq_zero.mpr (by simpa [Simple.commit] using h₁.trans h₂.symm) + simp [ModuleSIS.relation, sub_ne_zero.mpr hne, sub_lInftyNorm_le Φ _ _ hγ₁ hγ₂, hker] /-! ## The subtract-and-divide core step (`inner_eq` via `Ring.inverse`) -/ omit [NeZero q] in /-- The c5-side unit-cancellation of the subtract-and-divide extraction (Hachi Lemma 8, case (C)): from the c5-subtract chain `c̄ᵢ •ᵥ (G_{n_A} t̂ᵢ) = A *ᵥ Δz` and `IsUnit c̄ᵢ`, the extracted -message block -`sᵢ := Ring.inverse c̄ᵢ •ᵥ Δz` satisfies the weak-opening inner gadget relation -(`VerifiedBlock.inner_eq`). Total at the definition site — no `IsUnit` needed to *define* `sᵢ`. -/ +message block `sᵢ := Ring.inverse c̄ᵢ •ᵥ Δz` satisfies the weak-opening inner gadget relation +(`VerifiedBlock.inner_eq`). -/ theorem inner_eq_of_chain {base : ZMod q} {cols : Nat} (A : Simple.PublicParams Φ innerRows cols) (that : Simple.Message Φ (innerRows * innerDigits)) (zdiff : PolyVec (Rq Φ) cols) @@ -357,7 +296,7 @@ theorem inner_eq_of_chain {base : ZMod q} {cols : Nat} rw [hAs, ← hchain]; funext i simp only [scalarVecMul_apply, ← mul_assoc, Ring.inverse_mul_cancel c hc, one_mul] -/-! ## The protocol: pure verifier, `IsPure`, prover skeleton -/ +/-! ## The protocol: pure pass-through verifier and honest prover -/ section Protocol @@ -365,8 +304,8 @@ variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} /-- The reduction's verifier (Hachi §4.2, Figure 3) is a **pure pass-through**: it re-emits the statement, the round-0 carrier commitment `v`, and the round-1 challenge vector. The Eq.-(20) -checks live in `relOut` (the `(ŵ, t̂, ẑ)` triple is never sent — §4.3 proves knowledge of it), so -there is no runtime `guard` and the verifier is `IsPure`. -/ +checks live in `relOut` (the `(ŵ, t̂, ẑ)` triple is never sent — §4.3 proves knowledge of it), +so there is no runtime `guard`. -/ def verifier : Verifier oSpec (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) @@ -375,26 +314,12 @@ def verifier : (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where verify := fun stmt tr => pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) -omit [NeZero q] [IsCyclotomic Φ] in -/-- The `hpure` shape consumed by `branch_relOut_language` (`SingleRound.lean`). -/ -theorem verifier_verify_pure - (stmt : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) - (tr : (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r).FullTranscript) : - (verifier (oSpec := oSpec) (ω := ω) Φ).verify stmt tr = - pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) := rfl - -/-- The Hachi Figure 3 verifier is pure: its output is a total function of `(stmt, tr)`. -/ -instance instIsPure : (verifier (oSpec := oSpec) (ω := ω) Φ - (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r)).IsPure := - ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ - -/-- Prover skeleton (Hachi §4.2, Figure 3; completeness is out of scope for Lemma 8): round 0 sends -the carrier commitment `v` (cf. `SendClaim.oracleProver`), round 1 receives the challenge vector -(cf. `SendChallenge.oracleProver`), and the output witness is the `QuadEvalResponse` `(ŵ, t̂, ẑ)` -of Eq. (20). The honest computations (`v = D ŵ` with `ŵ = G⁻¹(w)`, `ẑ = J⁻¹(Σᵢ cᵢ sᵢ)`, …) are the -parameters `computeV` / `computeResp`, to be instantiated by the completeness layer from the -`QuadEvalGadgets` carrier/decomposition definitions (§9.3). -/ +/-- The honest prover (Hachi §4.2, Figure 3; completeness is out of scope for Lemma 8): round 0 +sends the carrier commitment `v`, round 1 receives the challenge vector, and the output witness +is the `QuadEvalResponse` `(ŵ, t̂, ẑ)` of Eq. (20). The honest computations (`v = D ŵ` with +`ŵ = G⁻¹(w)`, `ẑ = J⁻¹(Σᵢ cᵢ sᵢ)`, …) are the parameters `computeV` / `computeResp`, to be +instantiated by the completeness layer from the `QuadEvalGadgets` carrier/decomposition +definitions. -/ def prover (WitIn : Type) (computeV : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → @@ -434,18 +359,6 @@ end Protocol end ZModDefs -end ArkLib.Lattices.Ajtai.InnerOuter - --- ================= PolynomialQuadraticEq/QuadEval.lean — extraction assembly ================= - -section QuadEvalExtraction - -namespace ArkLib.Lattices.Ajtai.InnerOuter - -open CompPoly ArkLib.Lattices.CyclotomicModulus -open WeakBinding -open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound - /-! ## The extracted opening and the witness assembler (generic `Φ`) -/ section BuildWitness @@ -478,16 +391,15 @@ open Classical in argument of the generic extractor `E`: * some branch's (flattened) inner decomposition `t̂` differs from the central one → a - `B`-kernel Module-SIS solution (the `msisB_of_two_valid` data); + `B`-kernel Module-SIS solution; * else some branch's carrier decomposition `ŵ` differs from the central one → a `D`-kernel - Module-SIS solution (the `msisD_of_two_valid` data); + Module-SIS solution; * else all branches share `t̂` and `ŵ`, and the star's subtract-and-divide yields the weak opening `extractedOpening`. ("Two branches differ" is equivalent to "some branch differs from the central one": if two -branches disagree, at least one of them disagrees with the central branch.) Fully defined — -the classical `dite`s always produce a term; that each case lands in `relIn` is -`buildWitness_mem_relIn`. -/ +branches disagree, at least one of them disagrees with the central branch.) Fully defined; +that each case lands in `relIn` is `buildWitness_mem_relIn`. -/ noncomputable def buildWitness (base : ZMod q) (_stmt : QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) @@ -508,7 +420,7 @@ noncomputable def buildWitness (base : ZMod q) omit [NeZero q] [IsCyclotomic Φ] in /-- Coordinate difference transfers from the `ShortChallenge` subtype to the underlying ring vectors — the bridge from `sib_coordEq` (subtype-level `CoordEq`) to the ring-level -coordinate-isolation lemmas `tensorG_coordDiff`/`tensorG1_coordDiff` (`QuadEvalGadgets.lean`). -/ +coordinate-isolation lemmas `tensorG_coord_diff`/`tensorG1_coord_diff` (`QuadEvalGadgets.lean`). -/ theorem ShortChallenge.coordEq_val {ℓ : ℕ} {i : Fin ℓ} {x y : Fin ℓ → ShortChallenge Φ ω} (h : CoordEq i x y) : CoordEq i (fun j => (x j).val) (fun j => (y j).val) := @@ -527,10 +439,22 @@ section Pinned variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} variable {innerRows messageDigits outerRows innerDigits dRows zDigits m r : Nat} -/-- **Sublemma 1 (an internal step of Hachi Lemma 8, case (C), weak-opening part).** At a -star-shaped family of `2^r + 1` `relOut`-accepting branches sharing the carrier commitment `v` -and (cases (A)/(B) excluded) sharing `t̂` and `ŵ`, the subtract-and-divide `extractedOpening` is a -`VerifiedOpening` at +/-- The slack `c̄ᵢ := c_{sib i, i} − c_{central, i}` of a star-shaped short-challenge family is a +unit: it is nonzero (the sibling differs at `i`), `ℓ₁`-bounded by `2ω` (two subtype challenges), +and `(2ω)² < q` — Lyubashevsky–Seiler [LS18] invertibility. -/ +theorem slack_isUnit (hq5 : q % 8 = 5) {ω : ℕ} (hκ : (2 * ω) ^ 2 < q) + (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (hstar : ∃ e, StarAt fam e) (i : Fin (2 ^ r)) : + IsUnit ((fam (sib fam i) i).val - (fam (central fam) i).val) := + isUnit_of_l1Norm_le α hq5 + (Rq.l1Norm_pos_of_ne_zero 𝓜(q, α) + (sub_ne_zero_of_ne (ShortChallenge.val_ne_of_ne (sib_coordEq_ne fam hstar i)))) + (ShortChallenge.l1Norm_val_sub_le _ _) hκ + +/-- **Weak-opening validity of the extracted opening** (Hachi Lemma 8, case (C), part 1). +At a star-shaped family of `2^r + 1` `relOut`-accepting branches sharing the carrier commitment +`v` and (cases (A)/(B) excluded) sharing `t̂` and `ŵ`, the subtract-and-divide +`extractedOpening` is a `VerifiedOpening` at * `βSq := quadEvalBetaSq γ b zDigits (deg φ) m messageDigits = 4·B_z`, the `GadgetNorms`-derived `J`-recomposition bound (`deg φ = 2^α`; no primitive `‖z‖₂²` verifier check anywhere); @@ -547,69 +471,32 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) (ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec) (_hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : - VerifiedOpening 𝓜(q, α) ((b : ZMod q)) + VerifiedOpening 𝓜(q, α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) stmt.pp.toPublicParams stmt.u - (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) := by - -- PROOF PLAN (rank 7). Notation: e := central fam, sibᵢ := sib fam i, - -- c̄ᵢ := (fam sibᵢ i).val − (fam e i).val, zⱼ := jMatrix 𝓜 b _ zDigits *ᵥ (resp j).zDec, - -- Δzᵢ := z sibᵢ − z e. Destructure each hrel j by plain - -- `obtain ⟨c1, c2, c3, c4, c5, c6w, c6t, c6z⟩ := hrel j` (smoke test above: the match - -- iota-reduces on the literal tuple). - -- * outer_eq := c2 of the central branch (relOut c2 is verbatim - -- `Simple.commit 𝓜 B (flattenBlocks t̂) = stmt.u`). - -- * outer_short := c6t of the central branch — the γ (NOT 2γ) bound; the extracted - -- `innerDecomp` IS `(resp e).innerDec`, syntactically. - -- * block i, fieldwise: - -- - hne : c̄ᵢ ≠ 0 — from sib_coordEq fam hstar i : CoordEq i (fam e) (fam sibᵢ) (SingleRound), - -- sib_coordEq_ne + ShortChallenge.val_ne_of_ne + sub_ne_zero_of_ne. - -- - hpos := Rq.l1Norm_pos_of_ne_zero 𝓜(q,α) hne (NormBounds/Basic.lean). - -- - challenge_short := ShortChallenge.l1Norm_val_sub_le _ _ : ‖c̄ᵢ‖₁ ≤ 2ω (via - -- Rq.l1Norm_sub_le). - -- - unit := isUnit_of_l1Norm_le α hq5 hpos challenge_short hκ (LS18, 𝓜-pinned; κ = 2ω). - -- - scaled_short : challenge i •ᵥ message i = c̄ᵢ •ᵥ (Ring.inverse c̄ᵢ •ᵥ Δzᵢ) = Δzᵢ - -- (scalarVecMul associativity + Ring.mul_inverse_cancel with unit); then - -- ‖Δzᵢ‖₂² ≤ subL2NormSqBound (quadEvalZL2SqBound γ b zDigits (deg φ) m messageDigits) - -- = quadEvalBetaSq … (rfl) - -- by gadgetMul_zmod_sub_l2NormSq_le 𝓜 hτ h1 (resp sibᵢ).zDec (resp e).zDec c6z c6z - -- (GadgetNorms.lean; `jMatrix … *ᵥ ·` is `gadgetMul` by rfl), with - -- h1 : 1 ≤ (𝓜(q,α)).φ.natDegree from hachiModulus_natDegree ▸ Nat.one_le_two_pow. - -- - inner_eq : the c5-subtract chain. Rewrite c5 of branch sibᵢ along ht sibᵢ to the - -- shared t̂ := (resp e).innerDec; subtract c5 of the central branch: - -- tensorG_sub_challenge + matVecMul_sub give - -- tensorG 𝓜 b innerRows innerDigits (c_sib − c_e) t̂ = A *ᵥ Δzᵢ; - -- coordinate-isolate with tensorG_coordDiff at - -- ShortChallenge.coordEq_val (coordEq_symm (sib_coordEq fam hstar i)) - -- to get c̄ᵢ •ᵥ (gadgetMatrix 𝓜 b innerRows innerDigits *ᵥ t̂ i) = A *ᵥ Δzᵢ; close by - -- inner_eq_of_chain 𝓜 stmt.pp.innerMatrix (t̂ i) Δzᵢ c̄ᵢ unit (above) - -- (`Simple.commit 𝓜 (gadgetMatrix …)` is `gadgetMatrix … *ᵥ ·` by rfl). - -- outer_eq / outer_short := c2 / c6t of the central branch. + (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp) := by + -- `outer_eq` / `outer_short` are c2 / c6t of the central branch (the extracted `innerDecomp` + -- IS the central `t̂`, so the `γ` bound applies verbatim — no `2γ` slack). obtain ⟨-, hc2e, -, -, -, -, hc6te, -⟩ := hrel (central fam) refine ⟨hc2e, hc6te, fun i => ?_⟩ - -- Block `i`: the extracted challenge `c̄ᵢ := c_{sib i, i} − c_{e, i}` is nonzero, `2ω`-short, - -- hence a unit (Lyubashevsky–Seiler). - have hne : (fam (sib fam i) i).val - (fam (central fam) i).val ≠ 0 := - sub_ne_zero_of_ne (ShortChallenge.val_ne_of_ne (sib_coordEq_ne fam hstar i)) - have hshort : ‖(fam (sib fam i) i).val - (fam (central fam) i).val‖₁ ≤ 2 * ω := - ShortChallenge.l1Norm_val_sub_le _ _ - have hunit : IsUnit ((fam (sib fam i) i).val - (fam (central fam) i).val) := - isUnit_of_l1Norm_le α hq5 (Rq.l1Norm_pos_of_ne_zero 𝓜(q, α) hne) hshort hκ - refine ⟨hunit, hshort, ?_, ?_⟩ - · -- scaled_short : `c̄ᵢ •ᵥ (c̄ᵢ⁻¹ •ᵥ Δzᵢ) = Δzᵢ = J ẑ^{(sib i)} − J ẑ^{(e)}`, then the + -- Block `i`: the slack `c̄ᵢ` is a unit (Lyubashevsky–Seiler). + have hunit := slack_isUnit hq5 hκ fam hstar i + refine ⟨hunit, ShortChallenge.l1Norm_val_sub_le _ _, ?_, ?_⟩ + · -- scaled_short: `c̄ᵢ •ᵥ (c̄ᵢ⁻¹ •ᵥ Δzᵢ) = Δzᵢ = J ẑ^{(sib i)} − J ẑ^{(e)}`, then the -- `J`-recomposition ℓ₂² bound from the two branches' c6z (`GadgetNorms.lean`). have h1 : 1 ≤ (𝓜(q, α)).φ.natDegree := by rw [hachiModulus_natDegree]; exact Nat.one_le_two_pow obtain ⟨-, -, -, -, -, -, -, hc6zs⟩ := hrel (sib fam i) obtain ⟨-, -, -, -, -, -, -, hc6ze⟩ := hrel (central fam) - have hcancel : (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp).challenge i •ᵥ - (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp).message i - = Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + have hcancel : (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp).challenge i •ᵥ + (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp).message i + = Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (sib fam i)).zDec - - Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + - Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (central fam)).zDec := by simp only [extractedOpening] funext k @@ -617,7 +504,7 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω rw [hcancel] exact gadgetMul_zmod_sub_l2NormSq_le 𝓜(q, α) hτ h1 (resp (sib fam i)).zDec (resp (central fam)).zDec hc6zs hc6ze - · -- inner_eq : the c5-subtract chain, shared `t̂ := (resp e).innerDec`, coordinate-isolated + · -- inner_eq: the c5-subtract chain, shared `t̂ := (resp e).innerDec`, coordinate-isolated -- and unit-divided. have hcoord : CoordEq i (fun k => (fam (sib fam i) k).val) (fun k => (fam (central fam) k).val) := @@ -626,24 +513,24 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω obtain ⟨-, -, -, -, hc5e, -, -, -⟩ := hrel (central fam) rw [ht (sib fam i)] at hc5s have hchain : ((fam (sib fam i) i).val - (fam (central fam) i).val) •ᵥ - (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) innerRows innerDigits *ᵥ + (gadgetMatrix 𝓜(q, α) (b : ZMod q) innerRows innerDigits *ᵥ (resp (central fam)).innerDec i) = stmt.pp.innerMatrix *ᵥ - (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + (Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (sib fam i)).zDec - - Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + - Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (central fam)).zDec) := by rw [matVecMul_sub, ← hc5s, ← hc5e, ← Hachi.tensorG_sub_challenge, - Hachi.tensorG_coordDiff 𝓜(q, α) ((b : ZMod q)) innerRows innerDigits hcoord] + Hachi.tensorG_coord_diff 𝓜(q, α) (b : ZMod q) innerRows innerDigits hcoord] simp only [extractedOpening] exact inner_eq_of_chain 𝓜(q, α) stmt.pp.innerMatrix ((resp (central fam)).innerDec i) _ ((fam (sib fam i) i).val - (fam (central fam) i).val) hunit hchain -/-- **Sublemma 2 wiring (Eq. (15) for the extracted opening).** The shared-`ŵ` c3 row plus the -coordinate-isolated, unit-divided c4 rows give eval-consistency of `extractedOpening`. The -core computation `evalConsistency_of_star` is PROVED above (Part 4 / A4); this statement -discharges its `w`/`c3`/`c4` hypotheses from the star. -/ +/-- **Eval-consistency of the extracted opening** (Hachi Lemma 8, case (C), part 2 — Eq. (15)): +the shared-`ŵ` c3 row plus the coordinate-isolated, unit-divided c4 rows discharge the +`w`/`c3`/`c4` hypotheses of `evalConsistency_of_star` at the shared recomposed carrier +`w := G_{2^r} *ᵥ ŵ`. -/ theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) @@ -651,16 +538,14 @@ theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) (hw : ∀ j, (resp j).carrierDec = (resp (central fam)).carrierDec) : - evalConsistency 𝓜(q, α) ((b : ZMod q)) stmt.avec stmt.bvec stmt.y - (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) := by - -- Apply `evalConsistency_of_star` at the shared recomposed carrier - -- `w := G_{2^r} *ᵥ (resp (central fam)).carrierDec`. - refine evalConsistency_of_star 𝓜(q, α) ((b : ZMod q)) stmt.avec stmt.bvec stmt.y - (extractedOpening 𝓜(q, α) ((b : ZMod q)) fam resp) - (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ (resp (central fam)).carrierDec) + evalConsistency 𝓜(q, α) (b : ZMod q) stmt.avec stmt.bvec stmt.y + (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp) := by + refine evalConsistency_of_star 𝓜(q, α) (b : ZMod q) stmt.avec stmt.bvec stmt.y + (extractedOpening 𝓜(q, α) (b : ZMod q) fam resp) + (gadgetMatrix 𝓜(q, α) (b : ZMod q) (2 ^ r) messageDigits *ᵥ (resp (central fam)).carrierDec) ?_ ?_ · -- c3: verbatim c3 row of the central branch. obtain ⟨-, -, hc3, -, -, -, -, -⟩ := hrel (central fam) @@ -669,46 +554,38 @@ theorem evalConsistency_of_relOut_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : intro j obtain ⟨-, -, -, hc4s, -, -, -, -⟩ := hrel (sib fam j) obtain ⟨-, -, -, hc4e, -, -, -, -⟩ := hrel (central fam) - -- Move the sibling's c4 onto the shared carrier `ŵ := (resp (central fam)).carrierDec`. rw [hw (sib fam j)] at hc4s - -- The difference challenge `c̄ⱼ = c_{sib j, j} − c_{central, j}` differs from `0`, hence is a - -- unit (Lyubashevsky–Seiler, `‖c̄ⱼ‖₁ ≤ 2ω < √q`). - have hne : (fam (sib fam j) j).val - (fam (central fam) j).val ≠ 0 := - sub_ne_zero_of_ne (ShortChallenge.val_ne_of_ne (sib_coordEq_ne fam hstar j)) - have hunit : IsUnit ((fam (sib fam j) j).val - (fam (central fam) j).val) := - isUnit_of_l1Norm_le α hq5 (Rq.l1Norm_pos_of_ne_zero 𝓜(q, α) hne) - (ShortChallenge.l1Norm_val_sub_le _ _) hκ - -- Subtract-and-isolate: the two branches' c4 rows, sharing `ŵ`, give `c̄ⱼ · wⱼ = aᵀ G Δzⱼ`. + have hunit := slack_isUnit hq5 hκ fam hstar j have hcoord : CoordEq j (fun i => (fam (sib fam j) i).val) (fun i => (fam (central fam) i).val) := ShortChallenge.coordEq_val 𝓜(q, α) (coordEq_symm (sib_coordEq fam hstar j)) - have hchain : dot stmt.avec (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ m) messageDigits *ᵥ - ((Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + -- Subtract-and-isolate: the two branches' c4 rows, sharing `ŵ`, give `c̄ⱼ · wⱼ = aᵀ G Δzⱼ`. + have hchain : dot stmt.avec (gadgetMatrix 𝓜(q, α) (b : ZMod q) (2 ^ m) messageDigits *ᵥ + ((Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (sib fam j)).zDec) - - (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + - (Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (central fam)).zDec))) = ((fam (sib fam j) j).val - (fam (central fam) j).val) * - (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ + (gadgetMatrix 𝓜(q, α) (b : ZMod q) (2 ^ r) messageDigits *ᵥ (resp (central fam)).carrierDec) j := by rw [matVecMul_sub, dot_sub, ← hc4s, ← hc4e, ← Hachi.tensorG1_sub_challenge, - Hachi.tensorG1_coordDiff 𝓜(q, α) ((b : ZMod q)) messageDigits hcoord] + Hachi.tensorG1_coord_diff 𝓜(q, α) (b : ZMod q) messageDigits hcoord] -- Divide by `c̄ⱼ`: the extracted message `sⱼ := c̄ⱼ⁻¹ •ᵥ Δzⱼ` recovers `wⱼ` under `aᵀ G`. - change (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ r) messageDigits *ᵥ + change (gadgetMatrix 𝓜(q, α) (b : ZMod q) (2 ^ r) messageDigits *ᵥ (resp (central fam)).carrierDec) j - = dot stmt.avec (gadgetMatrix 𝓜(q, α) ((b : ZMod q)) (2 ^ m) messageDigits *ᵥ + = dot stmt.avec (gadgetMatrix 𝓜(q, α) (b : ZMod q) (2 ^ m) messageDigits *ᵥ (Ring.inverse ((fam (sib fam j) j).val - (fam (central fam) j).val) •ᵥ - ((Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + ((Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (sib fam j)).zDec) - - (Hachi.jMatrix 𝓜(q, α) ((b : ZMod q)) ((2 ^ m) * messageDigits) zDigits *ᵥ + - (Hachi.jMatrix 𝓜(q, α) (b : ZMod q) ((2 ^ m) * messageDigits) zDigits *ᵥ (resp (central fam)).zDec)))) rw [matVecMul_scalarVecMul, dot_scalarVecMul, hchain, ← mul_assoc, Ring.inverse_mul_cancel _ hunit, one_mul] /-- **The witness assembler is correct** — the `hmk` input to the generic assembly -`coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`): at every star-shaped family of -`relOut`-accepting branches, `buildWitness` lands in `relIn`. This is the ONLY remaining -mathematical obligation of the top-level theorem (see -`quadEval_coordinateWiseSpecialSound'`). -/ +`coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), and the mathematical content of +Hachi Lemma 8's three-case split: at every star-shaped family of `relOut`-accepting branches, +`buildWitness` lands in `relIn`. -/ theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) (stmt : QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits @@ -717,46 +594,28 @@ theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) (resp : Fin (2 ^ r + 1) → QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) ((b : ZMod q)) ω γ) + (hrel : ∀ j, ((stmt, v, fam j), resp j) ∈ relOut 𝓜(q, α) (b : ZMod q) ω γ) (hstar : ∃ e, StarAt fam e) : - (stmt, buildWitness 𝓜(q, α) ((b : ZMod q)) stmt v fam resp) ∈ - relIn 𝓜(q, α) ((b : ZMod q)) + (stmt, buildWitness 𝓜(q, α) (b : ZMod q) stmt v fam resp) ∈ + relIn 𝓜(q, α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) := by - -- PROOF PLAN (rank 6) — Hachi Lemma 8's three-case split. `unfold buildWitness` and split - -- the two classical `dite`s (`rw [dif_pos hB]` / `rw [dif_neg hB, dif_pos hD]` / both neg). - -- 1. Case hB (∃ j, flattenBlocks (resp j).innerDec ≠ flattenBlocks (resp e).innerDec): - -- relIn membership at the literal `.msisB _` iota-reduces to the bare MSIS disjunct - -- (smoke test above); close with msisB_of_two_valid (PROVED above) at - -- B := stmt.pp.outerMatrix, - -- hu₁/hu₂ := c2 of branches hB.choose / e (both equal stmt.u), hγ₁/hγ₂ := their c6t, - -- hne := hB.choose_spec. - -- 2. Case hD (¬hB, ∃ j, (resp j).carrierDec ≠ (resp e).carrierDec): close with - -- msisD_of_two_valid (PROVED above) at D := stmt.pp.dMatrix, hv₁/hv₂ := c1 of branches - -- hD.choose / e — both commit to the SHARED round-0 message `v` (the tree sharing of `v` - -- across branches is exactly what fires this case), hγ₁/hγ₂ := their c6w, - -- hne := hD.choose_spec. - -- 3. Case ¬hB ∧ ¬hD: push_neg gives ∀-equalities; hw is direct; ht at family level via - -- PolyVec.block_eq_of_flattenBlocks_eq (Vectors.lean:59) + funext from the flattened - -- equality. relIn membership at the literal `.opening _` iota-reduces to - -- `VerifiedOpening … ∧ evalConsistency …` (smoke test above); close with - -- ⟨verifiedOpening_of_star hq5 hκ hτ … hrel hstar ht hw, - -- evalConsistency_of_relOut_star hq5 hκ … hrel hstar hw⟩. unfold buildWitness by_cases hB : ∃ j, PolyVec.flattenBlocks (resp j).innerDec ≠ PolyVec.flattenBlocks (resp (central fam)).innerDec - · -- Case (A): some branch's inner decomposition differs → `B`-kernel MSIS solution. + · -- Case (A): some branch's inner decomposition differs → `B`-kernel MSIS solution + -- (both branches' c2 commit to the shared `stmt.u`). rw [dif_pos hB] obtain ⟨-, hu₁, -, -, -, -, hγ₁, -⟩ := hrel hB.choose obtain ⟨-, hu₂, -, -, -, -, hγ₂, -⟩ := hrel (central fam) - exact msisB_of_two_valid 𝓜(q, α) hu₁ hu₂ hγ₁ hγ₂ hB.choose_spec + exact msis_of_commit_eq 𝓜(q, α) stmt.pp.outerMatrix hu₁ hu₂ hγ₁ hγ₂ hB.choose_spec · by_cases hD : ∃ j, (resp j).carrierDec ≠ (resp (central fam)).carrierDec · -- Case (B): shared `t̂` but some carrier decomposition differs → `D`-kernel MSIS solution -- (the shared round-0 message `v` is what makes both branches commit to the same `v`). rw [dif_neg hB, dif_pos hD] obtain ⟨hv₁, -, -, -, -, hγ₁, -, -⟩ := hrel hD.choose obtain ⟨hv₂, -, -, -, -, hγ₂, -, -⟩ := hrel (central fam) - exact msisD_of_two_valid 𝓜(q, α) hv₁ hv₂ hγ₁ hγ₂ hD.choose_spec - · -- Case (C): shared `t̂` and `ŵ` → the subtract-and-divide weak opening (Sublemmas 1 & 2). + exact msis_of_commit_eq 𝓜(q, α) stmt.pp.dMatrix hv₁ hv₂ hγ₁ hγ₂ hD.choose_spec + · -- Case (C): shared `t̂` and `ŵ` → the subtract-and-divide weak opening. rw [dif_neg hB, dif_neg hD] push Not at hB hD have ht : ∀ j, (resp j).innerDec = (resp (central fam)).innerDec := @@ -765,17 +624,14 @@ theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) evalConsistency_of_relOut_star hq5 hκ stmt v fam resp hrel hstar hD⟩ /-- **Hachi Lemma 8 (CWSS of Hachi's polynomial-evaluation reduction, Figure 3; originally -Greyhound's [NS24, §3.1] folding protocol), top-level statement.** -The reduction's verifier is coordinate-wise special sound for the `(ℓ, k) = (2^r, 2)` -structure, with `relOut` = Eq. (20) + the `S_b` range checks and `relIn` = weak opening -(eval-consistent) ∨ MSIS(B) ∨ MSIS(D), at the derived constants -`βSq = quadEvalBetaSq γ b zDigits (deg φ) m messageDigits` and `κ = 2ω`. - -The first proof step below is the load-bearing WitIn/WitOut wiring whose absence -invalidated v1 of this plan: the generic extractor `E` at `WitOut := QuadEvalResponse` -(relOut's witness) and `WitIn := QuadEvalWitness` (relIn's witness), assembled by `buildWitness` — -verified here to -elaborate. -/ +Greyhound's [NS24, §3.1] folding protocol).** The reduction's verifier is coordinate-wise +special sound for the `(ℓ, k) = (2^r, 2)` structure, with `relOut` = Eq. (20) + the `S_b` range +checks and `relIn` = weak opening (eval-consistent) ∨ MSIS(B) ∨ MSIS(D), at the derived +constants `βSq = quadEvalBetaSq γ b zDigits (deg φ) m messageDigits` and `κ = 2ω`. + +Assembled by `coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), which discharges +every tree/extractor/guard obligation generically; the whole of Hachi Lemma 8 thereby reduces +to the single math lemma `buildWitness_mem_relIn`. -/ theorem quadEval_coordinateWiseSpecialSound {ι : Type} {oSpec : OracleSpec ι} {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : @@ -785,81 +641,43 @@ theorem quadEval_coordinateWiseSpecialSound {ι : Type} {oSpec : OracleSpec ι} (r := r)).coordinateWiseSpecialSound init impl (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r)) - (relIn 𝓜(q, α) ((b : ZMod q)) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := by - -- THE WIRING STEP (v1's blocker, now verified to typecheck): - refine ⟨E (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) - (buildWitness 𝓜(q, α) ((b : ZMod q))), ?_⟩ - -- The direct route mirrors the generic assembly `coordinateWiseSpecialSound_of_mkWitness` - -- (`SingleRound.lean`), documenting the tree mechanics inline: shape-recover the tree, fire - -- each branch's `relOut.language` guard, obtain the star center, then close with the sole - -- math lemma `buildWitness_mem_relIn`. - classical - intro stmtIn tree hStruct hAcc - -- 1. shape recovery: every accepting tree of `pSpec` is `tree2 v challenges`. - obtain ⟨v, challenges, rfl⟩ := tree_shape tree - have harity := (foldStructure_arity (CarrierCom := CarrierCom 𝓜(q, α) dRows) - (C := ShortChallenge 𝓜(q, α) ω) (r := r)).symm - -- 2. per-branch language membership (the extractor's guards fire on accepting trees). - have hmem : ∀ j : Fin (2 ^ r + 1), - ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ - relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ := by - intro j - have h := branch_relOut_language init impl _ (fun _ _ => rfl) - (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) stmtIn v challenges hAcc - (Fin.cast harity j) - exact (Set.mem_language_iff _ _).1 h - -- 3. the sibling family is special sound, hence has a star center. - have hfam := (nodeOk_iff_family challenges).1 hStruct.1 - have hstar : ∃ e, StarAt - (fun j : Fin (2 ^ r + 1) => challenges (Fin.cast harity j)) e := - exists_starAt (le_refl 2) (by omega) _ hfam - -- 4. each chosen response satisfies `relOut` — `E` computes definitionally on `tree2`. - have hbranch : ∀ j : Fin (2 ^ r + 1), - ((stmtIn, v, challenges (Fin.cast harity j)), - if h : ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ - relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ - then h.choose else Classical.ofNonempty) ∈ - relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ := by - intro j - rw [dif_pos (hmem j)] - exact (hmem j).choose_spec - -- 5. close with the sole math lemma. - exact buildWitness_mem_relIn hq5 hκ hτ stmtIn v _ _ hbranch hstar - -/-- The same statement, proved in ONE LINE through A1's generic assembly -`coordinateWiseSpecialSound_of_mkWitness`: every tree/extractor/guard obligation is discharged -generically, and the whole of Hachi Lemma 8 reduces to the single math lemma -`buildWitness_mem_relIn`. Kept alongside `quadEval_coordinateWiseSpecialSound` so BOTH proof -routes' wiring is probe-verified. -/ -theorem quadEval_coordinateWiseSpecialSound' {ι : Type} {oSpec : OracleSpec ι} {σ : Type} - (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) - (messageDigits := messageDigits) (outerRows := outerRows) - (innerDigits := innerDigits) (dRows := dRows) (m := m) - (r := r)).coordinateWiseSpecialSound init impl - (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) - (C := ShortChallenge 𝓜(q, α) ω) (r := r)) - (relIn 𝓜(q, α) ((b : ZMod q)) + (relIn 𝓜(q, α) (b : ZMod q) (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) ((b : ZMod q)) ω γ) := + (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := coordinateWiseSpecialSound_of_mkWitness init impl _ (fun _ _ => rfl) _ _ - (buildWitness 𝓜(q, α) ((b : ZMod q))) + (buildWitness 𝓜(q, α) (b : ZMod q)) (fun stmtIn v fam resp hbranch hstar => buildWitness_mem_relIn hq5 hκ hτ stmtIn v fam resp hbranch hstar) --- NOTE (oracleVerifier wrapper): deliberately NOT included. An --- `OracleVerifier.coordinateWiseSpecialSound` wrapper needs (i) an actual `OracleVerifier` --- for the reduction, whose round-0 message `v : CarrierCom 𝓜(q,α) dRows` requires an --- `OracleInterface (Simple.Commitment 𝓜(q,α) dRows)` instance that does not exist in the repo --- (a query-model design decision), and (ii) the repo's oracle-level append theorem is itself --- still sorried — so the plain-`Verifier` statement above is the --- right interface for Lemma 8 now; the oracle wrapper is future work at the composition step. +-- An `OracleVerifier` wrapper is deliberately not included: it needs an `OracleInterface` +-- instance for `Simple.Commitment` (a query-model design decision that does not exist in the +-- repo yet) and the still-sorried oracle-level append theorem. The plain-`Verifier` statement +-- above is the right interface for Lemma 8; the oracle wrapper belongs to the composition step. + +/-- **`QuadEval` as a `CWSSPackage`** (Hachi [NOZ26, §4.2, Figure 3]; Lemma 8): the two-round fold +`verifier` bundled with its `foldStructure` CWSS certificate `quadEval_coordinateWiseSpecialSound`, +ready to be `▷`-composed after the polynomial-level bridge. -/ +def quadEvalPackage {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + CWSSPackage init impl + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows × + CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + (pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where + verifier := verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) + struct := + foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) (C := ShortChallenge 𝓜(q, α) ω) (r := r) + relIn := relIn 𝓜(q, α) (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) + relOut := relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ + isPure := ⟨fun stmt tr => (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩), fun _ _ => rfl⟩ + isCWSS := quadEval_coordinateWiseSpecialSound init impl hq5 hκ hτ end Pinned end ArkLib.Lattices.Ajtai.InnerOuter - -end QuadEvalExtraction diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean index 219d2d9b46..f79f4abc80 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean +++ b/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEvalGadgets.lean @@ -10,19 +10,16 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic # Hachi polynomial-evaluation reduction — gadget algebra (Hachi §4.2, Figure 3) Gadget algebra supporting **Hachi Lemma 8** (coordinate-wise special soundness of Hachi's - polynomial-evaluation reduction — proving `f(x) = y` via the quadratic form `bᵀ M a`, Hachi - [NOZ26] §4.2, Figure 3): the short-commitment matrix `D` and its commitment `v`, the carrier - `w`/`ŵ`, the `J` gadget and `ẑ`, and the block-weighted `tensorG` / `tensorG1` sums together - with their algebra (`tensorG_sub`, `tensorG_sub_challenge`, `tensorG_coordDiff` — the - coordinate-isolation crux). + polynomial-evaluation reduction, Hachi [NOZ26] §4.2, Figure 3): the public parameters extended + with the short-commitment matrix `D` (`PublicParamsD`); the honest-prover layer — the carrier + `w`/`ŵ` and its short commitment `v = D ŵ` (`carrier`, `carrierDecomp`, `carrierCommit`) and + the `J` gadget with the response decomposition `ẑ` (`jMatrix`, `zDecomp`); and the + block-weighted gadget sums `tensorG` / `tensorG1` with their subtraction and + coordinate-isolation lemmas (`tensorG_sub_challenge`, `tensorG_coord_diff`, and the `tensorG1` + analogues) — the algebraic crux of the Lemma 8 subtract-and-divide extraction. Hachi's reduction (§4.2) is the multilinear / inner-outer lift of Greyhound's [NS24, §3.1] - folding-based polynomial-evaluation protocol; this file is its gadget layer. The naming - `QuadEval` reflects the content — an evaluation claim expressed as a quadratic equation — while - "fold" below refers to Greyhound's mechanism of folding the `2ʳ` carrier blocks under the - verifier's challenge vector. - - Implements §9.3 of `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` (milestone M1). + folding-based polynomial-evaluation protocol; this file is its gadget layer. ## References @@ -47,13 +44,15 @@ structure PublicParamsD (Φ : CyclotomicModulus R) /-- The Hachi short-commitment matrix `D` (Hachi [NOZ26] Eq. (16)). -/ dMatrix : Simple.PublicParams Φ dRows (blocks * messageDigits) -/-! ## The carrier `w`, its decomposition `ŵ`, and the short commitment `v = D ŵ` -/ +/-! ## The carrier `w`, its decomposition `ŵ`, and the short commitment `v = D ŵ` + +The honest-prover side of Figure 3 round 0; the completeness layer instantiates +`QuadEval.prover` from these definitions. -/ section Carrier variable {messageRows messageDigits blocks : Nat} (base : R) -/-- The carrier entry `wᵢ := aᵀ · G_{2^m} · sᵢ` (Hachi Eq. (16)/(17); only `gadgetMatrix`, no -`DecidableEq R`). -/ +/-- The carrier entry `wᵢ := aᵀ · G_{2^m} · sᵢ` (Hachi Eq. (16)/(17)). -/ def carrierEntry (a : PolyVec (Rq Φ) messageRows) (s : PolyVec (Rq Φ) (messageRows * messageDigits)) : Rq Φ := ArkLib.Lattices.splitForm (gadgetMatrix Φ base messageRows messageDigits) a s @@ -63,7 +62,7 @@ def carrier (a : PolyVec (Rq Φ) messageRows) (s : PolyVec (PolyVec (Rq Φ) (messageRows * messageDigits)) blocks) : PolyVec (Rq Φ) blocks := fun i => carrierEntry Φ base a (s i) -variable [DecidableEq R] -- ← introduced AFTER the pure defs (else unusedSectionVars) +variable [DecidableEq R] -- introduced after the pure defs (else `unusedSectionVars`) /-- The carrier decomposition `ŵ := G⁻¹_{blocks}(w)` (Hachi [NOZ26] Eq. (16)/(17)), block-major, length `blocks * messageDigits`. `base` is IMPLICIT (pinned by `ddCarrier`). -/ @@ -96,7 +95,8 @@ end Carrier section JGadget /-- The `J` gadget `J_n := I_n ⊗ [1, base, …, base^(zDigits-1)]` (Hachi Eq. (18)–(20)); in this -reduction `n = messageRows * messageDigits` and `zDigits = τ`. -/ +reduction `n = messageRows * messageDigits` and `zDigits = τ`. The verifier reconstructs +`z = J *ᵥ ẑ` from the decomposed response `ẑ`. -/ def jMatrix (base : R) (n zDigits : Nat) : PolyMatrix (Rq Φ) n (n * zDigits) := gadgetMatrix Φ base n zDigits @@ -127,19 +127,6 @@ def tensorG (base : R) (k digits : Nat) (c : PolyVec (Rq Φ) blocks) (x : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : PolyVec (Rq Φ) k := ∑ i : Fin blocks, (c i) •ᵥ (gadgetMatrix Φ base k digits *ᵥ x i) -/-- `tensorG` is subtractive in the block family (algebra for Hachi Lemma 8's two-transcript -subtraction of Eq. (20) row 5). -/ -theorem tensorG_sub (base : R) (k digits : Nat) (c : PolyVec (Rq Φ) blocks) - (x y : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : - tensorG Φ base k digits c (x - y) - = tensorG Φ base k digits c x - tensorG Φ base k digits c y := by - simp only [tensorG, ← Finset.sum_sub_distrib] - refine Finset.sum_congr rfl fun i _ => ?_ - have hxy : (x - y) i = x i - y i := rfl - rw [hxy, matVecMul_sub] - funext r - simp only [scalarVecMul_apply, Pi.sub_apply, mul_sub] - /-- `tensorG` is subtractive in the challenge vector (Hachi Lemma 8's two-transcript subtraction of Eq. (20) row 5). -/ theorem tensorG_sub_challenge (base : R) (k digits : Nat) (c c' : PolyVec (Rq Φ) blocks) @@ -154,7 +141,7 @@ theorem tensorG_sub_challenge (base : R) (k digits : Nat) (c c' : PolyVec (Rq Φ /-- Coordinate isolation (Hachi Lemma 8, case (C), the c5 subtract-and-divide crux): if `c ≡ⱼ c'`, the challenge-difference sum collapses to the `j`-th block, `tensorG (c − c') x = (cⱼ − c'ⱼ) •ᵥ (G_k *ᵥ xⱼ)`. -/ -theorem tensorG_coordDiff (base : R) (k digits : Nat) +theorem tensorG_coord_diff (base : R) (k digits : Nat) {c c' : PolyVec (Rq Φ) blocks} {j : Fin blocks} (h : CoordinateWise.CoordEq j c c') (x : PolyVec (PolyVec (Rq Φ) (k * digits)) blocks) : tensorG Φ base k digits (c - c') x = (c j - c' j) •ᵥ (gadgetMatrix Φ base k digits *ᵥ x j) := by @@ -177,18 +164,6 @@ def tensorG1 (base : R) (digits : Nat) (c : PolyVec (Rq Φ) blocks) (x : PolyVec (Rq Φ) (blocks * digits)) : Rq Φ := dot c (gadgetMatrix Φ base blocks digits *ᵥ x) -/-- `tensorG1` is literally the per-block `G₁`-row form `Σᵢ cᵢ · (Σₑ baseᵉ · x_{(i,e)})` — the -Kronecker reading `(cᵀ ⊗ G₁) ŵ` of Hachi Eq. (18)/(20) row 4. -/ -theorem tensorG1_eq_sum_blocks (base : R) (digits : Nat) (hd : 0 < digits) - (c : PolyVec (Rq Φ) blocks) (x : PolyVec (Rq Φ) (blocks * digits)) : - tensorG1 Φ base digits c x - = ∑ i : Fin blocks, c i - * ∑ e : Fin digits, constRq Φ (base ^ (e : ℕ)) * x (finProdFinEquiv (i, e)) := by - simp only [tensorG1, dot_eq_sum] - refine Finset.sum_congr rfl fun i _ => ?_ - rw [show (gadgetMatrix Φ base blocks digits *ᵥ x) i = gadgetMul Φ base x i from rfl, - gadgetMul_apply Φ base hd] - /-- `tensorG1` is subtractive in the challenge vector (Hachi Lemma 8's two-transcript subtraction of Eq. (20) row 4). -/ theorem tensorG1_sub_challenge (base : R) (digits : Nat) (c c' : PolyVec (Rq Φ) blocks) @@ -200,7 +175,7 @@ theorem tensorG1_sub_challenge (base : R) (digits : Nat) (c c' : PolyVec (Rq Φ) /-- Coordinate isolation at `k = 1` (Hachi Lemma 8, case (C), the c4 subtract-and-divide crux): if `c ≡ⱼ c'`, then `tensorG1 (c − c') ŵ = (cⱼ − c'ⱼ) · wⱼ` where `w := G_blocks *ᵥ ŵ` is the recomposed carrier. -/ -theorem tensorG1_coordDiff (base : R) (digits : Nat) +theorem tensorG1_coord_diff (base : R) (digits : Nat) {c c' : PolyVec (Rq Φ) blocks} {j : Fin blocks} (h : CoordinateWise.CoordEq j c c') (x : PolyVec (Rq Φ) (blocks * digits)) : tensorG1 Φ base digits (c - c') x @@ -214,58 +189,4 @@ theorem tensorG1_coordDiff (base : R) (digits : Nat) end TensorG1 -/-- `splitForm` is subtractive in the inner (right) basis vector (companion to Vectors.lean's -`splitForm_add_right`; Hachi Lemma 8's c4-side two-transcript subtraction `aᵀG(z⁽ʲ⁾) − aᵀG(z⁽⁰⁾)` -of Eq. (20) row 4). -/ -theorem splitForm_sub_right {P : Type} [CommRing P] {n m : ℕ} (M : PolyMatrix P n m) - (u : PolyVec P n) (v w : PolyVec P m) : - ArkLib.Lattices.splitForm M u (v - w) - = ArkLib.Lattices.splitForm M u v - ArkLib.Lattices.splitForm M u w := by - simp only [ArkLib.Lattices.splitForm, matVecMul_sub, dot_sub] - -/-! ## Interface checks: the c3/c4/c5 verifier rows of Eq. (20) read off these defs -/ - -section InterfaceChecks -variable {messageRows messageDigits zDigits blocks innerRows innerDigits : Nat} - --- c3 (Eq. 20 row 3): `bᵀ (G_{2ʳ} ŵ) = 𝓎` on the flat carrier decomposition `ŵ`. -example (base : R) (b : PolyVec (Rq Φ) blocks) (what : PolyVec (Rq Φ) (blocks * messageDigits)) - (y : Rq Φ) : Prop := - dot b (gadgetMatrix Φ base blocks messageDigits *ᵥ what) = y - --- c4 (Eq. 20 row 4): `(cᵀ ⊗ G₁) ŵ = aᵀ G_{2^m} (J ẑ)` reads off `tensorG1`/`jMatrix`. -example (base : R) (c : PolyVec (Rq Φ) blocks) (what : PolyVec (Rq Φ) (blocks * messageDigits)) - (zhat : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits)) - (a : PolyVec (Rq Φ) messageRows) : Prop := - tensorG1 Φ base messageDigits c what - = ArkLib.Lattices.splitForm (gadgetMatrix Φ base messageRows messageDigits) a - (jMatrix Φ base (messageRows * messageDigits) zDigits *ᵥ zhat) - --- c5 (Eq. 20 row 5): `(cᵀ ⊗ G_{n_A}) t̂ = A (J ẑ)` reads off `tensorG`/`jMatrix`. -example (base : R) (c : PolyVec (Rq Φ) blocks) - (that : PolyVec (PolyVec (Rq Φ) (innerRows * innerDigits)) blocks) - (A : Simple.PublicParams Φ innerRows (messageRows * messageDigits)) - (zhat : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits)) : Prop := - tensorG Φ base innerRows innerDigits c that - = A *ᵥ (jMatrix Φ base (messageRows * messageDigits) zDigits *ᵥ zhat) - --- c4-subtract chain (Lemma 8, extraction step 2): from the two branches' c4 rows sharing `ŵ` --- and `CoordEq j c_s c_e`, the challenge difference isolates `(c_sⱼ − c_eⱼ) · wⱼ`, where --- `w := G_blocks *ᵥ ŵ`. (`z_s`/`z_e` stand for the J-recompositions `J ẑ⁽ʲ⁾`/`J ẑ⁽⁰⁾`.) -example (base : R) {c_s c_e : PolyVec (Rq Φ) blocks} {j : Fin blocks} - (h : CoordinateWise.CoordEq j c_s c_e) - (what : PolyVec (Rq Φ) (blocks * messageDigits)) (a : PolyVec (Rq Φ) messageRows) - (z_s z_e : PolyVec (Rq Φ) (messageRows * messageDigits)) - (hc4s : tensorG1 Φ base messageDigits c_s what - = splitForm (gadgetMatrix Φ base messageRows messageDigits) a z_s) - (hc4e : tensorG1 Φ base messageDigits c_e what - = splitForm (gadgetMatrix Φ base messageRows messageDigits) a z_e) : - (c_s j - c_e j) * (gadgetMatrix Φ base blocks messageDigits *ᵥ what) j - = splitForm (gadgetMatrix Φ base messageRows messageDigits) a (z_s - z_e) := by - rw [splitForm_sub_right, ← hc4s, ← hc4e, ← tensorG1_sub_challenge, - tensorG1_coordDiff Φ base messageDigits h] - -end InterfaceChecks - - end ArkLib.Lattices.Hachi diff --git a/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean b/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean index 75291c71d4..bbbde91cd5 100644 --- a/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean +++ b/ArkLib/Data/Lattices/CyclotomicRing/NormBounds/Basic.lean @@ -240,7 +240,7 @@ theorem sub_lInftyNorm_le {cols : ℕ} (v w : PolyVec (Rq Φ) cols) {bound : ℕ unfold subLInftyNormBound omega -/-! ## Deliverable 1: ℓ₁ triangle inequality for subtraction -/ +/-! ## `ℓ₁` triangle inequality for subtraction -/ /-- **`ℓ₁` subtraction triangle inequality** (ring element): `‖a - b‖₁ ≤ ‖a‖₁ + ‖b‖₁`. -/ theorem Rq.l1Norm_sub_le (a b : Rq Φ) : ‖a - b‖₁ ≤ ‖a‖₁ + ‖b‖₁ := by @@ -252,14 +252,7 @@ theorem Rq.l1Norm_sub_le (a b : Rq Φ) : ‖a - b‖₁ ≤ ‖a‖₁ + ‖b‖ rw [hcoeff] exact valMinAbs_sub_natAbs_le _ _ -/-! ## Deliverable 2: ℓ₁ positivity (the `hpos` bridge for `isUnit_of_l1Norm_le`) -/ - -omit [NeZero q] in -/-- The centered `ℓ₁` norm of `0` is `0`. -/ -theorem Rq.l1Norm_zero : ‖(0 : Rq Φ)‖₁ = 0 := by - unfold Rq.l1Norm - refine Finset.sum_eq_zero fun k _ => ?_ - rw [Rq.zero_val, CompPoly.CPolynomial.coeff_zero, ZMod.valMinAbs_zero, Int.natAbs_zero] +/-! ## `ℓ₁` positivity (the `hpos` bridge for `isUnit_of_l1Norm_le`) -/ omit [NeZero q] in /-- A ring element with zero centered `ℓ₁` norm is `0`: every centered coefficient @@ -293,18 +286,13 @@ theorem Rq.eq_zero_of_l1Norm_eq_zero {x : Rq Φ} (h : ‖x‖₁ = 0) : x = 0 := have hx1 : x.1 = 0 := (CompPoly.CPolynomial.toPoly_eq_zero_iff x.1).mp htoP exact Subtype.ext (by rw [Rq.zero_val]; exact hx1) -omit [NeZero q] in -/-- The centered `ℓ₁` norm vanishes exactly on `0`. -/ -theorem Rq.l1Norm_eq_zero_iff (x : Rq Φ) : ‖x‖₁ = 0 ↔ x = 0 := - ⟨fun h => Rq.eq_zero_of_l1Norm_eq_zero Φ h, fun h => h ▸ Rq.l1Norm_zero Φ⟩ - omit [NeZero q] in /-- **`ℓ₁` positivity.** A nonzero ring element has positive centered `ℓ₁` norm — the `hpos` input to `isUnit_of_l1Norm_le` for the extracted difference challenge `c̄ⱼ ≠ 0`. -/ theorem Rq.l1Norm_pos_of_ne_zero {x : Rq Φ} (hx : x ≠ 0) : 0 < ‖x‖₁ := Nat.pos_of_ne_zero fun h0 => hx (Rq.eq_zero_of_l1Norm_eq_zero Φ h0) -/-! ## Deliverable 3(b): ℓ∞ → ℓ₂² aggregation bridge -/ +/-! ## `ℓ∞ → ℓ₂²` aggregation bridge -/ omit [NeZero q] [IsCyclotomic Φ] in /-- **`ℓ∞ → ℓ₂²` bridge** (ring element): `‖x‖₂² ≤ deg φ · ‖x‖∞²` — each of the `deg φ` @@ -334,7 +322,7 @@ theorem vecL2NormSq_le_card_mul_lInftyNorm_sq {cols : ℕ} (v : PolyVec (Rq Φ) _ = cols * (Φ.φ.natDegree * (vecLInftyNorm Φ v) ^ 2) := by rw [Finset.sum_const, Finset.card_univ, Fintype.card_fin, smul_eq_mul] -/-! ## Deliverable 3(c), constant: the recomposition growth bound -/ +/-! ## The recomposition growth bound -/ /-- Squared-`ℓ₂` bound for a base-`b` gadget **recomposition** `z = J·ẑ` of an `ℓ∞`-range-checked decomposed vector (`‖ẑ‖∞ ≤ γ`): each of the `cols` entries of `z` is a diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean index 9b4ee26243..bfbfb8c025 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/NoChallenge.lean @@ -78,6 +78,7 @@ def onlyTranscript [IsEmpty pSpec.ChallengeIdx] (tree : ChallengeTree pSpec arity 0) : FullTranscript pSpec := (fullTranscripts_eq_singleton tree).choose +/-- The unique full transcript `onlyTranscript` is a member of the tree's `fullTranscripts`. -/ theorem onlyTranscript_mem [IsEmpty pSpec.ChallengeIdx] (tree : ChallengeTree pSpec arity 0) : tree.onlyTranscript ∈ tree.fullTranscripts := by diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.lean new file mode 100644 index 0000000000..5ee5a89080 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Package.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.Composition +import ArkLib.OracleReduction.Composition.Sequential.IsPure + +/-! +# Composable coordinate-wise-special-sound reductions (`CWSSPackage`) + +A `CWSSPackage` bundles a verifier with everything needed to state and reuse its coordinate-wise +special soundness (CWSS): the challenge structure `struct`, the input/output relations +`relIn`/`relOut`, a purity witness `isPure`, and the CWSS certificate `isCWSS`, all with respect to +a fixed sampling `(init, impl)`. + +The point is composition. `CWSSPackage.append` — written with the infix `▷` — chains two packages +along a matching seam (`L₁.relOut = L₂.relIn`, discharged by `rfl`): it appends the verifiers +(`Verifier.append`), appends the structures (`CWSSStructure.append`), composes the purity witnesses +(`Verifier.IsPure.append`), and threads the two certificates through +`Verifier.append_coordinateWiseSpecialSound`. Because purity is a package field, the composed +package is itself pure and can be a left factor again, so a multi-step reduction reads as a single +pleasant chain: + +``` +def chain := head ▷ middle ▷ tail +theorem chain_cwss := chain.isCWSS +``` + +Each protocol component exports its own package next to its CWSS theorem; the composition site only +imports and chains them. `▷` is `scoped` in `CoordinateWise`, so `open scoped CoordinateWise` +(or `open CoordinateWise`) activates it. + +## 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 + +universe u + +namespace CoordinateWise + +variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + +/-- A **bundled coordinate-wise-special-sound reduction**: a verifier together with its CWSS +structure, input/output relations, a purity witness, and the CWSS certificate, all with respect to +a fixed sampling `(init, impl)`. Compose packages with `CWSSPackage.append` / the infix `▷`. -/ +structure CWSSPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (StmtIn WitIn StmtOut WitOut : Type) {n : ℕ} (pSpec : ProtocolSpec n) where + /-- The package's verifier. -/ + verifier : Verifier oSpec StmtIn StmtOut pSpec + /-- The coordinate-wise structure the verifier is special sound for. -/ + struct : CWSSStructure pSpec + /-- The input relation. -/ + relIn : Set (StmtIn × WitIn) + /-- The output relation. -/ + relOut : Set (StmtOut × WitOut) + /-- The verifier is pure: its verdict is a deterministic function of statement and transcript. + Needed to place this package as the left factor of an `append`. -/ + isPure : verifier.IsPure + /-- The certificate: `verifier` is coordinate-wise special sound for `struct`, reducing `relIn` + to `relOut`. -/ + isCWSS : verifier.coordinateWiseSpecialSound init impl struct relIn relOut + +namespace CWSSPackage + +/-- **Compose two packages along a matching seam.** Given a left package `L₁ : relIn ⇒ mid`, a right +package `L₂ : mid ⇒ relOut` whose seam agrees (`hseam : L₁.relOut = L₂.relIn`, discharged by `rfl`), +this produces the composed package `relIn ⇒ relOut` over `pSpec₁ ++ₚ pSpec₂`: the verifiers are +chained by `Verifier.append`, the structures by `CWSSStructure.append`, the purity witnesses by +`Verifier.IsPure.append`, and the certificates by `Verifier.append_coordinateWiseSpecialSound` +(the left package's `isPure` discharges its purity hypothesis). Written infix as `L₁ ▷ L₂`. -/ +def append {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + {StmtA WitA StmtB WitB StmtC WitC : Type} + {m n : ℕ} {pSpec₁ : ProtocolSpec m} {pSpec₂ : ProtocolSpec n} + [∀ i, SampleableType (pSpec₁.Challenge i)] + (L₁ : CWSSPackage init impl StmtA WitA StmtB WitB pSpec₁) + (L₂ : CWSSPackage init impl StmtB WitB StmtC WitC pSpec₂) + (hseam : L₁.relOut = L₂.relIn := by rfl) : + CWSSPackage 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 + isPure := Verifier.IsPure.append L₁.verifier L₂.verifier L₁.isPure L₂.isPure + isCWSS := by + obtain ⟨verify₁, hV₁⟩ := L₁.isPure.is_pure + have h₂ := L₂.isCWSS + rw [← hseam] at h₂ + exact Verifier.append_coordinateWiseSpecialSound init impl + L₁.verifier L₂.verifier L₁.struct L₂.struct verify₁ hV₁ L₁.isCWSS h₂ + +end CWSSPackage + +@[inherit_doc CWSSPackage.append] +scoped infixr:65 " ▷ " => CWSSPackage.append + +end CoordinateWise + +end diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean index 0a433da285..2f634c822c 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound.lean @@ -8,51 +8,38 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose /-! # Single-challenge-round tree navigation (generic CWSS building block) - Reusable navigation for a challenge tree of shape `msgNode(pre) → chalNode → leaves`: exactly - one pre-challenge message, one challenge round, and nothing after. This is the generic core - shared by any coordinate-wise special sound (CWSS) protocol with a single challenge round over - a `2^r`-coordinate challenge vector — in particular Hachi's polynomial-evaluation reduction - (`QuadEval`, Hachi [NOZ26] Lemma 8; originally Greyhound's [NS24, §3.1] folding protocol). - - It provides: - - - the two-round `pSpec` (`P_to_V` carrier commitment, then `V_to_P` challenge vector); - - the index-generic round readers `topMsgAux`/`readPre` (round-0 message) and - `chalsAux`/`readChallenges` (the round-1 sibling-challenge family, *plainly typed* as - `Fin (arity ⟨1, rfl⟩) → Challenge ⟨1, rfl⟩` — the arity is baked into the tree's type, so no - `Σ'`-packaging or arity guard is needed); - - the star tree `tree2` with the reader computation rules (`readPre_tree2`, - `readChallenges_tree2`, both `rfl`); - - **shape recovery** (`tree_shape`, `tree_eq_tree2`): *every* tree of this `pSpec` rooted at - round `0` is a `tree2` — in fact `tree = tree2 (readPre tree) (readChallenges tree)`. This - is what lets the top-level CWSS proof rewrite an arbitrary structured accepting tree into - the synthetic star shape that all branch lemmas below are pinned to; - - the per-branch transcripts `branchPath`/`branchTr` and the navigation lemmas `branch_pre`, - `branch_challenge`, `branch_mem`; - - the CWSS structure `foldStructure` (`ℓ = 2^r`, `k = 2`, arity `2^r + 1`) with - `foldStructure_arity` and `nodeOk_iff_family`; - - the star-center machinery `StarAt`/`central`/`sib`, `exists_starAt`, `sib_coordEq`, and the - orientation/pointwise bridges `coordEq_symm`, `sib_coordEq_ne`, `sib_coordEq_apply_off` - (consumers get `challenges (sib i) i ≠ challenges (central …) i`, hence - `challenges (sib i) i - challenges (central …) i ≠ 0` via `sub_ne_zero_of_ne`, and - off-coordinate agreement for the subtract-and-cancel step); - - the pure-acceptance bridge `branch_relOut_language`; - - the tree extractor `E`, generic over *separate* relation-witness (`WitOut`) and extracted - input-witness (`WitIn`) types: it reads `(v, challenge family)` off the tree, sources one - `WitOut` per branch from `relOut` by classical choice, and hands everything to a caller- - supplied `mkWitness`; and - - the **generic assembly** `coordinateWiseSpecialSound_of_mkWitness`: any pure statement- - extending verifier of this `pSpec` is CWSS for `foldStructure` given only the protocol- - specific witness assembler `hmk` — all tree navigation, shape recovery, guard-firing, and - star-center plumbing is discharged here once. - - This module is milestone **M2** of `Commitments/Functional/Hachi/LEMMA8_FOLDBLOCK_PLAN.md` - (§9.4). + Generic machinery for coordinate-wise special soundness (CWSS) of any two-round protocol — + one prover message, then one challenge vector `Fin (2 ^ r) → C` — such as Hachi's + polynomial-evaluation reduction (`QuadEval`, Hachi [NOZ26] Lemma 8; originally Greyhound's + [NS24, §3.1] folding protocol). + + Main pieces: + + - the two-round `pSpec` and its CWSS structure `foldStructure` + (`ℓ = 2 ^ r`, `k = 2`, arity `2 ^ r + 1`); + - **shape recovery** (`tree_shape`): every challenge tree of this `pSpec` is a star `tree2` — + one message `v`, one node of `arity` sibling challenge vectors, leaves below; + - the **star-center machinery** (`StarAt`, `central`, `sib`, `exists_starAt`, `sib_coordEq`): + a special-sound sibling family has a center and, per coordinate `i`, a sibling differing + from the center exactly at `i`; + - the tree extractor `treeExtractor` and the **generic assembly** + `coordinateWiseSpecialSound_of_mkWitness`: any pure statement-extending verifier of this + `pSpec` is CWSS for `foldStructure`, given only a protocol-specific witness assembler + `mkWitness` turning per-branch `relOut`-witnesses at star-shaped challenge families into a + `relIn`-witness — all tree navigation, shape recovery, and guard-firing is discharged here + once. + + ## References + + * [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] -/ open OracleComp OracleSpec ProtocolSpec ProtocolSpec.ChallengeTree CoordinateWise --- NB: `central`/`sib`/`E` need classical choice; the `linter.style.openClassical` linter (active +-- NB: `central`/`sib`/`treeExtractor` need classical choice; the `linter.style.openClassical` +-- linter (active -- via `mathlibStandardSet`) forbids a file-level `open Classical`, so we use `open Classical in` -- per definition instead. @@ -60,9 +47,9 @@ namespace CoordinateWise.SingleRound /-- The two-round single-challenge-round protocol (instantiated by Hachi's `QuadEval` reduction): the prover sends a carrier commitment `CarrierCom` (round 0, `P_to_V`), the verifier replies with -a challenge vector `Fin (2^r) → C` (round 1, `V_to_P`). -/ +a challenge vector `Fin (2 ^ r) → C` (round 1, `V_to_P`). -/ @[reducible] def pSpec (CarrierCom C : Type) (r : ℕ) : ProtocolSpec 2 := - ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin (2^r) → C]⟩ + ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin (2 ^ r) → C]⟩ variable {CarrierCom C : Type} {r : ℕ} {arity : (pSpec CarrierCom C r).ChallengeIdx → ℕ} @@ -72,10 +59,7 @@ variable {CarrierCom C : Type} {r : ℕ} Naive `match tree` on a `ChallengeTree … 0` fails ("dependent elimination failed"), so each reader is index-generic: it matches at an arbitrary round index `a` and carries the proof `a = 0` (resp. `a = 1`), discharged per constructor via `congrArg Fin.val` + -`Direction.noConfusion`. Since the arity function is a parameter of `ChallengeTree`'s *type* -(and challenge-index proofs are definitionally irrelevant), the sibling family stored in the -round-1 `chalNode` already has the plain type `Fin (arity ⟨1, rfl⟩) → Challenge ⟨1, rfl⟩` — no -`Σ' K, Fin K → _` packaging is needed. -/ +`Direction.noConfusion`. -/ /-- Index-generic round-0 message reader: peel the top `msgNode` of a tree at any index `a` together with a proof `a = 0`. -/ @@ -127,7 +111,7 @@ where /-! ## The star tree and shape recovery -/ /-- The star tree: one message node carrying `v`, one challenge node carrying the sibling -family, leaves below. Every tree of this `pSpec` has this shape (`tree_eq_tree2`). -/ +family, leaves below. Every tree of this `pSpec` has this shape (`tree_shape`). -/ def tree2 (v : CarrierCom) (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : ChallengeTree (pSpec CarrierCom C r) arity 0 := @@ -186,22 +170,15 @@ theorem tree_shape_aux : {a : Fin 3} → (t : ChallengeTree (pSpec CarrierCom C obtain rfl : m = 0 := Fin.ext (by have := congrArg Fin.val ha; simpa using this) exact absurd h Direction.noConfusion -/-- **Shape recovery (existential form).** Every full tree of the two-round `pSpec` is a star -tree. -/ +/-- **Shape recovery.** Every full tree of the two-round `pSpec` is a star tree. This is the +rewrite that turns an arbitrary structured accepting tree into the synthetic `tree2` that the +branch lemmas (`branch_pre`/`branch_challenge`/`branch_mem`/`branch_relOut_language`) are +pinned to. -/ theorem tree_shape (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : ∃ v challenges, tree = tree2 (arity := arity) v challenges := by obtain ⟨v, challenges, h⟩ := tree_shape_aux tree rfl exact ⟨v, challenges, eq_of_heq h⟩ -/-- **Shape recovery.** Every full tree of the two-round `pSpec` *is* the star tree of its -reads: `tree = tree2 (readPre tree) (readChallenges tree)`. This is the load-bearing rewrite -that turns an arbitrary structured accepting tree into the synthetic `tree2` that the branch -lemmas (`branch_pre`/`branch_challenge`/`branch_mem`/`branch_relOut_language`) are pinned to. -/ -theorem tree_eq_tree2 (tree : ChallengeTree (pSpec CarrierCom C r) arity 0) : - tree = tree2 (readPre tree) (readChallenges tree) := by - obtain ⟨v, challenges, rfl⟩ := tree_shape tree - rfl - /-! ## Per-branch transcripts -/ /-- The root-to-leaf path through `tree2` selecting branch `j` of the challenge node. Defined @@ -247,33 +224,33 @@ theorem branch_mem (v : CarrierCom) /-! ## The CWSS structure -/ /-- The single-round CWSS structure (Hachi's `QuadEval` reduction, Hachi Lemma 8): the single -challenge round carries `ℓ = 2^r` coordinates over -the alphabet `C`, decomposed by the identity (`Challenge ⟨1, rfl⟩ = (Fin (2^r) → C)` already), -with soundness parameter `k = 2`; hence arity `2^r·(2−1)+1 = 2^r+1` and -`nodeOk = IsSpecialSoundFamily (2^r) 2` — the branching of Hachi Lemma 8 / Def. 3. -/ +challenge round carries `ℓ = 2 ^ r` coordinates over +the alphabet `C`, decomposed by the identity (`Challenge ⟨1, rfl⟩ = (Fin (2 ^ r) → C)` already), +with soundness parameter `k = 2`; hence arity `2 ^ r·(2−1)+1 = 2 ^ r + 1` and +`nodeOk = IsSpecialSoundFamily (2 ^ r) 2` — the branching of Hachi Lemma 8 / Def. 3. -/ def foldStructure : CWSSStructure (pSpec CarrierCom C r) where - coordIndex := fun _ => ⟨2^r, Nat.two_pow_pos r⟩ + coordIndex := fun _ => ⟨2 ^ r, Nat.two_pow_pos r⟩ alphabet := fun _ => C decompose := fun i => Equiv.cast (by rcases i with ⟨j, hj⟩; fin_cases j · exact (Direction.noConfusion hj : _) · rfl) soundnessParam := fun _ => ⟨2, le_refl 2⟩ - arity := fun _ => 2^r * (2 - 1) + 1 + arity := fun _ => 2 ^ r * (2 - 1) + 1 arity_eq := rfl -/-- The single-round arity is `2^r + 1` (propositionally — `2^r * (2 - 1) + 1` is not `rfl`-equal -to `2^r + 1`; this is the bridge the extractor's `Fin.cast` uses). -/ +/-- The single-round arity is `2 ^ r + 1` (propositionally — `2 ^ r * (2 - 1) + 1` is not +`rfl`-equal to `2 ^ r + 1`; this is the bridge the extractor's `Fin.cast` uses). -/ theorem foldStructure_arity : - (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity ⟨1, rfl⟩ = 2^r + 1 := by + (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity ⟨1, rfl⟩ = 2 ^ r + 1 := by simp [foldStructure] -/-- The single-round node predicate is exactly the `SS(C, 2^r, 2)` condition on the sibling +/-- The single-round node predicate is exactly the `SS(C, 2 ^ r, 2)` condition on the sibling family (Hachi Lemma 8 / Def. 3). -/ theorem nodeOk_iff_family (challenges : Fin ((foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) : (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).nodeOk ⟨1, rfl⟩ challenges ↔ - IsSpecialSoundFamily (2^r) 2 + IsSpecialSoundFamily (2 ^ r) 2 (fun j => (challenges (Fin.cast (by simp [foldStructure]) j))) := by unfold CWSSStructure.nodeOk simp only [foldStructure, CWSSStructure.ell, CWSSStructure.k] @@ -302,7 +279,7 @@ noncomputable def sib {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [No /-- A special-sound family has a star center (promotes the family's central index; needs `2 ≤ k` so each coordinate's sibling set is nonempty). -/ -theorem exists_starAt {ℓ k K : ℕ} (hk : 2 ≤ k) (hK : K = ℓ*(k-1)+1) +theorem exists_starAt {ℓ k K : ℕ} (hk : 2 ≤ k) (hK : K = ℓ * (k - 1) + 1) (challenges : Fin K → (Fin ℓ → C)) (hfam : IsSpecialSoundFamily ℓ k (fun j => challenges (Fin.cast hK.symm j))) : ∃ e, StarAt challenges e := by @@ -333,13 +310,6 @@ theorem sib_coordEq_ne {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [N challenges (sib challenges i) i ≠ challenges (central challenges) i := (coordEq_symm (sib_coordEq challenges hstar i)).1 -/-- Sibling-first pointwise agreement off coordinate `i` (the off-coordinate cancellation of the -subtract-and-divide step). -/ -theorem sib_coordEq_apply_off {ℓ K : ℕ} (challenges : Fin K → (Fin ℓ → C)) [Nonempty (Fin K)] - (hstar : ∃ e, StarAt challenges e) (i : Fin ℓ) {j : Fin ℓ} (hj : j ≠ i) : - challenges (sib challenges i) j = challenges (central challenges) j := - ((sib_coordEq challenges hstar i).2 j hj).symm - /-! ## Pure-acceptance bridge and extractor -/ section Bridge @@ -351,9 +321,9 @@ verifier output `(stmtIn, v, challenges j)` in `relOut.language` — for any pur outputs the statement extended by the transcript's message and challenge. -/ theorem branch_relOut_language (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2^r) → C)) (pSpec CarrierCom C r)) + (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2 ^ r) → C)) (pSpec CarrierCom C r)) (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) - (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) + (relOut : Set ((StmtIn × CarrierCom × (Fin (2 ^ r) → C)) × WitOut)) (stmtIn : StmtIn) (v : CarrierCom) (challenges : Fin (arity ⟨1, rfl⟩) → (pSpec CarrierCom C r).Challenge ⟨1, rfl⟩) (hAcc : (tree2 v challenges).IsAccepting init impl V stmtIn relOut.language) @@ -368,21 +338,21 @@ end Bridge open Classical in /-- The tree extractor, generic over separate witness types: `relOut` relates the extended statement to a per-branch response `WitOut`; `mkWitness` assembles the extracted input witness -`WitIn` from the shared message, the `2^r + 1` sibling challenge vectors, and one classically +`WitIn` from the shared message, the `2 ^ r + 1` sibling challenge vectors, and one classically chosen `WitOut` per branch (`Classical.ofNonempty` where none exists — on structured accepting trees `branch_relOut_language` fires every guard). Hypothesis-free: all correctness is proven downstream. -/ -noncomputable def E {StmtIn WitOut WitIn : Type} [Nonempty WitOut] - (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) - (mkWitness : StmtIn → CarrierCom → (Fin (2^r+1) → (Fin (2^r) → C)) → - (Fin (2^r+1) → WitOut) → WitIn) : +noncomputable def treeExtractor {StmtIn WitOut WitIn : Type} [Nonempty WitOut] + (relOut : Set ((StmtIn × CarrierCom × (Fin (2 ^ r) → C)) × WitOut)) + (mkWitness : StmtIn → CarrierCom → (Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) → + (Fin (2 ^ r + 1) → WitOut) → WitIn) : Extractor.TreeBased StmtIn WitIn (pSpec CarrierCom C r) (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)).arity := fun stmtIn tree => let v := readPre tree - let fam : Fin (2^r+1) → (Fin (2^r) → C) := fun j => + let fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → C) := fun j => readChallenges tree (Fin.cast foldStructure_arity.symm j) - let resp : Fin (2^r+1) → WitOut := fun j => + let resp : Fin (2 ^ r + 1) → WitOut := fun j => if h : ∃ w, ((stmtIn, v, fam j), w) ∈ relOut then h.choose else Classical.ofNonempty mkWitness stmtIn v fam resp @@ -399,25 +369,25 @@ protocol-specific work (Hachi Lemma 8's case split and subtract-divide) lives en `hmk`. -/ theorem coordinateWiseSpecialSound_of_mkWitness (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2^r) → C)) (pSpec CarrierCom C r)) + (V : Verifier oSpec StmtIn (StmtIn × CarrierCom × (Fin (2 ^ r) → C)) (pSpec CarrierCom C r)) (hpure : ∀ s tr, V.verify s tr = pure (s, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩)) (relIn : Set (StmtIn × WitIn)) - (relOut : Set ((StmtIn × CarrierCom × (Fin (2^r) → C)) × WitOut)) - (mkWitness : StmtIn → CarrierCom → (Fin (2^r+1) → (Fin (2^r) → C)) → - (Fin (2^r+1) → WitOut) → WitIn) - (hmk : ∀ stmtIn v (fam : Fin (2^r+1) → (Fin (2^r) → C)) (resp : Fin (2^r+1) → WitOut), + (relOut : Set ((StmtIn × CarrierCom × (Fin (2 ^ r) → C)) × WitOut)) + (mkWitness : StmtIn → CarrierCom → (Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) → + (Fin (2 ^ r + 1) → WitOut) → WitIn) + (hmk : ∀ stmtIn v (fam : Fin (2 ^ r + 1) → (Fin (2 ^ r) → C)) (resp : Fin (2 ^ r + 1) → WitOut), (∀ j, ((stmtIn, v, fam j), resp j) ∈ relOut) → (∃ e, StarAt fam e) → (stmtIn, mkWitness stmtIn v fam resp) ∈ relIn) : V.coordinateWiseSpecialSound init impl (foldStructure (CarrierCom := CarrierCom) (C := C) (r := r)) relIn relOut := by classical - refine ⟨E relOut mkWitness, ?_⟩ + refine ⟨treeExtractor relOut mkWitness, ?_⟩ intro stmtIn tree hStruct hAcc obtain ⟨v, challenges, rfl⟩ := tree_shape tree have harity := (foldStructure_arity (CarrierCom := CarrierCom) (C := C) (r := r)).symm -- each branch's guard fires: per-branch membership in `relOut.language` - have hmem : ∀ j : Fin (2^r+1), + have hmem : ∀ j : Fin (2 ^ r + 1), ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut := by intro j have h := branch_relOut_language init impl V hpure relOut stmtIn v challenges hAcc @@ -426,10 +396,10 @@ theorem coordinateWiseSpecialSound_of_mkWitness -- the sibling family is special sound, hence has a star center have hfam := (nodeOk_iff_family challenges).1 hStruct.1 have hstar : ∃ e, StarAt - (fun j : Fin (2^r+1) => challenges (Fin.cast harity j)) e := + (fun j : Fin (2 ^ r + 1) => challenges (Fin.cast harity j)) e := exists_starAt (le_refl 2) (by omega) _ hfam -- each chosen response satisfies the relation (the extractor's guards fire) - have hbranch : ∀ j : Fin (2^r+1), + have hbranch : ∀ j : Fin (2 ^ r + 1), ((stmtIn, v, challenges (Fin.cast harity j)), if h : ∃ w, ((stmtIn, v, challenges (Fin.cast harity j)), w) ∈ relOut then h.choose else Classical.ofNonempty) ∈ relOut := by @@ -447,12 +417,10 @@ section Instances variable {CarrierCom C : Type} {r : ℕ} [SampleableType C] [OracleInterface CarrierCom] -/-- Hand-written 2-round instances (NOT auto-derived for `ProtocolSpec 2`). -Generic in `C`; with `C := ShortChallenge Φ ω` they discharge from the instance ARGUMENT -`[SampleableType (ShortChallenge Φ ω)]` (see the `example`s in `QuadEval.lean`). -/ +/-- Hand-written 2-round instances (not auto-derived for `ProtocolSpec 2`), generic in `C`. -/ instance : ∀ i, SampleableType ((pSpec CarrierCom C r).Challenge i) | ⟨0, h⟩ => nomatch h - | ⟨1, _⟩ => (inferInstance : SampleableType (Fin (2^r) → C)) + | ⟨1, _⟩ => (inferInstance : SampleableType (Fin (2 ^ r) → C)) instance : ∀ i, OracleInterface ((pSpec CarrierCom C r).Message i) | ⟨0, _⟩ => (inferInstance : OracleInterface CarrierCom) diff --git a/docs/skills/make-pr-ready.md b/docs/skills/make-pr-ready.md index 61a3f42288..d01653c7cf 100644 --- a/docs/skills/make-pr-ready.md +++ b/docs/skills/make-pr-ready.md @@ -28,6 +28,13 @@ Work through these in order. Do not stop until every item is complete. Compute the real PR surface as the **union** of `git diff --stat origin/main...HEAD` (committed) and `git diff --stat HEAD` (uncommitted), and audit/lint every file in that union — not just the committed ones. The whole-tree view is `git diff --stat origin/main`. +- **Separate your own work from a merged-in sibling branch.** If this branch has merged another + in-flight feature branch (e.g. `foo-infra`), the `origin/main...HEAD` scope will include files + that are **byte-identical** to that sibling branch's PR. Detect them with + `git diff --quiet origin/ HEAD -- ` (clean = owned by the sibling PR). Do + **not** audit or "fix" those files here: it is redundant with the sibling PR and any edit invites + a merge conflict. Scope the audit/lint/warning-fix work to files this branch actually authored or + extended (differs from both `origin/main` and the sibling branch). - **If `_generated/` drift is already committed** (the branch committed regenerated outputs, not just dirtied the working tree), `git checkout origin/main -- docs/kb/_generated/` stages a *revert*, and you must **commit it** for the guard to pass — the guard compares the committed @@ -71,7 +78,11 @@ Work through these in order. Do not stop until every item is complete. - `--lint` (`lint-style.sh`) reports **repo-wide pre-existing** style debt — hundreds of `ERR_*` lines in files you did not touch. Do **not** try to clear all of it; scope style fixes to your changed files (lint them individually with - `python3 scripts/lint-style.py `), and treat the **default** `validate.sh` + `python3 scripts/lint-style.py `). **Line length is codepoints, not bytes**: these + files are dense with multi-byte Unicode math (`ℓ₂²`, `∑`, `·ᵥ`, `c̄ⱼ`), so `awk length` / naive + byte counts over-report by 2–3× and can invent dozens of phantom "over-100" lines. Trust + `lint-style.py` and the Lean `linter.style.longLine` (both count codepoints); if in doubt verify + with Python `len(line)`, never `awk`/`wc -c`. Otherwise treat the **default** `validate.sh` (build + Data warning budget + `check-imports` + `check-docs-integrity` + `kb/lint`) as the real gate. Capture its true exit with `rc=$?` on its own line — a trailing `… ; echo "EXIT $?"` reports the `echo`'s exit (always 0) and masks a failing validate. diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index c14e6b0e65..be78c19041 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -81,11 +81,12 @@ home_page/ site assets and assembled website root `f(x) = y` by expressing the evaluation as the quadratic form `bᵀ M a` and folding the `2ʳ` carrier blocks under the challenge vector (hence the name `QuadEval`); it is Hachi's multilinear/inner-outer lift of Greyhound's [NS24, §3.1] folding protocol. `QuadEvalGadgets` - holds the gadget algebra (`PublicParamsD`, the carrier/short commitment `v = D ŵ`, the - `J`-decomposition of `z`, and the `tensorG`/`tensorG1` challenge combinations); `QuadEval` is + holds the gadget algebra (`PublicParamsD`, the honest-prover carrier/short commitment + `v = D ŵ`, the `J`-decomposition of `z`, and the `tensorG`/`tensorG1` challenge + combinations); `QuadEval` is the 2-round protocol, its `relOut` (Eq. (20) + range balls) / `relIn` (weak opening ∨ MSIS(B) ∨ MSIS(D)), the subtract-and-divide extractor `buildWitness`, and **Lemma 8** - (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound(')`, `sorryAx`-free. + (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound`, `sorryAx`-free. The generic tree plumbing lives in `Security/CoordinateWiseSpecialSoundness/SingleRound`; the supporting norm bounds are in `Data/Lattices/CyclotomicRing/NormBounds/Basic` and `GadgetNorms`. `PolynomialQuadraticEq/PolyEvalReduction` adds the **polynomial-level bridge**: a zero-round @@ -142,8 +143,7 @@ home_page/ site assets and assembled website root sequential wrappers. `NoChallenge` also provides `CWSSStructure.ofIsEmpty`, the concrete challenge-free structure used as the left factor when appending a zero-round `ReduceClaim` head (e.g. Hachi's `bridgeVerifier`). `SingleRound` is the generic single-challenge-round navigation - layer - (tree shape recovery `tree_shape`/`tree_eq_tree2`, the star-center machinery, the tree extractor + 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. From 1f0bfcc4e82a53d5558625f742984da0cef33dc0 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Fri, 10 Jul 2026 16:28:03 +0200 Subject: [PATCH 06/21] huge commit --- ArkLib.lean | 17 +- ArkLib/Commitments/Functional/Hachi.lean | 56 +++ .../Commitments/Functional/Hachi/Basic.lean | 274 -------------- .../Functional/Hachi/Commitment.lean | 156 ++++++++ .../Functional/Hachi/Composition.lean | 167 +++++++++ ...olynomialEvalSplit.lean => EvalSplit.lean} | 9 + .../Commitments/Functional/Hachi/Gadget.lean | 302 ++-------------- .../Functional/Hachi/Gadget/Basic.lean | 314 ++++++++++++++++ .../{GadgetNorms.lean => Gadget/Norms.lean} | 66 ++-- .../Functional/Hachi/InnerOuter.lean | 26 +- .../Hachi/InnerOuter/Arithmetic.lean | 8 +- .../Hachi/InnerOuter/Correctness.lean | 34 +- .../Functional/Hachi/InnerOuter/Scheme.lean | 20 +- .../Functional/Hachi/InnerOuter/Security.lean | 36 +- .../Functional/Hachi/QuadEval.lean | 47 +++ .../Bridge.lean} | 44 ++- .../Gadgets.lean} | 41 ++- .../Functional/Hachi/QuadEval/Reduction.lean | 304 ++++++++++++++++ .../QuadEval.lean => QuadEval/Soundness.lean} | 341 ++++-------------- docs/wiki/repo-map.md | 80 ++-- 20 files changed, 1403 insertions(+), 939 deletions(-) create mode 100644 ArkLib/Commitments/Functional/Hachi.lean delete mode 100644 ArkLib/Commitments/Functional/Hachi/Basic.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/Commitment.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/Composition.lean rename ArkLib/Commitments/Functional/Hachi/{PolynomialEvalSplit.lean => EvalSplit.lean} (95%) create mode 100644 ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean rename ArkLib/Commitments/Functional/Hachi/{GadgetNorms.lean => Gadget/Norms.lean} (84%) create mode 100644 ArkLib/Commitments/Functional/Hachi/QuadEval.lean rename ArkLib/Commitments/Functional/Hachi/{PolynomialQuadraticEq/PolyEvalReduction.lean => QuadEval/Bridge.lean} (85%) rename ArkLib/Commitments/Functional/Hachi/{PolynomialQuadraticEq/QuadEvalGadgets.lean => QuadEval/Gadgets.lean} (80%) create mode 100644 ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean rename ArkLib/Commitments/Functional/Hachi/{PolynomialQuadraticEq/QuadEval.lean => QuadEval/Soundness.lean} (61%) diff --git a/ArkLib.lean b/ArkLib.lean index 8fe69ab35d..69348f3a91 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -1,17 +1,22 @@ import ArkLib.AGM.Basic import ArkLib.Commitments.Functional.Basic -import ArkLib.Commitments.Functional.Hachi.Basic +import ArkLib.Commitments.Functional.Hachi +import ArkLib.Commitments.Functional.Hachi.Commitment +import ArkLib.Commitments.Functional.Hachi.Composition +import ArkLib.Commitments.Functional.Hachi.EvalSplit import ArkLib.Commitments.Functional.Hachi.Gadget -import ArkLib.Commitments.Functional.Hachi.GadgetNorms +import ArkLib.Commitments.Functional.Hachi.Gadget.Basic +import ArkLib.Commitments.Functional.Hachi.Gadget.Norms import ArkLib.Commitments.Functional.Hachi.InnerOuter import ArkLib.Commitments.Functional.Hachi.InnerOuter.Arithmetic import ArkLib.Commitments.Functional.Hachi.InnerOuter.Correctness import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security -import ArkLib.Commitments.Functional.Hachi.PolynomialEvalSplit -import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.PolyEvalReduction -import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEval -import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.QuadEvalGadgets +import ArkLib.Commitments.Functional.Hachi.QuadEval +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.KZG.Algebra import ArkLib.Commitments.Functional.KZG.Basic import ArkLib.Commitments.Functional.KZG.Binding diff --git a/ArkLib/Commitments/Functional/Hachi.lean b/ArkLib/Commitments/Functional/Hachi.lean new file mode 100644 index 0000000000..53fe5c1242 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi.lean @@ -0,0 +1,56 @@ +/- +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.Commitment +import ArkLib.Commitments.Functional.Hachi.Composition + +/-! +# Hachi: a Lattice-Based Multilinear Polynomial Commitment + +Formalization of the Hachi [NOZ26] functional commitment — a lattice-based commitment to +multilinear polynomials with evaluation-opening proofs, built on the Greyhound [NS24] +inner-outer Ajtai commitment over the power-of-two cyclotomic ring `Z_q[X] / (X^{2^α} + 1)`. + +**This development is in progress.** Finished and `sorry`-free: the inner-outer commitment +(§4.1) with perfect correctness and the weak-binding reduction to Module-SIS, and the +polynomial-evaluation reduction (§4.2, Lemma 8) with its polynomial-level bridge. Still to come: +the remaining §4.3+/§4.5 subprotocols and the completeness layer (see the `TODO` blocks in +`Composition.lean` and `Commitment.lean`). + +## Folder structure + +The folder `Hachi/` is organized by paper section, each subfolder carrying an umbrella `.lean` +re-export next to it (as this file does for the whole folder): + +* `Gadget/` (§2.1) — the base-`b` Ajtai gadget matrix `G` and its digit-decomposition inverse + `G⁻¹` (`Basic`), with centered `ℓ∞` / `ℓ₂²` norm bounds for both directions (`Norms`). +* `EvalSplit.lean` (§4, Eq. (12)) — multilinear evaluation as the vector–matrix–vector product + `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))`; kept top-level because the future §3 packing head reuses + it over the subfield. +* `InnerOuter/` (§4.1) — the inner-outer Ajtai commitment: the scheme with its weak openings + (`Scheme`), perfect correctness (`Correctness`), the weak-binding reduction to Module-SIS + (`Security`), and the pinned power-of-two ring (`Arithmetic`). +* `QuadEval/` (§4.2, Figure 3) — the quadratic polynomial-evaluation reduction: gadget algebra + (`Gadgets`), protocol data and relations (`Reduction`), Hachi Lemma 8 coordinate-wise special + soundness (`Soundness`), and the zero-round polynomial-level bridge (`Bridge`). +* `Composition.lean` — the CWSS composition home: the finished core + `evalChain = bridgePackage ▷ quadEvalPackage` with its certificate + `eval_coordinateWiseSpecialSound`; every further subprotocol lands as one more `CWSSPackage` + `▷`-appended there. +* `Commitment.lean` — Hachi as a `Commitment.Scheme`: the multilinear eval-oracle interface and + the honest `keygen` / `commit` (the opening `Proof` is a documented `sorry` pending the + remaining subprotocols). + +Generic infrastructure the development builds on: the coordinate-wise-special-soundness notion +and its composition live in `OracleReduction/Security/CoordinateWiseSpecialSoundness/`, the +cyclotomic-ring norm theory in `Data/Lattices/CyclotomicRing/`, and the simple Ajtai commitment +in `Commitments/Ordinary/Ajtai/Simple/`. + +## References + +* [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] +* [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ diff --git a/ArkLib/Commitments/Functional/Hachi/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Basic.lean deleted file mode 100644 index 928eddfe51..0000000000 --- a/ArkLib/Commitments/Functional/Hachi/Basic.lean +++ /dev/null @@ -1,274 +0,0 @@ -/- -Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann --/ -import ArkLib.Commitments.Functional.Hachi.PolynomialQuadraticEq.PolyEvalReduction -import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme -import ArkLib.Commitments.Functional.Basic -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge -import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package - -/-! -# Hachi as a Functional Commitment — the composition home - -This is the designated home of Hachi [NOZ26] as a **functional commitment** and of the growing -n-ary composition of its subprotocols. Each subprotocol is formalized in its own file and exported -as a coordinate-wise-special-sound (CWSS) `CWSSPackage`; 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 `▷`. - -Right now the finished core chains exactly two links — the polynomial-level bridge and `QuadEval` -(`evalChain = bridgePackage ▷ quadEvalPackage`, certificate `eval_coordinateWiseSpecialSound`). The -remaining §3/§4.3+/§4.5 subprotocols are placeholders (see the `TODO` block); each will land as one -more `CWSSPackage` `▷`-appended into the chain. - -## 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/§4.1) — `Gadget`, `GadgetNorms`. -* **Inner-outer Ajtai commitment** + weak binding (§4.1) — - `InnerOuter/{Scheme, Correctness, Security, Arithmetic}`. -* **Multilinear evaluation as a matrix–vector product** (§4) — `PolynomialEvalSplit`. -* **`QuadEval` gadget algebra** (§4.2, Figure 3) — `PolynomialQuadraticEq/QuadEvalGadgets`. -* **`QuadEval` reduction** (§4.2, Lemma 8) — `PolynomialQuadraticEq/QuadEval`, exported as - `quadEvalPackage`. -* **Polynomial-level bridge** (§4.2) — `PolynomialQuadraticEq/PolyEvalReduction`, exported as - `bridgePackage`. -* **Single-round CWSS tree navigation** *(generic)* — - `OracleReduction/Security/CoordinateWiseSpecialSoundness/SingleRound`. -* **`CWSSPackage` + the `▷` chain operator** *(generic)* — - `OracleReduction/Security/CoordinateWiseSpecialSoundness/Package`. - -## The composed verifier chain - -Top-to-bottom is the composition order (each `▷` is one `CWSSPackage`). The `═══` band is the -finished core (`evalChain`); everything else is a placeholder for a future package: - -```text - §3.2/§4.5 partial-evaluations head ── planned (pure, 1 msg) - │ ▷ - ▼ - §3.1 ring-switch packing head ── planned (guarded, 1 msg) - │ ▷ - ▼ - σ₋₁ statement adapter ── planned (0-round ReduceClaim) - │ ▷ - ═══════════════════════ evalChain (finished core) ═══════════════════════ - ▼ - bridge PolyEvalStatement ⇒ QuadEval.relIn bridgePackage (§4.2, 0-round) - │ ▷ - ▼ - QuadEval QuadEval.relIn ⇒ Eq.(20) relOut quadEvalPackage (§4.2, Lemma 8) - ══════════════════════════════════════════════════════════════════════════ - │ ▷ - ▼ - §4.3 Eq.(20) ⇒ R^lin ⇒ HMZ25 lift ⇒ - zero-check rounds ⇒ sumcheck ⇒ final eval ── planned (Lemmas 9–11) - │ ▷ - ▼ - §4.5 recursion handoff ⇒ next iteration ── planned (guarded) -``` - -## Growing the composition - -Each further subprotocol is exported from its file as a `CWSSPackage` and `▷`-appended here; 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`). Guarded subprotocols (the §3.1 head, the sumcheck -rounds, the final-eval and §4.5 handoff — those 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` (every finished factor is -`IsPure` and `seqCompose_succ_eq_append` is `rfl`, so no existing proof is reworked). - -## 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] -* [Lyubashevsky, V., and Seiler, G., *Short, Invertible Elements in Partially Splitting - Cyclotomic Rings and Applications to Lattice-Based Zero-Knowledge Proofs*][LS18] --/ - -namespace ArkLib.Lattices.Ajtai.InnerOuter - -open CompPoly ArkLib.Lattices.CyclotomicModulus -open WeakBinding -open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound - -section Composition - -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 ι} {ω : ℕ} -variable {σ : Type} - -/-- **The composed evaluation reduction** (Hachi [NOZ26, §4.2, Figure 3], `Rq`-level): the bridge -package (link 3) chained with the `QuadEval` package (link 4) via the `CWSSPackage` operator `▷`. -Both packages are defined next to their CWSS theorems in the component files (`bridgePackage` in -`PolyEvalReduction`, `quadEvalPackage` in `QuadEval`); 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 §3/§4.3+ -subprotocol is one more package `▷`-appended here (see the module header). -/ -def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - CWSSPackage init impl - (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) - (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) - (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits - dRows × - CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) - (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) - ((!p[] : ProtocolSpec 0) ++ₚ - pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := - bridgePackage 𝓜(q, α) init impl (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) ▷ - quadEvalPackage init impl hq5 hκ hτ - -/-- **Hachi evaluation reduction — coordinate-wise special soundness (Hachi [NOZ26, §4.2, -Figure 3], `Rq`-level), `sorry`-free.** This is the certificate carried by `evalChain`: the composed -verifier (bridge ⧺ `QuadEval`) is CWSS for `ofIsEmpty.append foldStructure`, reducing the -polynomial-level `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or MSIS(D)) to Hachi -Eq. (20) (`relOut`), pinned to `𝓜(q, α)` with the [LS18] hypotheses of -`quadEval_coordinateWiseSpecialSound`. -/ -theorem eval_coordinateWiseSpecialSound (init : ProbComp σ) - (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} - (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : - ((bridgeVerifier (oSpec := oSpec) (innerRows := innerRows) (messageDigits := messageDigits) - (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r) - 𝓜(q, α)).append - (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α))).coordinateWiseSpecialSound init impl - (CWSSStructure.ofIsEmpty.append - (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) - (C := ShortChallenge 𝓜(q, α) ω) (r := r))) - (relPolyEval 𝓜(q, α) (b : ZMod q) - (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) - (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := - (evalChain init impl hq5 hκ hτ).isCWSS - -end Composition - -/-! ## Functional-commitment scaffolding - -Hachi as a `Commitment.Scheme` (`ArkLib.Commitments.Functional.Basic`) over the multilinear data -`CMlPolynomial (Rq 𝓜(q,α)) (r + m)`. The eval-oracle interface and the honest committer operations -(`keygen` / `commit`) are real; the opening `Proof` is deferred (see the `TODO` block). -/ - -section FunctionalCommitment - -variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} -variable {innerRows outerRows dRows m r : Nat} {ω : ℕ} - -/-- The **multilinear evaluation oracle** on a committed `n`-variable multilinear polynomial: a -query is an evaluation point `x : Vector (Rq 𝓜(q,α)) n` and the answer is `f x`. This is the -`OracleInterface` that makes `CMlPolynomial (Rq 𝓜(q,α)) n` the `Data` of a functional commitment -(`Commitment.Scheme`); `toOC` follows `OracleContext.ofFunction`. -/ -instance multilinearEvalOracleInterface {n : ℕ} : - OracleInterface (CMlPolynomial (Rq 𝓜(q, α)) n) where - Query := Vector (Rq 𝓜(q, α)) n - toOC := - { spec := Vector (Rq 𝓜(q, α)) n →ₒ Rq 𝓜(q, α) - impl := fun p => do return CMlPolynomial.eval (← read) p } - --- `b > 1` is the gadget base used for **all** decompositions. Faithful to Hachi [NOZ26] §2.1/§4.1, --- every coefficient is written in `δ := ⌈log_b q⌉ = Nat.clog b q` base-`b` digits — a single `δ` --- shared by the message gadget `G⁻¹_{2ᵐ}` and the inner gadget `G⁻¹_{n_A}` — so both digit counts --- are `Nat.clog b q` (and `q ≤ bᵟ` holds by `Nat.le_pow_clog`). -variable (b : ℕ) - -variable - [SampleableType (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q))] - [SampleableType (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q)))] - [SampleableType (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q))] - -/-- Honest **key generation**: sample the inner/outer/short Ajtai matrices `(A, B, D)` uniformly -(matching `InnerOuter.commitmentScheme.setup`, extended with the Hachi short-commitment matrix `D`, -Eq. (16)) and return the resulting `PublicParamsD` as both the committer and the verifier key. -/ -def keygen : - ProbComp - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) - dRows × - Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) - (Nat.clog b q) dRows) := do - let A ← $ᵗ (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q)) - let B ← $ᵗ (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q))) - let D ← $ᵗ (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q)) - let pp : - Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) - dRows := - { innerMatrix := A, outerMatrix := B, dMatrix := D } - pure (pp, pp) - -/-- Honest **commitment** to a multilinear polynomial `p`: reshape it into its `2^r × 2^m` -coefficient matrix (`Hachi.toMatrix`, definitionally a `Message 𝓜(q,α) (2^m) (2^r)`), -gadget-decompose it into the per-block messages/inner decompositions with the **canonical base-`b` -digit decomposition** `zmodDigitDecomposition` at the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` -(the `q ≤ bᵟ` obligation is `Nat.le_pow_clog`), and outer-commit (`commitWithDecomps`). -Deterministic; the decommitment is the `Decomp` data. -/ -def commit [DecidableEq (ZMod q)] (hb : 1 < b) - (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) - (Nat.clog b q) dRows) - (p : CMlPolynomial (Rq 𝓜(q, α)) (r + m)) : - Commitment 𝓜(q, α) outerRows × - Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q) := - let decomps := generateDecomps 𝓜(q, α) - (Decomposition.ofDigits 𝓜(q, α) - (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q)) - (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q))) - pp.toPublicParams (Hachi.toMatrix p) - (commitWithDecomps 𝓜(q, α) pp.toPublicParams decomps, decomps) - -/-- **Hachi as a functional commitment** (`Commitment.Scheme`) over the multilinear data -`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable polynomial, with the `r`/`m` split -feeding the outer/inner gadgets. It commits a polynomial directly (no caller-supplied -decompositions): the honest `commit` uses the canonical base-`b` gadget decomposition at the -paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` (Hachi [NOZ26] §2.1/§4.1), shared by the message and -inner gadgets — so `messageDigits`/`innerDigits` are not free parameters. The only parameters are -the gadget base `b` and `1 < b`; the scheme carries the eval oracle -`multilinearEvalOracleInterface`, honest `keygen` / `commit`, committer and verifier key -`PublicParamsD`, and decommitment `Decomp`. - -The `opening` field — the complete opening `Proof` (a `Reduction … Bool Unit`) — is **provisional** -(`sorry`): its boolean verdict is Hachi Eq. (20) membership (`relOut`), which depends on the never- -sent triple `(ŵ, t̂, ẑ)`; it becomes verifier-computable only after the remaining §4.3+ subprotocols -and their honest-prover layer (`QuadEval.prover`'s `computeV`/`computeResp`) are formalized. -Everything else here is real. The stated `pSpec` is the composed evaluation protocol spec -(`!p[] ++ₚ pSpec …`), i.e. the shape the finished opening will run over — see the `TODO` block. -/ -def hachi [DecidableEq (ZMod q)] (hb : 1 < b) : - Commitment.Scheme unifSpec - (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) - (Commitment 𝓜(q, α) outerRows) - (Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q)) - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) - dRows) - (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) - dRows) - ((!p[] : ProtocolSpec 0) ++ₚ - pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where - keygen := keygen b - commit := fun pp p => pure (commit b hb pp p) - opening := sorry - -end FunctionalCommitment - -/-! ## TODO — remaining work toward the full Hachi functional commitment - -The composition (`evalChain` / `eval_coordinateWiseSpecialSound`) is the `Rq`-level CWSS core; the -FC scaffolding (`multilinearEvalOracleInterface`, `keygen`, `commit`, `fc`) is the honest committer -side. Still open, in rough dependency order: - -* **Remaining §3/§4.3+/§4.5 subprotocols.** Each is exported from its file as a `CWSSPackage` and - `▷`-appended into `evalChain`, bridged by 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`). -* **Completeness / honest-prover layer**: instantiate `QuadEval.prover`'s `computeV` / - `computeResp` from the `QuadEvalGadgets` carrier/decomposition definitions - (`carrierCommit`, `zDecomp`), discharging `Commitment.perfectCorrectness` for `fc` — - this is what materializes `fc.opening` (currently `sorry`). --/ - -end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Commitment.lean b/ArkLib/Commitments/Functional/Hachi/Commitment.lean new file mode 100644 index 0000000000..2faa206d84 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Commitment.lean @@ -0,0 +1,156 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.Commitments.Functional.Hachi.QuadEval +import ArkLib.Commitments.Functional.Basic + +/-! +# Hachi as a Functional Commitment + +Hachi [NOZ26] as a `Commitment.Scheme` (`ArkLib.Commitments.Functional.Basic`) over the multilinear +data `CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable multilinear polynomial with +coefficients in the power-of-two cyclotomic ring `Rq 𝓜(q,α) = (ZMod q)[X] / (X^{2^α} + 1)`. This +file supplies what the generic interface asks of a functional commitment: the multilinear +eval-oracle interface (`multilinearEvalOracleInterface`), honest key generation and commitment +(`keygen` / `commit`, using the canonical base-`b` gadget decomposition `zmodDigitDecomposition` at +the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q`, Hachi §2.1/§4.1), and the `hachi` scheme itself. + +The eval-oracle interface and the honest committer operations are real; the opening `Proof` is +deferred (`sorry`, see the `TODO`). The coordinate-wise-special-sound (CWSS) composition the +finished opening will run over lives in the sibling `Composition.lean` +(`evalChain` / `eval_coordinateWiseSpecialSound`). + +## Main definitions + +* `multilinearEvalOracleInterface`: the `OracleInterface` letting a committed polynomial be + queried at an evaluation point, returning its value there — the `Data` of the commitment. +* `keygen`: honest key generation — sample the inner/outer/short Ajtai matrices `(A, B, D)` + uniformly; the resulting `PublicParamsD` serves as both committer and verifier key. +* `commit`: honest commitment — reshape the polynomial into its `2^r × 2^m` coefficient matrix, + gadget-decompose it, and outer-commit; the decommitment is the `Decomp` data. Deterministic. +* `hachi`: the `Commitment.Scheme` value packaging the above; its `opening` field is a documented + `sorry` pending the §4.3+ subprotocols (see the `TODO` block). + +Same namespace/opens discipline as the rest of the Hachi tree +(`namespace ArkLib.Lattices.Ajtai.InnerOuter`, `open WeakBinding`). + +## 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] +-/ + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound + +section FunctionalCommitment + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] {α : ℕ} +variable {innerRows outerRows dRows m r : Nat} {ω : ℕ} + +/-- The **multilinear evaluation oracle** on a committed `n`-variable multilinear polynomial: a +query is an evaluation point `x : Vector (Rq 𝓜(q,α)) n` and the answer is `f x`. This is the +`OracleInterface` that makes `CMlPolynomial (Rq 𝓜(q,α)) n` the `Data` of a functional commitment +(`Commitment.Scheme`); `toOC` follows `OracleContext.ofFunction`. -/ +instance multilinearEvalOracleInterface {n : ℕ} : + OracleInterface (CMlPolynomial (Rq 𝓜(q, α)) n) where + Query := Vector (Rq 𝓜(q, α)) n + toOC := + { spec := Vector (Rq 𝓜(q, α)) n →ₒ Rq 𝓜(q, α) + impl := fun p => do return CMlPolynomial.eval (← read) p } + +-- `b > 1` is the gadget base used for **all** decompositions. Faithful to Hachi [NOZ26] §2.1/§4.1, +-- every coefficient is written in `δ := ⌈log_b q⌉ = Nat.clog b q` base-`b` digits — a single `δ` +-- shared by the message gadget `G⁻¹_{2ᵐ}` and the inner gadget `G⁻¹_{n_A}` — so both digit counts +-- are `Nat.clog b q` (and `q ≤ bᵟ` holds by `Nat.le_pow_clog`). +variable (b : ℕ) + +variable + [SampleableType (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q))] + [SampleableType (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q)))] + [SampleableType (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q))] + +/-- Honest **key generation**: sample the inner/outer/short Ajtai matrices `(A, B, D)` uniformly +(matching `InnerOuter.commitmentScheme.setup`, extended with the Hachi short-commitment matrix `D`, +Eq. (16)) and return the resulting `PublicParamsD` as both the committer and the verifier key. -/ +def keygen : + ProbComp + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) + dRows × + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) + (Nat.clog b q) dRows) := do + let A ← $ᵗ (Simple.PublicParams 𝓜(q, α) innerRows ((2 ^ m) * Nat.clog b q)) + let B ← $ᵗ (Simple.PublicParams 𝓜(q, α) outerRows ((2 ^ r) * (innerRows * Nat.clog b q))) + let D ← $ᵗ (Simple.PublicParams 𝓜(q, α) dRows ((2 ^ r) * Nat.clog b q)) + let pp : + Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) + dRows := + { innerMatrix := A, outerMatrix := B, dMatrix := D } + pure (pp, pp) + +/-- Honest **commitment** to a multilinear polynomial `p`: reshape it into its `2^r × 2^m` +coefficient matrix (`Hachi.toMatrix`, definitionally a `Message 𝓜(q,α) (2^m) (2^r)`), +gadget-decompose it into the per-block messages/inner decompositions with the **canonical base-`b` +digit decomposition** `zmodDigitDecomposition` at the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` +(the `q ≤ bᵟ` obligation is `Nat.le_pow_clog`), and outer-commit (`commitWithDecomps`). +Deterministic; the decommitment is the `Decomp` data. -/ +def commit [DecidableEq (ZMod q)] (hb : 1 < b) + (pp : Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) + (Nat.clog b q) dRows) + (p : CMlPolynomial (Rq 𝓜(q, α)) (r + m)) : + Commitment 𝓜(q, α) outerRows × + Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q) := + let decomps := generateDecomps 𝓜(q, α) + (Decomposition.ofDigits 𝓜(q, α) + (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q)) + (zmodDigitDecomposition b (Nat.clog b q) hb (Nat.le_pow_clog hb q))) + pp.toPublicParams (Hachi.toMatrix p) + (commitWithDecomps 𝓜(q, α) pp.toPublicParams decomps, decomps) + +/-- **Hachi as a functional commitment** (`Commitment.Scheme`) over the multilinear data +`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable polynomial, with the `r`/`m` split +feeding the outer/inner gadgets. It commits a polynomial directly (no caller-supplied +decompositions): the honest `commit` uses the canonical base-`b` gadget decomposition at the +paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` (Hachi [NOZ26] §2.1/§4.1), shared by the message and +inner gadgets — so `messageDigits`/`innerDigits` are not free parameters. The only parameters are +the gadget base `b` and `1 < b`; the scheme carries the eval oracle +`multilinearEvalOracleInterface`, honest `keygen` / `commit`, committer and verifier key +`PublicParamsD`, and decommitment `Decomp`. + +The `opening` field — the complete opening `Proof` (a `Reduction … Bool Unit`) — is **provisional** +(`sorry`): its boolean verdict is Hachi Eq. (20) membership (`relOut`), which depends on the never- +sent triple `(ŵ, t̂, ẑ)`; it becomes verifier-computable only after the remaining §4.3+ subprotocols +and their honest-prover layer (`QuadEval.prover`'s `computeV`/`computeResp`) are formalized. +Everything else here is real. The stated `pSpec` is the composed evaluation protocol spec +(`!p[] ++ₚ pSpec …`), i.e. the shape the finished opening will run over — see the `TODO` block. -/ +def hachi [DecidableEq (ZMod q)] (hb : 1 < b) : + Commitment.Scheme unifSpec + (CMlPolynomial (Rq 𝓜(q, α)) (r + m)) + (Commitment 𝓜(q, α) outerRows) + (Decomp 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) (2 ^ r) (Nat.clog b q)) + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) + dRows) + (Hachi.PublicParamsD 𝓜(q, α) innerRows (2 ^ m) (Nat.clog b q) outerRows (2 ^ r) (Nat.clog b q) + dRows) + ((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) where + keygen := keygen b + commit := fun pp p => pure (commit b hb pp p) + opening := sorry + +end FunctionalCommitment + +/-! ## TODO — completeness / honest-prover layer + +The `opening` field of `hachi` is provisional (`sorry`). Materializing it needs the honest-prover +layer: instantiate `QuadEval.prover`'s `computeV` / `computeResp` from the `QuadEval.Gadgets` +carrier/decomposition definitions (`carrierCommit`, `zDecomp`), discharging +`Commitment.perfectCorrectness` for `hachi`. -/ + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean new file mode 100644 index 0000000000..feaacec632 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.Commitments.Functional.Hachi.QuadEval +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.NoChallenge +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package + +/-! +# 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 §3/§4.3+/§4.5 subprotocols are placeholders (see the `TODO` block); each will land as one +more `CWSSPackage` `▷`-appended into the chain. + +## 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 + +Top-to-bottom is the composition order (each `▷` is one `CWSSPackage`). The `═══` band is the +finished core (`evalChain`); everything else is a placeholder for a future package: + +```text + §3.2/§4.5 partial-evaluations head ── planned (pure, 1 msg) + │ ▷ + ▼ + §3.1 ring-switch packing head ── planned (guarded, 1 msg) + │ ▷ + ▼ + σ₋₁ statement adapter ── planned (0-round ReduceClaim) + │ ▷ + ═══════════════════════ evalChain (finished core) ═══════════════════════ + ▼ + bridge PolyEvalStatement ⇒ QuadEval.relIn bridgePackage (§4.2, 0-round) + │ ▷ + ▼ + QuadEval QuadEval.relIn ⇒ Eq.(20) relOut quadEvalPackage (§4.2, Lemma 8) + ══════════════════════════════════════════════════════════════════════════ + │ ▷ + ▼ + §4.3 Eq.(20) ⇒ R^lin ⇒ HMZ25 lift ⇒ + zero-check rounds ⇒ sumcheck ⇒ final eval ── planned (Lemmas 9–11) + │ ▷ + ▼ + §4.5 recursion handoff ⇒ next iteration ── planned (guarded) +``` + +## Growing the composition + +Each further subprotocol is exported from its file as a `CWSSPackage` and `▷`-appended here; 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`). Guarded subprotocols (the §3.1 head, the sumcheck +rounds, the final-eval and §4.5 handoff — those 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` (every finished factor is +`IsPure` and `seqCompose_succ_eq_append` is `rfl`, so no existing proof is reworked). + +## 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] +* [Lyubashevsky, V., and Seiler, G., *Short, Invertible Elements in Partially Splitting + Cyclotomic Rings and Applications to Lattice-Based Zero-Knowledge Proofs*][LS18] +-/ + +namespace ArkLib.Lattices.Ajtai.InnerOuter + +open CompPoly ArkLib.Lattices.CyclotomicModulus +open WeakBinding +open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound + +section Composition + +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 ι} {ω : ℕ} +variable {σ : Type} + +/-- **The composed evaluation reduction** (Hachi [NOZ26, §4.2, Figure 3], `Rq`-level): the bridge +package (link 3) chained with the `QuadEval` package (link 4) 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 §3/§4.3+ subprotocol is one more package `▷`-appended here (see the module header). -/ +def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + CWSSPackage init impl + (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) + (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) + (QuadEvalStatement 𝓜(q, α) innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits + dRows × + CarrierCom 𝓜(q, α) dRows × (Fin (2 ^ r) → ShortChallenge 𝓜(q, α) ω)) + (QuadEvalResponse 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) + ((!p[] : ProtocolSpec 0) ++ₚ + pSpec (CarrierCom 𝓜(q, α) dRows) (ShortChallenge 𝓜(q, α) ω) r) := + bridgePackage 𝓜(q, α) init impl (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω) ▷ + quadEvalPackage init impl hq5 hκ hτ + +/-- **Hachi evaluation reduction — coordinate-wise special soundness (Hachi [NOZ26, §4.2, +Figure 3], `Rq`-level), `sorry`-free.** This is the certificate carried by `evalChain`: the composed +verifier (bridge ⧺ `QuadEval`) is CWSS for `ofIsEmpty.append foldStructure`, reducing the +polynomial-level `relPolyEval` (a weak eval-consistent opening, or MSIS(B), or MSIS(D)) to Hachi +Eq. (20) (`relOut`), pinned to `𝓜(q, α)` with the [LS18] hypotheses of +`quadEval_coordinateWiseSpecialSound`. -/ +theorem eval_coordinateWiseSpecialSound (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} + (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + ((bridgeVerifier (oSpec := oSpec) (innerRows := innerRows) (messageDigits := messageDigits) + (outerRows := outerRows) (innerDigits := innerDigits) (dRows := dRows) (m := m) (r := r) + 𝓜(q, α)).append + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α))).coordinateWiseSpecialSound init impl + (CWSSStructure.ofIsEmpty.append + (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r))) + (relPolyEval 𝓜(q, α) (b : ZMod q) + (quadEvalBetaSq γ b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) γ (2 * ω)) + (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω γ) := + (evalChain init impl hq5 hκ hτ).isCWSS + +end Composition + +/-! ## TODO — growing the composition + +`evalChain` / `eval_coordinateWiseSpecialSound` is the finished `Rq`-level CWSS core (bridge ▷ +`QuadEval`). Still open: + +* **Remaining §3/§4.3+/§4.5 subprotocols.** Each is exported from its file as a `CWSSPackage` and + `▷`-appended into `evalChain`, bridged by 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`). -/ + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean b/ArkLib/Commitments/Functional/Hachi/EvalSplit.lean similarity index 95% rename from ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean rename to ArkLib/Commitments/Functional/Hachi/EvalSplit.lean index 60344f2f6c..66358fc00c 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialEvalSplit.lean +++ b/ArkLib/Commitments/Functional/Hachi/EvalSplit.lean @@ -37,6 +37,15 @@ _` and the basis vectors `PolyVec (Rq Φ) _` slot directly into the Ajtai commit `ArkLib.Lattices.Ajtai.Simple.commit` (which is itself a `matVecMul`) and the inner-outer construction. +The reshape `toMatrix` has an explicit inverse `toPolynomial`, reading a matrix back into the +coefficient vector along `splitEquiv`, with round-trip lemmas `toMatrix_toPolynomial` / +`toPolynomial_toMatrix`. Through it, the bridge lemma `splitForm_monomialBasis_eq_eval` restates +the bilinear form `splitForm M u v = u ⬝ᵥ (M *ᵥ v)` of *any* matrix `M` against the two monomial +bases as the polynomial-level evaluation `eval (toPolynomial M) (xl ++ xh)`; the Hachi evaluation +bridge (`QuadEval/Bridge.lean`) consumes it to turn its matrix-shaped consistency claim into a +`CMlPolynomial` evaluation claim. `evalSplit` itself is linear in the coefficient vector +(`evalSplit_add` / `evalSplit_smul`), as needed for random-linear-combination / batching steps. + ## Index convention The coefficient/value vectors are indexed **little-endian** (bit `0` is the least significant), as diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget.lean b/ArkLib/Commitments/Functional/Hachi/Gadget.lean index a902a2830a..c8052e0d8a 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget.lean @@ -3,27 +3,31 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Data.Lattices.ModuleSIS -import Mathlib.Data.Nat.Digits.Lemmas -import Mathlib.Data.ZMod.Basic -import Mathlib.Algebra.Field.ZMod +import ArkLib.Commitments.Functional.Hachi.Gadget.Basic +import ArkLib.Commitments.Functional.Hachi.Gadget.Norms /-! # Ajtai Gadget Matrices -The base-`b` gadget matrix `G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic -ring `Rq Φ`, mapping `rows * digits` ring elements to `rows` ring elements, used by the -inner-outer (Greyhound [NS24] / Hachi [NOZ26]) commitment. Gadget entries are *ring -constants* `C(bᵉ)` embedded into `Rq Φ`. `IsLawfulGadgetDecomposition` records when a -decomposition is inverted by gadget multiplication (`G · G⁻¹(x) = x`). - -The norm-reducing inverse `G⁻¹` is the genuine **base-`b` digit decomposition** of the -Hachi paper [NOZ26]: each coefficient of a ring element is written in base `b`, and digit `e` of -each coefficient is placed in the `bᵉ`-slot of its block. This is captured abstractly by -`DigitDecomposition` (a per-coefficient digit map satisfying the base-`b` reconstruction -law) and realized concretely over `ZMod q` by `zmodDigitDecomposition`. The associated -`gadgetDecompose` is then lawful (`gadgetDecompose_lawful`), replacing the earlier -units-place placeholder. +Umbrella for `Hachi/Gadget/`: the base-`b` Ajtai gadget matrix +`G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic ring `Rq Φ` and its norm-reducing +inverse `G⁻¹`, the base-`b` digit decomposition of Hachi [NOZ26, §2.1]. The gadget trades one +ring element for `digits` elements with small coefficients: `G⁻¹` shortens, `G` recombines. It is +the shortness workhorse of the Greyhound [NS24] / Hachi [NOZ26] inner-outer commitment — honest +commitments consist of gadget digits (hence are short), and the verifier's checks recombine them +through `G`. + +## Folder structure + +* `Gadget/Basic.lean` — the algebra: the gadget matrix `G` (`gadgetMatrix` / `gadgetMul`), lawful + decompositions (`IsLawfulGadgetDecomposition`, i.e. `G · G⁻¹(x) = x`), the abstract + per-coefficient digit map (`DigitDecomposition`) with its concrete `ZMod q` instance + (`zmodDigitDecomposition`), and the induced gadget inverse `gadgetDecompose` with its + lawfulness proof. +* `Gadget/Norms.lean` — the analysis: centered `ℓ∞` / `ℓ₂²` shortness of the honest + decomposition `G⁻¹(x)` (feeding perfect correctness in `InnerOuter/Correctness.lean`), and + controlled norm growth of the recomposition `G·ẑ` for any range-checked `ẑ` (feeding Lemma 8 + in `QuadEval/Soundness.lean`). ## References @@ -31,267 +35,3 @@ units-place placeholder. * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over Extension Fields*][NOZ26] -/ - -open CompPoly ArkLib.Lattices ArkLib.Lattices.CyclotomicModulus - -namespace ArkLib.Lattices.Ajtai - -/-! ## Base-`b` reconstruction of `Nat.ofDigits` as a finite sum -/ - -/-- `Nat.ofDigits` as the finite sum of digit-weighted powers over the length of the list. -/ -private theorem ofDigits_eq_sum_range {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) : - Nat.ofDigits β L = ∑ i ∈ Finset.range L.length, (L.getD i 0 : α) * β ^ i := by - induction L with - | nil => simp [Nat.ofDigits] - | cons h t ih => - rw [show Nat.ofDigits β (h :: t) = (h : α) + β * Nat.ofDigits β t from rfl, ih, - List.length_cons, Finset.sum_range_succ', Finset.mul_sum] - simp only [List.getD_cons_succ, List.getD_cons_zero, pow_zero, mul_one, pow_succ] - rw [add_comm] - congr 1 - apply Finset.sum_congr rfl - intro i _ - ring - -/-- `Nat.ofDigits` as a finite sum over any range `D` at least the list length (the extra -high-order digits are zero). -/ -private theorem ofDigits_eq_sum_range_of_len_le {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) - {D : ℕ} (hLD : L.length ≤ D) : - Nat.ofDigits β L = ∑ i ∈ Finset.range D, (L.getD i 0 : α) * β ^ i := by - rw [ofDigits_eq_sum_range β L] - apply Finset.sum_subset (fun x hx => - Finset.mem_range.mpr (lt_of_lt_of_le (Finset.mem_range.mp hx) hLD)) - intro i _ hi - rw [Finset.mem_range, not_lt] at hi - rw [List.getD_eq_default _ _ hi, Nat.cast_zero, zero_mul] - -/-! ## Abstract digit decompositions of the coefficient ring -/ - -section Digit - -variable {R : Type*} [CommSemiring R] - -/-- A base-`base` digit decomposition of the coefficient ring `R`: for each coefficient `c`, -`digit c e` is the `e`-th base-`base` digit, and the `digits` digits reconstruct `c` via -`∑ₑ baseᵉ · digit c e = c`. This is the per-coefficient data behind the Hachi gadget inverse -`G⁻¹`. -/ -structure DigitDecomposition (base : R) (digits : Nat) where - /-- The `e`-th base-`base` digit of a coefficient. -/ - digit : R → Fin digits → R - /-- The digits reconstruct the coefficient: `∑ₑ baseᵉ · digit c e = c`. -/ - reconstruct : ∀ c : R, ∑ e : Fin digits, base ^ (e : ℕ) * digit c e = c - -end Digit - -/-! ## The concrete base-`b` digit decomposition over `ZMod q` -/ - -section ZModDigit - -variable {q : ℕ} [NeZero q] - -/-- The genuine base-`b` (binary, for `b = 2`) digit decomposition over `ZMod q`: digit `e` -of a coefficient `c` is the `e`-th base-`b` digit of its canonical representative `c.val`. -Reconstruction holds whenever `1 < b` and `q ≤ b ^ digits` (so every residue fits in -`digits` base-`b` digits). This is the coefficient-level Hachi `G⁻¹`. -/ -def zmodDigitDecomposition (b digits : ℕ) (hb : 1 < b) (hq : q ≤ b ^ digits) : - DigitDecomposition (R := ZMod q) (b : ZMod q) digits where - digit c e := ((Nat.digits b c.val).getD (e : ℕ) 0 : ZMod q) - reconstruct c := by - set L := Nat.digits b c.val with hL - have hlen : L.length ≤ digits := - (Nat.digits_length_le_iff hb c.val).mpr (lt_of_lt_of_le (ZMod.val_lt c) hq) - calc ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * ((L.getD (e : ℕ) 0 : ZMod q)) - = ∑ e : Fin digits, ((L.getD (e : ℕ) 0 : ZMod q)) * (b : ZMod q) ^ (e : ℕ) := by - apply Finset.sum_congr rfl; intro e _; ring - _ = ∑ i ∈ Finset.range digits, ((L.getD i 0 : ZMod q)) * (b : ZMod q) ^ i := - Fin.sum_univ_eq_sum_range (fun i => (L.getD i 0 : ZMod q) * (b : ZMod q) ^ i) digits - _ = Nat.ofDigits (b : ZMod q) L := (ofDigits_eq_sum_range_of_len_le (b : ZMod q) L hlen).symm - _ = ((Nat.ofDigits b L : ℕ) : ZMod q) := (Nat.coe_ofDigits (ZMod q) b L).symm - _ = ((c.val : ℕ) : ZMod q) := by rw [hL, Nat.ofDigits_digits] - _ = c := ZMod.natCast_zmod_val c - -end ZModDigit - -/-! ## The gadget matrix over `Rq Φ` -/ - -variable {R : Type} [Field R] [BEq R] [LawfulBEq R] [DecidableEq R] - (Φ : CyclotomicModulus R) [IsCyclotomic Φ] - -/-- Embed a base-ring scalar `c : R` as the constant element `C c ∈ Rq Φ`. -/ -def constRq (c : R) : Rq Φ := Rq.mk Φ (CPolynomial.C c) - -/-- Entry of the base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`: -column `j` of row `i` is `base^(j % digits)` when `j / digits = i`, else `0`. -/ -def gadgetEntry (base : R) {rows digits : Nat} (i : Fin rows) (j : Fin (rows * digits)) : Rq Φ := - if j.val / digits = i.val then constRq Φ (base ^ (j.val % digits)) else 0 - -/-- The base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`. -/ -def gadgetMatrix (base : R) (rows digits : Nat) : PolyMatrix (Rq Φ) rows (rows * digits) := - fun i j => gadgetEntry Φ base i j - -/-- Apply the gadget matrix to a decomposed vector. -/ -def gadgetMul (base : R) {rows digits : Nat} (v : PolyVec (Rq Φ) (rows * digits)) : - PolyVec (Rq Φ) rows := - gadgetMatrix Φ base rows digits *ᵥ v - -/-- A gadget decomposition is lawful when gadget multiplication reconstructs its input. -/ -def IsLawfulGadgetDecomposition (base : R) {rows digits : Nat} - (decompose : PolyVec (Rq Φ) rows → PolyVec (Rq Φ) (rows * digits)) : Prop := - ∀ x, gadgetMul Φ base (decompose x) = x - -omit [DecidableEq R] in -@[simp] theorem constRq_one : constRq Φ (1 : R) = 1 := by - have hC : (CompPoly.CPolynomial.C (1 : R)) = 1 := by - refine CompPoly.CPolynomial.eq_iff_coeff.mpr (fun i => ?_) - rw [CompPoly.CPolynomial.coeff_C, CompPoly.CPolynomial.coeff_one] - change Rq.mk Φ (CompPoly.CPolynomial.C 1) = 1 - rw [hC]; rfl - -/-! ## Degree / coefficient facts for reduced representatives -/ - -omit [DecidableEq R] in -/-- `Φ.φ.natDegree`, the truncation length of decompositions, does not exceed `deg φ`. -/ -theorem phi_natDegree_le_degree : (Φ.φ.natDegree : WithBot ℕ) ≤ Φ.φ.toPoly.degree := - le_of_eq (by rw [CompPoly.CPolynomial.natDegree_toPoly, - Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero]) - -omit [DecidableEq R] in -/-- A reduced representative has zero coefficients at and beyond `deg φ`. -/ -theorem coeff_eq_zero_of_natDegree_le (a : Rq Φ) {k : ℕ} (hk : Φ.φ.natDegree ≤ k) : - a.1.coeff k = 0 := by - rw [CompPoly.CPolynomial.coeff_toPoly] - apply Polynomial.coeff_eq_zero_of_degree_lt - calc a.1.toPoly.degree - < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced a.2 - _ = (Φ.φ.natDegree : WithBot ℕ) := by - rw [CompPoly.CPolynomial.natDegree_toPoly] - exact Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero - _ ≤ (k : WithBot ℕ) := by exact_mod_cast hk - -omit [DecidableEq R] in -/-- The constant `constRq Φ c` has underlying polynomial `C c` (no reduction occurs, as -`deg (C c) = 0 < deg φ`). -/ -theorem constRq_val (h1 : 1 ≤ Φ.φ.natDegree) (c : R) : - (constRq Φ c).1 = CompPoly.CPolynomial.C c := by - change Φ.reduce (CompPoly.CPolynomial.C c) = CompPoly.CPolynomial.C c - apply Φ.reduce_eq_self_of_degree_lt - rw [CompPoly.CPolynomial.toPoly_C] - have hpos : (0 : WithBot ℕ) < Φ.φ.toPoly.degree := by - rw [Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero, - ← CompPoly.CPolynomial.natDegree_toPoly] - exact_mod_cast (h1 : 0 < Φ.φ.natDegree) - exact lt_of_le_of_lt Polynomial.degree_C_le hpos - -omit [DecidableEq R] in -/-- Multiplying by the constant `constRq Φ c` scales coefficients by `c`. -/ -theorem constRq_mul_coeff (h1 : 1 ≤ Φ.φ.natDegree) (c : R) (x : Rq Φ) (k : ℕ) : - (constRq Φ c * x).1.coeff k = c * x.1.coeff k := by - have hmul : (constRq Φ c * x).1 = Φ.reduce ((constRq Φ c).1 * x.1) := rfl - have hred : Φ.reduce (CompPoly.CPolynomial.C c * x.1) = CompPoly.CPolynomial.C c * x.1 := by - apply Φ.reduce_eq_self_of_degree_lt - rw [CompPoly.CPolynomial.toPoly_mul, CompPoly.CPolynomial.toPoly_C] - have hx : x.1.toPoly.degree < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced x.2 - rcases eq_or_ne c 0 with hc | hc - · simpa [hc] using lt_of_le_of_lt bot_le hx - · rwa [Polynomial.degree_C_mul hc] - rw [hmul, constRq_val Φ h1, hred] - exact CompPoly.CPolynomial.coeff_C_mul x.1 c k - -/-! ## The gadget product as a block digit-sum -/ - -omit [DecidableEq R] in -/-- The gadget entry at the flattened index `finProdFinEquiv (i', e)` is `constRq (base^e)` -on the diagonal block and `0` elsewhere. -/ -theorem gadgetEntry_finProdFinEquiv (base : R) {rows digits : Nat} (hd : 0 < digits) - (i i' : Fin rows) (e : Fin digits) : - gadgetEntry Φ base i (finProdFinEquiv (i', e)) - = if i' = i then constRq Φ (base ^ (e : ℕ)) else 0 := by - unfold gadgetEntry - have hval : (finProdFinEquiv (i', e)).val = e.val + digits * i'.val := rfl - have hdiv : (finProdFinEquiv (i', e)).val / digits = i'.val := by - rw [hval, Nat.add_mul_div_left _ _ hd, Nat.div_eq_of_lt e.isLt, zero_add] - have hmod : (finProdFinEquiv (i', e)).val % digits = e.val := by - rw [hval, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt e.isLt] - rw [hdiv, hmod] - simp only [Fin.ext_iff] - -omit [DecidableEq R] in -/-- The gadget product, evaluated at row `i`, is the base-weighted sum of the `digits` -slots of block `i`. -/ -theorem gadgetMul_apply (base : R) {rows digits : Nat} (hd : 0 < digits) - (v : PolyVec (Rq Φ) (rows * digits)) (i : Fin rows) : - gadgetMul Φ base v i - = ∑ e : Fin digits, constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by - rw [gadgetMul, matVecMul_apply, dot_eq_sum] - simp only [gadgetMatrix] - rw [← Equiv.sum_comp finProdFinEquiv (fun j => gadgetEntry Φ base i j * v j), - Fintype.sum_prod_type] - rw [Finset.sum_eq_single i] - · apply Finset.sum_congr rfl - intro e _ - rw [gadgetEntry_finProdFinEquiv Φ base hd i i e, if_pos rfl] - · intro i' _ hne - apply Finset.sum_eq_zero - intro e _ - rw [gadgetEntry_finProdFinEquiv Φ base hd i i' e, if_neg hne, zero_mul] - · intro h - exact absurd (Finset.mem_univ i) h - -/-! ## The base-`b` gadget decomposition and its lawfulness - -`gadgetDecompose dd` is the Hachi gadget inverse `G⁻¹` built from a `DigitDecomposition dd`: -block `i`'s slot `e` is the ring element whose `k`-th coefficient is the `e`-th base-`b` -digit of the `k`-th coefficient of `x i`. By the reconstruction law of `dd`, gadget -multiplication recovers `x` (`gadgetDecompose_lawful`), so the inner-outer correctness -theorem instantiates with this genuine binary decomposition. -/ - -variable {base : R} - -/-- The base-`b` gadget decomposition (Hachi `G⁻¹`) induced by a `DigitDecomposition`. -/ -def gadgetDecompose {rows digits : Nat} (dd : DigitDecomposition base digits) - (x : PolyVec (Rq Φ) rows) : PolyVec (Rq Φ) (rows * digits) := - fun j => Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k => dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2) - -/-- Value of `gadgetDecompose` at the flattened index `finProdFinEquiv (i, e)`. -/ -theorem gadgetDecompose_apply {rows digits : Nat} (dd : DigitDecomposition base digits) - (x : PolyVec (Rq Φ) rows) (i : Fin rows) (e : Fin digits) : - gadgetDecompose Φ dd x (finProdFinEquiv (i, e)) - = Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k => dd.digit ((x i).1.coeff k) e) := by - unfold gadgetDecompose - simp only [Equiv.symm_apply_apply] - -/-- The base-`b` gadget decomposition is a lawful gadget decomposition. -/ -theorem gadgetDecompose_lawful {rows digits : Nat} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) - (dd : DigitDecomposition base digits) : - IsLawfulGadgetDecomposition Φ base (gadgetDecompose Φ dd (rows := rows)) := by - intro x - funext i - rw [gadgetMul_apply Φ base hd] - simp_rw [gadgetDecompose_apply Φ dd x i] - apply Subtype.ext - rw [CompPoly.CPolynomial.eq_iff_coeff] - intro k - have hsum : (∑ e : Fin digits, - constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k - = ∑ e : Fin digits, - (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k := by - rw [← Rq.coeffHom_apply Φ k, map_sum] - simp only [Rq.coeffHom_apply] - have hterm : ∀ e : Fin digits, - (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree - (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k - = base ^ (e : ℕ) * (if k < Φ.φ.natDegree then dd.digit ((x i).1.coeff k) e else 0) := by - intro e - rw [constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (phi_natDegree_le_degree Φ)] - rw [hsum] - simp_rw [hterm] - by_cases hk : k < Φ.φ.natDegree - · simp only [if_pos hk] - exact dd.reconstruct ((x i).1.coeff k) - · simp only [if_neg hk, mul_zero, Finset.sum_const_zero] - exact (coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm - -end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean new file mode 100644 index 0000000000..8892570576 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean @@ -0,0 +1,314 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Tobias Rothmann +-/ +import ArkLib.Data.Lattices.ModuleSIS +import Mathlib.Data.Nat.Digits.Lemmas +import Mathlib.Data.ZMod.Basic +import Mathlib.Algebra.Field.ZMod + +/-! +# Ajtai Gadget Matrices + +The base-`b` gadget matrix `G = I_rows ⊗ [1, b, b², …, b^(digits-1)]` over the cyclotomic +ring `Rq Φ`, mapping `rows * digits` ring elements to `rows` ring elements, used by the +inner-outer (Greyhound [NS24] / Hachi [NOZ26]) commitment. Gadget entries are *ring +constants* `C(bᵉ)` embedded into `Rq Φ`. `IsLawfulGadgetDecomposition` records when a +decomposition is inverted by gadget multiplication (`G · G⁻¹(x) = x`). + +The norm-reducing inverse `G⁻¹` is the genuine **base-`b` digit decomposition** of the Hachi +paper ([NOZ26], §2.1): each coefficient of a ring element is written in base `b`, and digit `e` +of each coefficient is placed in the `bᵉ`-slot of its block. Trading one ring element for +`digits` elements with small digit coefficients is what keeps honest Ajtai openings short +(the norm bounds live in `Gadget.Norms`). The decomposition is captured abstractly by +`DigitDecomposition` (a per-coefficient digit map satisfying the base-`b` reconstruction law) +and realized concretely over `ZMod q` by `zmodDigitDecomposition`. + +## Main definitions + +* `gadgetMatrix`, `gadgetMul`: the gadget matrix `G` and multiplication by it. +* `IsLawfulGadgetDecomposition`: a decomposition is lawful when `G · G⁻¹(x) = x` for all `x`. +* `DigitDecomposition`: abstract base-`b` digit map on the coefficient ring, with the + reconstruction law `∑ₑ bᵉ · digit c e = c`. +* `zmodDigitDecomposition`: the concrete digits of the canonical representative over `ZMod q`, + valid when `1 < b` and `q ≤ b ^ digits`. +* `gadgetDecompose`: the gadget inverse `G⁻¹` induced by a `DigitDecomposition`. + +## Main results + +* `gadgetMul_apply`: row `i` of the gadget product is the base-weighted sum of the `digits` + slots of block `i`. +* `gadgetDecompose_lawful`: `gadgetDecompose` is a lawful gadget decomposition, so the + inner-outer correctness theorem instantiates with this genuine base-`b` decomposition. + +## References + +* [Nguyen, N. K., and Seiler, G., *Greyhound: Fast Polynomial Commitments from Lattices*][NS24] +* [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +open CompPoly ArkLib.Lattices ArkLib.Lattices.CyclotomicModulus + +namespace ArkLib.Lattices.Ajtai + +/-! ## Base-`b` reconstruction of `Nat.ofDigits` as a finite sum -/ + +/-- `Nat.ofDigits` as the finite sum of digit-weighted powers over the length of the list. -/ +private theorem ofDigits_eq_sum_range {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) : + Nat.ofDigits β L = ∑ i ∈ Finset.range L.length, (L.getD i 0 : α) * β ^ i := by + induction L with + | nil => simp [Nat.ofDigits] + | cons h t ih => + rw [show Nat.ofDigits β (h :: t) = (h : α) + β * Nat.ofDigits β t from rfl, ih, + List.length_cons, Finset.sum_range_succ', Finset.mul_sum] + simp only [List.getD_cons_succ, List.getD_cons_zero, pow_zero, mul_one, pow_succ] + rw [add_comm] + congr 1 + apply Finset.sum_congr rfl + intro i _ + ring + +/-- `Nat.ofDigits` as a finite sum over any range `D` at least the list length (the extra +high-order digits are zero). -/ +private theorem ofDigits_eq_sum_range_of_len_le {α : Type*} [CommSemiring α] (β : α) (L : List ℕ) + {D : ℕ} (hLD : L.length ≤ D) : + Nat.ofDigits β L = ∑ i ∈ Finset.range D, (L.getD i 0 : α) * β ^ i := by + rw [ofDigits_eq_sum_range β L] + apply Finset.sum_subset (fun x hx => + Finset.mem_range.mpr (lt_of_lt_of_le (Finset.mem_range.mp hx) hLD)) + intro i _ hi + rw [Finset.mem_range, not_lt] at hi + rw [List.getD_eq_default _ _ hi, Nat.cast_zero, zero_mul] + +/-! ## Abstract digit decompositions of the coefficient ring -/ + +section Digit + +variable {R : Type*} [CommSemiring R] + +/-- A base-`base` digit decomposition of the coefficient ring `R`: for each coefficient `c`, +`digit c e` is the `e`-th base-`base` digit, and the `digits` digits reconstruct `c` via +`∑ₑ baseᵉ · digit c e = c`. This is the per-coefficient data behind the Hachi gadget inverse +`G⁻¹`. -/ +structure DigitDecomposition (base : R) (digits : Nat) where + /-- The `e`-th base-`base` digit of a coefficient. -/ + digit : R → Fin digits → R + /-- The digits reconstruct the coefficient: `∑ₑ baseᵉ · digit c e = c`. -/ + reconstruct : ∀ c : R, ∑ e : Fin digits, base ^ (e : ℕ) * digit c e = c + +end Digit + +/-! ## The concrete base-`b` digit decomposition over `ZMod q` -/ + +section ZModDigit + +variable {q : ℕ} [NeZero q] + +/-- The genuine base-`b` (binary, for `b = 2`) digit decomposition over `ZMod q`: digit `e` +of a coefficient `c` is the `e`-th base-`b` digit of its canonical representative `c.val`. +Reconstruction holds whenever `1 < b` and `q ≤ b ^ digits` (so every residue fits in +`digits` base-`b` digits). This is the coefficient-level Hachi `G⁻¹`. -/ +def zmodDigitDecomposition (b digits : ℕ) (hb : 1 < b) (hq : q ≤ b ^ digits) : + DigitDecomposition (R := ZMod q) (b : ZMod q) digits where + digit c e := ((Nat.digits b c.val).getD (e : ℕ) 0 : ZMod q) + reconstruct c := by + set L := Nat.digits b c.val with hL + have hlen : L.length ≤ digits := + (Nat.digits_length_le_iff hb c.val).mpr (lt_of_lt_of_le (ZMod.val_lt c) hq) + calc ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * ((L.getD (e : ℕ) 0 : ZMod q)) + = ∑ e : Fin digits, ((L.getD (e : ℕ) 0 : ZMod q)) * (b : ZMod q) ^ (e : ℕ) := by + apply Finset.sum_congr rfl; intro e _; ring + _ = ∑ i ∈ Finset.range digits, ((L.getD i 0 : ZMod q)) * (b : ZMod q) ^ i := + Fin.sum_univ_eq_sum_range (fun i => (L.getD i 0 : ZMod q) * (b : ZMod q) ^ i) digits + _ = Nat.ofDigits (b : ZMod q) L := (ofDigits_eq_sum_range_of_len_le (b : ZMod q) L hlen).symm + _ = ((Nat.ofDigits b L : ℕ) : ZMod q) := (Nat.coe_ofDigits (ZMod q) b L).symm + _ = ((c.val : ℕ) : ZMod q) := by rw [hL, Nat.ofDigits_digits] + _ = c := ZMod.natCast_zmod_val c + +end ZModDigit + +/-! ## The gadget matrix over `Rq Φ` -/ + +variable {R : Type} [Field R] [BEq R] [LawfulBEq R] [DecidableEq R] + (Φ : CyclotomicModulus R) [IsCyclotomic Φ] + +/-- Embed a base-ring scalar `c : R` as the constant element `C c ∈ Rq Φ`. -/ +def constRq (c : R) : Rq Φ := Rq.mk Φ (CPolynomial.C c) + +/-- Entry of the base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`: +column `j` of row `i` is `base^(j % digits)` when `j / digits = i`, else `0`. -/ +def gadgetEntry (base : R) {rows digits : Nat} (i : Fin rows) (j : Fin (rows * digits)) : Rq Φ := + if j.val / digits = i.val then constRq Φ (base ^ (j.val % digits)) else 0 + +/-- The base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`. -/ +def gadgetMatrix (base : R) (rows digits : Nat) : PolyMatrix (Rq Φ) rows (rows * digits) := + fun i j => gadgetEntry Φ base i j + +/-- Apply the gadget matrix to a decomposed vector. -/ +def gadgetMul (base : R) {rows digits : Nat} (v : PolyVec (Rq Φ) (rows * digits)) : + PolyVec (Rq Φ) rows := + gadgetMatrix Φ base rows digits *ᵥ v + +/-- A gadget decomposition is lawful when gadget multiplication reconstructs its input. -/ +def IsLawfulGadgetDecomposition (base : R) {rows digits : Nat} + (decompose : PolyVec (Rq Φ) rows → PolyVec (Rq Φ) (rows * digits)) : Prop := + ∀ x, gadgetMul Φ base (decompose x) = x + +omit [DecidableEq R] in +@[simp] theorem constRq_one : constRq Φ (1 : R) = 1 := by + have hC : (CompPoly.CPolynomial.C (1 : R)) = 1 := by + refine CompPoly.CPolynomial.eq_iff_coeff.mpr (fun i => ?_) + rw [CompPoly.CPolynomial.coeff_C, CompPoly.CPolynomial.coeff_one] + change Rq.mk Φ (CompPoly.CPolynomial.C 1) = 1 + rw [hC]; rfl + +/-! ## Degree / coefficient facts for reduced representatives -/ + +omit [DecidableEq R] in +/-- `Φ.φ.natDegree`, the truncation length of decompositions, does not exceed `deg φ`. -/ +theorem phi_natDegree_le_degree : (Φ.φ.natDegree : WithBot ℕ) ≤ Φ.φ.toPoly.degree := + le_of_eq (by rw [CompPoly.CPolynomial.natDegree_toPoly, + Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero]) + +omit [DecidableEq R] in +/-- A reduced representative has zero coefficients at and beyond `deg φ`. -/ +theorem coeff_eq_zero_of_natDegree_le (a : Rq Φ) {k : ℕ} (hk : Φ.φ.natDegree ≤ k) : + a.1.coeff k = 0 := by + rw [CompPoly.CPolynomial.coeff_toPoly] + apply Polynomial.coeff_eq_zero_of_degree_lt + calc a.1.toPoly.degree + < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced a.2 + _ = (Φ.φ.natDegree : WithBot ℕ) := by + rw [CompPoly.CPolynomial.natDegree_toPoly] + exact Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero + _ ≤ (k : WithBot ℕ) := by exact_mod_cast hk + +omit [DecidableEq R] in +/-- The constant `constRq Φ c` has underlying polynomial `C c` (no reduction occurs, as +`deg (C c) = 0 < deg φ`). -/ +theorem constRq_val (h1 : 1 ≤ Φ.φ.natDegree) (c : R) : + (constRq Φ c).1 = CompPoly.CPolynomial.C c := by + change Φ.reduce (CompPoly.CPolynomial.C c) = CompPoly.CPolynomial.C c + apply Φ.reduce_eq_self_of_degree_lt + rw [CompPoly.CPolynomial.toPoly_C] + have hpos : (0 : WithBot ℕ) < Φ.φ.toPoly.degree := by + rw [Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero, + ← CompPoly.CPolynomial.natDegree_toPoly] + exact_mod_cast (h1 : 0 < Φ.φ.natDegree) + exact lt_of_le_of_lt Polynomial.degree_C_le hpos + +omit [DecidableEq R] in +/-- Multiplying by the constant `constRq Φ c` scales coefficients by `c`. -/ +theorem constRq_mul_coeff (h1 : 1 ≤ Φ.φ.natDegree) (c : R) (x : Rq Φ) (k : ℕ) : + (constRq Φ c * x).1.coeff k = c * x.1.coeff k := by + have hmul : (constRq Φ c * x).1 = Φ.reduce ((constRq Φ c).1 * x.1) := rfl + have hred : Φ.reduce (CompPoly.CPolynomial.C c * x.1) = CompPoly.CPolynomial.C c * x.1 := by + apply Φ.reduce_eq_self_of_degree_lt + rw [CompPoly.CPolynomial.toPoly_mul, CompPoly.CPolynomial.toPoly_C] + have hx : x.1.toPoly.degree < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced x.2 + rcases eq_or_ne c 0 with hc | hc + · simpa [hc] using lt_of_le_of_lt bot_le hx + · rwa [Polynomial.degree_C_mul hc] + rw [hmul, constRq_val Φ h1, hred] + exact CompPoly.CPolynomial.coeff_C_mul x.1 c k + +/-! ## The gadget product as a block digit-sum -/ + +omit [DecidableEq R] in +/-- The gadget entry at the flattened index `finProdFinEquiv (i', e)` is `constRq (base^e)` +on the diagonal block and `0` elsewhere. -/ +theorem gadgetEntry_finProdFinEquiv (base : R) {rows digits : Nat} (hd : 0 < digits) + (i i' : Fin rows) (e : Fin digits) : + gadgetEntry Φ base i (finProdFinEquiv (i', e)) + = if i' = i then constRq Φ (base ^ (e : ℕ)) else 0 := by + unfold gadgetEntry + have hval : (finProdFinEquiv (i', e)).val = e.val + digits * i'.val := rfl + have hdiv : (finProdFinEquiv (i', e)).val / digits = i'.val := by + rw [hval, Nat.add_mul_div_left _ _ hd, Nat.div_eq_of_lt e.isLt, zero_add] + have hmod : (finProdFinEquiv (i', e)).val % digits = e.val := by + rw [hval, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt e.isLt] + rw [hdiv, hmod] + simp only [Fin.ext_iff] + +omit [DecidableEq R] in +/-- The gadget product, evaluated at row `i`, is the base-weighted sum of the `digits` +slots of block `i`. -/ +theorem gadgetMul_apply (base : R) {rows digits : Nat} (hd : 0 < digits) + (v : PolyVec (Rq Φ) (rows * digits)) (i : Fin rows) : + gadgetMul Φ base v i + = ∑ e : Fin digits, constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by + rw [gadgetMul, matVecMul_apply, dot_eq_sum] + simp only [gadgetMatrix] + rw [← Equiv.sum_comp finProdFinEquiv (fun j => gadgetEntry Φ base i j * v j), + Fintype.sum_prod_type] + rw [Finset.sum_eq_single i] + · apply Finset.sum_congr rfl + intro e _ + rw [gadgetEntry_finProdFinEquiv Φ base hd i i e, if_pos rfl] + · intro i' _ hne + apply Finset.sum_eq_zero + intro e _ + rw [gadgetEntry_finProdFinEquiv Φ base hd i i' e, if_neg hne, zero_mul] + · intro h + exact absurd (Finset.mem_univ i) h + +/-! ## The base-`b` gadget decomposition and its lawfulness + +`gadgetDecompose dd` is the Hachi gadget inverse `G⁻¹` built from a `DigitDecomposition dd`: +block `i`'s slot `e` is the ring element whose `k`-th coefficient is the `e`-th base-`b` +digit of the `k`-th coefficient of `x i`. By the reconstruction law of `dd`, gadget +multiplication recovers `x` (`gadgetDecompose_lawful`), so the inner-outer correctness +theorem instantiates with this genuine binary decomposition. -/ + +variable {base : R} + +/-- The base-`b` gadget decomposition (Hachi `G⁻¹`) induced by a `DigitDecomposition`. -/ +def gadgetDecompose {rows digits : Nat} (dd : DigitDecomposition base digits) + (x : PolyVec (Rq Φ) rows) : PolyVec (Rq Φ) (rows * digits) := + fun j => Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k => dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2) + +/-- Value of `gadgetDecompose` at the flattened index `finProdFinEquiv (i, e)`. -/ +theorem gadgetDecompose_apply {rows digits : Nat} (dd : DigitDecomposition base digits) + (x : PolyVec (Rq Φ) rows) (i : Fin rows) (e : Fin digits) : + gadgetDecompose Φ dd x (finProdFinEquiv (i, e)) + = Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k => dd.digit ((x i).1.coeff k) e) := by + unfold gadgetDecompose + simp only [Equiv.symm_apply_apply] + +/-- The base-`b` gadget decomposition is a lawful gadget decomposition. -/ +theorem gadgetDecompose_lawful {rows digits : Nat} (hd : 0 < digits) (h1 : 1 ≤ Φ.φ.natDegree) + (dd : DigitDecomposition base digits) : + IsLawfulGadgetDecomposition Φ base (gadgetDecompose Φ dd (rows := rows)) := by + intro x + funext i + rw [gadgetMul_apply Φ base hd] + simp_rw [gadgetDecompose_apply Φ dd x i] + apply Subtype.ext + rw [CompPoly.CPolynomial.eq_iff_coeff] + intro k + have hsum : (∑ e : Fin digits, + constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k + = ∑ e : Fin digits, + (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k := by + rw [← Rq.coeffHom_apply Φ k, map_sum] + simp only [Rq.coeffHom_apply] + have hterm : ∀ e : Fin digits, + (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k + = base ^ (e : ℕ) * (if k < Φ.φ.natDegree then dd.digit ((x i).1.coeff k) e else 0) := by + intro e + rw [constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (phi_natDegree_le_degree Φ)] + rw [hsum] + simp_rw [hterm] + by_cases hk : k < Φ.φ.natDegree + · simp only [if_pos hk] + exact dd.reconstruct ((x i).1.coeff k) + · simp only [if_neg hk, mul_zero, Finset.sum_const_zero] + exact (coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm + +end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean similarity index 84% rename from ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean rename to ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean index eb93906eb2..697eecbd99 100644 --- a/ArkLib/Commitments/Functional/Hachi/GadgetNorms.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean @@ -3,23 +3,47 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.Gadget +import ArkLib.Commitments.Functional.Hachi.Gadget.Basic import ArkLib.Data.Lattices.CyclotomicRing.NormBounds /-! -# Centered Norm Bounds for the Gadget Decomposition `G⁻¹` +# Centered Norm Bounds for the Gadget Decomposition `G⁻¹` and Recomposition `G·ẑ` -Centered `ℓ₂²` and `ℓ∞` shortness of the Hachi gadget inverse `gadgetDecompose` over `ZMod q`, +Centered `ℓ₂²` and `ℓ∞` norm bounds for both directions of the base-`b` gadget algebra over +`ZMod q`: the gadget matrix `G` packs the powers `1, b, …, b^(digits-1)`, so `G⁻¹` rewrites a +vector over `Rq Φ` in base-`b` digits and `G` recombines it. "Centered" means every coefficient +is measured through its representative in `(-q/2, q/2]` — the shortness measure of the lattice +commitment. + +**Part I — decomposition (honest case).** Shortness of the gadget inverse `gadgetDecompose` when instantiated with the genuine base-`b` digit decomposition `zmodDigitDecomposition`. These are the honest-case norm bounds the inner-outer Ajtai commitment needs for perfect correctness -(`InnerOuter.Correctness.perfectlyCorrect`). - -The single analytic input is `zmodDigit_natAbs_le`: each base-`b` digit, as a centered residue, -has absolute value `≤ b - 1` (under `b - 1 ≤ q/2`, so the residue does not wrap). Everything else -is bookkeeping over the gadget's coefficient layout (`Rq.ofFinCoeff_coeff`). - -This file bridges the gadget algebra (`CommitmentScheme.Ajtai.Gadget`) and the centered norms -(`Data.Lattices.CyclotomicRing.NormBounds`). +(`InnerOuter.Correctness.perfectlyCorrect`). The single analytic input is `zmodDigit_natAbs_le`: +each base-`b` digit, as a centered residue, has absolute value `≤ b - 1` (under `b - 1 ≤ q/2`, +so the residue does not wrap). Everything else is bookkeeping over the gadget's coefficient +layout (`Rq.ofFinCoeff_coeff`). + +**Part II — recomposition (adversarial case).** Multiplying by the gadget matrix (`gadgetMul`) +grows norms controllably for **any** `ℓ∞`-range-bounded input — in particular an adversarial +`ẑ` that merely passed the verifier's range check (Eq. (20) of [NOZ26]), not an honest digit +decomposition. The analytic input is `valMinAbs_natAbs_le`: the centered representative of a +residue is minimal among all its integer representatives, so wraparound of the `ZMod q` powers +`bᵘ` is immaterial. The resulting subtraction bound has exactly the `βSq = 4·B_z` shape that +the extractor's `VerifiedBlock.scaled_short` obligation consumes in Lemma 8 +(`QuadEval.Soundness`). + +This file bridges the gadget algebra (`Hachi.Gadget.Basic`) and the centered norms +(`Data.Lattices.CyclotomicRing.NormBounds`). The Hachi-reduction-specific norm constants +`B_z` / `βSq` that these bounds feed live with Lemma 8 in `QuadEval.Soundness`, not here. + +## Main results + +* `gadgetDecompose_zmod_vecLInftyNorm_le` / `gadgetDecompose_zmod_vecL2NormSq_le`: the honest + decomposition satisfies `‖·‖∞ ≤ b - 1` and `‖·‖₂² ≤ (rows·digits)·(deg φ)·(b-1)²`. +* `gadgetMul_zmod_vecLInftyNorm_le` / `gadgetMul_zmod_vecL2NormSq_le`: if `‖v‖∞ ≤ γ` then + `‖G ·ᵥ v‖∞ ≤ (∑_{u 0, fun _ _ => 0, fun _ => 0⟩⟩ + +/-- The extracted (input-side) witness of Hachi Lemma 8: either a weak `Opening` for `u`, or a +Module-SIS solution for the outer matrix `B`, or one for the short-commitment matrix `D`. -/ +inductive QuadEvalWitness (Φ : CyclotomicModulus R) + (innerRows messageRows messageDigits blocks innerDigits : Nat) where + /-- A weak opening `(sᵢ, t̂ᵢ, c̄ᵢ)ᵢ` for the outer commitment `u`. -/ + | opening (o : Opening Φ innerRows messageRows messageDigits blocks innerDigits) + /-- A Module-SIS solution for the outer matrix `B`. -/ + | msisB (z : ModuleSIS.Solution Φ (blocks * (innerRows * innerDigits))) + /-- A Module-SIS solution for the short-commitment matrix `D`. -/ + | msisD (z : ModuleSIS.Solution Φ (blocks * messageDigits)) + +/-- `QuadEvalWitness` is inhabited (a trivial `msisB` witness). -/ +instance : Nonempty (QuadEvalWitness Φ innerRows messageRows messageDigits blocks innerDigits) := + ⟨.msisB (fun _ => 0)⟩ + +end Defs + +/-! ## The challenge space (over `ZMod q`, where the norms live) -/ + +section ShortChallenge + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + +/-- **The reduction's challenge space, carried by the type** (Hachi §4.2): the paper's +`C ⊆ {c ∈ Rq : ‖c‖₁ ≤ ω}` is rendered as the subtype of `ℓ₁`-short ring elements. The +verifier's relation `relOut` therefore needs NO challenge-norm checks (faithful to Eq. (20), +which never checks the challenge); extraction recovers `‖cᵢ‖₁ ≤ ω` from the subtype property. -/ +def ShortChallenge (Φ : CyclotomicModulus (ZMod q)) (ω : ℕ) : Type := + {c : Rq Φ // Rq.l1Norm Φ c ≤ ω} + +variable {Φ : CyclotomicModulus (ZMod q)} [IsCyclotomic Φ] {ω : ℕ} + +namespace ShortChallenge + +/-- The underlying ring element `c ∈ Rq` of a short challenge (Hachi §4.2's challenge `cᵢ`). -/ +def val (c : ShortChallenge Φ ω) : Rq Φ := Subtype.val c + +omit [NeZero q] [IsCyclotomic Φ] in +/-- The subtype bound: every challenge is `ℓ₁`-short. -/ +theorem l1Norm_le (c : ShortChallenge Φ ω) : ‖c.val‖₁ ≤ ω := Subtype.prop c + +/-- Coordinate difference of two short challenges is `ℓ₁`-bounded by `2ω` — the extractor's +`hshort` for the slack `c̄ⱼ = c_{j,j} - c_{0,j}` (Hachi Lemma 8), for free from the subtype. -/ +theorem l1Norm_val_sub_le (c c' : ShortChallenge Φ ω) : ‖c.val - c'.val‖₁ ≤ 2 * ω := + calc ‖c.val - c'.val‖₁ ≤ ‖c.val‖₁ + ‖c'.val‖₁ := Rq.l1Norm_sub_le Φ c.val c'.val + _ ≤ ω + ω := Nat.add_le_add c.l1Norm_le c'.l1Norm_le + _ = 2 * ω := (Nat.two_mul ω).symm + +omit [NeZero q] [IsCyclotomic Φ] in +/-- `≠` on the subtype transfers to `≠` on the underlying ring elements — the extractor's +nonzero-slack input (`CoordEq` gives subtype-`≠` at the differing coordinate). -/ +theorem val_ne_of_ne {c c' : ShortChallenge Φ ω} (h : c ≠ c') : c.val ≠ c'.val := + fun hval => h (Subtype.ext hval) + +end ShortChallenge + +end ShortChallenge + +/-! ## Eval consistency (Eq. 15) and the relations -/ + +section ZModDefs + +variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] + (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] +variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits + m r : Nat} + +/-- The matrix `M` of Hachi Eq. (15): row `i` = derived message block `G_{2^m} · sᵢ`; rows are +indexed by the outer basis `b`, columns by the inner basis `a`. -/ +def derivedMsgMatrix (base : ZMod q) + (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : + PolyMatrix (Rq Φ) (2 ^ r) (2 ^ m) := fun i k => derivedMessage Φ base o.toDecomp i k + +/-- Eq. (15): the derived messages of the weak opening evaluate to `y` under the split +bilinear form (`splitForm`, argument order `b a` load-bearing). -/ +def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec (Rq Φ) (2 ^ r)) + (y : Rq Φ) (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : Prop := + splitForm (derivedMsgMatrix Φ base o) b a = y + +/-- Shortness predicate for the extracted `D`-kernel witness (Hachi Lemma 8, case (B)): the +`D`-matrix analogue of `outerShort`, at `subLInftyNormBound γ = 2·γ`. -/ +def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool := + fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ) + +/-- **`relOut` — exactly Hachi Eq. (20) plus the `S_b` range checks** on +`((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: + +* c1: `D ŵ = v` +* c2: `B (flatten t̂) = u` +* c3: `bᵀ (G_{2^r} ŵ) = y` (row 3 of Eq. (20), `u_eval`) +* c4: `(cᵀ ⊗ G₁) ŵ = aᵀ G_{2^m} J ẑ` (row 4; challenges coerced from the subtype) +* c5: `(cᵀ ⊗ G_{n_A}) t̂ = A J ẑ` (row 5) +* c6: the `S_b` range checks, as symmetric `ℓ∞` balls `≤ γ`. + +**`S_b` modeling**: Eq. (20) checks `(ŵ, t̂, ẑ) ∈ S_b^…`, whose elements have centered +coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 uses the symmetric ball `‖·‖∞ ≤ γ`, which with `γ ≥ +⌈b/2⌉` **contains** the paper's box — so every Eq.-(20)-valid transcript is `relOut`-valid and +the CWSS theorem covers the paper's verifier. No challenge-norm checks appear (the challenge +TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from +c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `Gadget/Norms.lean`) — both exactly as in the +paper. -/ +def relOut (base : ZMod q) (ω γ : ℕ) : + 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) := + { p | match p with + | ((stmt, v, chals), resp) => + let c : PolyVec (Rq Φ) (2 ^ r) := fun i => (chals i).val + let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := + Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec + -- c1: `D ŵ = v` + Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ + -- c2: `B (flatten t̂) = u` + Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ + -- c3: `bᵀ (G_{2^r} ŵ) = y` + dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ + -- c4: `(cᵀ ⊗ G₁) ŵ = aᵀ (G_{2^m} z)` + Hachi.tensorG1 Φ base messageDigits c resp.carrierDec = + dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ + -- c5: `(cᵀ ⊗ G_{n_A}) t̂ = A z` + Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = + stmt.pp.innerMatrix *ᵥ z ∧ + -- c6: the `S_b` range checks (as `ℓ∞` balls) + vecLInftyNorm Φ resp.carrierDec ≤ γ ∧ + vecLInftyNorm Φ (PolyVec.flattenBlocks resp.innerDec) ≤ γ ∧ + vecLInftyNorm Φ resp.zDec ≤ γ } + +/-- **`relIn` — Hachi Lemma 8's extraction disjunction**: a weak `VerifiedOpening` for `u` that is +also eval-consistent (Eq. 15), or a Module-SIS solution for `B`, or one for `D`. The `.opening` +disjunct is the interface into `outputToModuleSIS_valid_of_verified` for the downstream +cross-run knowledge-soundness step. -/ +def relIn (base : ZMod q) (βSq γ κ : ℕ) : + Set (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × + QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := + { p | match p with + | (stmt, .opening o) => + VerifiedOpening Φ base βSq γ κ stmt.pp.toPublicParams stmt.u o ∧ + evalConsistency Φ base stmt.avec stmt.bvec stmt.y o + | (stmt, .msisB z) => + ModuleSIS.relation Φ (outerShort Φ γ) stmt.pp.outerMatrix z = true + | (stmt, .msisD z) => + ModuleSIS.relation Φ (dShort Φ γ) stmt.pp.dMatrix z = true } + +/-! ## The protocol: pure pass-through verifier and honest prover -/ + +section Protocol + +variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} + +/-- The reduction's verifier (Hachi §4.2, Figure 3) is a **pure pass-through**: it re-emits the +statement, the round-0 carrier commitment `v`, and the round-1 challenge vector. The Eq.-(20) +checks live in `relOut` (the `(ŵ, t̂, ẑ)` triple is never sent — §4.3 proves knowledge of it), +so there is no runtime `guard`. -/ +def verifier : + Verifier oSpec + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × + CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) + (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where + verify := fun stmt tr => pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) + +/-- The honest prover (Hachi §4.2, Figure 3; completeness is out of scope for Lemma 8): round 0 +sends the carrier commitment `v`, round 1 receives the challenge vector, and the output witness +is the `QuadEvalResponse` `(ŵ, t̂, ẑ)` of Eq. (20). The honest computations (`v = D ŵ` with +`ŵ = G⁻¹(w)`, `ẑ = J⁻¹(Σᵢ cᵢ sᵢ)`, …) are the parameters `computeV` / `computeResp`, to be +instantiated by the completeness layer from the `QuadEval/Gadgets` carrier/decomposition +definitions. -/ +def prover (WitIn : Type) + (computeV : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → + WitIn → CarrierCom Φ dRows) + (computeResp : + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → + WitIn → (Fin (2 ^ r) → ShortChallenge Φ ω) → + QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : + Prover oSpec + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) + WitIn + (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) + (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where + PrvState + | 0 => + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn + | 1 => + QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn + | 2 => + (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows + × WitIn) × (Fin (2 ^ r) → ShortChallenge Φ ω) + input := id + sendMessage + | ⟨0, _⟩ => fun st => pure (computeV 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, computeV stmt wit, c), computeResp stmt wit c) + +end Protocol + +end ZModDefs + +end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean similarity index 61% rename from ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean rename to ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean index ef4fef804a..8ee8d8bf77 100644 --- a/ArkLib/Commitments/Functional/Hachi/PolynomialQuadraticEq/QuadEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean @@ -3,42 +3,60 @@ 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.PolynomialQuadraticEq.QuadEvalGadgets -import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security +import ArkLib.Commitments.Functional.Hachi.QuadEval.Reduction +import ArkLib.Commitments.Functional.Hachi.Gadget.Norms import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SingleRound import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! - # Hachi polynomial-evaluation reduction (QuadEval) — coordinate-wise special soundness + # Hachi polynomial-evaluation reduction (`QuadEval`) — coordinate-wise special soundness (Hachi Lemma 8) - Hachi's polynomial-evaluation reduction proves `f(x) = y` by rewriting the evaluation as the - quadratic form `bᵀ M a` and folding the `2ʳ` carrier blocks under the verifier's challenge - vector — hence the name `QuadEval`. It is Hachi's multilinear / inner-outer lift of Greyhound's - [NS24, §3.1] polynomial-evaluation protocol. - - **Lemma 8** (Hachi [NOZ26] §4.2, Figure 3, p. 17–18): this reduction is coordinate-wise special - sound. From `2ʳ+1` accepting transcripts with challenge vectors in `SS(C, 2ʳ, 2)`, the tree - extractor either reconstructs a valid weak `InnerOuter.Opening` by subtract-and-divide, or - outputs a Module-SIS solution for `B` or `D`. - - The reduction is modeled as the two-round - `pSpec ⟨!v[.P_to_V, .V_to_P], !v[CarrierCom, Fin 2ʳ → C]⟩`: round 0 (P→V) sends the short - commitment `v = D ŵ`; round 1 (V→P) is the challenge vector. The triple `(ŵ, t̂, ẑ)` is the - **output witness** (`QuadEvalResponse`, never sent — §4.3 proves knowledge of it instead), so - the verifier is a pure pass-through and the extractor sources per-branch triples from - `relOut.language`. - - Contents: the reduction's types and relations (`QuadEvalStatement`, `QuadEvalResponse`, - `QuadEvalWitness`, `ShortChallenge`, `relOut` = Eq. (20) + range checks, `relIn` = weak opening - ∨ MSIS(B) ∨ MSIS(D)); the pure `verifier` and the honest `prover` skeleton; the extractor - (`extractedOpening`, `buildWitness`) with its correctness lemmas; and the top-level theorem - `quadEval_coordinateWiseSpecialSound`. - - The file sits inside `namespace ArkLib.Lattices.Ajtai.InnerOuter` (required: that namespace - activates the scoped `PolyVec`/`*ᵥ`/`•ᵥ`/`dot`/`splitForm`), with `open WeakBinding` (so - `VerifiedOpening`/`outerShort` resolve). Never `open ArkLib.Lattices` here (the `⬝ᵥ` token is - ambiguous between `Matrix.dotProduct` and `ArkLib.Lattices.dot`); spell `dot _ _`. + **Hachi Lemma 8** (Hachi [NOZ26] §4.2, Figure 3, p. 17–18): the `QuadEval` reduction of + `QuadEval/Reduction.lean` is coordinate-wise special sound. Concretely: from `2ʳ+1` accepting + transcripts whose challenge vectors form a star in `SS(C, 2ʳ, 2)` — a central branch plus, for + each coordinate `j`, a sibling branch differing from it exactly at `j` — the tree extractor + either reconstructs a valid weak `InnerOuter.Opening` by subtract-and-divide (subtract the + sibling's response from the central one, then divide by the invertible challenge difference), + or outputs a Module-SIS solution for `B` or `D`. The file is `sorry`-free. + + ## Main definitions + + * `quadEvalZL2SqBound` — the reduction's derived `B_z`: the `ℓ₂²` bound on the recomposed + `z = J ẑ` that follows from Eq. (20)'s range check on `ẑ` alone (no extra verifier check). + * `quadEvalBetaSq` — Lemma 8's `βSq := 4·B_z`, the bound on the extracted `c̄ⱼ •ᵥ sⱼ` fed to + `VerifiedBlock.scaled_short`. + * `extractedOpening` — the subtract-and-divide weak opening assembled from a star of accepting + branches (total, no `IsUnit`/star hypotheses; correctness lives in the lemmas below). + * `buildWitness` — Lemma 8's three-case witness assembler: a divergent inner decomposition `t̂` + gives a `B`-kernel MSIS solution, a divergent carrier decomposition `ŵ` a `D`-kernel one, + and otherwise the star yields `extractedOpening`. + * `quadEvalPackage` — the reduction's `verifier` bundled with its CWSS certificate as a + composable `CWSSPackage`, ready to be `▷`-composed after the polynomial-level bridge. + + ## Main results + + * `msis_of_commit_eq` — two-transcript step of cases (A)/(B): two `γ`-short openings of the + same commitment differ by an `ℓ∞`-short (bound `2·γ`) Module-SIS solution. + * `inner_eq_of_chain` — the unit-cancellation core of subtract-and-divide. + * `slack_isUnit` — the challenge slack `c̄ⱼ` is a unit, by Lyubashevsky–Seiler [LS18] + invertibility of short elements. + * `verifiedOpening_of_star`, `evalConsistency_of_relOut_star` — case (C): the extracted + opening is a `VerifiedOpening` at `βSq`/`γ`/`2ω` and satisfies Eq. (15) eval-consistency. + * `buildWitness_mem_relIn` — every case of `buildWitness` lands in `relIn`; the single math + lemma to which the whole of Lemma 8 reduces. + * `quadEval_coordinateWiseSpecialSound` — **Hachi Lemma 8**: assembled from + `buildWitness_mem_relIn` by the generic `coordinateWiseSpecialSound_of_mkWitness` + (`SingleRound.lean`), which discharges every tree/extractor/guard obligation. + + Mirroring `InnerOuter/Security.lean`, the extraction lemmas carry the Lyubashevsky–Seiler + [LS18] hypotheses `q ≡ 5 (mod 8)`, `(2ω)² < q` (only there does challenge invertibility + enter), so they are stated over the power-of-two modulus `𝓜(q, α)`. An `OracleVerifier` + wrapper is deliberately omitted (see the comment after the top-level theorem); the + plain-`Verifier` statement is the intended Lemma 8 interface. + + Same namespace/opens discipline as `QuadEval/Reduction.lean` + (`namespace ArkLib.Lattices.Ajtai.InnerOuter`, `open WeakBinding`; never `open ArkLib.Lattices`). ## References @@ -55,129 +73,33 @@ open CompPoly ArkLib.Lattices.CyclotomicModulus open WeakBinding open OracleComp OracleSpec ProtocolSpec CoordinateWise CoordinateWise.SingleRound -/-! ## Generic definitions (any coefficient field `R`) -/ - -section Defs - -variable {R : Type} [Field R] [BEq R] [LawfulBEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] -variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits : Nat} - -/-- The carrier commitment space: `v = D ŵ` lives in the `D`-row space. -/ -abbrev CarrierCom (Φ : CyclotomicModulus R) (dRows : Nat) := Simple.Commitment Φ dRows - -/-- Input statement of Hachi's polynomial-evaluation reduction (Hachi §4.2, Figure 3): the public -parameters `(A, B, D)`, the outer commitment `u`, the two evaluation basis vectors -`a ∈ Rq^{2^m}` (`avec`) and `b ∈ Rq^{2^r}` (`bvec`) of Eq. (12), and the claimed evaluation -`y = u_eval`. -/ -structure QuadEvalStatement (Φ : CyclotomicModulus R) - (innerRows messageRows messageDigits outerRows blocks innerDigits dRows : Nat) where - /-- Public matrices `(A, B, D)`. -/ - pp : Hachi.PublicParamsD Φ innerRows messageRows messageDigits outerRows blocks innerDigits dRows - /-- The outer commitment `u`. -/ - u : Commitment Φ outerRows - /-- The inner evaluation basis `aᵀ = (x_{r+1}^{j₁} ⋯ x_l^{j_m})_j ∈ Rq^{2^m}` (Eq. 12). -/ - avec : PolyVec (Rq Φ) messageRows - /-- The outer evaluation basis `bᵀ = (x_1^{i₁} ⋯ x_r^{i_r})_i ∈ Rq^{2^r}` (Eq. 12). -/ - bvec : PolyVec (Rq Φ) blocks - /-- The claimed evaluation `y = u_eval = f(x)`. -/ - y : Rq Φ - -/-- The reduction's output witness `(ŵ, t̂, ẑ)` of Hachi Eq. (20) — Figure 3's final "message", -never sent in the composed protocol (§4.3 proves knowledge of it instead). Block-major layouts -(`finProdFinEquiv`, block = outer index). -/ -structure QuadEvalResponse (Φ : CyclotomicModulus R) - (innerRows messageRows messageDigits blocks innerDigits zDigits : Nat) where - /-- `ŵ := G⁻¹_{2^r}(w)`, the decomposed carrier (block-major, `blocks · messageDigits`). -/ - carrierDec : PolyVec (Rq Φ) (blocks * messageDigits) - /-- `t̂ = (t̂ᵢ)ᵢ`, the per-block inner decompositions. -/ - innerDec : PolyVec (PolyVec (Rq Φ) (innerRows * innerDigits)) blocks - /-- `ẑ := J⁻¹(z)`, the decomposed masked opening (`τ = zDigits` digits). -/ - zDec : PolyVec (Rq Φ) ((messageRows * messageDigits) * zDigits) - -/-- `QuadEvalResponse` is inhabited (the all-zero triple). -/ -instance : Nonempty - (QuadEvalResponse Φ innerRows messageRows messageDigits blocks innerDigits zDigits) := - ⟨⟨fun _ => 0, fun _ _ => 0, fun _ => 0⟩⟩ - -/-- The extracted (input-side) witness of Hachi Lemma 8: either a weak `Opening` for `u`, or a -Module-SIS solution for the outer matrix `B`, or one for the short-commitment matrix `D`. -/ -inductive QuadEvalWitness (Φ : CyclotomicModulus R) - (innerRows messageRows messageDigits blocks innerDigits : Nat) where - /-- A weak opening `(sᵢ, t̂ᵢ, c̄ᵢ)ᵢ` for the outer commitment `u`. -/ - | opening (o : Opening Φ innerRows messageRows messageDigits blocks innerDigits) - /-- A Module-SIS solution for the outer matrix `B`. -/ - | msisB (z : ModuleSIS.Solution Φ (blocks * (innerRows * innerDigits))) - /-- A Module-SIS solution for the short-commitment matrix `D`. -/ - | msisD (z : ModuleSIS.Solution Φ (blocks * messageDigits)) - -/-- `QuadEvalWitness` is inhabited (a trivial `msisB` witness). -/ -instance : Nonempty (QuadEvalWitness Φ innerRows messageRows messageDigits blocks innerDigits) := - ⟨.msisB (fun _ => 0)⟩ - -end Defs - -/-! ## The challenge space (over `ZMod q`, where the norms live) -/ - -section ShortChallenge +/-! ## The constants of Hachi's polynomial-evaluation reduction (`B_z`, `βSq`) -/ -variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] - -/-- **The reduction's challenge space, carried by the type** (Hachi §4.2): the paper's -`C ⊆ {c ∈ Rq : ‖c‖₁ ≤ ω}` is rendered as the subtype of `ℓ₁`-short ring elements. The -verifier's relation `relOut` therefore needs NO challenge-norm checks (faithful to Eq. (20), -which never checks the challenge); extraction recovers `‖cᵢ‖₁ ≤ ω` from the subtype property. -/ -def ShortChallenge (Φ : CyclotomicModulus (ZMod q)) (ω : ℕ) : Type := - {c : Rq Φ // Rq.l1Norm Φ c ≤ ω} - -variable {Φ : CyclotomicModulus (ZMod q)} [IsCyclotomic Φ] {ω : ℕ} - -namespace ShortChallenge +/-- **The reduction's derived `B_z`** (Hachi Lemma 8) — the `ℓ₂²` bound on `z = J_{2^m}·ẑ` that +follows from +Eq. (20)'s range check on `ẑ` alone (no extra verifier check): `z` has `2^m·δ` entries +(`cols = 2^m·δ`, `d = deg φ`, `τ = ⌈log_b β⌉` digits of the `J` gadget), so +`B_z = 2^m·δ · (d · ((∑_{u<τ} bᵘ)·γ)²)`. -/-- The underlying ring element `c ∈ Rq` of a short challenge (Hachi §4.2's challenge `cᵢ`). -/ -def val (c : ShortChallenge Φ ω) : Rq Φ := Subtype.val c +**Honest values (paper footnote):** `γ` plays the paper's `b` — Eq. (20) checks +`ẑ ∈ S_b` (centered coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`, magnitude `≤ b`), which the +symmetric model relaxes to `‖ẑ‖∞ ≤ γ` with `γ := b`. Then the entrywise `ℓ∞` bound +`(∑_{u<τ} bᵘ)·b = b·(b^τ-1)/(b-1) ≤ 2·b^τ` recovers (up to the constant 2) the paper's +derived `‖z⁽ʲ⁾‖∞ ≤ b^τ` (Lemma 8's `β̄ = 2b^τ` slack), and +`B_z ≈ 2^m·δ·d·b^{2τ}` up to small constants. -/ +def quadEvalZL2SqBound (γ b τ d m δ : ℕ) : ℕ := zRecomposeL2SqBound γ b τ d (2 ^ m * δ) -omit [NeZero q] [IsCyclotomic Φ] in -/-- The subtype bound: every challenge is `ℓ₁`-short. -/ -theorem l1Norm_le (c : ShortChallenge Φ ω) : ‖c.val‖₁ ≤ ω := Subtype.prop c - -/-- Coordinate difference of two short challenges is `ℓ₁`-bounded by `2ω` — the extractor's -`hshort` for the slack `c̄ⱼ = c_{j,j} - c_{0,j}` (Hachi Lemma 8), for free from the subtype. -/ -theorem l1Norm_val_sub_le (c c' : ShortChallenge Φ ω) : ‖c.val - c'.val‖₁ ≤ 2 * ω := - calc ‖c.val - c'.val‖₁ ≤ ‖c.val‖₁ + ‖c'.val‖₁ := Rq.l1Norm_sub_le Φ c.val c'.val - _ ≤ ω + ω := Nat.add_le_add c.l1Norm_le c'.l1Norm_le - _ = 2 * ω := (Nat.two_mul ω).symm - -omit [NeZero q] [IsCyclotomic Φ] in -/-- `≠` on the subtype transfers to `≠` on the underlying ring elements — the extractor's -nonzero-slack input (`CoordEq` gives subtype-`≠` at the differing coordinate). -/ -theorem val_ne_of_ne {c c' : ShortChallenge Φ ω} (h : c ≠ c') : c.val ≠ c'.val := - fun hval => h (Subtype.ext hval) +/-- **The reduction's `βSq`** (Hachi Lemma 8) := `subL2NormSqBound B_z = 4·B_z` — the `ℓ₂²` bound +on the extracted `c̄ⱼ •ᵥ sⱼ = z_sib − z_central` fed to `VerifiedBlock.scaled_short`. -/ +def quadEvalBetaSq (γ b τ d m δ : ℕ) : ℕ := subL2NormSqBound (quadEvalZL2SqBound γ b τ d m δ) -end ShortChallenge - -end ShortChallenge - -/-! ## Eval consistency (Eq. 15) and the relations -/ - -section ZModDefs +section Extraction variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits m r : Nat} -/-- The matrix `M` of Hachi Eq. (15): row `i` = derived message block `G_{2^m} · sᵢ`; rows are -indexed by the outer basis `b`, columns by the inner basis `a`. -/ -def derivedMsgMatrix (base : ZMod q) - (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : - PolyMatrix (Rq Φ) (2 ^ r) (2 ^ m) := fun i k => derivedMessage Φ base o.toDecomp i k - -/-- Eq. (15): the derived messages of the weak opening evaluate to `y` under the split -bilinear form (`splitForm`, argument order `b a` load-bearing). -/ -def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec (Rq Φ) (2 ^ r)) - (y : Rq Φ) (o : Opening Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) : Prop := - splitForm (derivedMsgMatrix Φ base o) b a = y - omit [NeZero q] in /-- Eq. (15) for the extracted opening (an internal step of Hachi Lemma 8, case (C)): from c3 (`dot b w = y`) and the c4-subtractions (`wⱼ = dot a (derivedMessage o j)`), the extracted @@ -194,70 +116,6 @@ theorem evalConsistency_of_star (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) rw [c4 j, matVecMul_apply, dot_comm] rfl -/-- Shortness predicate for the extracted `D`-kernel witness (Hachi Lemma 8, case (B)): the -`D`-matrix analogue of `outerShort`, at `subLInftyNormBound γ = 2·γ`. -/ -def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool := - fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ) - -/-- **`relOut` — exactly Hachi Eq. (20) plus the `S_b` range checks** on -`((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: - -* c1: `D ŵ = v` -* c2: `B (flatten t̂) = u` -* c3: `bᵀ (G_{2^r} ŵ) = y` (row 3 of Eq. (20), `u_eval`) -* c4: `(cᵀ ⊗ G₁) ŵ = aᵀ G_{2^m} J ẑ` (row 4; challenges coerced from the subtype) -* c5: `(cᵀ ⊗ G_{n_A}) t̂ = A J ẑ` (row 5) -* c6: the `S_b` range checks, as symmetric `ℓ∞` balls `≤ γ`. - -**`S_b` modeling**: Eq. (20) checks `(ŵ, t̂, ẑ) ∈ S_b^…`, whose elements have centered -coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 uses the symmetric ball `‖·‖∞ ≤ γ`, which with `γ ≥ -⌈b/2⌉` **contains** the paper's box — so every Eq.-(20)-valid transcript is `relOut`-valid and -the CWSS theorem covers the paper's verifier. No challenge-norm checks appear (the challenge -TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from -c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `GadgetNorms.lean`) — both exactly as in the -paper. -/ -def relOut (base : ZMod q) (ω γ : ℕ) : - Set ((QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × - CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) × - QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) := - { p | match p with - | ((stmt, v, chals), resp) => - let c : PolyVec (Rq Φ) (2 ^ r) := fun i => (chals i).val - let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := - Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec - -- c1: `D ŵ = v` - Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ - -- c2: `B (flatten t̂) = u` - Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ - -- c3: `bᵀ (G_{2^r} ŵ) = y` - dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ - -- c4: `(cᵀ ⊗ G₁) ŵ = aᵀ (G_{2^m} z)` - Hachi.tensorG1 Φ base messageDigits c resp.carrierDec = - dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ - -- c5: `(cᵀ ⊗ G_{n_A}) t̂ = A z` - Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = - stmt.pp.innerMatrix *ᵥ z ∧ - -- c6: the `S_b` range checks (as `ℓ∞` balls) - vecLInftyNorm Φ resp.carrierDec ≤ γ ∧ - vecLInftyNorm Φ (PolyVec.flattenBlocks resp.innerDec) ≤ γ ∧ - vecLInftyNorm Φ resp.zDec ≤ γ } - -/-- **`relIn` — Hachi Lemma 8's extraction disjunction**: a weak `VerifiedOpening` for `u` that is -also eval-consistent (Eq. 15), or a Module-SIS solution for `B`, or one for `D`. The `.opening` -disjunct is the interface into `outputToModuleSIS_valid_of_verified` for the downstream -cross-run knowledge-soundness step. -/ -def relIn (base : ZMod q) (βSq γ κ : ℕ) : - Set (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × - QuadEvalWitness Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits) := - { p | match p with - | (stmt, .opening o) => - VerifiedOpening Φ base βSq γ κ stmt.pp.toPublicParams stmt.u o ∧ - evalConsistency Φ base stmt.avec stmt.bvec stmt.y o - | (stmt, .msisB z) => - ModuleSIS.relation Φ (outerShort Φ γ) stmt.pp.outerMatrix z = true - | (stmt, .msisD z) => - ModuleSIS.relation Φ (dShort Φ γ) stmt.pp.dMatrix z = true } - /-! ## The two-transcript MSIS extraction step (Hachi Lemma 8, cases (A)/(B)) -/ /-- **Hachi Lemma 8, cases (A)/(B), two-transcript step**: two `γ`-short openings of the same @@ -296,68 +154,7 @@ theorem inner_eq_of_chain {base : ZMod q} {cols : Nat} rw [hAs, ← hchain]; funext i simp only [scalarVecMul_apply, ← mul_assoc, Ring.inverse_mul_cancel c hc, one_mul] -/-! ## The protocol: pure pass-through verifier and honest prover -/ - -section Protocol - -variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} - -/-- The reduction's verifier (Hachi §4.2, Figure 3) is a **pure pass-through**: it re-emits the -statement, the round-0 carrier commitment `v`, and the round-1 challenge vector. The Eq.-(20) -checks live in `relOut` (the `(ŵ, t̂, ẑ)` triple is never sent — §4.3 proves knowledge of it), -so there is no runtime `guard`. -/ -def verifier : - Verifier oSpec - (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) - (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows × - CarrierCom Φ dRows × (Fin (2 ^ r) → ShortChallenge Φ ω)) - (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where - verify := fun stmt tr => pure (stmt, tr.messages ⟨0, rfl⟩, tr.challenges ⟨1, rfl⟩) - -/-- The honest prover (Hachi §4.2, Figure 3; completeness is out of scope for Lemma 8): round 0 -sends the carrier commitment `v`, round 1 receives the challenge vector, and the output witness -is the `QuadEvalResponse` `(ŵ, t̂, ẑ)` of Eq. (20). The honest computations (`v = D ŵ` with -`ŵ = G⁻¹(w)`, `ẑ = J⁻¹(Σᵢ cᵢ sᵢ)`, …) are the parameters `computeV` / `computeResp`, to be -instantiated by the completeness layer from the `QuadEvalGadgets` carrier/decomposition -definitions. -/ -def prover (WitIn : Type) - (computeV : - QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → - WitIn → CarrierCom Φ dRows) - (computeResp : - QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows → - WitIn → (Fin (2 ^ r) → ShortChallenge Φ ω) → - QuadEvalResponse Φ innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits zDigits) : - Prover oSpec - (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows) - WitIn - (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) - (pSpec (CarrierCom Φ dRows) (ShortChallenge Φ ω) r) where - PrvState - | 0 => - QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows - × WitIn - | 1 => - QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows - × WitIn - | 2 => - (QuadEvalStatement Φ innerRows (2 ^ m) messageDigits outerRows (2 ^ r) innerDigits dRows - × WitIn) × (Fin (2 ^ r) → ShortChallenge Φ ω) - input := id - sendMessage - | ⟨0, _⟩ => fun st => pure (computeV 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, computeV stmt wit, c), computeResp stmt wit c) - -end Protocol - -end ZModDefs +end Extraction /-! ## The extracted opening and the witness assembler (generic `Φ`) -/ @@ -420,7 +217,7 @@ noncomputable def buildWitness (base : ZMod q) omit [NeZero q] [IsCyclotomic Φ] in /-- Coordinate difference transfers from the `ShortChallenge` subtype to the underlying ring vectors — the bridge from `sib_coordEq` (subtype-level `CoordEq`) to the ring-level -coordinate-isolation lemmas `tensorG_coord_diff`/`tensorG1_coord_diff` (`QuadEvalGadgets.lean`). -/ +coordinate-isolation lemmas `tensorG_coord_diff`/`tensorG1_coord_diff` (`QuadEval/Gadgets.lean`). -/ theorem ShortChallenge.coordEq_val {ℓ : ℕ} {i : Fin ℓ} {x y : Fin ℓ → ShortChallenge Φ ω} (h : CoordEq i x y) : CoordEq i (fun j => (x j).val) (fun j => (y j).val) := @@ -456,7 +253,7 @@ At a star-shaped family of `2^r + 1` `relOut`-accepting branches sharing the car `v` and (cases (A)/(B) excluded) sharing `t̂` and `ŵ`, the subtract-and-divide `extractedOpening` is a `VerifiedOpening` at -* `βSq := quadEvalBetaSq γ b zDigits (deg φ) m messageDigits = 4·B_z`, the `GadgetNorms`-derived +* `βSq := quadEvalBetaSq γ b zDigits (deg φ) m messageDigits = 4·B_z`, the `Gadget/Norms`-derived `J`-recomposition bound (`deg φ = 2^α`; no primitive `‖z‖₂²` verifier check anywhere); * `γ' := γ` — **not** `2γ`: `outer_short` constrains the extracted opening's `innerDecomp`, which is the CENTRAL branch's `t̂` verbatim, and relOut c6 bounds it by `γ` directly (the @@ -487,7 +284,7 @@ theorem verifiedOpening_of_star (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω have hunit := slack_isUnit hq5 hκ fam hstar i refine ⟨hunit, ShortChallenge.l1Norm_val_sub_le _ _, ?_, ?_⟩ · -- scaled_short: `c̄ᵢ •ᵥ (c̄ᵢ⁻¹ •ᵥ Δzᵢ) = Δzᵢ = J ẑ^{(sib i)} − J ẑ^{(e)}`, then the - -- `J`-recomposition ℓ₂² bound from the two branches' c6z (`GadgetNorms.lean`). + -- `J`-recomposition ℓ₂² bound from the two branches' c6z (`Gadget/Norms.lean`). have h1 : 1 ≤ (𝓜(q, α)).φ.natDegree := by rw [hachiModulus_natDegree]; exact Nat.one_le_two_pow obtain ⟨-, -, -, -, -, -, -, hc6zs⟩ := hrel (sib fam i) diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index be78c19041..ef264c4470 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -66,47 +66,51 @@ home_page/ site assets and assembled website root CPolynomial/Polynomial division bridge lemmas live under `ArkLib/ToCompPoly/`. - Hachi commitment-scheme modules live under `ArkLib/Commitments/Functional/Hachi/` and formalize the Greyhound [NS24] / Hachi [NOZ26] *inner-outer* Ajtai lattice commitment over a cyclotomic - ring `Rq Φ`. **This development is in progress.** Layout: - - `Gadget.lean` / `GadgetNorms.lean` — the base-`b` gadget matrix `G`, its norm-reducing digit - decomposition `G⁻¹`, and the centered `ℓ₂²`/`ℓ∞` shortness bounds the honest case needs. - - `InnerOuter/` — the scheme itself: `Scheme` (the inner/outer commit composition and its + ring `Rq Φ`. **This development is in progress.** The folder is organized by paper section, each + subfolder carrying an umbrella `.lean` re-export next to it (the `Ajtai/Simple.lean + Simple/` + convention); `ArkLib/Commitments/Functional/Hachi.lean` is the folder-level landing page, with + the full folder map in its module docstring. Layout: + - `Gadget/` (§2.1) — `Gadget/Basic` is the base-`b` gadget matrix `G` and its norm-reducing digit + decomposition `G⁻¹`; `Gadget/Norms` is the centered `ℓ₂²`/`ℓ∞` shortness bounds for both + directions the honest case and Lemma 8 need. `Gadget.lean` re-exports both. + - `EvalSplit.lean` (§4, Eq. (12)) — the matrix split underlying the evaluation argument: + multilinear evaluation `eval p (xl ++ xh)` factors as the vector–matrix–vector product + `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))` (`evalSplit_eq_eval`), with the inverse reshape + `toPolynomial` and the bridge lemma `splitForm_monomialBasis_eq_eval` consumed by + `QuadEval/Bridge`. Kept top-level because the future §3 packing head reuses it over the subfield. + - `InnerOuter/` (§4.1) — the scheme itself: `Scheme` (the inner/outer commit composition and its *weak opening*, following [NOZ26, §4.1]), `Correctness` (perfect correctness for lawful gadget decompositions), `Security` (the weak-binding reduction to Module-SIS via `verify_weak`), and `Arithmetic` (pins the modulus to the power-of-two cyclotomic - `X^{2^α}+1`, which the security proofs genuinely require). - - `InnerOuter.lean` — top-level re-export of the scheme, its correctness, and its - weak-binding reduction. - - `PolynomialQuadraticEq/` — Hachi's polynomial-evaluation reduction ([NOZ26, §4.2, Figure 3]), - which proves - `f(x) = y` by expressing the evaluation as the quadratic form `bᵀ M a` and folding the `2ʳ` - carrier blocks under the challenge vector (hence the name `QuadEval`); it is Hachi's - multilinear/inner-outer lift of Greyhound's [NS24, §3.1] folding protocol. `QuadEvalGadgets` - holds the gadget algebra (`PublicParamsD`, the honest-prover carrier/short commitment - `v = D ŵ`, the `J`-decomposition of `z`, and the `tensorG`/`tensorG1` challenge - combinations); `QuadEval` is - the 2-round protocol, its `relOut` (Eq. (20) + range balls) / `relIn` (weak opening - ∨ MSIS(B) ∨ MSIS(D)), the subtract-and-divide extractor `buildWitness`, and **Lemma 8** - (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound`, `sorryAx`-free. - The generic tree plumbing lives in `Security/CoordinateWiseSpecialSoundness/SingleRound`; the - supporting norm bounds are in `Data/Lattices/CyclotomicRing/NormBounds/Basic` and `GadgetNorms`. - `PolynomialQuadraticEq/PolyEvalReduction` adds the **polynomial-level bridge**: a zero-round - `ReduceClaim` head - (`bridgeVerifier`) that reinterprets a `CMlPolynomial`-level statement `PolyEvalStatement` as a - `QuadEvalStatement` via the monomial tensor bases (`toQuadEvalStatement`), the pulled-back input - relation `relPolyEval` (the extracted polynomial evaluates to `y`, or MSIS(B/D)), and its CWSS - `bridge_coordinateWiseSpecialSound` (any `D`, via the pull-back `mem_relPolyEval_of_relIn`). - - `PolynomialEvalSplit.lean` — the matrix split underlying the evaluation argument: multilinear - evaluation `eval p (xl ++ xh)` factors as the vector–matrix–vector product - `mb(xl) ⬝ᵥ (toMatrix p *ᵥ mb(xh))` (`evalSplit_eq_eval`), with the inverse reshape - `toPolynomial` and the bridge lemma `splitForm_monomialBasis_eq_eval` consumed by - `PolynomialQuadraticEq/PolyEvalReduction`. - - `Basic.lean` — the **designated home of Hachi as a functional commitment** and of the growing - n-ary composition. `evalVerifier` is `bridge.append QuadEval.verifier`; - `hachi_eval_coordinateWiseSpecialSound` is the composed CWSS (`sorryAx`-free), assembled by - `Verifier.append_coordinateWiseSpecialSound` from the bridge and Lemma 8. It also holds the FC - scaffolding: the eval `OracleInterface`, honest `hachiKeygen`/`hachiCommit`, and the - `Commitment.Scheme` value `hachiFC` (its opening `Proof` is a documented `sorry` pending the - remaining §4.3+ subprotocols and the completeness layer). + `X^{2^α}+1`, which the security proofs genuinely require). `InnerOuter.lean` re-exports the + scheme, its correctness, and its weak-binding reduction. + - `QuadEval/` (§4.2, "Polynomial Evaluation as Quadratic Equation", Figure 3) — Hachi's + polynomial-evaluation reduction, which proves `f(x) = y` by expressing the evaluation as the + quadratic form `bᵀ M a` and folding the `2ʳ` carrier blocks under the challenge vector (hence + the name `QuadEval`); it is Hachi's multilinear/inner-outer lift of Greyhound's [NS24, §3.1] + folding protocol. `QuadEval/Gadgets` holds the gadget algebra (`PublicParamsD`, the + honest-prover carrier/short commitment `v = D ŵ`, the `J`-decomposition of `z`, and the + `tensorG`/`tensorG1` challenge combinations). `QuadEval/Reduction` is the 2-round protocol with + its types, `relOut` (Eq. (20) + range balls), and `relIn` (weak opening ∨ MSIS(B) ∨ MSIS(D)). + `QuadEval/Soundness` is the subtract-and-divide extractor `buildWitness`, **Lemma 8** + (coordinate-wise special soundness) as `quadEval_coordinateWiseSpecialSound` (`sorryAx`-free), + the composable `quadEvalPackage`, and the reduction's derived norm constants + `quadEvalZL2SqBound` = `B_z` / `quadEvalBetaSq` = `4·B_z` (the generic tree plumbing lives in + `Security/CoordinateWiseSpecialSoundness/SingleRound`; the supporting norm growth is in + `Data/Lattices/CyclotomicRing/NormBounds/Basic` and `Gadget/Norms`). `QuadEval/Bridge` is the + **polynomial-level bridge**: a zero-round `ReduceClaim` head (`bridgeVerifier`) reinterpreting a + `CMlPolynomial`-level `PolyEvalStatement` as a `QuadEvalStatement` via the monomial tensor bases + (`toQuadEvalStatement`), the pulled-back input relation `relPolyEval`, and its CWSS + `bridge_coordinateWiseSpecialSound`. `QuadEval.lean` re-exports the reduction, its soundness, + and the bridge. + - `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. + - `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+ + subprotocols and the completeness layer). - The Merkle tree implementations now live upstream in `VCVio`, so use `VCVio.CryptoFoundations.MerkleTree` or `VCVio.CryptoFoundations.InductiveMerkleTree` instead of the old ArkLib-local modules. From 4224003bfd9b7a0ac347661439dba2524fd0efd7 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Fri, 10 Jul 2026 17:08:22 +0200 Subject: [PATCH 07/21] merge --- .../Functional/Hachi/Gadget/Basic.lean | 77 ++----------------- .../Functional/Hachi/Gadget/Norms.lean | 16 +--- ArkLib/Data/Lattices/CyclotomicRing/Rq.lean | 53 +++++++++++++ .../CyclotomicRing/Subfield/Basis.lean | 11 +-- 4 files changed, 67 insertions(+), 90 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean index 8892570576..07368a1528 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Basic.lean @@ -134,13 +134,10 @@ end ZModDigit variable {R : Type} [Field R] [BEq R] [LawfulBEq R] [DecidableEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] -/-- Embed a base-ring scalar `c : R` as the constant element `C c ∈ Rq Φ`. -/ -def constRq (c : R) : Rq Φ := Rq.mk Φ (CPolynomial.C c) - /-- Entry of the base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`: column `j` of row `i` is `base^(j % digits)` when `j / digits = i`, else `0`. -/ def gadgetEntry (base : R) {rows digits : Nat} (i : Fin rows) (j : Fin (rows * digits)) : Rq Φ := - if j.val / digits = i.val then constRq Φ (base ^ (j.val % digits)) else 0 + if j.val / digits = i.val then Rq.constRq Φ (base ^ (j.val % digits)) else 0 /-- The base-`base` gadget matrix `I_rows ⊗ [1, base, …, base^(digits-1)]`. -/ def gadgetMatrix (base : R) (rows digits : Nat) : PolyMatrix (Rq Φ) rows (rows * digits) := @@ -156,64 +153,6 @@ def IsLawfulGadgetDecomposition (base : R) {rows digits : Nat} (decompose : PolyVec (Rq Φ) rows → PolyVec (Rq Φ) (rows * digits)) : Prop := ∀ x, gadgetMul Φ base (decompose x) = x -omit [DecidableEq R] in -@[simp] theorem constRq_one : constRq Φ (1 : R) = 1 := by - have hC : (CompPoly.CPolynomial.C (1 : R)) = 1 := by - refine CompPoly.CPolynomial.eq_iff_coeff.mpr (fun i => ?_) - rw [CompPoly.CPolynomial.coeff_C, CompPoly.CPolynomial.coeff_one] - change Rq.mk Φ (CompPoly.CPolynomial.C 1) = 1 - rw [hC]; rfl - -/-! ## Degree / coefficient facts for reduced representatives -/ - -omit [DecidableEq R] in -/-- `Φ.φ.natDegree`, the truncation length of decompositions, does not exceed `deg φ`. -/ -theorem phi_natDegree_le_degree : (Φ.φ.natDegree : WithBot ℕ) ≤ Φ.φ.toPoly.degree := - le_of_eq (by rw [CompPoly.CPolynomial.natDegree_toPoly, - Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero]) - -omit [DecidableEq R] in -/-- A reduced representative has zero coefficients at and beyond `deg φ`. -/ -theorem coeff_eq_zero_of_natDegree_le (a : Rq Φ) {k : ℕ} (hk : Φ.φ.natDegree ≤ k) : - a.1.coeff k = 0 := by - rw [CompPoly.CPolynomial.coeff_toPoly] - apply Polynomial.coeff_eq_zero_of_degree_lt - calc a.1.toPoly.degree - < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced a.2 - _ = (Φ.φ.natDegree : WithBot ℕ) := by - rw [CompPoly.CPolynomial.natDegree_toPoly] - exact Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero - _ ≤ (k : WithBot ℕ) := by exact_mod_cast hk - -omit [DecidableEq R] in -/-- The constant `constRq Φ c` has underlying polynomial `C c` (no reduction occurs, as -`deg (C c) = 0 < deg φ`). -/ -theorem constRq_val (h1 : 1 ≤ Φ.φ.natDegree) (c : R) : - (constRq Φ c).1 = CompPoly.CPolynomial.C c := by - change Φ.reduce (CompPoly.CPolynomial.C c) = CompPoly.CPolynomial.C c - apply Φ.reduce_eq_self_of_degree_lt - rw [CompPoly.CPolynomial.toPoly_C] - have hpos : (0 : WithBot ℕ) < Φ.φ.toPoly.degree := by - rw [Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero, - ← CompPoly.CPolynomial.natDegree_toPoly] - exact_mod_cast (h1 : 0 < Φ.φ.natDegree) - exact lt_of_le_of_lt Polynomial.degree_C_le hpos - -omit [DecidableEq R] in -/-- Multiplying by the constant `constRq Φ c` scales coefficients by `c`. -/ -theorem constRq_mul_coeff (h1 : 1 ≤ Φ.φ.natDegree) (c : R) (x : Rq Φ) (k : ℕ) : - (constRq Φ c * x).1.coeff k = c * x.1.coeff k := by - have hmul : (constRq Φ c * x).1 = Φ.reduce ((constRq Φ c).1 * x.1) := rfl - have hred : Φ.reduce (CompPoly.CPolynomial.C c * x.1) = CompPoly.CPolynomial.C c * x.1 := by - apply Φ.reduce_eq_self_of_degree_lt - rw [CompPoly.CPolynomial.toPoly_mul, CompPoly.CPolynomial.toPoly_C] - have hx : x.1.toPoly.degree < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced x.2 - rcases eq_or_ne c 0 with hc | hc - · simpa [hc] using lt_of_le_of_lt bot_le hx - · rwa [Polynomial.degree_C_mul hc] - rw [hmul, constRq_val Φ h1, hred] - exact CompPoly.CPolynomial.coeff_C_mul x.1 c k - /-! ## The gadget product as a block digit-sum -/ omit [DecidableEq R] in @@ -222,7 +161,7 @@ on the diagonal block and `0` elsewhere. -/ theorem gadgetEntry_finProdFinEquiv (base : R) {rows digits : Nat} (hd : 0 < digits) (i i' : Fin rows) (e : Fin digits) : gadgetEntry Φ base i (finProdFinEquiv (i', e)) - = if i' = i then constRq Φ (base ^ (e : ℕ)) else 0 := by + = if i' = i then Rq.constRq Φ (base ^ (e : ℕ)) else 0 := by unfold gadgetEntry have hval : (finProdFinEquiv (i', e)).val = e.val + digits * i'.val := rfl have hdiv : (finProdFinEquiv (i', e)).val / digits = i'.val := by @@ -238,7 +177,7 @@ slots of block `i`. -/ theorem gadgetMul_apply (base : R) {rows digits : Nat} (hd : 0 < digits) (v : PolyVec (Rq Φ) (rows * digits)) (i : Fin rows) : gadgetMul Φ base v i - = ∑ e : Fin digits, constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by + = ∑ e : Fin digits, Rq.constRq Φ (base ^ (e : ℕ)) * v (finProdFinEquiv (i, e)) := by rw [gadgetMul, matVecMul_apply, dot_eq_sum] simp only [gadgetMatrix] rw [← Equiv.sum_comp finProdFinEquiv (fun j => gadgetEntry Φ base i j * v j), @@ -290,25 +229,25 @@ theorem gadgetDecompose_lawful {rows digits : Nat} (hd : 0 < digits) (h1 : 1 ≤ rw [CompPoly.CPolynomial.eq_iff_coeff] intro k have hsum : (∑ e : Fin digits, - constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k = ∑ e : Fin digits, - (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k := by rw [← Rq.coeffHom_apply Φ k, map_sum] simp only [Rq.coeffHom_apply] have hterm : ∀ e : Fin digits, - (constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree + (Rq.constRq Φ (base ^ (e : ℕ)) * Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k' => dd.digit ((x i).1.coeff k') e)).1.coeff k = base ^ (e : ℕ) * (if k < Φ.φ.natDegree then dd.digit ((x i).1.coeff k) e else 0) := by intro e - rw [constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (phi_natDegree_le_degree Φ)] + rw [Rq.constRq_mul_coeff Φ h1, Rq.ofFinCoeff_coeff Φ _ (Rq.phi_natDegree_le_degree Φ)] rw [hsum] simp_rw [hterm] by_cases hk : k < Φ.φ.natDegree · simp only [if_pos hk] exact dd.reconstruct ((x i).1.coeff k) · simp only [if_neg hk, mul_zero, Finset.sum_const_zero] - exact (coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm + exact (Rq.coeff_eq_zero_of_natDegree_le Φ (x i) (not_lt.mp hk)).symm end ArkLib.Lattices.Ajtai diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean index 697eecbd99..8f97a8d26a 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean @@ -61,16 +61,6 @@ section ZModGadgetNorms variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] -omit [NeZero q] [IsCyclotomic Φ] in -/-- The degree bound needed to read off gadget coefficients: `deg φ` does not exceed the degree -of the modulus polynomial. -/ -theorem natDegree_le_degree_toPoly (h : 1 ≤ Φ.φ.natDegree) : - (Φ.φ.natDegree : WithBot ℕ) ≤ Φ.φ.toPoly.degree := by - have hnd : 1 ≤ Φ.φ.toPoly.natDegree := by - rw [← CompPoly.CPolynomial.natDegree_toPoly]; exact h - have hne : Φ.φ.toPoly ≠ 0 := fun h0 => by simp [h0] at hnd - rw [Polynomial.degree_eq_natDegree hne, ← CompPoly.CPolynomial.natDegree_toPoly] - omit [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] in /-- **Core digit bound.** Each base-`b` digit of `zmodDigitDecomposition`, viewed as a centered residue, has absolute value at most `b - 1` — provided `b - 1 ≤ q/2`, so the digit (a natural @@ -93,14 +83,14 @@ omit [NeZero q] in /-- The `k`-th coefficient (`k < deg φ`) of a gadget-decomposition block is exactly the corresponding digit of the corresponding input coefficient. -/ theorem gadgetDecompose_coeff {base : ZMod q} {rows digits : ℕ} - (dd : DigitDecomposition base digits) (h : 1 ≤ Φ.φ.natDegree) + (dd : DigitDecomposition base digits) (_h : 1 ≤ Φ.φ.natDegree) (x : PolyVec (Rq Φ) rows) (j : Fin (rows * digits)) {k : ℕ} (hk : k < Φ.φ.natDegree) : (gadgetDecompose Φ dd x j).1.coeff k = dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2 := by rw [show gadgetDecompose Φ dd x j = Rq.ofFinCoeff Φ Φ.φ.natDegree (fun k => dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2) from rfl, - Rq.ofFinCoeff_coeff Φ _ (natDegree_le_degree_toPoly Φ h) k, if_pos hk] + Rq.ofFinCoeff_coeff Φ _ (Rq.phi_natDegree_le_degree Φ) k, if_pos hk] /-! ## `ℓ∞` bound -/ @@ -182,7 +172,7 @@ theorem gadgetMul_zmod_coeff_natAbs_le {b rows digits : ℕ} (hd : 0 < digits) = ∑ e : Fin digits, (b : ZMod q) ^ (e : ℕ) * (v (finProdFinEquiv (i, e))).1.coeff k := by rw [gadgetMul_apply Φ (b : ZMod q) hd v i, ← Rq.coeffHom_apply Φ k, map_sum] simp only [Rq.coeffHom_apply] - exact Finset.sum_congr rfl fun e _ => constRq_mul_coeff Φ h1 _ _ k + exact Finset.sum_congr rfl fun e _ => Rq.constRq_mul_coeff Φ h1 _ _ k -- the explicit integer representative of that coefficient have hrep : ((∑ e : Fin digits, (b : ℤ) ^ (e : ℕ) * ((v (finProdFinEquiv (i, e))).1.coeff k).valMinAbs : ℤ) : ZMod q) diff --git a/ArkLib/Data/Lattices/CyclotomicRing/Rq.lean b/ArkLib/Data/Lattices/CyclotomicRing/Rq.lean index 1394815b43..79a630cc9a 100644 --- a/ArkLib/Data/Lattices/CyclotomicRing/Rq.lean +++ b/ArkLib/Data/Lattices/CyclotomicRing/Rq.lean @@ -304,6 +304,59 @@ theorem natDegree_val_toPoly_lt (α : ℕ) (a : Rq (powTwoCyclotomic (R := R) α exact Polynomial.natDegree_lt_natDegree hne ((powTwoCyclotomic (R := R) α).degree_toPoly_lt_of_reduced a.2) +/-! ## Constant embedding and coefficient-vanishing facts + +General (any-modulus) degree/coefficient lemmas used by the inner-outer gadget commitment +(`ArkLib/Commitments/Functional/Hachi/Gadget.lean`); the power-of-two special cases live in +`Subfield/Basis.lean`. -/ + +/-- `Φ.φ.natDegree`, the truncation length of decompositions, does not exceed `deg φ`. -/ +theorem phi_natDegree_le_degree : (Φ.φ.natDegree : WithBot ℕ) ≤ Φ.φ.toPoly.degree := + le_of_eq (by rw [CompPoly.CPolynomial.natDegree_toPoly, + Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero]) + +/-- A reduced representative has zero coefficients at and beyond `deg φ`. -/ +theorem coeff_eq_zero_of_natDegree_le (a : Rq Φ) {k : ℕ} (hk : Φ.φ.natDegree ≤ k) : + a.1.coeff k = 0 := by + rw [CompPoly.CPolynomial.coeff_toPoly] + apply Polynomial.coeff_eq_zero_of_degree_lt + calc a.1.toPoly.degree + < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced a.2 + _ = (Φ.φ.natDegree : WithBot ℕ) := by + rw [CompPoly.CPolynomial.natDegree_toPoly] + exact Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero + _ ≤ (k : WithBot ℕ) := by exact_mod_cast hk + +/-- Embed a base-ring scalar `c : R` as the constant element `C c ∈ Rq Φ`. -/ +def constRq (c : R) : Rq Φ := Rq.mk Φ (CompPoly.CPolynomial.C c) + +/-- The constant `constRq Φ c` has underlying polynomial `C c` (no reduction occurs, as +`deg (C c) = 0 < deg φ`). -/ +theorem constRq_val (h1 : 1 ≤ Φ.φ.natDegree) (c : R) : + (constRq Φ c).1 = CompPoly.CPolynomial.C c := by + change Φ.reduce (CompPoly.CPolynomial.C c) = CompPoly.CPolynomial.C c + apply Φ.reduce_eq_self_of_degree_lt + rw [CompPoly.CPolynomial.toPoly_C] + have hpos : (0 : WithBot ℕ) < Φ.φ.toPoly.degree := by + rw [Polynomial.degree_eq_natDegree (IsCyclotomic.monic (Φ := Φ)).ne_zero, + ← CompPoly.CPolynomial.natDegree_toPoly] + exact_mod_cast (h1 : 0 < Φ.φ.natDegree) + exact lt_of_le_of_lt Polynomial.degree_C_le hpos + +/-- Multiplying by the constant `constRq Φ c` scales coefficients by `c`. -/ +theorem constRq_mul_coeff (h1 : 1 ≤ Φ.φ.natDegree) (c : R) (x : Rq Φ) (k : ℕ) : + (constRq Φ c * x).1.coeff k = c * x.1.coeff k := by + have hmul : (constRq Φ c * x).1 = Φ.reduce ((constRq Φ c).1 * x.1) := rfl + have hred : Φ.reduce (CompPoly.CPolynomial.C c * x.1) = CompPoly.CPolynomial.C c * x.1 := by + apply Φ.reduce_eq_self_of_degree_lt + rw [CompPoly.CPolynomial.toPoly_mul, CompPoly.CPolynomial.toPoly_C] + have hx : x.1.toPoly.degree < Φ.φ.toPoly.degree := Φ.degree_toPoly_lt_of_reduced x.2 + rcases eq_or_ne c 0 with hc | hc + · simpa [hc] using lt_of_le_of_lt bot_le hx + · rwa [Polynomial.degree_C_mul hc] + rw [hmul, constRq_val Φ h1, hred] + exact CompPoly.CPolynomial.coeff_C_mul x.1 c k + end Rq end ArkLib.Lattices.CyclotomicModulus diff --git a/ArkLib/Data/Lattices/CyclotomicRing/Subfield/Basis.lean b/ArkLib/Data/Lattices/CyclotomicRing/Subfield/Basis.lean index 8b71a5346f..e70f54f998 100644 --- a/ArkLib/Data/Lattices/CyclotomicRing/Subfield/Basis.lean +++ b/ArkLib/Data/Lattices/CyclotomicRing/Subfield/Basis.lean @@ -260,15 +260,10 @@ theorem powTwoCyclotomic_toPoly_degree (α : ℕ) : omit [DecidableEq R] in /-- A reduced representative of `Rq (powTwoCyclotomic α)` has vanishing coefficients at and above -the modulus degree `2^α`. -/ +the modulus degree `2^α`. The power-of-two special case of `Rq.coeff_eq_zero_of_natDegree_le`. -/ theorem coeff_eq_zero_of_le (α : ℕ) (a : Rq (powTwoCyclotomic (R := R) α)) {k : ℕ} - (hk : 2 ^ α ≤ k) : a.1.coeff k = 0 := by - have hdeg : a.1.toPoly.degree < ((2 ^ α : ℕ) : WithBot ℕ) := by - rw [← powTwoCyclotomic_toPoly_degree (R := R) α] - exact (powTwoCyclotomic α).degree_toPoly_lt_of_reduced a.2 - have hz : a.1.toPoly.coeff k = 0 := - Polynomial.coeff_eq_zero_of_degree_lt (lt_of_lt_of_le hdeg (by exact_mod_cast hk)) - rw [coeff_toPoly]; exact hz + (hk : 2 ^ α ≤ k) : a.1.coeff k = 0 := + Rq.coeff_eq_zero_of_natDegree_le _ a (by rw [powTwoCyclotomic_natDegree]; exact hk) /-- **`Rq` is in bijection with its coefficient vectors** `Fin (2^α) → R`: a reduced representative is determined by its `2^α` low coefficients. -/ From f8eca056a05afdb943be2bd98a5405304ecbe6fc Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Mon, 13 Jul 2026 11:57:37 +0200 Subject: [PATCH 08/21] composition plan --- .../Functional/Hachi/Composition.lean | 83 ++++++++++++------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index feaacec632..347eeb7ead 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -23,8 +23,8 @@ 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 §3/§4.3+/§4.5 subprotocols are placeholders (see the `TODO` block); each will land as one -more `CWSSPackage` `▷`-appended into the chain. +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 @@ -47,43 +47,62 @@ Finished pieces, each in its own file (paths under `Commitments/Functional/Hachi ## The composed verifier chain -Top-to-bottom is the composition order (each `▷` is one `CWSSPackage`). The `═══` band is the -finished core (`evalChain`); everything else is a placeholder for a future package: +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): ```text - §3.2/§4.5 partial-evaluations head ── planned (pure, 1 msg) - │ ▷ - ▼ - §3.1 ring-switch packing head ── planned (guarded, 1 msg) - │ ▷ - ▼ - σ₋₁ statement adapter ── planned (0-round ReduceClaim) - │ ▷ + 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, Lemma 8) + 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 Eq.(20) ⇒ R^lin ⇒ HMZ25 lift ⇒ - zero-check rounds ⇒ sumcheck ⇒ final eval ── planned (Lemmas 9–11) + §4.3, Figure 5 batch the linear and range constraints into + H_α and H_0; sample evaluation points τ₁ and τ₀ planned (Lemma 10) │ ▷ ▼ - §4.5 recursion handoff ⇒ next iteration ── planned (guarded) + §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 ``` ## Growing the composition -Each further subprotocol is exported from its file as a `CWSSPackage` and `▷`-appended here; 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`). Guarded subprotocols (the §3.1 head, the sumcheck -rounds, the final-eval and §4.5 handoff — those 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` (every finished factor is -`IsPure` and `seqCompose_succ_eq_append` is `rfl`, so no existing proof is reworked). +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). ## References @@ -108,12 +127,13 @@ variable {ι : Type} {oSpec : OracleSpec ι} {ω : ℕ} variable {σ : Type} /-- **The composed evaluation reduction** (Hachi [NOZ26, §4.2, Figure 3], `Rq`-level): the bridge -package (link 3) chained with the `QuadEval` package (link 4) via the `CWSSPackage` operator `▷`. +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 §3/§4.3+ subprotocol is one more package `▷`-appended here (see the module header). -/ +further §4.3 opening subprotocol is appended after this core, while §3 closes the recursion back +to the next invocation (see the module header). -/ def evalChain (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : CWSSPackage init impl @@ -157,11 +177,12 @@ end Composition `evalChain` / `eval_coordinateWiseSpecialSound` is the finished `Rq`-level CWSS core (bridge ▷ `QuadEval`). Still open: -* **Remaining §3/§4.3+/§4.5 subprotocols.** Each is exported from its file as a `CWSSPackage` and - `▷`-appended into `evalChain`, bridged by 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`). -/ +* **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`). -/ end ArkLib.Lattices.Ajtai.InnerOuter From 59c9af8a16f9ff2df98bf496548adbda48868b93 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Tue, 14 Jul 2026 09:23:58 +0200 Subject: [PATCH 09/21] initial skeleton --- ArkLib.lean | 15 + .../Functional/Hachi/Composition.lean | 367 +++++++++++++----- .../Hachi/LinSumcheck/BatchBridge.lean | 106 +++++ .../Hachi/LinSumcheck/Constraints.lean | 237 +++++++++++ .../Functional/Hachi/LinSumcheck/Escape.lean | 192 +++++++++ .../Hachi/LinSumcheck/FinalEval.lean | 178 +++++++++ .../Functional/Hachi/LinSumcheck/Lift.lean | 246 ++++++++++++ .../Functional/Hachi/LinSumcheck/Rlin.lean | 158 ++++++++ .../Functional/Hachi/LinSumcheck/Rounds.lean | 267 +++++++++++++ .../Hachi/LinSumcheck/SumcheckBridge.lean | 87 +++++ .../Hachi/LinSumcheck/ZeroCheck.lean | 196 ++++++++++ .../Hachi/Recursion/PartialEval.lean | 204 ++++++++++ .../Hachi/Recursion/TraceHandoff.lean | 216 +++++++++++ .../Hachi/Recursion/ZBatchBridge.lean | 146 +++++++ .../Escape.lean | 77 ++++ .../Guarded.lean | 221 +++++++++++ .../ScalarRound.lean | 106 +++++ docs/wiki/repo-map.md | 32 +- 18 files changed, 2951 insertions(+), 100 deletions(-) create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/BatchBridge.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/FinalEval.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/Lift.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rlin.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/SumcheckBridge.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean create mode 100644 ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Escape.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ScalarRound.lean diff --git a/ArkLib.lean b/ArkLib.lean index 0de3d6b0b6..22fdf34fdf 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -12,11 +12,23 @@ import ArkLib.Commitments.Functional.Hachi.InnerOuter.Arithmetic import ArkLib.Commitments.Functional.Hachi.InnerOuter.Correctness import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.BatchBridge +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Constraints +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Escape +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.FinalEval +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Lift +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rlin +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rounds +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.SumcheckBridge +import ArkLib.Commitments.Functional.Hachi.LinSumcheck.ZeroCheck import ArkLib.Commitments.Functional.Hachi.QuadEval 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.KZG.Algebra import ArkLib.Commitments.Functional.KZG.Basic import ArkLib.Commitments.Functional.KZG.Binding @@ -202,8 +214,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..88492b7cce 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.LinSumcheck.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 (LinSumcheck/Rlin) | 0 | relOutE → relRlinE | any + 4 | lift (LinSumcheck/Lift) | msg t; α ∈ F | relRlinE → relLiftE | ℓ=1, k=2d (L9) + 5 | batch (…/BatchBridge) | 0 | relLiftE → relBatchedE | any + 6 | zero-check (…/ZeroCheck) | (ρ₀,ρ_α) ∈ F² | relBatchedE → relZeroChkE | ℓ=2, k=D (L10*) + 7 | sc bridge (…/SumcheckBridge)| 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` +(`LinSumcheck/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/LinSumcheck/BatchBridge.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/BatchBridge.lean new file mode 100644 index 0000000000..ed0671129a --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/BatchBridge.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.LinSumcheck.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 (`LinSumcheck/Lift.lean`); + * `relOut = relBatchedE` — opening `w̃` of `t`, `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` as + `MvPolynomial` identities (Eqs. (22)–(23), `LinSumcheck/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/LinSumcheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean new file mode 100644 index 0000000000..c0217de844 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean @@ -0,0 +1,237 @@ +/- +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.LinSumcheck.Lift +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 (`LinSumcheck/ZeroCheck.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) -/ + +/-- **`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 (`LinSumcheck/SumcheckBridge.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/LinSumcheck/Escape.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean new file mode 100644 index 0000000000..ce4de5bf13 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean @@ -0,0 +1,192 @@ +/- +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 (`LinSumcheck/`) 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 (`LinSumcheck/`) 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/LinSumcheck/FinalEval.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/FinalEval.lean new file mode 100644 index 0000000000..2970566f20 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/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.LinSumcheck.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/LinSumcheck/Lift.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Lift.lean new file mode 100644 index 0000000000..316b40366b --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Lift.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.LinSumcheck.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, `LinSumcheck/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 +(`LinSumcheck/BatchBridge.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/LinSumcheck/Rlin.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rlin.lean new file mode 100644 index 0000000000..0fa759d0d8 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/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.LinSumcheck.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 `LinSumcheck/Escape.lean`), and its `relOut` + `relRlinE` is definitionally the next link's (`LinSumcheck/Lift.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/LinSumcheck/Rounds.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean new file mode 100644 index 0000000000..1535500b0d --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean @@ -0,0 +1,267 @@ +/- +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.LinSumcheck.SumcheckBridge +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`, `LinSumcheck/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`). -/ +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/LinSumcheck/SumcheckBridge.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/SumcheckBridge.lean new file mode 100644 index 0000000000..b1bd4030b4 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/SumcheckBridge.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.LinSumcheck.ZeroCheck + +/-! + # 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`, `LinSumcheck/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/LinSumcheck/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.lean new file mode 100644 index 0000000000..9593bc4cb4 --- /dev/null +++ b/ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.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.LinSumcheck.BatchBridge + +/-! + # 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/Recursion/PartialEval.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean new file mode 100644 index 0000000000..855e6e94ec --- /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.LinSumcheck.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/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..b930d03f80 --- /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/LinSumcheck/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/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index ef264c4470..3a1008122e 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -103,10 +103,28 @@ 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. + - `LinSumcheck/` (§4.3, Figures 4–7) — the **skeleton** of Hachi's sumcheck-based opening + chain, one file per subprotocol/bridge, each exporting a `CWSSPackage`/`GCWSSPackage` with a + sorried CWSS theorem: `Escape` (escape-threaded front `evalChainE`, design G1), `Rlin` + (Eq. (20) → `R^lin` zero-round adapter, F2), `Lift` (HMZ25 lift, Figure 4/Lemma 9, `k = 2d`; + the abstract `w̃`-commitment `LiftCom`), `Constraints` (Eq. (21)–(23) encodings, sumcheck + polynomials, degree pins, the Kronecker curve `kroneckerPoint`, per-round seam `roundRel`), + `BatchBridge` (per-row/range ⇄ `H₀ ≡ 0 ∧ H_α ≡ 0`), `ZeroCheck` (Figure 5 / **corrected** + Lemma 10 — Kronecker seed pair, `(ℓ, k) = (2, D)`; see `HACHI_LEMMA10_GAP.md`), + `SumcheckBridge` (point claims → initial hypercube sums), `Rounds` (Figure 6/Lemma 11 — + guarded paired rounds, loop by recursion over `▷ᵍ`), `FinalEval` (Figure 7 tail — guarded + reveal of `w̃(a)`). + - `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 +167,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 From 4a10a658c1fe1d1140e3a0190155750c73948da4 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Tue, 14 Jul 2026 11:13:04 +0200 Subject: [PATCH 10/21] address PR review --- ArkLib/Commitments/Functional/Hachi.lean | 13 ++++++---- .../Functional/Hachi/Commitment.lean | 20 +++++++++----- .../Functional/Hachi/Gadget/Norms.lean | 20 +++++++------- .../Hachi/InnerOuter/Correctness.lean | 4 +-- .../Functional/Hachi/QuadEval.lean | 7 +++-- .../Functional/Hachi/QuadEval/Reduction.lean | 26 +++++++++++++------ .../Functional/Hachi/QuadEval/Soundness.lean | 16 +++++++++--- .../ProofSystem/Component/SendChallenge.lean | 6 ++++- 8 files changed, 74 insertions(+), 38 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi.lean b/ArkLib/Commitments/Functional/Hachi.lean index 53fe5c1242..907103bb49 100644 --- a/ArkLib/Commitments/Functional/Hachi.lean +++ b/ArkLib/Commitments/Functional/Hachi.lean @@ -13,11 +13,14 @@ Formalization of the Hachi [NOZ26] functional commitment — a lattice-based com multilinear polynomials with evaluation-opening proofs, built on the Greyhound [NS24] inner-outer Ajtai commitment over the power-of-two cyclotomic ring `Z_q[X] / (X^{2^α} + 1)`. -**This development is in progress.** Finished and `sorry`-free: the inner-outer commitment -(§4.1) with perfect correctness and the weak-binding reduction to Module-SIS, and the -polynomial-evaluation reduction (§4.2, Lemma 8) with its polynomial-level bridge. Still to come: -the remaining §4.3+/§4.5 subprotocols and the completeness layer (see the `TODO` blocks in -`Composition.lean` and `Commitment.lean`). +**This development is in progress.** Finished and `sorry`-free — axiom-clean down to the +Lyubashevsky–Seiler short-element invertibility (`isUnit_of_l1Norm_le`) the soundness rests on, +which is itself proven, not deferred: the inner-outer commitment (§4.1) with perfect correctness +and the weak-binding reduction to Module-SIS, and the polynomial-evaluation reduction +(§4.2, Lemma 8) with its polynomial-level bridge. Still to come: the remaining §4.3+/§4.5 +subprotocols and the completeness layer — the honest-prover `opening` (`hachi.opening` in +`Commitment.lean`) is the one documented `sorry` in the tree; see the `TODO` blocks in +`Composition.lean` and `Commitment.lean`. ## Folder structure diff --git a/ArkLib/Commitments/Functional/Hachi/Commitment.lean b/ArkLib/Commitments/Functional/Hachi/Commitment.lean index 2faa206d84..3a62c9c6d1 100644 --- a/ArkLib/Commitments/Functional/Hachi/Commitment.lean +++ b/ArkLib/Commitments/Functional/Hachi/Commitment.lean @@ -113,13 +113,19 @@ def commit [DecidableEq (ZMod q)] (hb : 1 < b) pp.toPublicParams (Hachi.toMatrix p) (commitWithDecomps 𝓜(q, α) pp.toPublicParams decomps, decomps) -/-- **Hachi as a functional commitment** (`Commitment.Scheme`) over the multilinear data -`CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable polynomial, with the `r`/`m` split -feeding the outer/inner gadgets. It commits a polynomial directly (no caller-supplied -decompositions): the honest `commit` uses the canonical base-`b` gadget decomposition at the -paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` (Hachi [NOZ26] §2.1/§4.1), shared by the message and -inner gadgets — so `messageDigits`/`innerDigits` are not free parameters. The only parameters are -the gadget base `b` and `1 < b`; the scheme carries the eval oracle +/-- **Hachi as a functional commitment** (`Commitment.Scheme`) — ⚠ **WIP scaffold.** The eval +oracle and the honest `keygen` / `commit` are real, but the `opening` field is a placeholder +(`sorry`, see below and the `TODO` block), so this value does **not** yet certify end-to-end +opening correctness. It is committed now only as the target packaging the finished opening will +slot into once the §4.3+ subprotocols and the honest-prover layer land (the follow-up tracked by +the `TODO` here and in `Composition.lean`). + +Over the multilinear data `CMlPolynomial (Rq 𝓜(q,α)) (r + m)` — an `(r + m)`-variable polynomial, +with the `r`/`m` split feeding the outer/inner gadgets. It commits a polynomial directly (no +caller-supplied decompositions): the honest `commit` uses the canonical base-`b` gadget +decomposition at the paper's width `δ = ⌈log_b q⌉ = Nat.clog b q` (Hachi [NOZ26] §2.1/§4.1), shared +by the message and inner gadgets — so `messageDigits`/`innerDigits` are not free parameters. The +only parameters are the gadget base `b` and `1 < b`; the scheme carries the eval oracle `multilinearEvalOracleInterface`, honest `keygen` / `commit`, committer and verifier key `PublicParamsD`, and decommitment `Decomp`. diff --git a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean index 8f97a8d26a..5910dea588 100644 --- a/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean +++ b/ArkLib/Commitments/Functional/Hachi/Gadget/Norms.lean @@ -83,8 +83,8 @@ omit [NeZero q] in /-- The `k`-th coefficient (`k < deg φ`) of a gadget-decomposition block is exactly the corresponding digit of the corresponding input coefficient. -/ theorem gadgetDecompose_coeff {base : ZMod q} {rows digits : ℕ} - (dd : DigitDecomposition base digits) (_h : 1 ≤ Φ.φ.natDegree) - (x : PolyVec (Rq Φ) rows) (j : Fin (rows * digits)) {k : ℕ} (hk : k < Φ.φ.natDegree) : + (dd : DigitDecomposition base digits) (x : PolyVec (Rq Φ) rows) (j : Fin (rows * digits)) + {k : ℕ} (hk : k < Φ.φ.natDegree) : (gadgetDecompose Φ dd x j).1.coeff k = dd.digit ((x (finProdFinEquiv.symm j).1).1.coeff k) (finProdFinEquiv.symm j).2 := by rw [show gadgetDecompose Φ dd x j = @@ -96,27 +96,27 @@ theorem gadgetDecompose_coeff {base : ZMod q} {rows digits : ℕ} /-- Each gadget-decomposition block is `ℓ∞`-short: its centered `ℓ∞` norm is `≤ b - 1`. -/ theorem gadgetDecompose_zmod_lInftyNorm_le {b digits rows : ℕ} (hb : 1 < b) (hq : q ≤ b ^ digits) - (hbq : b - 1 ≤ q / 2) (h : 1 ≤ Φ.φ.natDegree) (x : PolyVec (Rq Φ) rows) + (hbq : b - 1 ≤ q / 2) (x : PolyVec (Rq Φ) rows) (j : Fin (rows * digits)) : Rq.lInftyNorm Φ (gadgetDecompose Φ (zmodDigitDecomposition b digits hb hq) x j) ≤ b - 1 := by unfold Rq.lInftyNorm refine Finset.sup_le (fun k hk => ?_) - rw [gadgetDecompose_coeff Φ _ h x j (Finset.mem_range.mp hk)] + rw [gadgetDecompose_coeff Φ _ x j (Finset.mem_range.mp hk)] exact zmodDigit_natAbs_le hb hq hbq _ _ /-- **`ℓ∞` shortness of `G⁻¹`.** The full gadget decomposition has centered `ℓ∞` norm `≤ b - 1`. -/ theorem gadgetDecompose_zmod_vecLInftyNorm_le {b digits rows : ℕ} (hb : 1 < b) (hq : q ≤ b ^ digits) - (hbq : b - 1 ≤ q / 2) (h : 1 ≤ Φ.φ.natDegree) (x : PolyVec (Rq Φ) rows) : + (hbq : b - 1 ≤ q / 2) (x : PolyVec (Rq Φ) rows) : vecLInftyNorm Φ (gadgetDecompose Φ (zmodDigitDecomposition b digits hb hq) x) ≤ b - 1 := by unfold vecLInftyNorm - exact Finset.sup_le (fun j _ => gadgetDecompose_zmod_lInftyNorm_le Φ hb hq hbq h x j) + exact Finset.sup_le (fun j _ => gadgetDecompose_zmod_lInftyNorm_le Φ hb hq hbq x j) /-! ## `ℓ₂²` bound -/ /-- Each gadget-decomposition block is `ℓ₂²`-short: its centered squared-`ℓ₂` norm is at most `(deg φ)·(b-1)²` (each of the `deg φ` coefficients contributes at most `(b-1)²`). -/ theorem gadgetDecompose_zmod_l2NormSq_le {b digits rows : ℕ} (hb : 1 < b) (hq : q ≤ b ^ digits) - (hbq : b - 1 ≤ q / 2) (h : 1 ≤ Φ.φ.natDegree) (x : PolyVec (Rq Φ) rows) + (hbq : b - 1 ≤ q / 2) (x : PolyVec (Rq Φ) rows) (j : Fin (rows * digits)) : ‖gadgetDecompose Φ (zmodDigitDecomposition b digits hb hq) x j‖₂² ≤ Φ.φ.natDegree * (b - 1) ^ 2 := by @@ -126,7 +126,7 @@ theorem gadgetDecompose_zmod_l2NormSq_le {b digits rows : ℕ} (hb : 1 < b) (hq ^ 2 ≤ ∑ _k ∈ Finset.range Φ.φ.natDegree, (b - 1) ^ 2 := by refine Finset.sum_le_sum (fun k hk => ?_) - rw [gadgetDecompose_coeff Φ _ h x j (Finset.mem_range.mp hk)] + rw [gadgetDecompose_coeff Φ _ x j (Finset.mem_range.mp hk)] exact Nat.pow_le_pow_left (zmodDigit_natAbs_le hb hq hbq _ _) 2 _ = Φ.φ.natDegree * (b - 1) ^ 2 := by rw [Finset.sum_const, Finset.card_range, smul_eq_mul] @@ -134,14 +134,14 @@ theorem gadgetDecompose_zmod_l2NormSq_le {b digits rows : ℕ} (hb : 1 < b) (hq /-- **`ℓ₂²` shortness of `G⁻¹`.** The full gadget decomposition has centered squared-`ℓ₂` norm at most `(rows·digits)·(deg φ)·(b-1)²`. -/ theorem gadgetDecompose_zmod_vecL2NormSq_le {b digits rows : ℕ} (hb : 1 < b) (hq : q ≤ b ^ digits) - (hbq : b - 1 ≤ q / 2) (h : 1 ≤ Φ.φ.natDegree) (x : PolyVec (Rq Φ) rows) : + (hbq : b - 1 ≤ q / 2) (x : PolyVec (Rq Φ) rows) : ‖gadgetDecompose Φ (zmodDigitDecomposition b digits hb hq) x‖₂² ≤ rows * digits * (Φ.φ.natDegree * (b - 1) ^ 2) := by unfold vecL2NormSq calc ∑ i : Fin (rows * digits), Rq.l2NormSq Φ (gadgetDecompose Φ (zmodDigitDecomposition b digits hb hq) x i) ≤ ∑ _i : Fin (rows * digits), Φ.φ.natDegree * (b - 1) ^ 2 := - Finset.sum_le_sum (fun i _ => gadgetDecompose_zmod_l2NormSq_le Φ hb hq hbq h x i) + Finset.sum_le_sum (fun i _ => gadgetDecompose_zmod_l2NormSq_le Φ hb hq hbq x i) _ = rows * digits * (Φ.φ.natDegree * (b - 1) ^ 2) := by rw [Finset.sum_const, Finset.card_univ, Fintype.card_fin, smul_eq_mul] diff --git a/ArkLib/Commitments/Functional/Hachi/InnerOuter/Correctness.lean b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Correctness.lean index 29344c0302..4ccdaa9720 100644 --- a/ArkLib/Commitments/Functional/Hachi/InnerOuter/Correctness.lean +++ b/ArkLib/Commitments/Functional/Hachi/InnerOuter/Correctness.lean @@ -235,10 +235,10 @@ theorem perfectlyCorrect (b κ : ℕ) (hb : 1 < b) (hκ : 1 ≤ κ) (hbq : b - 1 · rw [Rq.l1Norm_one Φ hdeg]; norm_num · rw [Rq.l1Norm_one Φ hdeg]; exact hκ · intro pp m i - exact gadgetDecompose_zmod_vecL2NormSq_le Φ hb hqm hbq hdeg (m i) + exact gadgetDecompose_zmod_vecL2NormSq_le Φ hb hqm hbq (m i) · intro pp m exact vecLInftyNorm_flattenBlocks_le Φ _ - (fun i => gadgetDecompose_zmod_vecLInftyNorm_le Φ hb hqi hbq hdeg _) + (fun i => gadgetDecompose_zmod_vecLInftyNorm_le Φ hb hqi hbq _) end ZMod diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval.lean index 32153d5f42..5b1ca8a082 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval.lean @@ -28,9 +28,12 @@ inner-outer lift of Greyhound's [NS24, §3.1] folding protocol. Module-SIS(D)) and `relOut` (Eq. (20) + the range checks), and the pure pass-through `verifier` with the honest `prover` skeleton. * `QuadEval/Soundness.lean` — **Hachi Lemma 8**: the subtract-and-divide extractor - (`buildWitness`) and the `sorry`-free coordinate-wise special soundness + (`buildWitness`) and the coordinate-wise special soundness `quadEval_coordinateWiseSpecialSound`, bundled as the composable `quadEvalPackage`; also the - reduction's derived norm constants `B_z` / `βSq`. + reduction's derived norm constants `B_z` / `βSq`. The soundness is genuinely `sorry`-free — + axiom-clean (`#print axioms` gives only `propext` / `Classical.choice` / `Quot.sound`), and its + one deep input, Lyubashevsky–Seiler short-element invertibility `isUnit_of_l1Norm_le`, is itself + proven, not deferred. * `QuadEval/Bridge.lean` — the zero-round polynomial-level head: reinterprets a `CMlPolynomial` evaluation statement (`PolyEvalStatement`) as a `QuadEvalStatement` via the monomial tensor bases, with the pulled-back relation `relPolyEval` and the composable `bridgePackage`. diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean index 59445e6d17..89eabc5088 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean @@ -58,13 +58,18 @@ section Defs variable {R : Type} [Field R] [BEq R] [LawfulBEq R] (Φ : CyclotomicModulus R) [IsCyclotomic Φ] variable {innerRows messageRows messageDigits outerRows blocks innerDigits dRows zDigits : Nat} -/-- The carrier commitment space: `v = D ŵ` lives in the `D`-row space. -/ +/-- The **carrier commitment** space (`CarrierCom` = carrier commitment): the short commitment +`v = D ŵ` lives in the `D`-row space. -/ abbrev CarrierCom (Φ : CyclotomicModulus R) (dRows : Nat) := Simple.Commitment Φ dRows /-- Input statement of Hachi's polynomial-evaluation reduction (Hachi §4.2, Figure 3): the public parameters `(A, B, D)`, the outer commitment `u`, the two evaluation basis vectors `a ∈ Rq^{2^m}` (`avec`) and `b ∈ Rq^{2^r}` (`bvec`) of Eq. (12), and the claimed evaluation -`y = u_eval`. -/ +`y = u_eval`. + +The dimension parameters (`messageRows`, `blocks`, …) are left generic on the structure; the +relations and protocol below specialize `messageRows := 2^m` and `blocks := 2^r` (the paper's +Figure 3 shape), matching the genericity of the other reduction structures. -/ structure QuadEvalStatement (Φ : CyclotomicModulus R) (innerRows messageRows messageDigits outerRows blocks innerDigits dRows : Nat) where /-- Public matrices `(A, B, D)`. -/ @@ -179,8 +184,8 @@ def evalConsistency (base : ZMod q) (a : PolyVec (Rq Φ) (2 ^ m)) (b : PolyVec ( def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool := fun z => decide (vecLInftyNorm Φ z ≤ subLInftyNormBound γ) -/-- **`relOut` — exactly Hachi Eq. (20) plus the `S_b` range checks** on -`((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: +/-- **`relOut` — Hachi Eq. (20) (rows c1–c5 verbatim) plus a symmetric-`ℓ∞`-ball model of the +`S_b` range checks (c6)** on `((stmt, v, c), (ŵ, t̂, ẑ))`, with `z := J ẑ`: * c1: `D ŵ = v` * c2: `B (flatten t̂) = u` @@ -189,10 +194,15 @@ def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool * c5: `(cᵀ ⊗ G_{n_A}) t̂ = A J ẑ` (row 5) * c6: the `S_b` range checks, as symmetric `ℓ∞` balls `≤ γ`. -**`S_b` modeling**: Eq. (20) checks `(ŵ, t̂, ẑ) ∈ S_b^…`, whose elements have centered -coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 uses the symmetric ball `‖·‖∞ ≤ γ`, which with `γ ≥ -⌈b/2⌉` **contains** the paper's box — so every Eq.-(20)-valid transcript is `relOut`-valid and -the CWSS theorem covers the paper's verifier. No challenge-norm checks appear (the challenge +**`S_b` modeling (a deliberate generalization).** Eq. (20) checks `(ŵ, t̂, ẑ) ∈ S_b^…`, whose +elements have centered coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 instead uses the symmetric ball +`‖·‖∞ ≤ γ`, so `relOut` is *not* pointwise identical to Eq. (20) — it is the strictly weaker +(larger) relation obtained by replacing the `S_b` box with its enclosing `ℓ∞` ball. For any +`γ ≥ ⌈b/2⌉` the paper's `S_b` box is **contained** in c6's ball, hence `{Eq. (20)-valid +transcripts} ⊆ relOut`: every honest/paper-accepted transcript is `relOut`-valid, so the CWSS +theorem below covers the paper's verifier. This generalized `relOut` (rather than an exact `S_b` +predicate, which is intentionally not defined) is the reduction's intended output relation, and is +the relation downstream Hachi code should cite. No challenge-norm checks appear (the challenge TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `Gadget/Norms.lean`) — both exactly as in the paper. -/ diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean index 8ee8d8bf77..6920667e33 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean @@ -422,9 +422,19 @@ theorem buildWitness_mem_relIn (hq5 : q % 8 = 5) {b ω γ : ℕ} (hκ : (2 * ω) /-- **Hachi Lemma 8 (CWSS of Hachi's polynomial-evaluation reduction, Figure 3; originally Greyhound's [NS24, §3.1] folding protocol).** The reduction's verifier is coordinate-wise -special sound for the `(ℓ, k) = (2^r, 2)` structure, with `relOut` = Eq. (20) + the `S_b` range -checks and `relIn` = weak opening (eval-consistent) ∨ MSIS(B) ∨ MSIS(D), at the derived -constants `βSq = quadEvalBetaSq γ b zDigits (deg φ) m messageDigits` and `κ = 2ω`. +special sound for the `(ℓ, k) = (2^r, 2)` structure, with `relOut` = Eq. (20) rows + the +symmetric-ball `S_b` model (`QuadEval/Reduction.lean`) and `relIn` = weak opening (eval-consistent) +∨ MSIS(B) ∨ MSIS(D), at the derived constants `βSq = quadEvalBetaSq γ b zDigits (deg φ) m +messageDigits` and `κ = 2ω`. + +**Paper parameter mapping (an intentional generalization).** The theorem is stated over ArkLib's +generalized relation and exposes `(βSq, γ, κ)` as free parameters: `βSq = quadEvalBetaSq γ b …` is +a squared-`ℓ₂` bound on the scaled blocks (the shape `VerifiedOpening` records), `γ` is the +symmetric-ball range bound of c6, and `κ = 2ω`. Hachi Lemma 8 fixes the specific triple +`(β̄, ω̄, γ̄) = (2·bᵗ, 2ω, b)`; instantiating `γ := b` recovers the paper's `S_b`/weak-opening +contract — the `β̄ = 2·bᵗ` bound up to the documented constant-2 slack (see `quadEvalZL2SqBound`). +No exact-`S_b` specialization theorem is proved here: the generalized statement is deliberate and, +via the `relOut` containment, already covers the paper's verifier. Assembled by `coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), which discharges every tree/extractor/guard obligation generically; the whole of Hachi Lemma 8 thereby reduces diff --git a/ArkLib/ProofSystem/Component/SendChallenge.lean b/ArkLib/ProofSystem/Component/SendChallenge.lean index df35eadd6d..a6fd4f0c3b 100644 --- a/ArkLib/ProofSystem/Component/SendChallenge.lean +++ b/ArkLib/ProofSystem/Component/SendChallenge.lean @@ -109,7 +109,11 @@ instance instIsPure : (oracleVerifier oSpec Statement OStatement C ℓ).toVerifi carries `ℓ` coordinates over the alphabet `C`, decomposed by the identity (`Challenge = Fin ℓ → C` already), with soundness parameter `k = 2`. Hence `arity = ℓ·(2−1)+1 = ℓ+1` and the node predicate is `IsSpecialSoundFamily ℓ 2` — exactly the branching required by [NOZ26, Lemma 4 / Definition 3] -(with `ℓ = 2ʳ`). This is the shape the fold block's CWSS ([NOZ26, Lemma 8]) is proven against. -/ +(with `ℓ = 2ʳ`). This is the shape the fold block's CWSS ([NOZ26, Lemma 8]) is proven against. + +The component is deliberately generic over `ℓ` (only `0 < ℓ` is needed): the power-of-two +instantiation `ℓ = 2ʳ` of Figure 3 is imposed by the caller in the `QuadEval` composition layer, +not here. -/ def foldBlockStructure (hℓ : 0 < ℓ) : CWSSStructure (pSpec C ℓ) where coordIndex := fun _ => ⟨ℓ, hℓ⟩ alphabet := fun _ => C From 1998bc305bf343bd2d0b7d33085849797be008c4 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Tue, 14 Jul 2026 12:09:56 +0200 Subject: [PATCH 11/21] add paper def for relout Fig. 3 --- .../Functional/Hachi/QuadEval/Reduction.lean | 92 ++++++++++++++++++- .../Functional/Hachi/QuadEval/Soundness.lean | 34 ++++++- 2 files changed, 118 insertions(+), 8 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean index 89eabc5088..1699dfb5ef 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Reduction.lean @@ -198,11 +198,13 @@ def dShort (γ : ℕ) : ModuleSIS.Solution Φ (blocks * messageDigits) → Bool elements have centered coefficients in `[⌈-b/2⌉, ⌈b/2⌉-1]`; c6 instead uses the symmetric ball `‖·‖∞ ≤ γ`, so `relOut` is *not* pointwise identical to Eq. (20) — it is the strictly weaker (larger) relation obtained by replacing the `S_b` box with its enclosing `ℓ∞` ball. For any -`γ ≥ ⌈b/2⌉` the paper's `S_b` box is **contained** in c6's ball, hence `{Eq. (20)-valid +`γ ≥ ⌊b/2⌋` the paper's `S_b` box is **contained** in c6's ball, hence `{Eq. (20)-valid transcripts} ⊆ relOut`: every honest/paper-accepted transcript is `relOut`-valid, so the CWSS -theorem below covers the paper's verifier. This generalized `relOut` (rather than an exact `S_b` -predicate, which is intentionally not defined) is the reduction's intended output relation, and is -the relation downstream Hachi code should cite. No challenge-norm checks appear (the challenge +theorem below covers the paper's verifier. This box→ball containment is formalized faithfully just +below: the paper's exact `S_b` output relation is `paperRelOut` (built from the box `InSb`, Hachi +[NOZ26] §2.1), and the inclusion is `paperRelOut_subset_relOut`. This generalized `relOut` is the +reduction's intended output relation and the one downstream Hachi code should cite. No +challenge-norm checks appear (the challenge TYPE carries `‖cᵢ‖₁ ≤ ω`), and no `‖z‖₂²` check appears (`‖z‖∞ ≤ …` is derived downstream from c6's `‖ẑ‖∞ ≤ γ` via the `J`-recomposition norm lemma, `Gadget/Norms.lean`) — both exactly as in the paper. -/ @@ -232,6 +234,88 @@ def relOut (base : ZMod q) (ω γ : ℕ) : vecLInftyNorm Φ (PolyVec.flattenBlocks resp.innerDec) ≤ γ ∧ vecLInftyNorm Φ resp.zDec ≤ γ } +/-! ### The paper's exact `S_b` range check and the `paperRelOut ⊆ relOut` containment + +`relOut` relaxes Eq. (20)'s `S_b` box (Hachi [NOZ26] §2.1) to a symmetric `ℓ∞` ball. Here we +formalize that box faithfully (`InSb`) and prove that the paper's exact output relation +`paperRelOut` is contained in `relOut`, so the Lemma 8 CWSS theorem covers the paper's verifier. -/ + +/-- **The paper's balanced-digit box `S_β`** (Hachi [NOZ26] §2.1, p. 9): a ring element lies in +`S_β` when every centered coefficient (its `ZMod.valMinAbs` representative) is in the box +`[⌈-β/2⌉, ⌈β/2⌉-1]`. In `ℕ`/`ℤ` arithmetic these endpoints are `⌈-β/2⌉ = -(β/2)` and +`⌈β/2⌉-1 = (β+1)/2 - 1` (both `/` are `Nat` division). This is exactly the set the Figure 3 +verifier checks in Eq. (20) (`(ŵ, t̂, ẑ) ∈ S_b`). -/ +def InSb (β : ℕ) (a : Rq Φ) : Prop := + ∀ k, k < Φ.φ.natDegree → + -((β / 2 : ℕ) : ℤ) ≤ (a.1.coeff k).valMinAbs ∧ + (a.1.coeff k).valMinAbs ≤ (((β + 1) / 2 : ℕ) : ℤ) - 1 + +/-- Vector version of `InSb`: every entry lies in the box `S_β`. -/ +def vecInSb (β : ℕ) {cols : ℕ} (z : PolyVec (Rq Φ) cols) : Prop := ∀ i, InSb Φ β (z i) + +omit [NeZero q] [IsCyclotomic Φ] in +/-- **Box ⊆ ball.** An `S_β` ring element has centered `ℓ∞` norm `≤ γ` for any `γ ≥ ⌊β/2⌋`: the box +`[⌈-β/2⌉, ⌈β/2⌉-1]` has maximum centered magnitude `⌊β/2⌋ = β/2` (for both parities of `β`). -/ +theorem lInftyNorm_le_of_InSb {β γ : ℕ} (hγ : β / 2 ≤ γ) {a : Rq Φ} (h : InSb Φ β a) : + Rq.lInftyNorm Φ a ≤ γ := by + unfold Rq.lInftyNorm + refine Finset.sup_le fun k hk => ?_ + obtain ⟨hlo, hhi⟩ := h k (Finset.mem_range.mp hk) + omega + +omit [NeZero q] [IsCyclotomic Φ] in +/-- Vector box ⊆ ball: `vecInSb β z → vecLInftyNorm z ≤ γ` for any `γ ≥ ⌊β/2⌋`. -/ +theorem vecLInftyNorm_le_of_vecInSb {β γ cols : ℕ} (hγ : β / 2 ≤ γ) + {z : PolyVec (Rq Φ) cols} (h : vecInSb Φ β z) : vecLInftyNorm Φ z ≤ γ := by + unfold vecLInftyNorm + exact Finset.sup_le fun i _ => lInftyNorm_le_of_InSb Φ hγ (h i) + +/-- **`paperRelOut` — the Figure 3 / Eq. (20) verifier verbatim.** Identical to `relOut` except the +c6 range checks are the paper's exact `S_b` box membership (`vecInSb`, Hachi [NOZ26] §2.1) instead +of the symmetric `ℓ∞` ball. This is the relation the Hachi verifier actually checks; rows c1–c5 +mirror `relOut` verbatim (only c6 differs). -/ +def paperRelOut (base : ZMod q) (ω b : ℕ) : + 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) := + { p | match p with + | ((stmt, v, chals), resp) => + let c : PolyVec (Rq Φ) (2 ^ r) := fun i => (chals i).val + let z : PolyVec (Rq Φ) ((2 ^ m) * messageDigits) := + Hachi.jMatrix Φ base ((2 ^ m) * messageDigits) zDigits *ᵥ resp.zDec + -- c1–c5: the linear system, identical to `relOut` + Simple.commit Φ stmt.pp.dMatrix resp.carrierDec = v ∧ + Simple.commit Φ stmt.pp.outerMatrix (PolyVec.flattenBlocks resp.innerDec) = stmt.u ∧ + dot stmt.bvec (gadgetMatrix Φ base (2 ^ r) messageDigits *ᵥ resp.carrierDec) = stmt.y ∧ + Hachi.tensorG1 Φ base messageDigits c resp.carrierDec = + dot stmt.avec (gadgetMatrix Φ base (2 ^ m) messageDigits *ᵥ z) ∧ + Hachi.tensorG Φ base innerRows innerDigits c resp.innerDec = + stmt.pp.innerMatrix *ᵥ z ∧ + -- c6: the paper's exact `S_b` box (Eq. (20)'s `(ŵ, t̂, ẑ) ∈ S_b`) + vecInSb Φ b resp.carrierDec ∧ + vecInSb Φ b (PolyVec.flattenBlocks resp.innerDec) ∧ + vecInSb Φ b resp.zDec } + +omit [NeZero q] in +/-- **`paperRelOut ⊆ relOut`** — the paper-to-code containment (the reviewer's `paper_relOut ⊆ +relOut`). Every transcript the Figure 3 verifier accepts (Eq. (20), `(ŵ, t̂, ẑ) ∈ S_b`) is accepted +by ArkLib's generalized `relOut` at any range `γ ≥ ⌊b/2⌋`: rows c1–c5 pass through verbatim, and +each `S_b` box check (`vecInSb b`) implies the ball check `vecLInftyNorm ≤ γ` via +`vecLInftyNorm_le_of_vecInSb`. In particular at the paper's own `γ := b` (`b ≥ ⌊b/2⌋`) this shows +the Lemma 8 CWSS theorem (`quadEval_coordinateWiseSpecialSound`) covers the paper's verifier. -/ +theorem paperRelOut_subset_relOut (base : ZMod q) (ω : ℕ) {b γ : ℕ} (hγ : b / 2 ≤ γ) : + paperRelOut Φ (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (zDigits := zDigits) (m := m) (r := r) + base ω b + ⊆ relOut Φ (innerRows := innerRows) (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (zDigits := zDigits) (m := m) (r := r) + base ω γ := by + rintro ⟨⟨stmt, v, chals⟩, resp⟩ ⟨h1, h2, h3, h4, h5, hb1, hb2, hb3⟩ + exact ⟨h1, h2, h3, h4, h5, + vecLInftyNorm_le_of_vecInSb Φ hγ hb1, + vecLInftyNorm_le_of_vecInSb Φ hγ hb2, + vecLInftyNorm_le_of_vecInSb Φ hγ hb3⟩ + /-- **`relIn` — Hachi Lemma 8's extraction disjunction**: a weak `VerifiedOpening` for `u` that is also eval-consistent (Eq. 15), or a Module-SIS solution for `B`, or one for `D`. The `.opening` disjunct is the interface into `outputToModuleSIS_valid_of_verified` for the downstream diff --git a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean index 6920667e33..741ff4f34b 100644 --- a/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean +++ b/ArkLib/Commitments/Functional/Hachi/QuadEval/Soundness.lean @@ -431,10 +431,12 @@ messageDigits` and `κ = 2ω`. generalized relation and exposes `(βSq, γ, κ)` as free parameters: `βSq = quadEvalBetaSq γ b …` is a squared-`ℓ₂` bound on the scaled blocks (the shape `VerifiedOpening` records), `γ` is the symmetric-ball range bound of c6, and `κ = 2ω`. Hachi Lemma 8 fixes the specific triple -`(β̄, ω̄, γ̄) = (2·bᵗ, 2ω, b)`; instantiating `γ := b` recovers the paper's `S_b`/weak-opening -contract — the `β̄ = 2·bᵗ` bound up to the documented constant-2 slack (see `quadEvalZL2SqBound`). -No exact-`S_b` specialization theorem is proved here: the generalized statement is deliberate and, -via the `relOut` containment, already covers the paper's verifier. +`(β̄, ω̄, γ̄) = (2·bᵗ, 2ω, b)`. Instantiating `γ := b` matches `γ̄ = b` and `ω̄ = 2ω` exactly, but +**not** `β̄`: ArkLib's `VerifiedOpening` records a squared-`ℓ₂` bound `βSq` on the scaled blocks +(not the paper's `ℓ₂`/`ℓ∞` value `2·bᵗ`), a deliberate modeling choice (see `quadEvalZL2SqBound`). +That `γ := b` instantiation is the named corollary `quadEval_coordinateWiseSpecialSound_paperParams` +below. The paper's exact `S_b`-box output relation is `QuadEval/Reduction.paperRelOut`, with the +`paperRelOut ⊆ relOut` containment proved as `QuadEval/Reduction.paperRelOut_subset_relOut`. Assembled by `coordinateWiseSpecialSound_of_mkWitness` (`SingleRound.lean`), which discharges every tree/extractor/guard obligation generically; the whole of Hachi Lemma 8 thereby reduces @@ -456,6 +458,30 @@ theorem quadEval_coordinateWiseSpecialSound {ι : Type} {oSpec : OracleSpec ι} (fun stmtIn v fam resp hbranch hstar => buildWitness_mem_relIn hq5 hκ hτ stmtIn v fam resp hbranch hstar) +/-- **Paper-parameter instantiation of Hachi Lemma 8** — the named bridge to the paper's +weak-opening contract. This is `quadEval_coordinateWiseSpecialSound` specialized to the paper's +range `γ := b`. Two of the paper's three Lemma 8 bounds match **exactly**: `γ̄ = b` and `ω̄ = 2ω`. +The third does **not**: the paper's `β̄ = 2·bᵗ` (an `ℓ₂`/`ℓ∞` bound on `‖c̄ᵢsᵢ‖`) is replaced by +ArkLib's `βSq = quadEvalBetaSq b b zDigits (deg φ) m messageDigits`, a *squared-`ℓ₂`* bound on the +scaled blocks carrying extra `2ᵐ·δ·(deg φ)` dimensional factors — a deliberate `VerifiedOpening` +modeling choice, not a paper-faithful value (see `quadEvalZL2SqBound`). Later binding code should +cite this entry point; the general-`γ` theorem above is the intentional ArkLib generalization, and +`QuadEval/Reduction.paperRelOut_subset_relOut` proves the `paper ⊆ code` containment on the output +relation (for `b / 2 ≤ γ`, in particular `γ := b`). -/ +theorem quadEval_coordinateWiseSpecialSound_paperParams {ι : Type} {oSpec : OracleSpec ι} {σ : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hq5 : q % 8 = 5) {b ω : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) : + (verifier (oSpec := oSpec) (ω := ω) 𝓜(q, α) (innerRows := innerRows) + (messageDigits := messageDigits) (outerRows := outerRows) + (innerDigits := innerDigits) (dRows := dRows) (m := m) + (r := r)).coordinateWiseSpecialSound init impl + (foldStructure (CarrierCom := CarrierCom 𝓜(q, α) dRows) + (C := ShortChallenge 𝓜(q, α) ω) (r := r)) + (relIn 𝓜(q, α) (b : ZMod q) + (quadEvalBetaSq b b zDigits ((𝓜(q, α)).φ.natDegree) m messageDigits) b (2 * ω)) + (relOut (zDigits := zDigits) 𝓜(q, α) (b : ZMod q) ω b) := + quadEval_coordinateWiseSpecialSound (γ := b) init impl hq5 hκ hτ + -- An `OracleVerifier` wrapper is deliberately not included: it needs an `OracleInterface` -- instance for `Simple.Commitment` (a query-model design decision that does not exist in the -- repo yet) and the still-sorried oracle-level append theorem. The plain-`Verifier` statement From 1a648d934cf3f41b2e6250e577e4c648d485994e Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Tue, 14 Jul 2026 16:24:39 +0200 Subject: [PATCH 12/21] skeleton --- ArkLib.lean | 21 ++++---- .../Functional/Hachi/Composition.lean | 14 ++--- .../Hachi/{LinSumcheck => }/Escape.lean | 5 +- .../Hachi/Recursion/PartialEval.lean | 2 +- .../Functional/Hachi/RingSwitch.lean | 40 ++++++++++++++ .../Lift.lean => RingSwitch/Reduction.lean} | 6 +-- .../{LinSumcheck => RingSwitch}/Rlin.lean | 6 +-- .../Functional/Hachi/Sumcheck.lean | 54 +++++++++++++++++++ .../Bridge.lean} | 4 +- .../{LinSumcheck => Sumcheck}/FinalEval.lean | 2 +- .../{LinSumcheck => Sumcheck}/Rounds.lean | 13 +++-- .../Functional/Hachi/ZeroCheck.lean | 48 +++++++++++++++++ .../BatchBridge.lean => ZeroCheck/Batch.lean} | 6 +-- .../Constraints.lean | 15 ++++-- .../Reduction.lean} | 2 +- .../Guarded.lean | 2 +- docs/wiki/repo-map.md | 36 +++++++++---- 17 files changed, 225 insertions(+), 51 deletions(-) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck => }/Escape.lean (98%) create mode 100644 ArkLib/Commitments/Functional/Hachi/RingSwitch.lean rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck/Lift.lean => RingSwitch/Reduction.lean} (98%) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck => RingSwitch}/Rlin.lean (97%) create mode 100644 ArkLib/Commitments/Functional/Hachi/Sumcheck.lean rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck/SumcheckBridge.lean => Sumcheck/Bridge.lean} (96%) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck => Sumcheck}/FinalEval.lean (99%) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck => Sumcheck}/Rounds.lean (94%) create mode 100644 ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck/BatchBridge.lean => ZeroCheck/Batch.lean} (96%) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck => ZeroCheck}/Constraints.lean (94%) rename ArkLib/Commitments/Functional/Hachi/{LinSumcheck/ZeroCheck.lean => ZeroCheck/Reduction.lean} (99%) diff --git a/ArkLib.lean b/ArkLib.lean index 22fdf34fdf..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 @@ -12,15 +13,6 @@ import ArkLib.Commitments.Functional.Hachi.InnerOuter.Arithmetic import ArkLib.Commitments.Functional.Hachi.InnerOuter.Correctness import ArkLib.Commitments.Functional.Hachi.InnerOuter.Scheme import ArkLib.Commitments.Functional.Hachi.InnerOuter.Security -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.BatchBridge -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Constraints -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Escape -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.FinalEval -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Lift -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rlin -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rounds -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.SumcheckBridge -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.ZeroCheck import ArkLib.Commitments.Functional.Hachi.QuadEval import ArkLib.Commitments.Functional.Hachi.QuadEval.Bridge import ArkLib.Commitments.Functional.Hachi.QuadEval.Gadgets @@ -29,6 +21,17 @@ 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 diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 88492b7cce..778f77184c 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -4,7 +4,7 @@ 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.LinSumcheck.FinalEval +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 @@ -57,11 +57,11 @@ seam a home for the `w̃`-commitment's weak-binding break (design G1; `E` abstra ---+----------------------------+----------------------+---------------------------+--------------- 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 (LinSumcheck/Rlin) | 0 | relOutE → relRlinE | any - 4 | lift (LinSumcheck/Lift) | msg t; α ∈ F | relRlinE → relLiftE | ℓ=1, k=2d (L9) - 5 | batch (…/BatchBridge) | 0 | relLiftE → relBatchedE | any - 6 | zero-check (…/ZeroCheck) | (ρ₀,ρ_α) ∈ F² | relBatchedE → relZeroChkE | ℓ=2, k=D (L10*) - 7 | sc bridge (…/SumcheckBridge)| 0 | relZeroChkE → roundRelE 0 | any + 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 @@ -139,7 +139,7 @@ Both packages are defined next to their CWSS theorems in the component files (`b composed. The seam is definitional — the bridge's `relOut` *is* `QuadEval`'s `relIn` — so `▷` discharges it by `rfl`. The chain's `isCWSS` field is `eval_coordinateWiseSpecialSound`. This is the finished, sorry-free core; the escape-threaded variant `evalChainE` -(`LinSumcheck/Escape.lean`) is its drop-in for the extended opening chain below. -/ +(`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 diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean b/ArkLib/Commitments/Functional/Hachi/Escape.lean similarity index 98% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean rename to ArkLib/Commitments/Functional/Hachi/Escape.lean index ce4de5bf13..8d82089465 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Escape.lean +++ b/ArkLib/Commitments/Functional/Hachi/Escape.lean @@ -10,7 +10,8 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape /-! # Escape-threaded Hachi front (`evalChainE`) — skeleton (sumcheck-track milestone F2.0) - The §4.3 opening chain (`LinSumcheck/`) introduces a **new commitment** — Figure 4's + 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 @@ -171,7 +172,7 @@ def quadEvalPackageE (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ Pro /-- **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 (`LinSumcheck/`) composes onto this front's `relOutE` seam. -/ +§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 diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean index 855e6e94ec..2d03bce824 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.FinalEval +import ArkLib.Commitments.Functional.Hachi.Sumcheck.FinalEval /-! # Partial evaluations — Hachi §4.5, Eq. (24) — skeleton (milestone G2) 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/LinSumcheck/Lift.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean similarity index 98% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/Lift.lean rename to ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean index 316b40366b..6db2a68ea0 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Lift.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rlin +import ArkLib.Commitments.Functional.Hachi.RingSwitch.Rlin import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRound /-! @@ -19,7 +19,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRoun * **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, `LinSumcheck/Constraints.lean`). + 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 @@ -154,7 +154,7 @@ def relLift (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) bound ≤ p.1.1.bound} /-- Escape-threaded lift relation — the seam consumed by the batching bridge -(`LinSumcheck/BatchBridge.lean`). -/ +(`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)) := diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rlin.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean similarity index 97% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rlin.lean rename to ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean index 0fa759d0d8..cd9e30ac80 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rlin.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Rlin.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Escape +import ArkLib.Commitments.Functional.Hachi.Escape /-! # Eq. (20) → `R^lin` adapter — skeleton (Hachi §4.3 entry; sumcheck-track milestone F2) @@ -34,8 +34,8 @@ import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Escape `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 `LinSumcheck/Escape.lean`), and its `relOut` - `relRlinE` is definitionally the next link's (`LinSumcheck/Lift.lean`) `relIn`. + escape-threaded Eq. (20) relation from `Escape.lean`), and its `relOut` + `relRlinE` is definitionally the next link's (`RingSwitch/Reduction.lean`) `relIn`. ## References 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/LinSumcheck/SumcheckBridge.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean similarity index 96% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/SumcheckBridge.lean rename to ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean index b1bd4030b4..dfe2b52cf6 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/SumcheckBridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.ZeroCheck +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction /-! # Sumcheck bridge — point claims to hypercube sums — skeleton (zero-round) @@ -21,7 +21,7 @@ import ArkLib.Commitments.Functional.Hachi.LinSumcheck.ZeroCheck 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`, `LinSumcheck/Constraints.lean`) — so the + (`sum_sumcheckPolyZero` / `sum_sumcheckPolyAlpha`, `ZeroCheck/Constraints.lean`) — so the only sorried piece is the pull-back through those (themselves sorried F5) identities. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/FinalEval.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean similarity index 99% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/FinalEval.lean rename to ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean index 2970566f20..b1b41506ec 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/FinalEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Rounds +import ArkLib.Commitments.Functional.Hachi.Sumcheck.Rounds /-! # Final evaluation — Hachi Figure 7 tail — skeleton (milestone F8) diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean similarity index 94% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean rename to ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean index 1535500b0d..cc181915c8 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Rounds.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean @@ -3,14 +3,14 @@ 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.LinSumcheck.SumcheckBridge +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`, `LinSumcheck/Constraints.lean`) by one variable. + partial-hypercube-sum claims (`roundRel i`, `ZeroCheck/Constraints.lean`) by one variable. ## Paired rounds (design D9) @@ -161,7 +161,14 @@ tree node share the message pair `(g^{(0)}, g^{(α)})` and carry pairwise-distin 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`). -/ +`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)) 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/LinSumcheck/BatchBridge.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean similarity index 96% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/BatchBridge.lean rename to ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean index ed0671129a..baa10c5633 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/BatchBridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Constraints +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints /-! # Batching bridge — Hachi Eqs. (22)–(23) — skeleton (zero-round, part of milestone F6) @@ -12,9 +12,9 @@ import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Constraints polynomial-identity** form the zero-check tests: * `relIn = relLiftE` — opening `w̃` of `t`, per-row `α`-evaluated constraints, entrywise - ranges (`LinSumcheck/Lift.lean`); + ranges (`RingSwitch/Reduction.lean`); * `relOut = relBatchedE` — opening `w̃` of `t`, `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` as - `MvPolynomial` identities (Eqs. (22)–(23), `LinSumcheck/Constraints.lean`). + `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 diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean similarity index 94% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean rename to ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean index c0217de844..afcf94aed9 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/Constraints.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.Lift +import ArkLib.Commitments.Functional.Hachi.RingSwitch.Reduction import Mathlib.Algebra.MvPolynomial.Basic /-! @@ -47,7 +47,7 @@ import Mathlib.Algebra.MvPolynomial.Basic `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 (`LinSumcheck/ZeroCheck.lean`). + 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`/ @@ -129,7 +129,14 @@ theorem hAlpha_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement (hAlpha Φ m₁ φF b s a w).degreeOf j ≤ 1 := by sorry -/-! ## The sumcheck polynomials (sorried F5 content) -/ +/-! ## 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 @@ -160,7 +167,7 @@ def hypercubeSum (H : MvPolynomial (Fin m₀) F) (i : ℕ) (cs : Fin i → F) : sorry /-- The sum of `F_{0,τ₀}` over the cube is `H₀(τ₀)` — the algebraic identity behind the -sumcheck bridge (`LinSumcheck/SumcheckBridge.lean`). **Sorried (F5).** -/ +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) = diff --git a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean similarity index 99% rename from ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.lean rename to ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean index 9593bc4cb4..c8574942b3 100644 --- a/ArkLib/Commitments/Functional/Hachi/LinSumcheck/ZeroCheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -3,7 +3,7 @@ Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Tobias Rothmann -/ -import ArkLib.Commitments.Functional.Hachi.LinSumcheck.BatchBridge +import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch /-! # Zero-check — Hachi Figure 5 / **corrected** Lemma 10 — skeleton (milestone F6) diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean index b930d03f80..6d3959f4d7 100644 --- a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/Guarded.lean @@ -51,7 +51,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package 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/LinSumcheck/Rounds.lean`), which only needs the binary + (`ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean`), which only needs the binary theorem. ## References diff --git a/docs/wiki/repo-map.md b/docs/wiki/repo-map.md index 3a1008122e..1d12b949a5 100644 --- a/docs/wiki/repo-map.md +++ b/docs/wiki/repo-map.md @@ -103,17 +103,31 @@ 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. - - `LinSumcheck/` (§4.3, Figures 4–7) — the **skeleton** of Hachi's sumcheck-based opening - chain, one file per subprotocol/bridge, each exporting a `CWSSPackage`/`GCWSSPackage` with a - sorried CWSS theorem: `Escape` (escape-threaded front `evalChainE`, design G1), `Rlin` - (Eq. (20) → `R^lin` zero-round adapter, F2), `Lift` (HMZ25 lift, Figure 4/Lemma 9, `k = 2d`; - the abstract `w̃`-commitment `LiftCom`), `Constraints` (Eq. (21)–(23) encodings, sumcheck - polynomials, degree pins, the Kronecker curve `kroneckerPoint`, per-round seam `roundRel`), - `BatchBridge` (per-row/range ⇄ `H₀ ≡ 0 ∧ H_α ≡ 0`), `ZeroCheck` (Figure 5 / **corrected** - Lemma 10 — Kronecker seed pair, `(ℓ, k) = (2, D)`; see `HACHI_LEMMA10_GAP.md`), - `SumcheckBridge` (point claims → initial hypercube sums), `Rounds` (Figure 6/Lemma 11 — - guarded paired rounds, loop by recursion over `▷ᵍ`), `FinalEval` (Figure 7 tail — guarded - reveal of `w̃(a)`). + - §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) From c8ed7417d7e3c774987e7c683d9f93288bd5de02 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Wed, 15 Jul 2026 14:07:42 +0200 Subject: [PATCH 13/21] skeleton site --- docs/hachi-overview.html | 849 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 849 insertions(+) create mode 100644 docs/hachi-overview.html diff --git a/docs/hachi-overview.html b/docs/hachi-overview.html new file mode 100644 index 0000000000..b8ca885a4f --- /dev/null +++ b/docs/hachi-overview.html @@ -0,0 +1,849 @@ +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). One opening iteration is a + single line of twelve reductions — starting at the quadratic-form evaluation + (QuadEval) and ending at a handoff that lands back on the first link over the next ring, + so the protocol recurses. This map places every protocol-bearing file on that line, marks the bridges and + subfolders, and shows exactly what is proven, in progress, or open. +

+
+ 31 files + 25 with protocol content + 12-link opening chain + rows 1–2 sorry-free + ≈34 open sorries +
+
+
+ +
+ +
+ + +
+
+ At a glance +

Where the formalization stands

+

Counts are over the 25 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 + Open gap — flagged, expected unprovable as stated +
+
+
+ +
+ + +
+
+ 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. +
+
+ gap + A deliberately isolated open soundness question — the paper's step appears false as stated; the sorry is kept until a repair is chosen. +
+
+ planned + Named in the composition roadmap but not yet a file — future heads, tails, and the completeness layer. +
+
+
+
+ +
+ + +
+
+ The composed opening · one iteration +

The reduction line — QuadEval → recursion handoff

+

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 lands on the next iteration's QuadEval + input relation over the next ring Φ′.

+
+
+
+ +
+ + +
+
+ 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 31 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 recursion's base case, the completeness layer, and the one piece of generic machinery the guarded seams wait on.

+
+
+
+ +
+ +
+
+ References & provenance +
+
[NOZ26] Nguyen, O'Rourke & Zhang — Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over Extension Fields
+
[NS24] Nguyen & Seiler — Greyhound: Fast Polynomial Commitments from Lattices
+
[HMZ25] Huang, Mao & Zhang — Sublinear Proofs over Polynomial Rings
+
[LS18] Lyubashevsky & Seiler — Short, Invertible Elements in Partially Splitting Cyclotomic Rings
+
+

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

+
+
+ + From 7501ae8597a574a4eb899d19e00f2d66fd9cf793 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Wed, 15 Jul 2026 14:28:48 +0200 Subject: [PATCH 14/21] move hachi artifact --- {docs => ArkLib/Commitments/Functional/Hachi}/hachi-overview.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => ArkLib/Commitments/Functional/Hachi}/hachi-overview.html (100%) diff --git a/docs/hachi-overview.html b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html similarity index 100% rename from docs/hachi-overview.html rename to ArkLib/Commitments/Functional/Hachi/hachi-overview.html From f5135e242a187e0c3da8b98e91476e0e461bb37c Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Wed, 15 Jul 2026 16:26:41 +0200 Subject: [PATCH 15/21] prove milestone skill --- docs/skills/README.md | 2 + docs/skills/prove-milestone.md | 426 +++++++++++++++++++++++++++++++++ 2 files changed, 428 insertions(+) create mode 100644 docs/skills/prove-milestone.md 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. From 5c2f128a1ab2049d3ec8618ea81098dbac02f005 Mon Sep 17 00:00:00 2001 From: tobias-rothmann Date: Wed, 15 Jul 2026 18:42:05 +0200 Subject: [PATCH 16/21] update hachi plan --- .../Functional/Hachi/hachi-overview.html | 114 +++++------------- 1 file changed, 32 insertions(+), 82 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html index b8ca885a4f..d1a93e59cb 100644 --- a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html +++ b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html @@ -115,7 +115,7 @@ .rule { height:1px; background:var(--line); border:0; margin:0; } /* ---------- summary tiles ---------- */ - .tiles { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; } + .tiles { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; } .tile { background:var(--surface); border:1px solid var(--line); border-radius:14px; padding:18px 18px 16px; position:relative; box-shadow:var(--shadow); @@ -237,18 +237,6 @@ .desc { margin-top:11px; font-size:13.5px; color:var(--ink-soft); line-height:1.5; } .desc b { color:var(--ink); font-weight:600; } - /* recursion loop banner */ - .loopband { - position:relative; margin:6px 0 4px 64px; padding:14px 18px; - border:1.5px dashed color-mix(in srgb,var(--accent) 55%,var(--line)); - border-radius:12px; background:var(--accent-ghost); - display:flex; align-items:center; gap:14px; - } - .loopband .icon { font-size:22px; color:var(--accent); flex:none; } - .loopband .lt { font-size:13.5px; color:var(--ink-soft); } - .loopband .lt b { color:var(--ink); font-weight:650; } - .loopband .lt code { color:var(--accent); } - .iterband { margin:0 0 2px 64px; font-family:var(--font-mono); font-size:11.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); font-weight:600; @@ -348,7 +336,7 @@ .station::before{ left:19px; } .node{ left:5px; } .station.bridge .node{ left:10px; } - .loopband, .iterband{ margin-left:0; } + .iterband{ margin-left:0; } h1{ max-width:none; } } @@ -365,18 +353,18 @@

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). One opening iteration is a - single line of twelve reductions — starting at the quadratic-form evaluation - (QuadEval) and ending at a handoff that lands back on the first link over the next ring, - so the protocol recurses. This map places every protocol-bearing file on that line, marks the bridges and - subfolders, and shows exactly what is proven, in progress, or open. + 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.

- 31 files - 25 with protocol content - 12-link opening chain + 28 files + 22 with protocol content + 9-link opening chain rows 1–2 sorry-free - ≈34 open sorries + ≈24 open sorries
@@ -390,7 +378,7 @@

The Hachi evaluation opening, seam by seam

At a glance

Where the formalization stands

-

Counts are over the 25 files carrying definitions or theorems (the six folder umbrella +

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.

@@ -400,7 +388,6 @@

Where the formalization stands

Proven — sorry-free & complete WIP — theorems stated, proofs skeletal - Open gap — flagged, expected unprovable as stated
@@ -443,10 +430,6 @@

Proof status

wip A real skeleton: types, relations, verifier and package are defined and typecheck; the CWSS proof (and sometimes the encoding defs) are sorry. -
- gap - A deliberately isolated open soundness question — the paper's step appears false as stated; the sorry is kept until a repair is chosen. -
planned Named in the composition roadmap but not yet a file — future heads, tails, and the completeness layer. @@ -460,13 +443,13 @@

Proof status

- The composed opening · one iteration -

The reduction line — QuadEval → recursion handoff

+ 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 lands on the next iteration's QuadEval - input relation over the next ring Φ′.

+ (threaded by Escape.lean). The final link outputs a bare multilinear-evaluation + claim mle[w̃](a) = y′ on the committed table.

@@ -494,14 +477,13 @@

The substrate & the cross-cutting machinery

Complete accounting

Every file in Hachi/

-

All 31 files, including the six folder umbrellas. Filter by status; the row column is the +

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

- + -
@@ -522,7 +504,7 @@

Every file in Hachi/

Still to do

Beyond the current line

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

+ the completeness layer, and the one piece of generic machinery the guarded seams wait on.

@@ -539,10 +521,10 @@

Beyond the current line

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

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

@@ -606,23 +588,7 @@

Beyond the current line

{ n:9, kind:"step", name:"Final evaluation", sub:"Sumcheck", fn:"FinalEval.lean", files:[["Sumcheck","FinalEval.lean"]], ref:"§4.3 · Fig 7 tail", rounds:"msg y′ ∈ F", cwss:"any", relIn:"roundRelE m₀", relOut:"relWEvalE", status:"wip", sorry:2, badges:["guarded"], - desc:"The prover sends the claimed evaluation y′ = w̃(a) in the clear; the guarded verifier checks both final sumcheck targets. Output is the evaluation claim mle[w̃](a) = y′ — the currency the recursion consumes." }, - ]}, - { zt:"Recursion Adapters", zref:"§4.5 · Eqs. 24–28", - znote:"Peel the top κ variables, pack the pieces, and hand the result back as the next iteration's QuadEval statement — closing the loop with no new commitment (t is reinterpreted at the next ring dimension).", - rows:[ - { n:10, kind:"step", name:"Partial evaluations", sub:"Recursion", fn:"PartialEval.lean", - files:[["Recursion","PartialEval.lean"]], ref:"§4.5 · Eq. 24", rounds:"msg (yᵢ)_{i≠0}", cwss:"any (pure)", - relIn:"relWEvalE", relOut:"relPartialE", status:"wip", sorry:5, badges:[], - desc:"Peels the top κ variables into a per-i partial-evaluation family; the verifier derives y₀ instead of checking, keeping this head pure. A sound, zero-error seam — the gap is the next link, not this one." }, - { n:11, kind:"bridge", name:"Z-packing bridge", sub:"Recursion", fn:"ZBatchBridge.lean", - files:[["Recursion","ZBatchBridge.lean"]], ref:"§4.5 · Eqs. 25–26", rounds:"0 rounds", cwss:"any — ⚠ gap", - relIn:"relPartialE", relOut:"relHatEvalE", status:"gap", sorry:2, badges:["bridge","gap"], - desc:"Packs the per-i claims into a single Z-packed claim. ⚠ Open soundness question: the packed claim pins only one F-linear combination of the per-i defects, which has a nontrivial kernel for κ ≥ 1 — so the paper's step is (apparently) not knowledge-sound as stated. The sorry is kept, isolated in this one seam, until a repair is chosen." }, - { n:12, kind:"step", name:"Trace handoff", sub:"Recursion", fn:"TraceHandoff.lean", - files:[["Recursion","TraceHandoff.lean"]], ref:"§4.5 · Eqs. 27–28 · Thm 2", rounds:"msg p ∈ R′q", cwss:"any", - relIn:"relHatEvalE", relOut:"relInE (Φ′)", status:"wip", sorry:3, badges:["guarded"], - desc:"Converts the Z-packed F-claim into the next iteration's Rq-quadratic statement over the next ring Φ′, guarded on the trace equation. No new commitment: t is reinterpreted at dimension d′. Lands on the next iteration's QuadEval seam." }, + desc:"The prover sends the claimed evaluation y′ = w̃(a) in the clear; the guarded verifier checks both final sumcheck targets. Output is the evaluation claim mle[w̃](a) = y′ — the final currency the opening yields." }, ]}, ]; @@ -640,7 +606,7 @@

Beyond the current line

{ name:"Shared constraint encoding", ref:"§4.3 · Eqs. 21–23", files:[["ZeroCheck","Constraints.lean"]], status:"wip", sorry:12, desc:"The table w̃, the eq̃-batched constraint polynomials H₀/H_α, their sumcheck summands, and the per-round seam relations. Consumed by both the zero-check (row 6) and the sumcheck loop (rows 7–9). Definitions-only for now — the most sorried file in the tree." }, { name:"Composition home", ref:"the certificate", files:[["","Composition.lean"]], status:"wip", sorry:0, - desc:"Imports every subprotocol's package and chains them: evalChain (the proven core, sorry-free) · openCore (rows 1–7, pure) · openingChain (rows 1–12, guarded tail). Its isCWSS fields are the certificates; the file itself has no sorries — its soundness rests on the imported packages." }, + desc:"Imports every subprotocol's package and chains them: evalChain (the proven core, sorry-free) · openCore (rows 1–7, pure) · openingChain (rows 1–9, guarded tail). Its isCWSS fields are the certificates; the file itself has no sorries — its soundness rests on the imported packages." }, { name:"Scheme interface", ref:"the packaging", files:[["","Commitment.lean"]], status:"wip", sorry:1, desc:"Hachi as a Commitment.Scheme: the multilinear eval-oracle, honest keygen and commit are real. The single sorry is the opening proof — the one documented placeholder, pending the completeness layer." }, ]; @@ -648,14 +614,10 @@

Beyond the current line

var ROADMAP = [ { title:"§3 packing head — external extension-field claims", tag:"planned", body:"Wrap an F_{q^k} evaluation claim in front of relPolyEvalE, as an instance of the generalized RingSwitching packing phase — a separate track, not a Hachi-local head." }, - { title:"Recursion termination", tag:"planned", - body:"At the row-12 seam: the §4.4 asymptotic base case (reveal the final small polynomial) and the concrete §4.5 Greyhound / LaBRADOR cutoff — future zero-round / one-message tails." }, { title:"Honest-prover completeness layer", tag:"planned", body:"Materialize hachi.opening by instantiating QuadEval's computeV / computeResp from the carrier/decomposition defs, discharging Commitment.perfectCorrectness." }, - { title:"Discharge the row-11 gap", tag:"gap", - body:"Pick a repair for the Z-packing seam (a batching challenge round, or the generic §3.1 packing). Any repair changes only that bridge's protocol content, not the surrounding seams." }, { title:"Generic guarded-append machinery (B4)", tag:"planned", - body:"The one sorried piece of generic infrastructure the guarded seams (rows 8, 9, 12) compose through — Verifier.IsGuarded.append and its CWSS composition theorem, under OracleReduction/…/Guarded.lean." }, + body:"The one sorried piece of generic infrastructure the guarded seams (rows 8, 9) compose through — Verifier.IsGuarded.append and its CWSS composition theorem, under OracleReduction/…/Guarded.lean." }, { title:"Knowledge-error accounting · extractability · Fiat–Shamir", tag:"planned", body:"FMN24 Lemma 4 knowledge-error, Commitment.extractability, and the Fiat–Shamir transform remain out of scope by design (D12 / R6)." }, ]; @@ -663,7 +625,7 @@

Beyond the current line

/* full index: [subfolder, filename, role, ref, row, status, sorry] */ var INDEX = [ ["","Commitment.lean","commitment-interface","§2.1/§4.1","—","wip",1], - ["","Composition.lean","composition","§4.2–4.5","—","wip",0], + ["","Composition.lean","composition","§4.2–4.3","—","wip",0], ["","Escape.lean","infrastructure","F2.0","—","wip",1], ["","EvalSplit.lean","infrastructure","§4 · Eq.12","—","proven",0], ["","Gadget.lean","umbrella","§2.1","—","umbrella",0], @@ -690,9 +652,6 @@

Beyond the current line

["Sumcheck","Bridge.lean","bridge","§4.3","7","wip",1], ["Sumcheck","Rounds.lean","subprotocol","§4.3 · Fig6 · L11","8","wip",1], ["Sumcheck","FinalEval.lean","subprotocol","§4.3 · Fig7","9","wip",2], - ["Recursion","PartialEval.lean","subprotocol","§4.5 · Eq24","10","wip",5], - ["Recursion","ZBatchBridge.lean","bridge","§4.5 · Eq25–26","11","gap",2], - ["Recursion","TraceHandoff.lean","subprotocol","§4.5 · Eq27–28","12","wip",3], ]; /* ---------------- HELPERS ---------------- */ @@ -702,13 +661,12 @@

Beyond the current line

function pathHTML(sub, fn){ return (sub?(''+sub+'/'):'')+''+fn+''; } /* ---------------- TILES ---------------- */ - var counts = { proven:0, wip:0, gap:0 }; + var counts = { proven:0, wip:0 }; INDEX.forEach(function(r){ if(r[2]==="umbrella") return; if(counts[r[5]]!=null) counts[r[5]]++; }); var tiles = [ { cls:"ok", n:counts.proven, lbl:"Proven", sub:"Sorry-free & complete — all foundations plus the entire QuadEval subprotocol (rows 1–2)." }, - { cls:"warn", n:counts.wip, lbl:"In progress", sub:"Real skeletons: rows 3–10 & 12, escape threading, the shared encoding, composition and the scheme." }, - { cls:"bad", n:counts.gap, lbl:"Open gap", sub:"Row 11, the Z-packing bridge — an isolated open soundness question (HACHI_RECURSION_GAP.md)." }, - { cls:"plan", n:ROADMAP.length, lbl:"Planned", sub:"Named in the roadmap but not yet a file — heads, tails, completeness & generic machinery." }, + { cls:"warn", n:counts.wip, lbl:"In progress", sub:"Real skeletons: rows 3–9, escape threading, the shared encoding, composition and the scheme." }, + { cls:"plan", n:ROADMAP.length, lbl:"Planned", sub:"Named in the roadmap but not yet a file — the outer interface, the completeness layer & generic machinery." }, ]; var tilesEl = document.getElementById("tiles"); tiles.forEach(function(t){ @@ -718,15 +676,15 @@

Beyond the current line

tilesEl.appendChild(d); }); /* status bar (files, excluding umbrellas) */ - var total = counts.proven+counts.wip+counts.gap; + var total = counts.proven+counts.wip; var bar = document.getElementById("statusBar"); - [["var(--ok)",counts.proven],["var(--warn)",counts.wip],["var(--bad)",counts.gap]].forEach(function(seg){ + [["var(--ok)",counts.proven],["var(--warn)",counts.wip]].forEach(function(seg){ var s=el("span"); s.style.background=seg[0]; s.style.width=(100*seg[1]/total)+"%"; bar.appendChild(s); }); /* ---------------- SPINE ---------------- */ var spine = document.getElementById("spine"); - spine.appendChild(el("div","iterband reveal","▸ iteration i — one opening")); + spine.appendChild(el("div","iterband reveal","▸ one opening — QuadEval to evaluation claim")); var flatCount = 0, totalStations = SPINE.reduce(function(a,z){return a+z.rows.length;},0); SPINE.forEach(function(zone, zi){ var z = el("div","zone reveal"); @@ -765,14 +723,6 @@

Beyond the current line

}); spine.appendChild(z); }); - /* recursion loop band */ - var loop = el("div","loopband reveal"); - loop.innerHTML = ''+ - 'Recursion. Link 12 lands on relInE (Φ′) — the QuadEval input relation over the '+ - 'next ring Φ′. Iteration i+1 re-enters the line at link 2 (QuadEval), skipping the row-1 '+ - 'polynomial-level bridge: its bases are eq-tensor packings, not monomial bases. The loop closes here.'; - spine.appendChild(loop); - /* ---------------- SUBSTRATE ---------------- */ function subCard(c){ var d = el("div","sub-card reveal"); From 2927997e98e0cede5ebd37306f2db6bf3887e3f0 Mon Sep 17 00:00:00 2001 From: ErVinuelas Date: Fri, 17 Jul 2026 14:33:17 +0200 Subject: [PATCH 17/21] Towards the formalization of lemma 10: link 6 of artifact --- ArkLib.lean | 1 + .../Functional/Hachi/ZeroCheck.lean | 30 +- .../Functional/Hachi/ZeroCheck/Batch.lean | 39 +-- .../Hachi/ZeroCheck/Constraints.lean | 239 ++++++++----- .../Functional/Hachi/ZeroCheck/Reduction.lean | 324 +++++++++++------ .../Data/MvPolynomial/LinearMvExtension.lean | 179 ++++++++++ ArkLib/Data/MvPolynomial/Multilinear.lean | 12 + .../ChallengeRound.lean | 327 ++++++++++++++++++ docs/kb/audits/README.md | 4 + docs/kb/audits/noz26-zero-check-lemma10.md | 89 +++++ docs/kb/index.md | 3 + docs/kb/papers/NOZ26.md | 11 +- 12 files changed, 1031 insertions(+), 227 deletions(-) create mode 100644 ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ChallengeRound.lean create mode 100644 docs/kb/audits/noz26-zero-check-lemma10.md diff --git a/ArkLib.lean b/ArkLib.lean index becaefba7b..c9cf5b4b3d 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -216,6 +216,7 @@ import ArkLib.OracleReduction.Salt import ArkLib.OracleReduction.Security.Basic import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Basic +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ChallengeRound import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Composition import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Escape import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Guarded diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean index e293809517..da99c67378 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann +Authors: Tobias Rothmann, Pablo Martín Vinuelas -/ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction @@ -19,27 +19,35 @@ Hachi's Lemma 10 (uniform-vector-challenge extraction) is **not provable as stat coordinate-wise star certifies only axis-cross vanishing, and for `m ≥ 2` that does not imply `H ≡ 0`. `ZeroCheck/Reduction.lean` implements the adopted repair — two scalar **Kronecker seeds** `(ρ₀, ρ_α)`, with the evaluation points derived on the curves `κ_m(ρ) = (ρ, ρ², ρ⁴, …)`, where -univariate root counting is information-complete. Full analysis: `HACHI_LEMMA10_GAP.md`. +univariate root counting is information-complete. The formal counterexample to the paper's +argument is `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`; the specification boundary +is recorded in `docs/kb/audits/noz26-zero-check-lemma10.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. + `w̃`, the batched polynomials `H₀`/`H_α` (genuine `MvPolynomial.MLE`s, so their multilinearity + is `sorry`-free), the sumcheck polynomials `F_{0,τ₀}`/`F_{α,τ₁}` with their degree pins, the + Kronecker point `kroneckerPoint`, `hypercubeSum`, and `roundRel`/`roundRelE`. Consumed by both + this zero-check *and* the sumcheck round loop (`Sumcheck/`), so it sits at the shared base of the + batched-sumcheck machinery. The table entry function `wTable` and the `M̃_α` contraction + `hAlphaEvals` (the genuine F5 index bookkeeping) and the sumcheck-polynomial stubs remain + `sorry`. * `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. + (`relBatched`/`relBatchedE`, Eqs. (22)–(23)); the un-batching pull-back + `mem_relLiftE_of_relBatchedE` is the sorried F6 content. * `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**). + derived Kronecker points. The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` (`relBatchedE + → relZeroCheckE`, `k = D = zeroCheckD m₀ m₁`) is **proof-`sorry`-free** — its escape/collision/ + root-counting extraction is complete; it is not *axiom-clean* only because `hZero`/`hAlpha` are + built on the still-`sorry` F5 encodings. The Kronecker kernel `arm_eq_zero_of_family` is + axiom-clean. 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`. +is composed in `Composition.lean` (`openCore`, row 6). ## References diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean index baa10c5633..48b8abdbad 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -1,12 +1,12 @@ /- Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann +Authors: Tobias Rothmann, Pablo Martín Vinuelas -/ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints /-! - # Batching bridge — Hachi Eqs. (22)–(23) — skeleton (zero-round, part of milestone F6) + # Batching bridge — Hachi Eqs. (22)–(23) — escape-threaded (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: @@ -14,20 +14,18 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints * `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`). + `MvPolynomial` identities (Eqs. (22)–(23), `ZeroCheck/Constraints.lean`), and `w̃` short. 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: + Kronecker interpolation. The `liftShort` conjunct (resolution option 2) is preserved verbatim + from `relLift`; it is the precondition of the weak-binding escape (`LiftCom.collision_mem`) + invoked by the zero-check's extraction. - * **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. + `H_α ≡ 0` ⇒ per-row constraints, by non-degeneracy of the `eq̃` basis; `H₀ ≡ 0` ⇒ per-entry + range membership over the *field* `F` (needs `2b − 1 < q` to read field roots as centered + `Zq`-representatives), threaded to `liftShort` under the norm-parameter hypotheses. ## References @@ -46,14 +44,16 @@ 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`. -/ +/-- **The batched relation** (Hachi Eqs. (22)–(23) as polynomial identities): `w̃` opens `t`, is +short (`liftShort`), 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 ∧ + liftShort Φ bound ρBound p.2 ∧ 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} @@ -67,12 +67,11 @@ def relBatchedE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBou /-- **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. -/ +**Sorried.** Proof plan: `H_α ≡ 0` ⇒ all `eq̃`-basis coefficients vanish ⇒ 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`) +`liftShort bound ρBound w̃`; the `liftShort` and bound-sanity conjuncts are 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) diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean index afcf94aed9..bf36d055d3 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -1,27 +1,34 @@ /- Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann +Authors: Tobias Rothmann, Pablo Martín Vinuelas -/ import ArkLib.Commitments.Functional.Hachi.RingSwitch.Reduction -import Mathlib.Algebra.MvPolynomial.Basic +import ArkLib.Data.MvPolynomial.LinearMvExtension +import ArkLib.Data.MvPolynomial.Multilinear /-! - # Constraint encoding — Hachi Eqs. (21)–(23) — skeleton (sumcheck-track milestone F5) + # Constraint encoding — Hachi Eqs. (21)–(23) — escape-threaded (sumcheck-track milestone F5/F6) 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. + bridge (`ZeroCheck/Batch.lean`), the zero-check round (`ZeroCheck/Reduction.lean`), the sumcheck + rounds, and the final-evaluation step. All declarations live in the §4.3 chain's namespace + `ArkLib.Lattices.Ajtai.InnerOuter`, over the **concrete** lifted witness `LiftedWitness Φ μ n` + and the abstract weak-binding commitment `LiftCom` of `RingSwitch/Reduction.lean`, so this layer + composes directly into the escape-threaded opening chain (`Composition.lean`). ## The table `w̃` (Eq. (21)) - The committed lifted witness `(z, ρ)` is re-read as a `Zq`-valued table `w̃` indexed by the + The committed lifted witness `(z, ρ)` is re-read as an `F`-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). + two; `m₁` is the row-batching arity (`2 ^ m₁ ≥ n` rows of the lifted system). The table's + entry function `wTable` and the `M̃_α` contraction `hAlphaEvals` are the genuine F5 index + bookkeeping and remain `sorry`; the batched polynomials `hZero`/`hAlpha` are built from them via + the real multilinear extension `MvPolynomial.MLE`, so their multilinearity + (`hZero_degreeOf_le`/`hAlpha_degreeOf_le` — the hypothesis of the corrected Lemma 10's Kronecker + interpolation) is **`sorry`-free**. ## The batched constraint polynomials (Eqs. (22)–(23)) @@ -35,23 +42,17 @@ import Mathlib.Algebra.MvPolynomial.Basic ## 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. + `F_{0,τ₀}` 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_{α,τ₁}` has per-variable + degree `≤ 2`. Everything downstream is degree-parametric (`roundDegZero`/`roundDegAlpha`). - ## The Kronecker point (Lemma 10 repair, `HACHI_LEMMA10_GAP.md`) + ## The Kronecker point (Lemma 10 repair) `kroneckerPoint m ρ = (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})`: the pullback of an `m`-variate multilinear polynomial along this curve is univariate of degree `< 2^m` and the pullback is **injective** - (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. + (`LinearMvExtension.powAlgHom_eq_zero_iff`), so univariate root counting is information-complete + — the engine of the corrected zero-check (`ZeroCheck/Reduction.lean`). Re-exported here as the + same map used by the downstream sumcheck files. ## References @@ -63,119 +64,168 @@ namespace ArkLib.Lattices.Ajtai.InnerOuter open CompPoly ArkLib.Lattices.CyclotomicModulus open OracleComp OracleSpec ProtocolSpec CoordinateWise +open MvPolynomial 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 curve and the corrected soundness parameter -/ /-- **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 : ℕ)) +challenge-derivation curve. Definitionally the kernel map `LinearMvExtension.kroneckerPoint`, so +the zero-check's root-counting kernel applies verbatim; re-exported here as the name the +downstream sumcheck files use. -/ +@[reducible] def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F := + LinearMvExtension.kroneckerPoint (m := m) ρ + +/-- **The corrected Lemma 10 parameter** `D := max(2, 2^{m₀}, 2^{m₁})`: the maximum padded +constraint-table size, i.e. the univariate degree bound of the Kronecker pullbacks (plus the `2` +padding for degenerate zero-arity identities, meeting the CWSS convention `2 ≤ k`). The paper's +`max(2d, 2b-1)` conflates parameters of Lemmas 9 and 11; no value of it repairs the original +challenge encoding. -/ +def zeroCheckD (m₀ m₁ : ℕ) : ℕ := max 2 (max (2 ^ m₀) (2 ^ m₁)) + +theorem two_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ≤ zeroCheckD m₀ m₁ := le_max_left _ _ + +theorem two_pow_m₀_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ^ m₀ ≤ zeroCheckD m₀ m₁ := + (le_max_left _ _).trans (le_max_right _ _) -/-- 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). -/ +theorem two_pow_m₁_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ^ m₁ ≤ zeroCheckD m₀ m₁ := + (le_max_right _ _).trans (le_max_right _ _) + +/-- Per-round univariate degree of the range sumcheck (`F_{0,τ₀}`): 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). -/ +/-- Per-round univariate degree of the linear sumcheck (`F_{α,τ₁}`): 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 := +/-! ## The range factor and the table (genuine F5 content is `sorry`) -/ + +/-- Hachi Eq. (23)'s per-entry range factor `P_b(v) := v·∏_{j=1}^{b-1} (v - j)·(v + j)`: the +vanishing polynomial of the symmetric range `{-(b-1), …, b-1}`. -/ +def rangeProduct (b : ℕ) (v : F) : F := + v * ∏ j ∈ Finset.Icc 1 (b - 1), ((v - (j : F)) * (v + (j : F))) + +/-- **Root characterization of the range factor** (over a field): `P_b(v) = 0` iff `v` is (the +image of) an integer in the symmetric range `{-(b-1), …, b-1}`. -/ +theorem rangeProduct_eq_zero_iff {b : ℕ} {v : F} : + rangeProduct b v = 0 ↔ ∃ j : ℕ, j ≤ b - 1 ∧ (v = (j : F) ∨ v = -(j : F)) := by + unfold rangeProduct + rw [mul_eq_zero, Finset.prod_eq_zero_iff] + constructor + · rintro (h0 | ⟨j, hj, hfac⟩) + · exact ⟨0, Nat.zero_le _, Or.inl (by simpa using h0)⟩ + · rw [mul_eq_zero, sub_eq_zero, add_eq_zero_iff_eq_neg] at hfac + exact ⟨j, (Finset.mem_Icc.mp hj).2, hfac⟩ + · rintro ⟨j, hjb, hv⟩ + rcases Nat.eq_zero_or_pos j with rfl | hj0 + · exact Or.inl (by rcases hv with hv | hv <;> simpa using hv) + · refine Or.inr ⟨j, Finset.mem_Icc.mpr ⟨hj0, hjb⟩, ?_⟩ + rw [mul_eq_zero, sub_eq_zero, add_eq_zero_iff_eq_neg] + exact hv + +/-- **The Eq. (21) table**: the committed `(z, ρ)` re-read as an `F`-valued function on the +`m₀`-cube — `Zq`-coefficient rows of the `zⱼ` (through the embedding `φF`), 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 (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : + (Fin m₀ → Fin 2) → F := 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 := +/-- The `M̃_α`-contracted per-row value of Eq. (22): the Boolean-point coefficients of `H_α`, +`i ↦ (∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ)) − ŷᵢ(α)`. **Sorried (F5)**: the `M̃_α` contraction +(the `α`-evaluated lifted matrix `M`, including the `−(α^d + 1)` quotient columns) and the row +targets `ŷᵢ(α)`. -/ +def hAlphaEvals (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (w : LiftedWitness Φ μ n) : (Fin m₁ → Fin 2) → 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) : +/-! ## The batched constraint polynomials (genuine multilinear extensions) -/ + +/-- **Eq. (23)**: the `eq̃`-batched range polynomial as the real multilinear extension of the +per-entry range factor `P_b(w̃(·))` — 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`). -/ +noncomputable def hZero (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₀) F := - sorry + MLE fun v => rangeProduct b (wTable Φ m₀ φF b w v) -/-- **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) +/-- **Eq. (22)**: the `eq̃`-batched linear constraint polynomial as the real multilinear +extension of the `M̃_α`-contracted per-row values; `H_α ≡ 0` iff every lifted row of `relLift` +vanishes at `α`. -/ +noncomputable def hAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₁) F := - sorry + MLE (hAlphaEvals Φ m₁ φF b s a w) -/-- `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).** -/ +omit [NeZero q] [IsCyclotomic Φ] in +/-- `H₀` is multilinear (degree `≤ 1` in each batching variable) — the hypothesis of the +Kronecker pullback degree bound `< 2 ^ m₀`. `sorry`-free (a genuine `MLE`). -/ 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 + (j : Fin m₀) : (hZero Φ m₀ φF b w).degreeOf j ≤ 1 := + MLE_degreeOf _ j -/-- `H_α` is multilinear (degree `≤ 1` in each batching variable). Load-bearing for the -Kronecker pullback degree bound `< 2 ^ m₁`. **Sorried (F5).** -/ +omit [NeZero q] [IsCyclotomic Φ] in +/-- `H_α` is multilinear (degree `≤ 1` in each batching variable). `sorry`-free. -/ 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) + (hAlpha Φ m₁ φF b s a w).degreeOf j ≤ 1 := + MLE_degreeOf _ j + +/-- `H₀` as an element of the multilinear subtype (the input the Kronecker root-counting kernel +consumes). -/ +noncomputable def hZeroML (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : + MvPolynomial.restrictDegree (Fin m₀) F 1 := + ⟨hZero Φ m₀ φF b w, + (mem_restrictDegree_iff_degreeOf_le _ _).mpr fun j => hZero_degreeOf_le Φ m₀ φF b w j⟩ + +/-- `H_α` as an element of the multilinear subtype. -/ +noncomputable def hAlphaML (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (w : LiftedWitness Φ μ n) : MvPolynomial.restrictDegree (Fin m₁) F 1 := + ⟨hAlpha Φ m₁ φF b s a w, + (mem_restrictDegree_iff_degreeOf_le _ _).mpr fun j => hAlpha_degreeOf_le Φ m₁ φF b s a w j⟩ + +/-- Evaluation of the multilinear extension of the table `w̃` at a point `a ∈ F^{m₀}`: +`mle[w̃](a) = ∑ᵢ w̃(i)·eq̃(i, a)`. The final-evaluation step's claim currency +(`Sumcheck/FinalEval.lean`). `sorry`-free modulo the `wTable` encoding. -/ +noncomputable def wTableMleEval (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) + (a : Fin m₀ → F) : F := + MvPolynomial.eval a (MLE (wTable Φ m₀ φF b w)) -**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. -/ +/-! ## The sumcheck polynomials (sorried F5 content) -/ -/-- **`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₀(τ₀)` +/-- **`F_{0,τ₀}`** (the range sumcheck summand): satisfies `∑_{x} 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`). +/-- **`F_{α,τ₁}`** (the linear sumcheck summand): satisfies +`∑_{x} 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).** -/ +by the verifier from the statement alone. **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).** -/ +/-- Partial hypercube sum: `hypercubeSum H i cs = ∑_{x ∈ {0,1}^{m₀ − i}} H(cs, x)`. **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).** -/ +/-- The sum of `F_{0,τ₀}` over the cube is `H₀(τ₀)`. **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).** -/ +/-- The sum of `F_{α,τ₁}` over the cube is `H_α(τ₁) + zcTargetAlpha`. **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) = @@ -185,9 +235,8 @@ theorem sum_sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatemen /-! ## Statement types of the zero-check and sumcheck stages -/ /-- The zero-check's output statement: the lift statement extended by the two **Kronecker -seeds** `(ρ₀, ρ_α)` of the corrected Lemma 10 (`HACHI_LEMMA10_GAP.md` §3.K.2: the challenge is -the seed pair; the batching points `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)` are derived -deterministically). -/ +seeds** `(ρ₀, ρ_α)` of the corrected Lemma 10 (the challenge is the seed pair; the batching +points `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)` are derived deterministically). -/ structure ZeroCheckStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) where /-- The `R^lin` statement (carrying the public `M`, `yvec`, `bound`). -/ rlin : RlinStatement Φ n μ @@ -201,8 +250,7 @@ structure ZeroCheckStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) ( 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). -/ +`a₁, …, aᵢ` so far, and the current pair of sumcheck targets `(z_i^{(0)}, z_i^{(α)})`. -/ structure RoundStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) (i : ℕ) where /-- The zero-check statement (public data, commitment, `α`, Kronecker seeds). -/ @@ -216,18 +264,17 @@ structure RoundStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ 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). -/ +/-- **The per-round seam relation** of the paired sumcheck ([NOZ26] Lemma 11): `w̃` opens `t`, is +short (`liftShort` — the weak-binding escape's precondition, threaded through every seam; +resolution option 2 of the audit doc), and both partial-hypercube-sum claims at the current +challenge prefix equal the current targets. The public sanity conjunct `bound ≤ rlin.bound` +threads the global norm parameter back to the `R^lin` statement bound. -/ 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 ∧ + liftShort Φ bound ρBound p.2 ∧ hypercubeSum m₀ (sumcheckPolyZero Φ m₀ φF b (kroneckerPoint m₀ p.1.zc.seed₀) p.2) i p.1.challenges = p.1.target₀ ∧ hypercubeSum m₀ diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean index c8574942b3..2b28b2954c 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -1,57 +1,59 @@ /- Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Tobias Rothmann +Authors: Tobias Rothmann, Pablo Martín Vinuelas -/ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ChallengeRound +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! - # Zero-check — Hachi Figure 5 / **corrected** Lemma 10 — skeleton (milestone F6) + # Zero-check — Hachi Figure 5 / **corrected** Lemma 10 — escape-threaded (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. + One challenge round reducing the batched polynomial identities `H₀ ≡ 0 ∧ H_α ≡ 0` + (`relBatchedE`, `ZeroCheck/Batch.lean`) to their evaluations at random points; the two scalar + evaluation claims then seed the sumcheck (`Sumcheck/Bridge.lean`). Escape-threaded and stated + over the concrete `LiftedWitness Φ μ n` / weak-binding `LiftCom`, so it composes into the + §4.3 opening chain (`Composition.lean`, row 6). ## ⚠ 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). + A coordinate-wise star of accepting transcripts with the paper's **uniform vector challenges** + `(τ₀, τ_α) ∈ F^{m₀} × F^{m₁}` only certifies that a multilinear `H` vanishes on the *axis + cross* through the star's center, and for ≥ 2 challenge variables cross-vanishing does not + imply `H ≡ 0` (counterexample `(t₁-a)(t₂-b)`, + `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`). No choice of the paper's parameter + helps. + + **Adopted repair (one round, Kronecker curve):** the verifier's challenge is a pair of *scalar + seeds* `(ρ₀, ρ_α) ∈ F²`, and the evaluation points are derived on the **Kronecker curves** + `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)`. On the curve a multilinear `H` pulls back to the + univariate `powAlgHom H` of degree `< 2^m`, and the pullback is injective on multilinears + (`LinearMvExtension.powAlgHom_eq_zero_iff`), so univariate root counting is information-complete: + `D = zeroCheckD m₀ m₁ = max(2, 2^{m₀}, 2^{m₁})` distinct seeds per coordinate identify the + polynomial. Only the challenge *distribution* is restricted to the two curves (error scale + `D/|F|`). **This is the one place the formalization deliberately changes the paper's protocol to + repair its proof.** The algebraic kernel lives in + `ArkLib/Data/MvPolynomial/LinearMvExtension.lean`; the generic one-round CWSS engine is + `OracleReduction/…/CoordinateWiseSpecialSoundness/ChallengeRound.lean`. + + ## Corrected Hachi Lemma 10 — coordinate-wise special soundness + + `zeroCheck_coordinateWiseSpecialSound` is **`sorry`-free**: from `2D − 1` accepting transcripts + whose seed pairs form a special-sound family, the tree extractor (`buildWitnessE`) either + + 1. finds a branch carrying a downstream **escape** `.inr e` → passes it through; + 2. finds two branches carrying **distinct short openings** of the shared `t` → the weak-binding + escape `K.escOfCollision` (`K.collision_mem`, Hachi Remark 2 / Lemma 7 — the `liftShort` + conjunct of `relZeroCheck` supplies the shortness precondition); or + 3. fixes one common opening `w̃` and, per coordinate, uses the family's `D` distinct seeds as + `≥ 2^m` Kronecker roots of the multilinear identity (`arm_eq_zero_of_family`), giving + `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` — i.e. `relBatchedE` via `.inl w̃`. + + The theorem is not *axiom-clean*: `hZero`/`hAlpha` are built on the still-`sorry` F5 encodings + `wTable`/`hAlphaEvals`, so `#print axioms` reports `sorryAx` transitively through them. The + Lemma-10 *argument* is `sorry`-free and parametric in those encodings; the standalone kernel + `arm_eq_zero_of_family` is axiom-clean. ## References @@ -62,35 +64,25 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Batch namespace ArkLib.Lattices.Ajtai.InnerOuter open CompPoly ArkLib.Lattices.CyclotomicModulus -open OracleComp OracleSpec ProtocolSpec CoordinateWise +open OracleComp OracleSpec ProtocolSpec +open CoordinateWise CoordinateWise.ChallengeRound + +/-! ## Wire format and CWSS structure -/ /-- 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). -/ +`(ρ₀, ρ_α)` as a `Fin 2 → F`, so the generic one-round challenge engine (`ChallengeRound`) +applies verbatim. -/ @[reducible] def pSpecZeroCheck (F : Type) : ProtocolSpec 1 := - ⟨!v[.V_to_P], !v[F × F]⟩ - -section Instances - -variable {F : Type} [SampleableType F] + ChallengeRound.pSpec F 2 -instance : ∀ i, SampleableType ((pSpecZeroCheck F).Challenge i) - | ⟨0, _⟩ => (inferInstance : SampleableType (F × F)) - -end Instances +instance instSampleableTypeChallengePSpecZeroCheck {F : Type} [SampleableType F] : + ∀ i, SampleableType ((pSpecZeroCheck F).Challenge i) := inferInstance /-- **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 +scalar coordinates over `F`, with soundness parameter `k = D = zeroCheckD m₀ m₁`. Star arity +`2·(D−1)+1 = 2D−1`. -/ +def zeroCheckStructure (F : Type) (m₀ m₁ : ℕ) : CWSSStructure (pSpecZeroCheck F) := + chalStructure F 2 (zeroCheckD m₀ m₁) (by norm_num) (two_le_zeroCheckD m₀ m₁) section Protocol @@ -100,40 +92,46 @@ variable {n μ : ℕ} {E : Type} {F : Type} [Field F] variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} +open MvPolynomial + +/-- The zero-check's statement map: extend the lift statement by the two Kronecker seeds +`(seeds 0, seeds 1)`. -/ +def zcMapStmt {TCom : Type} (stmt : LiftStatement Φ TCom F n μ) (seeds : Fin 2 → F) : + ZeroCheckStatement Φ TCom F n μ := + ⟨stmt.1, stmt.2.1, stmt.2.2, seeds 0, seeds 1⟩ + /-- 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⟩ + verify := fun stmt tr => pure (zcMapStmt Φ stmt (tr.challenges ⟨0, rfl⟩)) -/-- 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). -/ +/-- The zero-check prover (challenge-only: the honest prover 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) + | 1 => (LiftStatement Φ TCom F n μ × LiftedWitness Φ μ n) × (Fin 2 → 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) + output := fun ⟨⟨stmt, wit⟩, c⟩ => pure (zcMapStmt Φ stmt c, 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₁}(ρ_α)`. -/ +is short (`liftShort` — the weak-binding escape's precondition; resolution option 2), and both +batched constraint polynomials vanish **at the derived Kronecker points**. -/ 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 ∧ + liftShort Φ bound ρBound p.2 ∧ 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 ∧ @@ -145,22 +143,145 @@ def relZeroCheckE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρ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₀}`. -/ +/-! ## The Kronecker root-counting arm (axiom-clean kernel) -/ + +/-- **One arm of corrected Lemma 10**: if a special-sound family of seed pairs (parameter +`D ≥ 2^m`) has, at coordinate `i`, all its seeds `ρ` satisfying `H(κ_m(ρ)) = 0` for one fixed +multilinear `H`, then `H ≡ 0`. The family supplies `D ≥ 2^m` distinct seeds at coordinate `i` +(`IsSpecialSoundFamily.exists_coord_finset`); each is a root of the univariate Kronecker pullback +of degree `< 2^m`, so the pullback is zero and Kronecker injectivity kills `H` +(`LinearMvExtension.multilinear_eq_zero_of_kronecker_roots`). -/ +theorem arm_eq_zero_of_family {m D : ℕ} (hm : 2 ^ m ≤ D) + (fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) + (hfam : IsSpecialSoundFamily 2 D fam) (i : Fin 2) + (H : MvPolynomial.restrictDegree (Fin m) F 1) + (hroots : ∀ j, MvPolynomial.eval (kroneckerPoint m (fam j i)) H.val = 0) : + H.val = 0 := by + obtain ⟨s, hcard, hmem⟩ := hfam.exists_coord_finset i + refine LinearMvExtension.multilinear_eq_zero_of_kronecker_roots (hm.trans hcard) (fun τ hτ => ?_) + obtain ⟨j, hj⟩ := hmem τ hτ + rw [← hj] + exact hroots j + +/-! ## The escape-threaded witness assembler -/ + +/-- Combine two branch responses of a *differing* pair into an escape: pass through either +branch's `.inr` escape, or route a collision of two distinct openings to `K.escOfCollision`. +Always outputs a `.inr` (its `relBatchedE`-membership is `collideOrPass_mem_relBatchedE`). -/ +def collideOrPass (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (a c : LiftedWitness Φ μ n ⊕ E) : LiftedWitness Φ μ n ⊕ E := + match a, c with + | Sum.inr e, _ => Sum.inr e + | Sum.inl _, Sum.inr e => Sum.inr e + | Sum.inl wa, Sum.inl wc => Sum.inr (K.escOfCollision wa wc) + +open Classical in +/-- The zero-check witness assembler (corrected Lemma 10's three-case extraction), the `mkWitness` +argument of the generic `ChallengeRound` extractor: + +* if all `2D − 1` branch responses equal branch 0's, output that response (a common opening — or a + passed-through escape if branch 0 is one); +* otherwise some branch differs from branch 0: `collideOrPass` produces the escape (pass-through + or weak-binding collision). -/ +noncomputable def buildWitnessE + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (D : ℕ) + (_stmt : LiftStatement Φ K.TCom F n μ) + (_fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) + (resp : Fin (2 * (D - 1) + 1) → (LiftedWitness Φ μ n ⊕ E)) : + LiftedWitness Φ μ n ⊕ E := + if h : ∃ j, resp j ≠ resp 0 then collideOrPass Φ bound ρBound K (resp h.choose) (resp 0) + else resp 0 + + +omit [NeZero q] [IsCyclotomic Φ] in +/-- `collideOrPass a c` lands in `relBatchedE` (always as an escape) provided `a ≠ c` and each of +`a`, `c` is either a `K.esc` escape or a short opening of the shared commitment `stmt.t`. -/ +theorem collideOrPass_mem_relBatchedE + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) + (stmt : LiftStatement Φ K.TCom F n μ) (a c : LiftedWitness Φ μ n ⊕ E) (hac : a ≠ c) + (hesc_a : ∀ e, a = Sum.inr e → e ∈ K.esc) + (hopen_a : ∀ w, a = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w) + (hesc_c : ∀ e, c = Sum.inr e → e ∈ K.esc) + (hopen_c : ∀ w, c = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w) : + (stmt, collideOrPass Φ bound ρBound K a c) ∈ relBatchedE Φ m₀ m₁ bound ρBound K φF b := by + rcases a with wa | ea <;> rcases c with wc | ec <;> + simp only [collideOrPass, relBatchedE, Set.mem_withEscape_inr] + · -- both openings: a genuine weak-binding collision + obtain ⟨hca, hsa⟩ := hopen_a wa rfl + obtain ⟨hcc, hsc⟩ := hopen_c wc rfl + have hne : wa ≠ wc := fun heq => hac (by rw [heq]) + exact K.collision_mem wa wc hne (by rw [hca, hcc]) hsa hsc + · exact hesc_c ec rfl + · exact hesc_a ea rfl + · exact hesc_a ea rfl + +-- `[IsCyclotomic Φ]`/`[NeZero q]` are needed to synthesize the `wTable`/`Rq` instances inside +-- `hZeroML`/`hAlphaML`, but the linter's usage analysis misses instance-synth-only section vars. +set_option linter.unusedSectionVars false in +/-- **The witness assembler is correct** (the math content of corrected Lemma 10): at every +special-sound family of `relZeroCheckE`-accepting branches, `buildWitnessE` lands in +`relBatchedE`. -/ +theorem buildWitnessE_mem_relBatchedE + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) + {D : ℕ} (hm₀ : 2 ^ m₀ ≤ D) (hm₁ : 2 ^ m₁ ≤ D) + (stmt : LiftStatement Φ K.TCom F n μ) + (fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) + (resp : Fin (2 * (D - 1) + 1) → (LiftedWitness Φ μ n ⊕ E)) + (hrel : ∀ j, (zcMapStmt Φ stmt (fam j), resp j) ∈ relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) + (hfam : IsSpecialSoundFamily 2 D fam) : + (stmt, buildWitnessE Φ bound ρBound K D stmt fam resp) ∈ + relBatchedE Φ m₀ m₁ bound ρBound K φF b := by + classical + -- per-branch facts pulled from `relZeroCheckE` membership + have hesc : ∀ (j) (e : E), resp j = Sum.inr e → e ∈ K.esc := by + intro j e hje + have := hrel j; rw [hje, relZeroCheckE, Set.mem_withEscape_inr] at this; exact this + have hopen : ∀ (j) (w : LiftedWitness Φ μ n), resp j = Sum.inl w → + K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w := by + intro j w hjw + have := hrel j; rw [hjw, relZeroCheckE, Set.mem_withEscape_inl] at this + simp only [relZeroCheck, Set.mem_setOf_eq] at this + exact ⟨this.1, this.2.1⟩ + unfold buildWitnessE + by_cases h : ∃ j, resp j ≠ resp 0 + · -- some branch differs from branch 0 → `collideOrPass` produces an escape + rw [dif_pos h] + exact collideOrPass_mem_relBatchedE Φ m₀ m₁ bound ρBound K φF b stmt + (resp h.choose) (resp 0) h.choose_spec + (hesc h.choose) (hopen h.choose) (hesc 0) (hopen 0) + · -- all branches equal branch 0 → common opening (or common escape) + rw [dif_neg h] + have hall : ∀ j, resp j = resp 0 := fun j => not_ne_iff.mp (fun hne => h ⟨j, hne⟩) + rcases hr0 : resp 0 with w0 | e0 + · -- common opening `w0`: both identities vanish by root counting + obtain ⟨hc0, hs0⟩ := hopen 0 w0 hr0 + have hbound : bound ≤ stmt.1.bound := by + have := hrel 0; rw [hr0, relZeroCheckE, Set.mem_withEscape_inl] at this + simp only [relZeroCheck, Set.mem_setOf_eq] at this + exact this.2.2.2.2 + simp only [relBatchedE, Set.mem_withEscape_inl, relBatched, Set.mem_setOf_eq] + refine ⟨hc0, hs0, ?_, ?_, hbound⟩ + · -- H₀ ≡ 0 + refine arm_eq_zero_of_family hm₀ fam hfam 0 (hZeroML Φ m₀ φF b w0) (fun j => ?_) + have hj := hrel j; rw [(hall j).trans hr0, relZeroCheckE, Set.mem_withEscape_inl] at hj + simp only [relZeroCheck, Set.mem_setOf_eq] at hj + exact hj.2.2.1 + · -- H_α ≡ 0 + refine arm_eq_zero_of_family hm₁ fam hfam 1 + (hAlphaML Φ m₁ φF b stmt.1 stmt.2.2 w0) (fun j => ?_) + have hj := hrel j; rw [(hall j).trans hr0, relZeroCheckE, Set.mem_withEscape_inl] at hj + simp only [relZeroCheck, Set.mem_setOf_eq] at hj + exact hj.2.2.2.1 + · -- common escape + simp only [relBatchedE, Set.mem_withEscape_inr] + exact hesc 0 e0 hr0 + +omit [NeZero q] in +/-- **Corrected Hachi Lemma 10 (CWSS of the zero-check, Figure 5, Kronecker-curve challenges), +`sorry`-free.** The one-round seed-pair verifier is coordinate-wise special sound for +`zeroCheckStructure` (`(ℓ, k) = (2, D)`, `D = zeroCheckD m₀ m₁`), reducing `relBatchedE` to +`relZeroCheckE`. Assembled by `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness`; the whole +of corrected Lemma 10 thereby reduces to `buildWitnessE_mem_relBatchedE`. -/ theorem zeroCheck_coordinateWiseSpecialSound (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) @@ -169,13 +290,20 @@ theorem zeroCheck_coordinateWiseSpecialSound (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 + (relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) := + coordinateWiseSpecialSound_of_mkWitness init impl (by norm_num) (two_le_zeroCheckD m₀ m₁) + (zeroCheckVerifier Φ) (fun stmt seeds => zcMapStmt Φ stmt seeds) (fun _ _ => rfl) + (relBatchedE Φ m₀ m₁ bound ρBound K φF b) (relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) + (buildWitnessE Φ bound ρBound K (zeroCheckD m₀ m₁)) + (fun stmt fam resp hrel hfam => + buildWitnessE_mem_relBatchedE Φ m₀ m₁ bound ρBound K φF b + (two_pow_m₀_le_zeroCheckD m₀ m₁) (two_pow_m₁_le_zeroCheckD m₀ m₁) stmt fam resp hrel hfam) /-- **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)) +`relZeroCheckE`. -/ +noncomputable 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 @@ -186,9 +314,7 @@ def zeroCheckPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ Pro 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⟩ + isPure := ⟨fun stmt tr => zcMapStmt Φ stmt (tr.challenges ⟨0, rfl⟩), fun _ _ => rfl⟩ isCWSS := zeroCheck_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound init impl K φF b end Protocol diff --git a/ArkLib/Data/MvPolynomial/LinearMvExtension.lean b/ArkLib/Data/MvPolynomial/LinearMvExtension.lean index 27cbbb085f..7ddaec58f2 100644 --- a/ArkLib/Data/MvPolynomial/LinearMvExtension.lean +++ b/ArkLib/Data/MvPolynomial/LinearMvExtension.lean @@ -248,4 +248,183 @@ lemma powAlgHom_is_right_inverse_to_linearMvExtension end +/-! ## Axis-cross vanishing does not determine a multilinear polynomial -/ + +open MvPolynomial + +variable {F : Type*} [CommRing F] + +/-- A checked counterexample to the uniform-vector argument in Hachi [NOZ26, Lemma 10]. For any +axis-cross center `(a, b)`, the nonzero multilinear polynomial +`(X₀ - a) * (X₁ - b)` vanishes whenever either coordinate is fixed at the center. Thus arbitrarily +many evaluations along the two arms of a coordinate-wise star do not imply a polynomial identity. +The Kronecker-curve construction below avoids this obstruction by reducing identity testing to a +single univariate polynomial. -/ +theorem exists_nonzero_vanishing_on_axis_cross [Nontrivial F] (a b : F) : + ∃ H : MvPolynomial (Fin 2) F, + H ≠ 0 ∧ (∀ y, eval ![a, y] H = 0) ∧ ∀ x, eval ![x, b] H = 0 := by + refine ⟨(X 0 - C a) * (X 1 - C b), ?_, fun y => by simp, fun x => by simp⟩ + intro h + have he := congrArg (eval ![a + 1, b + 1]) h + simp at he + +/-! ## The Kronecker-curve challenge encoding and injectivity of `powAlgHom` on multilinears + +This section is the algebraic core of the *repaired* Hachi zero-check (Hachi [NOZ26], §4.3, Fig. 5, +Lemma 10). The paper's stated coordinate-wise special soundness for that round is not provable: a +"star" of accepting transcripts only forces a multilinear `H` to vanish on the axis cross through +its centre, and for `m ≥ 2` cross-vanishing does **not** imply `H ≡ 0` (e.g. `(t₁-a)(t₂-b)`). +The adopted repair restricts each random evaluation point to the **Kronecker curve** +`κ_m(τ) = (τ^(2⁰), τ^(2¹), …, τ^(2^(m-1)))`. On that curve a multilinear `H` pulls back to +the *univariate* polynomial `powAlgHom H`, which has degree `< 2^m` and — crucially — is a +**deterministic identity equivalence**: `powAlgHom H = 0 ↔ H = 0`. Univariate root-counting on the +pullback then makes ordinary / coordinate-wise special soundness go through with `k = D = 2^m` +distinct scalar seeds. + +The map `powAlgHom` and its degree bound `powAlgHom_of_restrict_degree_natDegree` are above; the +missing companion — injectivity of `powAlgHom` on the per-variable-degree-`≤ 1` (multilinear) +subtype — is `powAlgHom_eq_zero_iff` / `powAlgHom_injective_on_multilinear` below. -/ + +noncomputable section + +open MvPolynomial + +variable {F : Type*} [CommRing F] {m : ℕ} + +/-- The Kronecker-curve point `κ_m(τ) = (τ^(2⁰), τ^(2¹), …, τ^(2^(m-1)))`. Restricting the +zero-check challenge to this curve is what makes the univariate pullback `powAlgHom` information +complete for multilinear polynomials (repaired Hachi [NOZ26] Lemma 10, §4.3). -/ +def kroneckerPoint (τ : F) : Fin m → F := fun j => τ ^ (2 ^ (j : ℕ)) + +/-- Evaluating the univariate pullback `powAlgHom H` at `τ` agrees with evaluating `H` on the +Kronecker-curve point `κ_m(τ)`. This holds for *every* polynomial; multilinearity is only needed +downstream for injectivity. It is the bridge that turns a zero-check evaluation `H(κ_m(τ)) = 0` +into a univariate root of `powAlgHom H`. -/ +lemma eval_powAlgHom_eq_eval_kronecker (H : MvPolynomial (Fin m) F) (τ : F) : + Polynomial.eval τ (powAlgHom H) = MvPolynomial.eval (kroneckerPoint (m := m) τ) H := by + have h : (Polynomial.aeval τ).comp + (powAlgHom : MvPolynomial (Fin m) F →ₐ[F] Polynomial F) + = MvPolynomial.aeval (kroneckerPoint (m := m) τ) := by + apply MvPolynomial.algHom_ext + intro j + simp [powAlgHom, kroneckerPoint] + have hH := AlgHom.congr_fun h H + simpa [AlgHom.comp_apply, Polynomial.coe_aeval_eq_eval, MvPolynomial.aeval_eq_eval] using hH + +/-- The Kronecker exponent `⟨d⟩ = ∑ⱼ dⱼ·2ʲ` attached to a monomial exponent vector `d`. +Under `powAlgHom`, the multilinear monomial `∏ⱼ Xⱼ^(dⱼ)` becomes the univariate power `X^⟨d⟩`; +on `{0,1}^m` the assignment `d ↦ ⟨d⟩` is the base-2 bijection onto `{0, …, 2^m-1}`. -/ +def kroneckerExp (d : Fin m →₀ ℕ) : ℕ := ∑ j : Fin m, d j * 2 ^ (j : ℕ) + +/-- `powAlgHom` sends the multilinear monomial `monomial d c` to the univariate monomial of degree +`⟨d⟩ = ∑ⱼ dⱼ·2ʲ`. -/ +lemma powAlgHom_monomial (d : Fin m →₀ ℕ) (c : F) : + powAlgHom (MvPolynomial.monomial d c) = Polynomial.monomial (kroneckerExp d) c := by + simp only [powAlgHom, MvPolynomial.aeval_monomial, Polynomial.algebraMap_eq, Finsupp.prod_pow] + simp_rw [← pow_mul] + rw [Finset.prod_pow_eq_pow_sum, + show (∑ i : Fin m, 2 ^ (i : ℕ) * d i) = kroneckerExp d from + Finset.sum_congr rfl (fun i _ => mul_comm _ _), + Polynomial.C_mul_X_pow_eq_monomial] + +/-- On `{0,1}^m` the Kronecker exponent `d ↦ ⟨d⟩ = ∑ⱼ dⱼ·2ʲ` is injective: it is the base-2 +encoding, so distinct multilinear monomials become distinct univariate powers. This is exactly +what `finFunctionFinEquiv` (the base-2 digit bijection) records. -/ +lemma kroneckerExp_injective_on_le_one {d e : Fin m →₀ ℕ} + (hd : ∀ j, d j ≤ 1) (he : ∀ j, e j ≤ 1) + (hde : kroneckerExp d = kroneckerExp e) : d = e := by + have hval : finFunctionFinEquiv (fun j => (⟨d j, Nat.lt_succ_of_le (hd j)⟩ : Fin 2)) + = finFunctionFinEquiv (fun j => (⟨e j, Nat.lt_succ_of_le (he j)⟩ : Fin 2)) := by + apply Fin.ext + simp only [finFunctionFinEquiv_apply] + exact hde + have hfun := finFunctionFinEquiv.injective hval + ext j + have hj := congrFun hfun j + simpa using congrArg Fin.val hj + +/-- **Coefficient extraction along the Kronecker curve.** For a multilinear `H`, the coefficient of +the multilinear monomial `e` equals the coefficient of `X^⟨e⟩` in the univariate pullback +`powAlgHom H`. This is where multilinearity is used: injectivity of `⟨·⟩` on the support stops +different monomials from colliding at the exponent `⟨e⟩`. -/ +lemma powAlgHom_coeff_kroneckerExp {H : MvPolynomial (Fin m) F} + (hH : H ∈ MvPolynomial.restrictDegree (Fin m) F 1) + {e : Fin m →₀ ℕ} (he : ∀ j, e j ≤ 1) : + Polynomial.coeff (powAlgHom H) (kroneckerExp e) = MvPolynomial.coeff e H := by + have hHsupp : ∀ s ∈ H.support, ∀ j, s j ≤ 1 := + (MvPolynomial.mem_restrictDegree (Fin m) H 1).mp hH + have hexp : powAlgHom H + = ∑ d ∈ H.support, Polynomial.monomial (kroneckerExp d) (MvPolynomial.coeff d H) := by + conv_lhs => rw [H.as_sum] + rw [map_sum] + exact Finset.sum_congr rfl (fun d _ => powAlgHom_monomial d (MvPolynomial.coeff d H)) + rw [hexp, Polynomial.finsetSum_coeff] + simp only [Polynomial.coeff_monomial] + have key : ∀ d ∈ H.support, + (if kroneckerExp d = kroneckerExp e then MvPolynomial.coeff d H else 0) + = (if d = e then MvPolynomial.coeff d H else 0) := by + intro d hd + by_cases hde : d = e + · subst hde; simp + · have hne : kroneckerExp d ≠ kroneckerExp e := + fun h => hde (kroneckerExp_injective_on_le_one (hHsupp d hd) he h) + rw [if_neg hne, if_neg hde] + rw [Finset.sum_congr rfl key, Finset.sum_ite_eq'] + by_cases he' : e ∈ H.support + · rw [if_pos he'] + · rw [if_neg he', MvPolynomial.notMem_support_iff.mp he'] + +/-- **Kronecker injectivity (the repaired Lemma 10 kernel).** For a multilinear `H`, the univariate +pullback `powAlgHom H` is zero *iff* `H` is zero. Unlike a Schwartz–Zippel statement this is a +deterministic polynomial-identity equivalence, and it is exactly what the paper's coordinate-wise +star fails to provide. -/ +theorem powAlgHom_eq_zero_iff {H : MvPolynomial (Fin m) F} + (hH : H ∈ MvPolynomial.restrictDegree (Fin m) F 1) : + powAlgHom H = 0 ↔ H = 0 := by + constructor + · intro h0 + refine MvPolynomial.ext _ _ (fun e => ?_) + rw [MvPolynomial.coeff_zero] + by_cases he : ∀ j, e j ≤ 1 + · rw [← powAlgHom_coeff_kroneckerExp hH he, h0, Polynomial.coeff_zero] + · rw [← MvPolynomial.notMem_support_iff] + intro hmem + obtain ⟨j, hj⟩ := not_forall.mp he + exact absurd ((MvPolynomial.mem_restrictDegree (Fin m) H 1).mp hH e hmem j) hj + · intro h; rw [h, map_zero] + +/-- **Injectivity of `powAlgHom` on multilinear polynomials** — the generic algebra lemma the +repaired zero-check needs. Distinct multilinear polynomials have distinct univariate pullbacks, so +recovering `H` from `powAlgHom H` (hence from finitely many curve evaluations) is deterministic. -/ +theorem powAlgHom_injective_on_multilinear : + Function.Injective (fun H : MvPolynomial.restrictDegree (Fin m) F 1 => powAlgHom H.val) := by + intro H H' h + dsimp only at h + have hmem : (H.val - H'.val) ∈ MvPolynomial.restrictDegree (Fin m) F 1 := + Submodule.sub_mem _ H.2 H'.2 + have hz : powAlgHom (H.val - H'.val) = 0 := by rw [map_sub, h, sub_self] + exact Subtype.ext (sub_eq_zero.mp ((powAlgHom_eq_zero_iff hmem).mp hz)) + +/-- **Root-counting on the Kronecker curve (the extraction step of repaired Lemma 10).** If a +multilinear `H` vanishes on the curve at `2^m` distinct scalar seeds, then `H = 0`. Concretely: the +pullback `powAlgHom H` has degree `< 2^m` but acquires `≥ 2^m` distinct roots, so it is the zero +polynomial, whence `H = 0` by Kronecker injectivity. This is the deterministic replacement for the +false "vanishes on the star ⇒ zero polynomial" step, giving `k = D = 2^m`-special soundness. -/ +theorem multilinear_eq_zero_of_kronecker_roots [IsDomain F] + {H : MvPolynomial.restrictDegree (Fin m) F 1} {s : Finset F} + (hcard : 2 ^ m ≤ s.card) + (hroots : ∀ τ ∈ s, MvPolynomial.eval (kroneckerPoint (m := m) τ) H.val = 0) : + H.val = 0 := by + have hp : powAlgHom H.val = 0 := by + refine Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero' (powAlgHom H.val) s + (fun τ hτ => ?_) ?_ + · rw [eval_powAlgHom_eq_eval_kronecker]; exact hroots τ hτ + · have h1 : (powAlgHom H.val).natDegree ≤ 2 ^ m - 1 := + powAlgHom_of_restrict_degree_natDegree (p := H) + have h2 : 1 ≤ 2 ^ m := Nat.one_le_pow m 2 (by norm_num) + omega + exact (powAlgHom_eq_zero_iff H.2).mp hp + +end + end LinearMvExtension diff --git a/ArkLib/Data/MvPolynomial/Multilinear.lean b/ArkLib/Data/MvPolynomial/Multilinear.lean index 7c8d2d151d..4fc7102892 100644 --- a/ArkLib/Data/MvPolynomial/Multilinear.lean +++ b/ArkLib/Data/MvPolynomial/Multilinear.lean @@ -212,6 +212,18 @@ theorem MLE_degreeOf (evals : (σ → Fin 2) → R) (i : σ) : degreeOf i (MLE e end DegreeOf +/-- **Nondegeneracy of the `eq̃`-basis batching**: a multilinear extension is the zero polynomial +iff every hypercube evaluation vanishes. Used to read batched zero-checks (e.g. Hachi's Eqs. +(22)–(23)) back as per-constraint statements. -/ +theorem MLE_eq_zero_iff (evals : (σ → Fin 2) → R) : MLE evals = 0 ↔ ∀ x, evals x = 0 := by + constructor + · intro h x + have heval := MLE_eval_zeroOne (R := R) x evals + rw [h, map_zero] at heval + exact heval.symm + · intro h + simp [MLE, h] + -- TODO: add lemmas about the uniqueness of multilinear polynomials up to evaluations on hypercube variable [DecidableEq R] [IsDomain R] diff --git a/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ChallengeRound.lean b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ChallengeRound.lean new file mode 100644 index 0000000000..64e9709d40 --- /dev/null +++ b/ArkLib/OracleReduction/Security/CoordinateWiseSpecialSoundness/ChallengeRound.lean @@ -0,0 +1,327 @@ +/- +Copyright (c) 2024-2026 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pablo Martín Vinuelas +-/ +import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.SeqCompose + +/-! + # Single challenge-only-round tree navigation (generic CWSS building block) + + Generic machinery for coordinate-wise special soundness (CWSS) of any **one-round, + challenge-only** protocol — the verifier sends a single challenge vector `Fin ℓ → C`; the + prover sends nothing on the wire (its response is the reduction's *output witness*) — at an + **arbitrary per-coordinate soundness parameter `k`**. It is the challenge-only, parametric-`k` + sibling of `SingleRound.lean` (which is pinned to a message round plus `k = 2`, the folding + shape of Hachi Lemma 8). + + The target instance is the **repaired Hachi zero-check** (Hachi [NOZ26] Lemma 10, Figure 5; + `Commitments/Functional/Hachi/ZeroCheck/`): `ℓ = 2` scalar Kronecker seeds at + `k = D = max(2^{m₀}, 2^{m_α})`. There, extraction consumes all `k` distinct challenge values + of a coordinate (univariate root-counting on the Kronecker pullback), not just the one sibling + a folding extractor subtracts. Accordingly, the star lemma exported here is + `IsSpecialSoundFamily.exists_coord_finset`: a special-sound family attains, per coordinate, a + `Finset` of at least `k` distinct values — the interpolation root supply. + + Main pieces (mirroring `SingleRound.lean`): + + - the one-round `pSpec` and its CWSS structure `chalStructure` + (`ℓ` coordinates, parameter `k`, arity `ℓ·(k-1)+1`); + - **shape recovery** (`tree_shape`): every challenge tree of this `pSpec` is a `tree1` — one + node of sibling challenge vectors, leaves below; + - per-branch transcripts and the pure-acceptance bridge (`branch_relOut_language`); + - the tree extractor `treeExtractor` and the **generic assembly** + `coordinateWiseSpecialSound_of_mkWitness`: any pure statement-extending verifier of this + `pSpec` is CWSS for `chalStructure`, given only a protocol-specific witness assembler + `mkWitness` turning per-branch `relOut`-witnesses at special-sound challenge families into a + `relIn`-witness. + + ## References + + * [Fenzi, G., Moghaddas, H., and Nguyen, N. K., *Lattice-Based Polynomial Commitments: Towards + Asymptotic and Concrete Efficiency*][FMN24] + * [Nguyen, N. K., O'Rourke, G., and Zhang, J., *Hachi: Efficient Lattice-Based Multilinear + Polynomial Commitments over Extension Fields*][NOZ26] +-/ + +open OracleComp OracleSpec ProtocolSpec ProtocolSpec.ChallengeTree + +namespace CoordinateWise + +/-- **Per-coordinate root supply of a special-sound family**: a family in `SS(S, ℓ, k)` attains, +in every coordinate `i`, at least `k` pairwise-distinct values — the center's value together with +the `k-1` siblings' values at `i` (all distinct: siblings agree with the center off `i`, so +injectivity of the family separates them at `i`). This is the extraction input of the repaired +Hachi Lemma 10: with Kronecker-curve challenges, the values of coordinate `i` are `k` distinct +roots of a univariate pullback of degree `< k`. -/ +theorem IsSpecialSoundFamily.exists_coord_finset {S : Type*} {ℓ k : ℕ} + {c : Fin (ℓ * (k - 1) + 1) → (Fin ℓ → S)} + (hfam : IsSpecialSoundFamily ℓ k c) (i : Fin ℓ) : + ∃ s : Finset S, k ≤ s.card ∧ ∀ τ ∈ s, ∃ j, c j i = τ := by + letI : DecidableEq S := Classical.decEq S + obtain ⟨hinj, e, h⟩ := hfam + obtain ⟨J, heJ, hcard, hJ⟩ := h i + refine ⟨insert (c e i) (J.image fun j => c j i), ?_, fun τ hτ => ?_⟩ + · -- Distinctness: two siblings agreeing at `i` agree everywhere (they share the center off + -- `i`), so injectivity of the family collapses them. + have himg : (J.image fun j => c j i).card = J.card := by + refine Finset.card_image_of_injOn fun j hj j' hj' hij => hinj (funext fun t => ?_) + by_cases ht : t = i + · subst ht; exact hij + · rw [← (hJ j hj).2 t ht]; exact (hJ j' hj').2 t ht + have hnot : c e i ∉ J.image fun j => c j i := by + simp only [Finset.mem_image, not_exists, not_and] + exact fun j hj heq => (hJ j hj).1 heq.symm + rw [Finset.card_insert_of_notMem hnot, himg, hcard] + omega + · rcases Finset.mem_insert.mp hτ with h1 | h1 + · exact ⟨e, h1.symm⟩ + · obtain ⟨j, _, hje⟩ := Finset.mem_image.mp h1 + exact ⟨j, hje⟩ + +namespace ChallengeRound + +/-- The one-round, challenge-only protocol (instantiated by the repaired Hachi zero-check, +Lemma 10 / Figure 5): the verifier sends a challenge vector `Fin ℓ → C` (round 0, `V_to_P`). +The prover's answer is the reduction's output witness, never sent on the wire. -/ +@[reducible] def pSpec (C : Type) (ℓ : ℕ) : ProtocolSpec 1 := + ⟨!v[.V_to_P], !v[Fin ℓ → C]⟩ + +variable {C : Type} {ℓ : ℕ} {arity : (pSpec C ℓ).ChallengeIdx → ℕ} + +/-! ## Round reader, the star tree, and shape recovery + +Naive `match tree` on a `ChallengeTree … 0` fails ("dependent elimination failed"), so — as in +`SingleRound.lean` — the reader is index-generic: it matches at an arbitrary round index `a` and +carries the proof `a = 0`. -/ + +/-- Index-generic round-0 reader: peel the sibling-challenge family off a `chalNode` at any +index `a` together with a proof `a = 0`. -/ +def chalsAux : {a : Fin 2} → ChallengeTree (pSpec C ℓ) arity a → a = (0 : Fin 2) → + (Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + | _, .leaf, ha => by simp at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 0 := Subsingleton.elim m 0 + exact absurd h Direction.noConfusion + | _, .chalNode m _ chals _, ha => by + obtain rfl : m = 0 := Subsingleton.elim m 0 + exact chals + +/-- Read the sibling-challenge family off a full tree. -/ +def readChallenges (tree : ChallengeTree (pSpec C ℓ) arity 0) : + Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩ := + chalsAux tree rfl + +/-- The star tree: one challenge node carrying the sibling family, leaves below. Every tree of +this `pSpec` has this shape (`tree_shape`). -/ +def tree1 (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) : + ChallengeTree (pSpec C ℓ) arity 0 := + .chalNode 0 rfl challenges (fun _ => .leaf) + +/-- The reader computes on the star tree. -/ +@[simp] theorem readChallenges_tree1 + (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) : + readChallenges (tree1 challenges) = challenges := rfl + +/-- Shape recovery, level 1: every subtree at the last round is a leaf. -/ +theorem eq_leaf : {a : Fin 2} → (t : ChallengeTree (pSpec C ℓ) arity a) → + (ha : a = Fin.last 1) → + HEq t (ChallengeTree.leaf : ChallengeTree (pSpec C ℓ) arity (Fin.last 1)) + | _, .leaf, _ => HEq.rfl + | _, .msgNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simp) + | _, .chalNode m _ _ _, ha => by + exact absurd (congrArg Fin.val ha) (by simp) + +/-- Shape recovery, level 0: every tree at round 0 is a `tree1`. -/ +theorem tree_shape_aux : {a : Fin 2} → (t : ChallengeTree (pSpec C ℓ) arity a) → + (ha : a = 0) → ∃ challenges, HEq t (tree1 (arity := arity) challenges) + | _, .leaf, ha => by simp at ha + | _, .msgNode m h _ _, ha => by + obtain rfl : m = 0 := Subsingleton.elim m 0 + exact absurd h Direction.noConfusion + | _, .chalNode m _ chals children, ha => by + obtain rfl : m = 0 := Subsingleton.elim m 0 + refine ⟨chals, ?_⟩ + have hch : children = fun _ => .leaf := by + funext j + exact eq_of_heq (eq_leaf (children j) rfl) + rw [hch] + exact HEq.rfl + +/-- **Shape recovery.** Every full tree of the one-round `pSpec` is a star tree. -/ +theorem tree_shape (tree : ChallengeTree (pSpec C ℓ) arity 0) : + ∃ challenges, tree = tree1 (arity := arity) challenges := by + obtain ⟨challenges, h⟩ := tree_shape_aux tree rfl + exact ⟨challenges, eq_of_heq h⟩ + +/-! ## Per-branch transcripts -/ + +/-- The root-to-leaf path through `tree1` selecting branch `j` of the challenge node. -/ +def branchPath (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + (j : Fin (arity ⟨0, rfl⟩)) : LeafPath (tree1 challenges) := + .chal j .leaf + +/-- The full transcript of branch `j` of the star tree: the single challenge `challenges j`. -/ +def branchTr (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + (j : Fin (arity ⟨0, rfl⟩)) : (pSpec C ℓ).FullTranscript := + (branchPath challenges j).fullTranscript + +/-- Branch `j`'s transcript carries challenge `challenges j` at round 0. -/ +theorem branch_challenge (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + (j : Fin (arity ⟨0, rfl⟩)) : + (branchTr challenges j).challenges ⟨0, rfl⟩ = challenges j := by + simp only [branchTr, branchPath, LeafPath.fullTranscript, LeafPath.transcript, + FullTranscript.challenges, Transcript.concat] + simp [Fin.snoc] + +/-- Branch `j`'s transcript is one of the star tree's leaf transcripts. -/ +theorem branch_mem (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + (j : Fin (arity ⟨0, rfl⟩)) : + branchTr challenges j ∈ (tree1 challenges).fullTranscripts := + LeafPath.mem_fullTranscripts _ + +/-! ## The CWSS structure -/ + +variable (C ℓ) in +/-- The challenge-only CWSS structure at coordinate count `ℓ` and soundness parameter `k`: the +single challenge round decomposes by the identity into `ℓ` coordinates over `C`, with branching +arity `ℓ·(k-1)+1`. The repaired Hachi zero-check instantiates `ℓ = 2` (the two Kronecker seeds) +and `k = D = max(2^{m₀}, 2^{m_α})` — Lemma 10's corrected parameter, **not** the paper's +`max(2d, 2b-1)`. -/ +def chalStructure (k : ℕ) (hℓ : 0 < ℓ) (hk : 2 ≤ k) : CWSSStructure (pSpec C ℓ) where + coordIndex := fun _ => ⟨ℓ, hℓ⟩ + alphabet := fun _ => C + decompose := fun i => Equiv.cast (by rcases i with ⟨j, hj⟩; fin_cases j; rfl) + soundnessParam := fun _ => ⟨k, hk⟩ + arity := fun _ => ℓ * (k - 1) + 1 + arity_eq := rfl + +/-- The node predicate of `chalStructure` is exactly the `SS(C, ℓ, k)` condition on the sibling +family. -/ +theorem nodeOk_iff_family {k : ℕ} {hℓ : 0 < ℓ} {hk : 2 ≤ k} + (challenges : Fin ((chalStructure C ℓ k hℓ hk).arity ⟨0, rfl⟩) → + (pSpec C ℓ).Challenge ⟨0, rfl⟩) : + (chalStructure C ℓ k hℓ hk).nodeOk ⟨0, rfl⟩ challenges ↔ + IsSpecialSoundFamily ℓ k challenges := by + unfold CWSSStructure.nodeOk + simp only [chalStructure, CWSSStructure.ell, CWSSStructure.k] + rfl + +/-! ## Pure-acceptance bridge and extractor -/ + +section Bridge + +variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitOut : Type} {σ : Type} + +/-- Acceptance of the star tree specializes, per branch `j`, to membership of the branch's +verifier output `mapStmt stmtIn (challenges j)` in `relOut.language` — for any pure verifier that +outputs `mapStmt` applied to the input statement and the transcript's challenge. The reshaping +`mapStmt` is the identity pair `(·, ·)` for a statement-extending verifier, but may repack the +challenge into a richer output-statement structure (the Hachi zero-check packs the two Kronecker +seeds into named fields). -/ +theorem branch_relOut_language {StmtOut : Type} (init : ProbComp σ) + (impl : QueryImpl oSpec (StateT σ ProbComp)) + (V : Verifier oSpec StmtIn StmtOut (pSpec C ℓ)) + (mapStmt : StmtIn → (Fin ℓ → C) → StmtOut) + (hpure : ∀ s tr, V.verify s tr = pure (mapStmt s (tr.challenges ⟨0, rfl⟩))) + (relOut : Set (StmtOut × WitOut)) + (stmtIn : StmtIn) + (challenges : Fin (arity ⟨0, rfl⟩) → (pSpec C ℓ).Challenge ⟨0, rfl⟩) + (hAcc : (tree1 challenges).IsAccepting init impl V stmtIn relOut.language) + (j : Fin (arity ⟨0, rfl⟩)) : + (mapStmt stmtIn (challenges j)) ∈ relOut.language := + Verifier.mem_of_pure_accepting init impl V stmtIn (branchTr challenges j) relOut.language + (mapStmt stmtIn (challenges j)) (by rw [hpure, branch_challenge]) + (hAcc _ (branch_mem challenges j)) + +end Bridge + +open Classical in +/-- The tree extractor, generic over separate witness types: `relOut` relates the extended +statement to a per-branch response `WitOut`; `mkWitness` assembles the extracted input witness +`WitIn` from the `ℓ·(k-1)+1` sibling challenge vectors and one classically chosen `WitOut` per +branch (`Classical.ofNonempty` where none exists — on structured accepting trees +`branch_relOut_language` fires every guard). Hypothesis-free: all correctness is proven +downstream. -/ +noncomputable def treeExtractor {k : ℕ} {StmtIn StmtOut WitOut WitIn : Type} [Nonempty WitOut] + (hℓ : 0 < ℓ) (hk : 2 ≤ k) + (mapStmt : StmtIn → (Fin ℓ → C) → StmtOut) + (relOut : Set (StmtOut × WitOut)) + (mkWitness : StmtIn → (Fin (ℓ * (k - 1) + 1) → (Fin ℓ → C)) → + (Fin (ℓ * (k - 1) + 1) → WitOut) → WitIn) : + Extractor.TreeBased StmtIn WitIn (pSpec C ℓ) (chalStructure C ℓ k hℓ hk).arity := + fun stmtIn tree => + let fam : Fin (ℓ * (k - 1) + 1) → (Fin ℓ → C) := readChallenges tree + let resp : Fin (ℓ * (k - 1) + 1) → WitOut := fun j => + if h : ∃ w, ((mapStmt stmtIn (fam j)), w) ∈ relOut then h.choose else Classical.ofNonempty + mkWitness stmtIn fam resp + +section Assembly + +variable {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitOut WitIn : Type} [Nonempty WitOut] + {σ : Type} + +/-- **Generic challenge-only-round CWSS assembly.** Any pure statement-extending verifier of the +one-round `pSpec` is coordinate-wise special sound for `chalStructure`, provided a witness +assembler `mkWitness` that turns per-branch `relOut`-witnesses at special-sound challenge +families into a `relIn`-witness. This discharges all tree/extractor plumbing once; the +protocol-specific work (the repaired Hachi Lemma 10's binding-or-roots case split) lives +entirely in `hmk`. Unlike `SingleRound`, `hmk` receives the full `IsSpecialSoundFamily` +certificate — extraction at parameter `k` needs all `k` values per coordinate +(`IsSpecialSoundFamily.exists_coord_finset`), not just a star center and one sibling. -/ +theorem coordinateWiseSpecialSound_of_mkWitness {k : ℕ} {StmtOut : Type} + (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) + (hℓ : 0 < ℓ) (hk : 2 ≤ k) + (V : Verifier oSpec StmtIn StmtOut (pSpec C ℓ)) + (mapStmt : StmtIn → (Fin ℓ → C) → StmtOut) + (hpure : ∀ s tr, V.verify s tr = pure (mapStmt s (tr.challenges ⟨0, rfl⟩))) + (relIn : Set (StmtIn × WitIn)) + (relOut : Set (StmtOut × WitOut)) + (mkWitness : StmtIn → (Fin (ℓ * (k - 1) + 1) → (Fin ℓ → C)) → + (Fin (ℓ * (k - 1) + 1) → WitOut) → WitIn) + (hmk : ∀ stmtIn (fam : Fin (ℓ * (k - 1) + 1) → (Fin ℓ → C)) + (resp : Fin (ℓ * (k - 1) + 1) → WitOut), + (∀ j, (mapStmt stmtIn (fam j), resp j) ∈ relOut) → + IsSpecialSoundFamily ℓ k fam → + (stmtIn, mkWitness stmtIn fam resp) ∈ relIn) : + V.coordinateWiseSpecialSound init impl (chalStructure C ℓ k hℓ hk) relIn relOut := by + classical + refine ⟨treeExtractor hℓ hk mapStmt relOut mkWitness, ?_⟩ + intro stmtIn tree hStruct hAcc + obtain ⟨challenges, rfl⟩ := tree_shape tree + -- each branch's guard fires: per-branch membership in `relOut.language` + have hmem : ∀ j : Fin (ℓ * (k - 1) + 1), + ∃ w, (mapStmt stmtIn (challenges j), w) ∈ relOut := fun j => + (Set.mem_language_iff relOut _).1 + (branch_relOut_language init impl V mapStmt hpure relOut stmtIn challenges hAcc j) + -- the sibling family is special sound + have hfam : IsSpecialSoundFamily ℓ k challenges := (nodeOk_iff_family challenges).1 hStruct.1 + -- each chosen response satisfies the relation (the extractor's guards fire) + have hbranch : ∀ j : Fin (ℓ * (k - 1) + 1), + (mapStmt stmtIn (challenges j), + if h : ∃ w, (mapStmt stmtIn (challenges j), w) ∈ relOut + then h.choose else Classical.ofNonempty) ∈ relOut := by + intro j + rw [dif_pos (hmem j)] + exact (hmem j).choose_spec + -- the extractor computes definitionally on the recovered star tree + exact hmk stmtIn _ _ hbranch hfam + +end Assembly + +/-! ## The 1-round protocol instances (NOT auto-derived for `ProtocolSpec 1`) -/ + +section Instances + +variable {C : Type} {ℓ : ℕ} [SampleableType C] + +/-- Hand-written 1-round instance (not auto-derived for `ProtocolSpec 1`), generic in `C`. -/ +instance : ∀ i, SampleableType ((pSpec C ℓ).Challenge i) + | ⟨0, _⟩ => (inferInstance : SampleableType (Fin ℓ → C)) + +end Instances + +end ChallengeRound + +end CoordinateWise diff --git a/docs/kb/audits/README.md b/docs/kb/audits/README.md index 0ef216570e..8d78115962 100644 --- a/docs/kb/audits/README.md +++ b/docs/kb/audits/README.md @@ -13,6 +13,10 @@ The long-term goal is for deep paper audits to live here rather than in ad hoc b Current audit pages: +- [`noz26-zero-check-lemma10.md`](noz26-zero-check-lemma10.md) + - Figure 5 / Lemma 10 correspondence, the Kronecker repair, and the weak-binding seam as + integrated into the escape-threaded opening chain. + - [`bciks20-appendix-a-rational-functions.md`](bciks20-appendix-a-rational-functions.md) - Appendix A rational-function and Hensel-lifting status for `BCIKS20`. - [`open-problems-list-decoding-and-correlated-agreement.md`](open-problems-list-decoding-and-correlated-agreement.md) diff --git a/docs/kb/audits/noz26-zero-check-lemma10.md b/docs/kb/audits/noz26-zero-check-lemma10.md new file mode 100644 index 0000000000..bcdd4df496 --- /dev/null +++ b/docs/kb/audits/noz26-zero-check-lemma10.md @@ -0,0 +1,89 @@ +# NOZ26 Figure 5 / Lemma 10 audit + +This page records the specification boundary for link 6 of ArkLib's Hachi opening chain. It is +based on the January 30, 2026 ePrint version of Nguyen–O'Rourke–Zhang, *Hachi: Efficient +Lattice-Based Multilinear Polynomial Commitments over Extension Fields* (`NOZ26`, §4.3, +Figure 5 and Lemma 10). + +> **Status (integrated).** The corrected Lemma 10 is now formalized *inside* the escape-threaded +> opening chain: `zeroCheckPackage` reduces `relBatchedE → relZeroCheckE` and is composed as +> `batchPackage ▷ zeroCheckPackage ▷ sumcheckBridgePackage` in `Composition.lean` (`openCore`). +> The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` is **proof-`sorry`-free**. The +> weak-binding seam that earlier blocked composition is discharged by the modelling decision +> recorded below (resolution option 2). All declarations live in the chain's namespace +> `ArkLib.Lattices.Ajtai.InnerOuter` (`Hachi/ZeroCheck/{Constraints,Batch,Reduction}.lean`); the +> generic engine is `OracleReduction/…/CoordinateWiseSpecialSoundness/ChallengeRound.lean`. + +## Paper claim + +Figure 5 samples two uniform vector challenges `τ₀` and `τ₁`, receives a table `w̃`, checks +`t = Com(w̃)`, reconstructs the batched polynomials from Equations (22)–(23), and checks +`H₀(τ₀) = 0` and `Hα(τ₁) = 0`. Lemma 10 claims that a coordinate-wise special-sound family either +yields one opening satisfying both polynomial identities or breaks commitment binding. Remark 2 +says the final instantiation should use the inner-outer commitment's weak binding from Lemma 7. + +## Paper-to-Lean ledger + +| Paper object or claim | Lean declaration | Status | Concern | +| --- | --- | --- | --- | +| Batched range identity, Eq. (23) | `ZeroCheck.hZero` (via `MvPolynomial.MLE`) | represented | `eq̃`-batching is the real `MLE`, so multilinearity (`hZero_degreeOf_le`) is sorry-free; entry content is `wTable` (F5). | +| Batched row identity, Eq. (22) | `ZeroCheck.hAlpha` (via `MvPolynomial.MLE`) | represented | Multilinearity sorry-free; coefficient content is `hAlphaEvals` (F5). | +| Figure-5 point checks | `ZeroCheck.relZeroCheck` / `relZeroCheckE` | deliberately repaired | Points are derived from scalar Kronecker seeds, not sampled uniformly as vectors; escape-threaded (`Set.withEscape K.esc`). | +| Axis-cross counterexample | `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross` | proven | Formally refutes the identity-testing step used by the uniform-vector argument. | +| Kronecker root-counting kernel | `LinearMvExtension.multilinear_eq_zero_of_kronecker_roots`, `ZeroCheck.arm_eq_zero_of_family` | proven, **axiom-clean** | `D ≥ 2^m` univariate roots + Kronecker injectivity; no `sorryAx`. | +| Lemma-10 extraction (escape-threaded) | `ZeroCheck.buildWitnessE`, `buildWitnessE_mem_relBatchedE` | proof-sorry-free | Escape pass-through ∨ weak-binding collision ∨ common opening with both identities zero. | +| Lemma-10 binding alternative | `LiftCom.escOfCollision` via `K.collision_mem` | integrated | Distinct short openings of the shared `t` become an escape `e ∈ K.esc` (Hachi weak binding). | +| Corrected Lemma 10 CWSS | `ZeroCheck.zeroCheck_coordinateWiseSpecialSound` | proof-sorry-free | `(ℓ, k) = (2, D)`; assembled by the generic `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness`. | +| Link-5/link-6/link-7 composition | `batchPackage ▷ zeroCheckPackage ▷ sumcheckBridgePackage` (`openCore`) | **defined, compiles** | The seam relations match by `rfl`; the whole chain builds. | + +## Uniform-vector challenge gap (why the repair is needed) + +A coordinate-wise star of vector challenges fixes all but one scalar coordinate on each arm. It +therefore proves vanishing only on the axis cross through its center. For at least two variables, +the nonzero multilinear polynomial `(X₁-a)(X₂-b)` vanishes on that entire cross. Increasing the +number of points on the same arms does not repair the argument. + +ArkLib's repair samples scalar seeds `ρ₀, ρα` and evaluates on Kronecker curves +`κ_m(ρ) = (ρ, ρ², ρ⁴, ...)`. A multilinear polynomial pulls back injectively to a univariate +polynomial of degree below `2^m`; `2^m` distinct seeds then determine the identity. This changes +the challenge distribution and raises the soundness parameter to `D = max(2, 2^{m₀}, 2^{mα})`. + +## Weak-binding seam — resolution adopted (option 2) + +The differing-witness branch of the paper's Lemma 10 gives two tables with the same commitment. +The concrete `LiftCom.collision_mem` axiom (matching Hachi Remark 2 / Lemma 7) is +**norm-conditioned**: it turns a collision into an escape only when *both* openings are short. +A single accepting Figure-5 branch pins two point evaluations, which do not by themselves imply +shortness. + +**Adopted fix.** The zero-check's output relation `relZeroCheck` carries the conjunct +`liftShort Φ bound ρBound w̃` (and `relZeroCheckE` its escape-threaded widening). Two accepting +branches therefore both certify short openings, so `K.collision_mem` applies and the binding +break becomes an escape. This is completeness-preserving (the honestly committed `w̃` is short), +and the conjunct is threaded onward through the sumcheck seam relation `roundRel` so every +downstream seam keeps the weak-binding escape available. This is *resolution option 2* below: +change the relation so every differing opening is known short before invoking weak binding. + +## Residual gaps (out of Lemma-10 scope) + +- **F5 encoding.** `hZero`/`hAlpha` are genuine multilinear extensions, but their coefficient + functions — `wTable` (the Eq. (21) `Zq`-table with the `ρ`-digit decomposition) and + `hAlphaEvals` (the `M̃_α` contraction) — are still `sorry` (milestone F5). Consequently + `zeroCheck_coordinateWiseSpecialSound` is proof-`sorry`-free but not yet *axiom-clean*: its + `#print axioms` reports `sorryAx` transitively through those encoding defs. The Lemma-10 + argument itself is parametric in them; the standalone kernel `arm_eq_zero_of_family` is + axiom-clean. +- **Constructivity.** `buildWitnessE` (and the generic `treeExtractor`) select per-branch + witnesses with classical choice. A constructive extractor would need witness-bearing trees or a + decidable enumeration interface. +- **Sumcheck seam.** `roundRel` now carries the `liftShort` conjunct, but the sumcheck bridge's + pull-back `mem_relZeroCheckE_of_roundRelE` that must re-supply it remains a skeleton `sorry` + (milestone F7). + +## Resolution options (for the record) + +1. assume full binding for arbitrary tables and accept a standalone abstract theorem; +2. **[adopted]** change the relation so every differing opening is known short before invoking weak + binding (`liftShort` in `relZeroCheck` / `roundRel`); +3. redesign the composed extraction interface so the zero-check consumes downstream + witness/extractor evidence constructively. diff --git a/docs/kb/index.md b/docs/kb/index.md index 9d0df3d662..cd0ffa4893 100644 --- a/docs/kb/index.md +++ b/docs/kb/index.md @@ -48,6 +48,9 @@ used in `ArkLib/**/*.lean`, including: - [`audits/README.md`](audits/README.md) - audit conventions and migration notes for paper-to-code comparison pages. +- [`audits/noz26-zero-check-lemma10.md`](audits/noz26-zero-check-lemma10.md) + - Hachi Figure 5 / Lemma 10 paper-to-Lean audit; the Kronecker repair and the weak-binding seam + as integrated into the escape-threaded opening chain. - [`audits/bciks20-appendix-a-rational-functions.md`](audits/bciks20-appendix-a-rational-functions.md) - status matrix for the rational-function and Hensel-lifting layer used by `BCIKS20`. - [`audits/open-problems-list-decoding-and-correlated-agreement.md`](audits/open-problems-list-decoding-and-correlated-agreement.md) diff --git a/docs/kb/papers/NOZ26.md b/docs/kb/papers/NOZ26.md index dd98e61ab0..12949c8062 100644 --- a/docs/kb/papers/NOZ26.md +++ b/docs/kb/papers/NOZ26.md @@ -6,13 +6,14 @@ year: "2026" bib_source: blueprint/src/references.bib canonical_url: https://eprint.iacr.org/2026/156 source_metadata: ../sources/NOZ26/metadata.yml -status: seeded +status: active-audit related_modules: - ArkLib/ProofSystem/RingSwitching/Profile.lean - ArkLib/Data/Lattices/CyclotomicRing/Core/Modulus.lean - ArkLib/Commitments/Functional/Hachi/Gadget.lean - ArkLib/Commitments/Functional/Hachi/InnerOuter/Scheme.lean - ArkLib/Commitments/Functional/Hachi/InnerOuter/Security.lean + - ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean --- # NOZ26 @@ -65,6 +66,14 @@ Ring-switching layer: - `R_q` is **not an integral domain**, so the generic `[IsDomain L]` Schwartz–Zippel soundness theorem does not instantiate Hachi. Hachi soundness (a CWSS-style argument) is a separate theorem with a different error and is out of scope for the current ring-switching module. +- Hachi Lemma 10's uniform-vector CWSS argument is invalid for multivariate multilinear + polynomials: a coordinate-wise star supplies only an axis cross, which does not determine the + polynomial. ArkLib's zero-check (`ZeroCheck/Reduction.lean`) restricts the two evaluation points + to Kronecker curves and uses univariate interpolation with `D = max(2, 2^{m₀}, 2^{mα})`. The + corrected CWSS theorem is proof-`sorry`-free and is composed into the escape-threaded opening + chain (`Composition.lean`); the weak-binding disjunct is discharged through `LiftCom`'s + norm-conditioned collision via a `liftShort` conjunct in the seam relations. See + [`../audits/noz26-zero-check-lemma10.md`](../audits/noz26-zero-check-lemma10.md). ## Version Notes From 63a3be201844bd4a3f92553ff5737a08b3dbeadc Mon Sep 17 00:00:00 2001 From: ErVinuelas Date: Fri, 17 Jul 2026 16:18:50 +0200 Subject: [PATCH 18/21] lemma 5 and wTable def --- .../Functional/Hachi/Composition.lean | 22 ++--- .../Functional/Hachi/ZeroCheck.lean | 25 ++++-- .../Functional/Hachi/ZeroCheck/Batch.lean | 50 +++++++---- .../Hachi/ZeroCheck/Constraints.lean | 86 +++++++++++++++---- .../Functional/Hachi/ZeroCheck/Reduction.lean | 9 +- .../Functional/Hachi/hachi-overview.html | 18 ++-- docs/kb/audits/noz26-zero-check-lemma10.md | 43 +++++++--- 7 files changed, 177 insertions(+), 76 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 778f77184c..3acd35cc15 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -234,7 +234,7 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT [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 ≤ γ) : + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) : CWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) @@ -250,7 +250,7 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT 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 ▷ + batchPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b hn ▷ zeroCheckPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b ▷ sumcheckBridgePackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b @@ -272,7 +272,7 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta [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 ≤ γ) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} @@ -300,7 +300,7 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta roundsSpec F b (mLow + κ)) ++ₚ pSpecFinalEval F).Challenge i) := ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := i₁) (h₂ := instSampleableTypeChallengePSpecFinalEval) - (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hq2 hb).toGuarded.append + (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hn).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 @@ -322,7 +322,7 @@ theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) [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 ≤ γ) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} @@ -330,16 +330,16 @@ theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : - ((openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ - hτ K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' + ((openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl hq5 hκ + hτ K φF hd hn 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 + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl + hq5 hκ hτ K φF hd hn 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 + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl hq5 hκ hτ + K φF hd hn zpow Φ' pp' reinterpretCom base' βSq' γ' κ').isCWSS end OpeningChain diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean index da99c67378..96f3471863 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean @@ -30,20 +30,27 @@ is recorded in `docs/kb/audits/noz26-zero-check-lemma10.md`. is `sorry`-free), the sumcheck polynomials `F_{0,τ₀}`/`F_{α,τ₁}` with their degree pins, the Kronecker point `kroneckerPoint`, `hypercubeSum`, and `roundRel`/`roundRelE`. Consumed by both this zero-check *and* the sumcheck round loop (`Sumcheck/`), so it sits at the shared base of the - batched-sumcheck machinery. The table entry function `wTable` and the `M̃_α` contraction - `hAlphaEvals` (the genuine F5 index bookkeeping) and the sumcheck-polynomial stubs remain - `sorry`. + batched-sumcheck machinery. The `M̃_α` contraction `hAlphaEvals` is now **concrete** — the + `α`-evaluated per-row lift defect (row-encoded into the `m₁`-cube), so `H_α` is a genuine + batched polynomial with meaningful coefficients (`hAlphaEvals_rowPoint`, axiom-clean). The table + entry function `wTable` is now **concrete** too (it reads the committed `z`/`ρ` coefficients + directly, so `H₀` non-vacuously tests shortness of the committed data). Only the range-side + soundness (`H₀ ≡ 0 ⇒ liftShort`) and the sumcheck-polynomial stubs remain F5. * `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` + per-row residual claims as the two `MvPolynomial` identities `H₀ ≡ 0 ∧ H_α ≡ 0` (`relBatched`/`relBatchedE`, Eqs. (22)–(23)); the un-batching pull-back - `mem_relLiftE_of_relBatchedE` is the sorried F6 content. + `mem_relLiftE_of_relBatchedE` (`relBatchedE → relLiftE`, arity pin `n ≤ 2 ^ m₁`) is + **proof-`sorry`-free** (via `MLE_eq_zero_iff` + `hAlphaEvals_rowPoint`; the residual `sorryAx` is + only the `wTable`/`H₀` conjunct in its statement type). * `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. The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` (`relBatchedE - → relZeroCheckE`, `k = D = zeroCheckD m₀ m₁`) is **proof-`sorry`-free** — its escape/collision/ - root-counting extraction is complete; it is not *axiom-clean* only because `hZero`/`hAlpha` are - built on the still-`sorry` F5 encodings. The Kronecker kernel `arm_eq_zero_of_family` is - axiom-clean. + → relZeroCheckE`, `k = D = zeroCheckD m₀ m₁`) is **`sorry`-free and now axiom-clean** — its + escape/collision/root-counting extraction is complete, and with the encodings `hAlphaEvals`/ + `wTable` now concrete, `#print axioms` reports no `sorryAx` (only the ambient + `propext`/`Classical.choice`/`Quot.sound`; the `Classical.choice` is from the classical-choice + branch selection in `buildWitnessE`, the documented constructivity caveat). The Kronecker kernel + `arm_eq_zero_of_family` is likewise axiom-clean. 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 diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean index 48b8abdbad..8ce9ba8cd3 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -22,10 +22,14 @@ import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints from `relLift`; it is the precondition of the weak-binding escape (`LiftCom.collision_mem`) invoked by the zero-check's extraction. - * **extraction direction** (the sorried pull-back `mem_relLiftE_of_relBatchedE`): - `H_α ≡ 0` ⇒ per-row constraints, by non-degeneracy of the `eq̃` basis; `H₀ ≡ 0` ⇒ per-entry - range membership over the *field* `F` (needs `2b − 1 < q` to read field roots as centered - `Zq`-representatives), threaded to `liftShort` under the norm-parameter hypotheses. + * **extraction direction** (the pull-back `mem_relLiftE_of_relBatchedE`, **axiom-clean**): + the only nontrivial conjunct is the per-row equation, recovered from `H_α ≡ 0` by non-degeneracy + of the `eq̃` basis (`MvPolynomial.MLE_eq_zero_iff`) followed by the row-encoding faithfulness + lemma `hAlphaEvals_rowPoint`. The `K.com`, `liftShort` and bound-sanity conjuncts are carried + **verbatim** by `relBatched`; in particular the *range* identity `H₀ ≡ 0` and the norm-parameter + hypotheses `2b ≤ q+1`, `b−1 ≤ bound` play **no role** in this pull-back (shortness is asserted + directly), so they are not assumed. The only genuine hypothesis is the row-encoding arity pin + `hn : n ≤ 2 ^ m₁`. (With `hAlphaEvals`/`wTable` now concrete, no `sorryAx` remains.) ## References @@ -64,28 +68,44 @@ def relBatchedE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBou Set (LiftStatement Φ K.TCom F n μ × (LiftedWitness Φ μ n ⊕ E)) := (relBatched Φ m₀ m₁ bound ρBound K φF b).withEscape K.esc +omit [NeZero q] [IsCyclotomic Φ] in /-- **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 ⇒ 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`) -`liftShort bound ρBound w̃`; the `liftShort` and bound-sanity conjuncts are shared verbatim. -/ +per-row claims. Escapes pass through. + +The only nontrivial conjunct is the per-row equation, recovered from `H_α ≡ 0`: by +`MvPolynomial.MLE_eq_zero_iff` every Boolean-point coefficient `hAlphaEvals` vanishes, and by +`hAlphaEvals_rowPoint` the coefficient at `rowPoint i` is exactly row `i`'s `α`-evaluated lift +defect, so the row equation of `relLift` follows. The `K.com`, `liftShort` and bound-sanity +conjuncts are carried verbatim by `relBatched` — in particular the *range* identity `H₀ ≡ 0` and +the norm-parameter hypotheses `2b ≤ q+1`, `b-1 ≤ bound` play **no role** in this pull-back +(shortness is already asserted directly), so they are not assumed. The arity pin `hn : n ≤ 2 ^ m₁` +is the batching cube's row-encoding requirement. -/ 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) + (φF : ZMod q →+* F) (b : ℕ) (hn : n ≤ 2 ^ m₁) (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 + rcases w with w | e + · -- real witness: only the per-row equation needs work + simp only [relBatchedE, Set.mem_withEscape_inl, relBatched, Set.mem_setOf_eq] at h + obtain ⟨hcom, hshort, _hZero, hAlphaZ, hbound⟩ := h + simp only [relLiftE, Set.mem_withEscape_inl, relLift, Set.mem_setOf_eq] + refine ⟨hcom, fun i => ?_, hshort, hbound⟩ + unfold hAlpha at hAlphaZ + rw [MvPolynomial.MLE_eq_zero_iff] at hAlphaZ + have hi := hAlphaZ (rowPoint m₁ hn i) + rw [hAlphaEvals_rowPoint] at hi + linear_combination hi + · -- escape: statement-independent pass-through + simpa only [relBatchedE, relLiftE, Set.mem_withEscape_inr] using h /-- **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) : + (φF : ZMod q →+* F) (b : ℕ) (hn : n ≤ 2 ^ m₁) : CWSSPackage init impl (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) @@ -100,6 +120,6 @@ def batchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbCom (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) + mem_relLiftE_of_relBatchedE Φ m₀ m₁ bound ρBound K φF b hn stmtIn witOut h) end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean index bf36d055d3..c0222670cd 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -23,12 +23,18 @@ import ArkLib.Data.MvPolynomial.Multilinear `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; `m₁` is the row-batching arity (`2 ^ m₁ ≥ n` rows of the lifted system). The table's - entry function `wTable` and the `M̃_α` contraction `hAlphaEvals` are the genuine F5 index - bookkeeping and remain `sorry`; the batched polynomials `hZero`/`hAlpha` are built from them via - the real multilinear extension `MvPolynomial.MLE`, so their multilinearity - (`hZero_degreeOf_le`/`hAlpha_degreeOf_le` — the hypothesis of the corrected Lemma 10's Kronecker - interpolation) is **`sorry`-free**. + two; `m₁` is the row-batching arity (`2 ^ m₁ ≥ n` rows of the lifted system, the arity pin + `hAlphaEvals`/`rowPoint` require). The `M̃_α` contraction `hAlphaEvals` is now **concrete** — the + `α`-evaluated per-row lift defect, row-encoded into the `m₁`-cube (`hAlphaEvals_rowPoint`, + axiom-clean) — so `H_α ≡ 0` genuinely characterizes "every lifted row vanishes at `α`" (consumed + by the batching bridge). The table entry function `wTable` is now **concrete** too — it reads the + committed `z`/`ρ` coefficients directly (decoding the `m₀`-cube to `row := idx / d`, + `col := idx % d`), so `H₀ ≡ 0` is a genuine (non-vacuous) shortness statement on the committed + data. Both `hZero`/`hAlpha` are built via the real multilinear extension `MvPolynomial.MLE`, so + their multilinearity (`hZero_degreeOf_le`/`hAlpha_degreeOf_le` — the hypothesis of the corrected + Lemma 10's Kronecker interpolation) is **`sorry`-free**, and the whole zero-check is now + **axiom-clean**. Matching `H₀ ≡ 0` to `liftShort`'s two bounds `(bound, ρBound)` (the range-side + soundness step) and the sumcheck-polynomial stubs remain F5. ## The batched constraint polynomials (Eqs. (22)–(23)) @@ -101,7 +107,7 @@ def roundDegZero (b : ℕ) : ℕ := 2 * b /-- Per-round univariate degree of the linear sumcheck (`F_{α,τ₁}`): degree `≤ 2` (pin R8). -/ def roundDegAlpha : ℕ := 2 -/-! ## The range factor and the table (genuine F5 content is `sorry`) -/ +/-! ## The range factor and the table (now concrete; range-side soundness is F5) -/ /-- Hachi Eq. (23)'s per-entry range factor `P_b(v) := v·∏_{j=1}^{b-1} (v - j)·(v + j)`: the vanishing polynomial of the symmetric range `{-(b-1), …, b-1}`. -/ @@ -127,20 +133,66 @@ theorem rangeProduct_eq_zero_iff {b : ℕ} {v : F} : exact hv /-- **The Eq. (21) table**: the committed `(z, ρ)` re-read as an `F`-valued function on the -`m₀`-cube — `Zq`-coefficient rows of the `zⱼ` (through the embedding `φF`), 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 (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : +`m₀`-cube. The cube point is decoded (via `finFunctionFinEquiv`) to a flat index `idx`, split into +a `row := idx / d` and `column := idx % d` (`d = deg Φ.φ`); rows `< μ` read the `Zq`-coefficients +of the committed `zⱼ ∈ Rq`, rows `μ ≤ · < μ + n` read the coefficients of the committed quotients +`ρᵢ`, both mapped through the base-field embedding `φF`; all other cube points are zero-padded. + +**The coefficients are read directly** — the range test `H₀` is on the *committed* data, so that +`H₀ ≡ 0 ⇒` every committed coefficient lies in `[−(b−1), b−1]` is a genuine (non-vacuous) shortness +statement. (The paper's base-`b` gadget decomposition is the *honest prover's* pre-commit step to +obtain short pieces; re-decomposing here would make every entry a base-`b` digit, hence trivially +in range, and `H₀` would test nothing.) The `b` argument is retained for signature compatibility +with `hZero`; matching `H₀ ≡ 0` to `liftShort`'s two bounds `(bound, ρBound)` is the range-side +soundness step (F5, out of scope here). -/ +noncomputable def wTable (φF : ZMod q →+* F) (_b : ℕ) (w : LiftedWitness Φ μ n) : (Fin m₀ → Fin 2) → F := - sorry + fun pt => + let idx : ℕ := (finFunctionFinEquiv pt : Fin (2 ^ m₀)) + let d : ℕ := Φ.φ.natDegree + if hz : idx / d < μ then + φF ((w.z ⟨idx / d, hz⟩).1.coeff (idx % d)) + else if hr : idx / d - μ < n then + φF ((w.ρ ⟨idx / d - μ, hr⟩).coeff (idx % d)) + else 0 + +/-- **The `m₁`-cube point encoding row `i : Fin n`** (arity pin `n ≤ 2 ^ m₁`): the inverse image +of `i` under the binary encoding `finFunctionFinEquiv : (Fin m₁ → Fin 2) ≃ Fin (2 ^ m₁)`. Rows +with index `≥ n` are the zero-padding of the batching cube. -/ +def rowPoint (hn : n ≤ 2 ^ m₁) (i : Fin n) : Fin m₁ → Fin 2 := + finFunctionFinEquiv.symm ⟨(i : ℕ), lt_of_lt_of_le i.isLt hn⟩ /-- The `M̃_α`-contracted per-row value of Eq. (22): the Boolean-point coefficients of `H_α`, -`i ↦ (∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ)) − ŷᵢ(α)`. **Sorried (F5)**: the `M̃_α` contraction -(the `α`-evaluated lifted matrix `M`, including the `−(α^d + 1)` quotient columns) and the row -targets `ŷᵢ(α)`. -/ -def hAlphaEvals (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +`i ↦ (∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ)) − ŷᵢ(α)`. + +**Given concretely (Lemma-10 design, Option A).** By the "represent the constraints by +polynomials" identity of [NOZ26] §4.3, this `M̃_α`-contraction equals the `α`-evaluated per-row +**defect** of the lift relation `relLift`, +`evalAt α (∑ⱼ Mᵢⱼ·zⱼ) − evalAt α ŷᵢ − evalAt α (X^d+1)·evalAt α ρᵢ`, so we take that defect as +the definition, row-encoded into the `m₁`-cube via `rowPoint` and zero-padded on rows `≥ n`. Its +vanishing at every Boolean point is then exactly the `relLift` row constraint +(`hAlphaEvals_rowPoint`) — the content the batching bridge's un-batching pull-back consumes. The +literal table-contraction form (needed only for the sumcheck *summand* `sumcheckPolyAlpha`) +remains F5 (`wTable`). The `b` argument is retained for signature compatibility with `hAlpha`. -/ +noncomputable def hAlphaEvals (φF : ZMod q →+* F) (_b : ℕ) (s : RlinStatement Φ n μ) (a : F) (w : LiftedWitness Φ μ n) : (Fin m₁ → Fin 2) → F := - sorry + fun pt => + if h : ((finFunctionFinEquiv pt : Fin (2 ^ m₁)) : ℕ) < n then + evalAt φF a (rowSum Φ s w.z ⟨_, h⟩) + - evalAt φF a ((s.yvec ⟨_, h⟩).1.toPoly) + - evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ ⟨_, h⟩) + else 0 + +omit [NeZero q] [IsCyclotomic Φ] in +/-- **Faithfulness of the row encoding**: at the Boolean point `rowPoint i`, the `H_α` +coefficient `hAlphaEvals` is exactly row `i`'s `α`-evaluated lift defect. This is the bridge +between `hAlpha ≡ 0` (via `MLE_eq_zero_iff`) and the per-row `relLift` constraints. -/ +theorem hAlphaEvals_rowPoint (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) + (w : LiftedWitness Φ μ n) (hn : n ≤ 2 ^ m₁) (i : Fin n) : + hAlphaEvals Φ m₁ φF b s a w (rowPoint m₁ hn i) = + evalAt φF a (rowSum Φ s w.z i) - evalAt φF a ((s.yvec i).1.toPoly) + - evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ i) := by + simp only [hAlphaEvals, rowPoint, Equiv.apply_symm_apply, Fin.eta, i.isLt, dif_pos] /-! ## The batched constraint polynomials (genuine multilinear extensions) -/ diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean index 2b28b2954c..8d685f995d 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -50,10 +50,11 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package `≥ 2^m` Kronecker roots of the multilinear identity (`arm_eq_zero_of_family`), giving `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` — i.e. `relBatchedE` via `.inl w̃`. - The theorem is not *axiom-clean*: `hZero`/`hAlpha` are built on the still-`sorry` F5 encodings - `wTable`/`hAlphaEvals`, so `#print axioms` reports `sorryAx` transitively through them. The - Lemma-10 *argument* is `sorry`-free and parametric in those encodings; the standalone kernel - `arm_eq_zero_of_family` is axiom-clean. + The theorem is now **axiom-clean**: with the encodings `wTable`/`hAlphaEvals` made concrete, + `#print axioms zeroCheck_coordinateWiseSpecialSound` reports no `sorryAx` — only the ambient + `propext`/`Classical.choice`/`Quot.sound` (the `Classical.choice` is the documented + constructivity caveat: `buildWitnessE` selects branch witnesses by classical choice). The + standalone kernel `arm_eq_zero_of_family` is axiom-clean. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html index d1a93e59cb..97ac912b35 100644 --- a/ArkLib/Commitments/Functional/Hachi/hachi-overview.html +++ b/ArkLib/Commitments/Functional/Hachi/hachi-overview.html @@ -567,12 +567,12 @@

Beyond the current line

rows:[ { n:5, kind:"bridge", name:"Batching bridge", sub:"ZeroCheck", fn:"Batch.lean", files:[["ZeroCheck","Batch.lean"]], ref:"§4.3 · Eqs. 22–23", rounds:"0 rounds", cwss:"any", - relIn:"relLiftE", relOut:"relBatchedE", status:"wip", sorry:1, badges:["bridge"], - desc:"Reads the lift's per-row/per-entry residual claims as the two MvPolynomial identities the zero-check tests. Statement reshaping only; the sole content is the sorried un-batching direction." }, + relIn:"relLiftE", relOut:"relBatchedE", status:"proven", sorry:0, badges:["bridge"], + desc:"Reads the lift's per-row residual claims as the two MvPolynomial identities the zero-check tests. Statement reshaping only. Un-batching pull-back mem_relLiftE_of_relBatchedE is proof-sorry-free: H_α ≡ 0 ⇒ (via MLE_eq_zero_iff + the now-concrete hAlphaEvals = per-row α-defect, arity pin n ≤ 2ᵐ¹) the per-row equations of relLift; liftShort/commitment carried verbatim (so the dead range hypotheses were dropped). Residual sorryAx only via wTable/H₀ in the relation type." }, { n:6, kind:"step", name:"Zero-check reduction", sub:"ZeroCheck", fn:"Reduction.lean", files:[["ZeroCheck","Reduction.lean"]], ref:"§4.3 · Fig 5 · Lemma 10*", rounds:"(ρ₀, ρ_α) ∈ F²", cwss:"ℓ = 2, k = D", - relIn:"relBatchedE", relOut:"relZeroChkE", status:"wip", sorry:1, badges:["repair"], - desc:"One challenge round carrying a pair of scalar Kronecker seeds, with batching points derived on the curves κ_m(ρ) = (ρ, ρ², ρ⁴, …). Deliberately repairs the paper's unprovable uniform-vector Lemma 10 (an axis-cross counterexample) to recover genuine (ℓ,k) = (2, D) CWSS." }, + relIn:"relBatchedE", relOut:"relZeroChkE", status:"proven", sorry:0, badges:["repair"], + desc:"One challenge round carrying a pair of scalar Kronecker seeds, with batching points derived on the curves κ_m(ρ) = (ρ, ρ², ρ⁴, …). Deliberately repairs the paper's unprovable uniform-vector Lemma 10 (an axis-cross counterexample) to recover genuine (ℓ,k) = (2, D) CWSS. CWSS theorem zeroCheck_coordinateWiseSpecialSound is now sorry-free and axiom-clean (the H_α/H₀ encodings hAlphaEvals/wTable are concrete)." }, ]}, { zt:"Sumcheck Loop", zref:"§4.3 · Figures 6–7 · Lemma 11", znote:"Runs m₀ paired sumcheck rounds down to a single evaluation of the committed table w̃, then reveals it. The guarded tail of the pure prefix.", @@ -603,8 +603,8 @@

Beyond the current line

var SUB_CROSS = [ { name:"Escape threading", ref:"milestone F2.0", files:[["","Escape.lean"]], status:"wip", sorry:1, desc:"Threads an abstract escape budget E through the finished front (evalChainE) so the Figure-4 commitment's binding break has a home at every upstream seam. Relations, pull-back and the bridge package are sorry-free; the one sorry is the widened Lemma 8." }, - { name:"Shared constraint encoding", ref:"§4.3 · Eqs. 21–23", files:[["ZeroCheck","Constraints.lean"]], status:"wip", sorry:12, - desc:"The table w̃, the eq̃-batched constraint polynomials H₀/H_α, their sumcheck summands, and the per-round seam relations. Consumed by both the zero-check (row 6) and the sumcheck loop (rows 7–9). Definitions-only for now — the most sorried file in the tree." }, + { name:"Shared constraint encoding", ref:"§4.3 · Eqs. 21–23", files:[["ZeroCheck","Constraints.lean"]], status:"wip", sorry:10, + desc:"The table w̃, the eq̃-batched constraint polynomials H₀/H_α, their sumcheck summands, and the per-round seam relations. Consumed by both the zero-check (row 6) and the sumcheck loop (rows 7–9). Both encodings are now concrete: hAlphaEvals (per-row α-defect, axiom-clean via hAlphaEvals_rowPoint) and wTable (committed z/ρ coefficients read directly). Remaining F5 sorries: the range-side soundness H₀⇒liftShort and the sumcheck summands (sumcheckPoly*/hypercubeSum/sum_*)." }, { name:"Composition home", ref:"the certificate", files:[["","Composition.lean"]], status:"wip", sorry:0, desc:"Imports every subprotocol's package and chains them: evalChain (the proven core, sorry-free) · openCore (rows 1–7, pure) · openingChain (rows 1–9, guarded tail). Its isCWSS fields are the certificates; the file itself has no sorries — its soundness rests on the imported packages." }, { name:"Scheme interface", ref:"the packaging", files:[["","Commitment.lean"]], status:"wip", sorry:1, @@ -645,9 +645,9 @@

Beyond the current line

["RingSwitch","Rlin.lean","bridge","§4.3","3","wip",3], ["RingSwitch","Reduction.lean","subprotocol","§4.3 · Fig4 · L9","4","wip",1], ["","ZeroCheck.lean","umbrella","§4.3","—","umbrella",0], - ["ZeroCheck","Batch.lean","bridge","§4.3 · Eq22–23","5","wip",1], - ["ZeroCheck","Constraints.lean","infrastructure","§4.3 · Eq21–23","—","wip",12], - ["ZeroCheck","Reduction.lean","subprotocol","§4.3 · Fig5 · L10*","6","wip",1], + ["ZeroCheck","Batch.lean","bridge","§4.3 · Eq22–23","5","proven",0], + ["ZeroCheck","Constraints.lean","infrastructure","§4.3 · Eq21–23","—","wip",10], + ["ZeroCheck","Reduction.lean","subprotocol","§4.3 · Fig5 · L10*","6","proven",0], ["","Sumcheck.lean","umbrella","§4.3","—","umbrella",0], ["Sumcheck","Bridge.lean","bridge","§4.3","7","wip",1], ["Sumcheck","Rounds.lean","subprotocol","§4.3 · Fig6 · L11","8","wip",1], diff --git a/docs/kb/audits/noz26-zero-check-lemma10.md b/docs/kb/audits/noz26-zero-check-lemma10.md index bcdd4df496..f761d2043f 100644 --- a/docs/kb/audits/noz26-zero-check-lemma10.md +++ b/docs/kb/audits/noz26-zero-check-lemma10.md @@ -8,7 +8,9 @@ Figure 5 and Lemma 10). > **Status (integrated).** The corrected Lemma 10 is now formalized *inside* the escape-threaded > opening chain: `zeroCheckPackage` reduces `relBatchedE → relZeroCheckE` and is composed as > `batchPackage ▷ zeroCheckPackage ▷ sumcheckBridgePackage` in `Composition.lean` (`openCore`). -> The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` is **proof-`sorry`-free**. The +> The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` is **`sorry`-free and axiom-clean** (the +> `H_α`/`H₀` encodings `hAlphaEvals`/`wTable` are now concrete), and the link-5 batching bridge's +> un-batching pull-back `mem_relLiftE_of_relBatchedE` is likewise **proven and axiom-clean**. The > weak-binding seam that earlier blocked composition is discharged by the modelling decision > recorded below (resolution option 2). All declarations live in the chain's namespace > `ArkLib.Lattices.Ajtai.InnerOuter` (`Hachi/ZeroCheck/{Constraints,Batch,Reduction}.lean`); the @@ -26,14 +28,15 @@ says the final instantiation should use the inner-outer commitment's weak bindin | Paper object or claim | Lean declaration | Status | Concern | | --- | --- | --- | --- | -| Batched range identity, Eq. (23) | `ZeroCheck.hZero` (via `MvPolynomial.MLE`) | represented | `eq̃`-batching is the real `MLE`, so multilinearity (`hZero_degreeOf_le`) is sorry-free; entry content is `wTable` (F5). | -| Batched row identity, Eq. (22) | `ZeroCheck.hAlpha` (via `MvPolynomial.MLE`) | represented | Multilinearity sorry-free; coefficient content is `hAlphaEvals` (F5). | +| Batched range identity, Eq. (23) | `ZeroCheck.hZero` (via `MvPolynomial.MLE`) | represented, **concrete** | `eq̃`-batching is the real `MLE` (multilinearity `hZero_degreeOf_le` sorry-free); entry content `wTable` now reads the committed `z`/`ρ` coefficients directly. | +| Batched row identity, Eq. (22) | `ZeroCheck.hAlpha` (via `MvPolynomial.MLE`) | represented, **concrete** | Multilinearity sorry-free; coefficient content `hAlphaEvals` = the `α`-evaluated per-row lift defect (`hAlphaEvals_rowPoint`, axiom-clean). | | Figure-5 point checks | `ZeroCheck.relZeroCheck` / `relZeroCheckE` | deliberately repaired | Points are derived from scalar Kronecker seeds, not sampled uniformly as vectors; escape-threaded (`Set.withEscape K.esc`). | | Axis-cross counterexample | `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross` | proven | Formally refutes the identity-testing step used by the uniform-vector argument. | | Kronecker root-counting kernel | `LinearMvExtension.multilinear_eq_zero_of_kronecker_roots`, `ZeroCheck.arm_eq_zero_of_family` | proven, **axiom-clean** | `D ≥ 2^m` univariate roots + Kronecker injectivity; no `sorryAx`. | | Lemma-10 extraction (escape-threaded) | `ZeroCheck.buildWitnessE`, `buildWitnessE_mem_relBatchedE` | proof-sorry-free | Escape pass-through ∨ weak-binding collision ∨ common opening with both identities zero. | | Lemma-10 binding alternative | `LiftCom.escOfCollision` via `K.collision_mem` | integrated | Distinct short openings of the shared `t` become an escape `e ∈ K.esc` (Hachi weak binding). | -| Corrected Lemma 10 CWSS | `ZeroCheck.zeroCheck_coordinateWiseSpecialSound` | proof-sorry-free | `(ℓ, k) = (2, D)`; assembled by the generic `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness`. | +| Corrected Lemma 10 CWSS | `ZeroCheck.zeroCheck_coordinateWiseSpecialSound` | sorry-free, **axiom-clean** | `(ℓ, k) = (2, D)`; assembled by `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness`; `#print axioms` = `propext`/`Classical.choice`/`Quot.sound` only. | +| Link-5 un-batching pull-back | `ZeroCheck.mem_relLiftE_of_relBatchedE` (`batchPackage`) | **proven, axiom-clean** | `relBatchedE → relLiftE`; `H_α ≡ 0 ⇒` per-row eqs via `MLE_eq_zero_iff` + `hAlphaEvals_rowPoint`; arity pin `n ≤ 2 ^ m₁`; dead range hypotheses dropped. | | Link-5/link-6/link-7 composition | `batchPackage ▷ zeroCheckPackage ▷ sumcheckBridgePackage` (`openCore`) | **defined, compiles** | The seam relations match by `rfl`; the whole chain builds. | ## Uniform-vector challenge gap (why the repair is needed) @@ -66,13 +69,31 @@ change the relation so every differing opening is known short before invoking we ## Residual gaps (out of Lemma-10 scope) -- **F5 encoding.** `hZero`/`hAlpha` are genuine multilinear extensions, but their coefficient - functions — `wTable` (the Eq. (21) `Zq`-table with the `ρ`-digit decomposition) and - `hAlphaEvals` (the `M̃_α` contraction) — are still `sorry` (milestone F5). Consequently - `zeroCheck_coordinateWiseSpecialSound` is proof-`sorry`-free but not yet *axiom-clean*: its - `#print axioms` reports `sorryAx` transitively through those encoding defs. The Lemma-10 - argument itself is parametric in them; the standalone kernel `arm_eq_zero_of_family` is - axiom-clean. +- **F5 encoding — now concrete.** `hZero`/`hAlpha` are genuine multilinear extensions, and both + coefficient functions are now **concrete** (no longer `sorry`): + - `hAlphaEvals` = the `α`-evaluated per-row lift defect, row-encoded into the `m₁`-cube via + `rowPoint` (`hAlphaEvals_rowPoint`, axiom-clean); arity pin `n ≤ 2 ^ m₁`. + - `wTable` reads the committed `z`/`ρ` coefficients **directly** (decoding the `m₀`-cube to + `row := idx / d`, `col := idx % d`), so `H₀ ≡ 0` is a genuine (non-vacuous) shortness statement + on the committed data. (Re-decomposing to base-`b` digits would be vacuous — digits are always + in range by construction; the paper's gadget decomposition is the honest prover's pre-commit + step, not part of the range test.) + + Consequently `zeroCheck_coordinateWiseSpecialSound` is now **axiom-clean**: `#print axioms` reports + no `sorryAx` (only ambient `propext`/`Classical.choice`/`Quot.sound`; the `Classical.choice` is the + constructivity caveat below, from `buildWitnessE`'s branch selection). The standalone kernel + `arm_eq_zero_of_family` is axiom-clean. **Still F5 (out of Lemma-10 scope):** the range-side + *soundness* `H₀ ≡ 0 ⇒ liftShort` (reconciling the single `rangeProduct b` with `liftShort`'s two + bounds `bound`/`ρBound`), and the sumcheck-summand stubs (`sumcheckPoly*`, `hypercubeSum`, + `sum_*`). +- **Link 5 (batching bridge).** The un-batching pull-back `mem_relLiftE_of_relBatchedE` + (`relBatchedE → relLiftE`, `ZeroCheck/Batch.lean`) is now **proof-`sorry`-free**: `H_α ≡ 0` gives + every `eq̃`-coefficient zero (`MvPolynomial.MLE_eq_zero_iff`), and `hAlphaEvals_rowPoint` + identifies the coefficient at row `i` with `relLift`'s per-row `α`-equation; `K.com`/`liftShort`/ + bound are carried verbatim. It needs only the arity pin `n ≤ 2 ^ m₁`; the range-side hypotheses + `2b ≤ q+1`, `b-1 ≤ bound` were **removed as unused** (shortness is carried directly, resolution + option 2). Its residual `sorryAx` is inherited solely from the `wTable`/`H₀` conjunct in the + relation's *type*, never from the proof term. - **Constructivity.** `buildWitnessE` (and the generic `treeExtractor`) select per-branch witnesses with classical choice. A constructive extractor would need witness-bearing trees or a decidable enumeration interface. From 34f7318c7851e59b34eaeed64ca6eafb7e057f21 Mon Sep 17 00:00:00 2001 From: ErVinuelas Date: Fri, 17 Jul 2026 18:07:03 +0200 Subject: [PATCH 19/21] variable renaming and documentation --- .../Functional/Hachi/Composition.lean | 34 +- .../Hachi/Recursion/PartialEval.lean | 22 +- .../Hachi/Recursion/TraceHandoff.lean | 12 +- .../Hachi/Recursion/ZBatchBridge.lean | 26 +- .../Hachi/RingSwitch/Reduction.lean | 68 ++-- .../Functional/Hachi/Sumcheck/Bridge.lean | 20 +- .../Functional/Hachi/Sumcheck/FinalEval.lean | 22 +- .../Functional/Hachi/Sumcheck/Rounds.lean | 52 +-- .../Functional/Hachi/ZeroCheck.lean | 68 ++-- .../Functional/Hachi/ZeroCheck/Batch.lean | 106 +++--- .../Hachi/ZeroCheck/Constraints.lean | 301 +++++++++--------- .../Functional/Hachi/ZeroCheck/Reduction.lean | 213 ++++++------- 12 files changed, 451 insertions(+), 493 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 3acd35cc15..c7978f2386 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -230,9 +230,9 @@ lift, the batching bridge, the (corrected-Lemma-10) zero-check, and the sumcheck 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) + (hq5 : q % 8 = 5) {b ω γ rBound m₀ m₁ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ rBound)) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) : CWSSPackage init impl @@ -249,10 +249,10 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT (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 hn ▷ - zeroCheckPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b ▷ - sumcheckBridgePackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b + liftPackage 𝓜(q, α) γ rBound K φF init impl hd ▷ + batchPackage 𝓜(q, α) m₀ m₁ γ rBound init impl K φF b hn ▷ + zeroCheckPackage 𝓜(q, α) m₀ m₁ γ rBound init impl K φF b ▷ + sumcheckBridgePackage 𝓜(q, α) m₀ m₁ γ rBound 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 @@ -268,9 +268,9 @@ links are finished, skeleton-sorried, or gap-flagged) is inventoried in the modu 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) + (hq5 : q % 8 = 5) {b ω γ rBound m₁ mLow κ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ rBound)) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) @@ -301,14 +301,14 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := i₁) (h₂ := instSampleableTypeChallengePSpecFinalEval) (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hn).toGuarded.append - (roundsChain 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF (mLow + κ)) - (roundsChain_relIn 𝓜(q, α) (mLow + κ) m₁ γ ρBound b init impl K φF + (roundsChain 𝓜(q, α) (mLow + κ) m₁ γ rBound b init impl K φF (mLow + κ)) + (roundsChain_relIn 𝓜(q, α) (mLow + κ) m₁ γ rBound 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' + (finalEvalPackage 𝓜(q, α) (mLow + κ) m₁ γ rBound b init impl K φF) + (roundsChain_relOut 𝓜(q, α) (mLow + κ) m₁ γ rBound b init impl K φF (mLow + κ))) ▷ᵍ + (partialEvalPackage 𝓜(q, α) mLow κ γ rBound b init impl K φF).toGuarded ▷ᵍ + (zBatchPackage 𝓜(q, α) mLow κ γ rBound init impl zpow K φF).toGuarded ▷ᵍ + handoffPackage 𝓜(q, α) Φ' mLow κ γ rBound init impl zpow K φF pp' reinterpretCom base' βSq' γ' κ' /-- **Hachi one-iteration opening — coordinate-wise special soundness (skeleton certificate).** @@ -318,9 +318,9 @@ just `openingChain.isCWSS`; its assumptions are exactly the sorried links invent 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) + (hq5 : q % 8 = 5) {b ω γ rBound m₁ mLow κ : ℕ} (hκ : (2 * ω) ^ 2 < q) (hτ : 0 < zDigits) [SampleableType (ShortChallenge 𝓜(q, α) ω)] - (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ ρBound)) + (K : LiftCom (LiftedWitness 𝓜(q, α) μ₀ n₀) E (liftShort 𝓜(q, α) γ rBound)) (φF : ZMod q →+* F) (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean index 2d03bce824..840e01ec0c 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/PartialEval.lean @@ -75,7 +75,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (mLow κ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable (mLow κ : ℕ) (bound rBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- The `i`-th true partial evaluation of the table (Eq. (24)): @@ -145,7 +145,7 @@ def partialEvalProver {TCom : Type} `t` and *every* partial evaluation in the derived family is well-formed. This seam is the sound stopping point of the §4.5 peeling; collapsing it into the single `Z`-packed claim is the `Recursion/ZBatchBridge.lean` step (⚠ see there). -/ -def relPartialEval (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relPartialEval (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (PartialEvalStatement K.TCom F mLow κ × LiftedWitness Φ μ n) := {p | @@ -153,10 +153,10 @@ def relPartialEval (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρ ∀ 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)) +def relPartialEvalE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (PartialEvalStatement K.TCom F mLow κ × (LiftedWitness Φ μ n ⊕ E)) := - (relPartialEval Φ mLow κ bound ρBound K φF).withEscape K.esc + (relPartialEval Φ mLow κ bound rBound K φF).withEscape K.esc variable [SampleableType F] @@ -170,20 +170,20 @@ statement, the mle splitting identity `wTableMleEval_split` plus the derivation membership; escapes pass through. -/ theorem partialEval_coordinateWiseSpecialSound (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (relWEvalClaimE Φ (mLow + κ) bound rBound b K φF) + (relPartialEvalE Φ mLow κ bound rBound 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : CWSSPackage init impl (WEvalStatement K.TCom F (mLow + κ)) (LiftedWitness Φ μ n ⊕ E) @@ -191,13 +191,13 @@ def partialEvalPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ P (pSpecPartialEval F κ) where verifier := partialEvalVerifier (oSpec := oSpec) mLow κ (TCom := K.TCom) (F := F) struct := CWSSStructure.ofIsEmpty - relIn := relWEvalClaimE Φ (mLow + κ) bound ρBound b K φF - relOut := relPartialEvalE Φ mLow κ bound ρBound K φF + relIn := relWEvalClaimE Φ (mLow + κ) bound rBound b K φF + relOut := relPartialEvalE Φ mLow κ bound rBound 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 + isCWSS := partialEval_coordinateWiseSpecialSound Φ mLow κ bound rBound b init impl K φF end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean index 629e01e159..4d724c556d 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/TraceHandoff.lean @@ -67,7 +67,7 @@ variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZM (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (mLow κ : ℕ) (bound ρBound : ℕ) +variable (mLow κ : ℕ) (bound rBound : ℕ) variable {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} @@ -170,7 +170,7 @@ instantiation obligation. -/ theorem handoff_coordinateWiseSpecialSound (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') @@ -179,7 +179,7 @@ theorem handoff_coordinateWiseSpecialSound (handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' reinterpretCom).coordinateWiseSpecialSound init impl CWSSStructure.ofIsEmpty - (relHatEvalE Φ mLow κ bound ρBound zpow K φF) + (relHatEvalE Φ mLow κ bound rBound zpow K φF) (relInE Φ' base' βSq' γ' κ' K.esc) := by sorry @@ -191,7 +191,7 @@ claim `relHatEvalE` to the **next iteration's** escape-threaded `QuadEval` input 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (pp' : Hachi.PublicParamsD Φ' innerRows' (2 ^ m') messageDigits' outerRows' (2 ^ r') innerDigits' dRows') @@ -205,10 +205,10 @@ def handoffPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbC (pSpecHandoff Φ') where verifier := handoffVerifier (oSpec := oSpec) Φ' mLow φF pp' reinterpretCom struct := CWSSStructure.ofIsEmpty - relIn := relHatEvalE Φ mLow κ bound ρBound zpow K φF + relIn := relHatEvalE Φ mLow κ bound rBound 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' + isCWSS := handoff_coordinateWiseSpecialSound Φ Φ' mLow κ bound rBound init impl zpow K φF pp' reinterpretCom base' βSq' γ' κ' end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean index 51a3569c2d..4d9d8643d6 100644 --- a/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/Recursion/ZBatchBridge.lean @@ -68,7 +68,7 @@ section Bridge variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (mLow κ : ℕ) (bound ρBound : ℕ) +variable (mLow κ : ℕ) (bound rBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- The `Z`-packed table evaluation (Hachi Eqs. (25)–(26) left-hand side): @@ -83,7 +83,7 @@ def hatEval (φF : ZMod q →+* F) (zpow : Fin (2 ^ κ) → F) (w : LiftedWitnes evaluates to the packed public value at the low point half. This is the claim the trace handoff (`Recursion/TraceHandoff.lean`) converts into the next iteration's `Rq`-statement. -/ def relHatEval (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (HatEvalStatement K.TCom F mLow × LiftedWitness Φ μ n) := {p | @@ -92,10 +92,10 @@ def relHatEval (zpow : Fin (2 ^ κ) → F) /-- Escape-threaded `Z`-packed claim relation. -/ def relHatEvalE (zpow : Fin (2 ^ κ) → F) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (HatEvalStatement K.TCom F mLow × (LiftedWitness Φ μ n ⊕ E)) := - (relHatEval Φ mLow κ bound ρBound zpow K φF).withEscape K.esc + (relHatEval Φ mLow κ bound rBound 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). -/ @@ -111,11 +111,11 @@ the module docstring for the explicit `κ = 1` cheat). The sorry is kept — del 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (h : (toHatEvalStatement mLow κ zpow s, w) ∈ relHatEvalE Φ mLow κ bound rBound zpow K φF) : + (s, w) ∈ relPartialEvalE Φ mLow κ bound rBound K φF := by sorry /-- **The `Z`-packing bridge as a `CWSSPackage`** (Hachi §4.5, Eqs. (25)–(26)): zero-round @@ -124,7 +124,7 @@ theorem mem_relPartialEvalE_of_relHatEvalE (zpow : Fin (2 ^ κ) → F) 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : CWSSPackage init impl (PartialEvalStatement K.TCom F mLow κ) (LiftedWitness Φ μ n ⊕ E) @@ -132,14 +132,14 @@ def zBatchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbCo (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec (toHatEvalStatement mLow κ zpow) struct := CWSSStructure.ofIsEmpty - relIn := relPartialEvalE Φ mLow κ bound ρBound K φF - relOut := relHatEvalE Φ mLow κ bound ρBound zpow K φF + relIn := relPartialEvalE Φ mLow κ bound rBound K φF + relOut := relHatEvalE Φ mLow κ bound rBound 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) + (relIn := relPartialEvalE Φ mLow κ bound rBound K φF) + (relOut := relHatEvalE Φ mLow κ bound rBound zpow K φF) (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (mem_relPartialEvalE_of_relHatEvalE Φ mLow κ bound ρBound zpow K φF) + (mem_relPartialEvalE_of_relHatEvalE Φ mLow κ bound rBound zpow K φF) end Bridge diff --git a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean index 6db2a68ea0..9610654245 100644 --- a/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/RingSwitch/Reduction.lean @@ -11,15 +11,15 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRoun 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 + quotient polynomials `rᵢ ∈ Zq[X]` of degree `≤ d − 2` with - `∑ⱼ Mᵢⱼ(X)·zⱼ(X) = yᵢ(X) + (X^d + 1)·ρᵢ(X)` in `Zq[X]`, for every row `i`. + `∑ⱼ Mᵢⱼ(X)·zⱼ(X) = yᵢ(X) + (X^d + 1)·rᵢ(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`). + witness* `w̃` — the `R^lin` witness `z` together with the quotients `r` (Hachi Eq. (21); the + gadget digits of `r` 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 @@ -31,7 +31,7 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRoun 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 + polynomial `∑ⱼ Mᵢⱼ·zⱼ − yᵢ − (X^d+1)·rᵢ` 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 @@ -42,8 +42,8 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ScalarRoun 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) + instantiates `Short := liftShort bound rBound` at the *global* norm parameters. `relLift` + therefore carries (i) `liftShort bound rBound 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 @@ -72,30 +72,30 @@ variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZM 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. -/ +with the per-row quotient polynomials `rᵢ` of the `Zq[X]`-lift, with their structural degree +bound `deg rᵢ ≤ 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 + /-- The per-row quotient polynomials `rᵢ ∈ Zq[X]`. -/ + r : Fin n → Polynomial (ZMod q) + /-- Structural degree bound: `deg rᵢ ≤ d − 2` (from `deg (∑ Mᵢⱼzⱼ − yᵢ) ≤ 2d − 2`). -/ + hr : ∀ i, (r 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 +/-- Coefficient-range predicate on the quotient polynomials (the `r`-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 +def rShort (rBound : ℕ) (r : Fin n → Polynomial (ZMod q)) : Prop := + ∀ i k, ((r i).coeff k).valMinAbs.natAbs ≤ rBound /-- 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.ρ +def liftShort (bound rBound : ℕ) (w : LiftedWitness Φ μ n) : Prop := + vecLInftyNorm Φ w.z ≤ bound ∧ rShort rBound w.r /-- **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). @@ -116,7 +116,7 @@ structure LiftCom (W E : Type) (Short : W → Prop) where 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 : ℕ) +variable {F : Type} [Field F] (bound rBound : ℕ) /-- 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`). -/ @@ -136,34 +136,34 @@ noncomputable def evalAt (φF : ZMod q →+* F) (a : F) : Polynomial (ZMod q) 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 +challenge `α` of the transcript): `w̃ = (z, r)` opens `t`; every lifted row vanishes at `α`, +i.e. `∑ⱼ Mᵢⱼ(α)·zⱼ(α) = yᵢ(α) + (α^d + 1)·rᵢ(α)`; 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)) +def relLift (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 ∧ + evalAt φF p.1.2.2 Φ.φ.toPoly * evalAt φF p.1.2.2 (p.2.r i)) ∧ + liftShort Φ bound rBound 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)) +def relLiftE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (LiftStatement Φ K.TCom F n μ × (LiftedWitness Φ μ n ⊕ E)) := - (relLift Φ bound ρBound K φF).withEscape K.esc + (relLift Φ bound rBound K φF).withEscape K.esc section Protocol variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} -variable (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +variable (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) /-- The lift's verifier (Hachi Figure 4): a **pure pass-through** extending the statement by the @@ -176,7 +176,7 @@ def liftVerifier : /-- 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ᵢ) /ₘ φ` +witness is `w̃` itself. The honest computations (quotient extraction `rᵢ := (∑ 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) @@ -207,7 +207,7 @@ variable [SampleableType F] * 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) + `rowSum − yᵢ.rep − φ·rᵢ` (degree `≤ 2d − 2 < 2d` by `w̃.hr` 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 — @@ -219,10 +219,10 @@ is the tree's obligation; only knowledge-error accounting, out of scope, needs ` theorem lift_coordinateWiseSpecialSound (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) (hd : 0 < Φ.φ.natDegree) : - (liftVerifier (oSpec := oSpec) Φ bound ρBound K).coordinateWiseSpecialSound init impl + (liftVerifier (oSpec := oSpec) Φ bound rBound K).coordinateWiseSpecialSound init impl (scalarStructure (2 * Φ.φ.natDegree) (by omega)) (relRlinE Φ (n := n) (μ := μ) K.esc) - (relLiftE Φ bound ρBound K φF) := by + (relLiftE Φ bound rBound K φF) := by sorry /-- **The HMZ25 lift as a `CWSSPackage`** (Hachi [NOZ26] Figure 4 / Lemma 9): the two-round @@ -234,12 +234,12 @@ def liftPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp (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 + verifier := liftVerifier (oSpec := oSpec) Φ bound rBound K struct := scalarStructure (2 * Φ.φ.natDegree) (by omega) relIn := relRlinE Φ (n := n) (μ := μ) K.esc - relOut := relLiftE Φ bound ρBound K φF + relOut := relLiftE Φ bound rBound 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 + isCWSS := lift_coordinateWiseSpecialSound Φ bound rBound K φF init impl hd end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean index dfe2b52cf6..3f9dcce37e 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Bridge.lean @@ -38,7 +38,7 @@ open OracleComp OracleSpec ProtocolSpec CoordinateWise variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) +variable (m₀ m₁ : ℕ) (bound rBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- The bridge's statement map: install the empty challenge prefix and the initial target pair @@ -55,11 +55,11 @@ 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (h : (toRoundStatement Φ m₁ φF s, w) ∈ roundRelE Φ m₀ m₁ bound rBound K φF b 0) : + (s, w) ∈ relZeroCheckE Φ m₀ m₁ bound rBound K φF b := by sorry /-- **The sumcheck bridge as a `CWSSPackage`**: zero-round `ReduceClaim` at @@ -67,7 +67,7 @@ theorem mem_relZeroCheckE_of_roundRelE with no soundness error. -/ noncomputable def sumcheckBridgePackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) : CWSSPackage init impl (ZeroCheckStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) @@ -75,13 +75,13 @@ noncomputable def sumcheckBridgePackage (init : ProbComp σ) (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec (toRoundStatement Φ m₁ φF) struct := CWSSStructure.ofIsEmpty - relIn := relZeroCheckE Φ m₀ m₁ bound ρBound K φF b - relOut := roundRelE Φ m₀ m₁ bound ρBound K φF b 0 + relIn := relZeroCheckE Φ m₀ m₁ bound rBound K φF b + relOut := roundRelE Φ m₀ m₁ bound rBound 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) + (relIn := relZeroCheckE Φ m₀ m₁ bound rBound K φF b) + (relOut := roundRelE Φ m₀ m₁ bound rBound K φF b 0) (mapWitInv := fun _ w => w) (D := CWSSStructure.ofIsEmpty) - (mem_relZeroCheckE_of_roundRelE Φ m₀ m₁ bound ρBound K φF b) + (mem_relZeroCheckE_of_roundRelE Φ m₀ m₁ bound rBound 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 index b1b41506ec..d14b99d3b9 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/FinalEval.lean @@ -70,7 +70,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable (m₀ m₁ : ℕ) (bound rBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- The final check ([NOZ26] Figure 7 tail): both final sumcheck targets against the public @@ -121,7 +121,7 @@ def finalEvalProver {TCom : Type} /-- **The evaluation-claim relation** — the §4.3 chain's final seam and the recursion's input: `w̃` opens `t` and its table's multilinear extension evaluates to the claimed value at the point. -/ -def relWEvalClaim (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +def relWEvalClaim (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (WEvalStatement K.TCom F m₀ × LiftedWitness Φ μ n) := {p | @@ -129,10 +129,10 @@ def relWEvalClaim (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρB 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)) +def relWEvalClaimE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : Set (WEvalStatement K.TCom F m₀ × (LiftedWitness Φ μ n ⊕ E)) := - (relWEvalClaim Φ m₀ bound ρBound b K φF).withEscape K.esc + (relWEvalClaim Φ m₀ bound rBound b K φF).withEscape K.esc variable [SampleableType F] @@ -147,20 +147,20 @@ recover `roundRel m₀`'s point claims (the round-`m₀` `hypercubeSum` is the p 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundRelE Φ m₀ m₁ bound rBound K φF b m₀) + (relWEvalClaimE Φ m₀ bound rBound 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) : GCWSSPackage init impl (RoundStatement Φ K.TCom F n μ m₀) (LiftedWitness Φ μ n ⊕ E) @@ -168,10 +168,10 @@ def finalEvalPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ Pro (pSpecFinalEval F) where verifier := finalEvalVerifier (oSpec := oSpec) Φ m₀ m₁ bound b (TCom := K.TCom) φF struct := CWSSStructure.ofIsEmpty - relIn := roundRelE Φ m₀ m₁ bound ρBound K φF b m₀ - relOut := relWEvalClaimE Φ m₀ bound ρBound b K φF + relIn := roundRelE Φ m₀ m₁ bound rBound K φF b m₀ + relOut := relWEvalClaimE Φ m₀ bound rBound b K φF isGuarded := finalEvalVerifier_isGuarded Φ m₀ m₁ bound b φF - isCWSS := finalEval_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound b init impl K φF + isCWSS := finalEval_coordinateWiseSpecialSound Φ m₀ m₁ bound rBound b init impl K φF end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean index cc181915c8..a1cbb54271 100644 --- a/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean +++ b/ArkLib/Commitments/Functional/Hachi/Sumcheck/Rounds.lean @@ -89,7 +89,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] [DecidableEq F] -variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) (b : ℕ) +variable (m₀ m₁ : ℕ) (bound rBound : ℕ) (b : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} /-- The round check ([NOZ26] Figure 6): both round polynomials sum to the current targets over @@ -171,15 +171,15 @@ engine applied to `Structured.roundOracleVerifier`, with the round relations rea 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundRelE Φ m₀ m₁ bound rBound K φF b i) + (roundRelE Φ m₀ m₁ bound rBound K φF b (i + 1)) := by sorry /-- The `i`-th paired sumcheck round as a **guarded** package (`GCWSSPackage`): the guarded @@ -187,7 +187,7 @@ round verifier with the `k = max (2b) 2 + 1` plain-special-soundness structure, 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (i : ℕ) : GCWSSPackage init impl (RoundStatement Φ K.TCom F n μ i) (LiftedWitness Φ μ n ⊕ E) @@ -197,10 +197,10 @@ def roundPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbCom 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) + relIn := roundRelE Φ m₀ m₁ bound rBound K φF b i + relOut := roundRelE Φ m₀ m₁ bound rBound K φF b (i + 1) isGuarded := roundVerifier_isGuarded Φ b i - isCWSS := round_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound b init impl K φF i + isCWSS := round_coordinateWiseSpecialSound Φ m₀ m₁ bound rBound 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⟩ @@ -212,62 +212,62 @@ are the round-`0`/round-`count` seam relations — the recursion's seams are def *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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 } + P.relIn = roundRelE Φ m₀ m₁ bound rBound K φF b 0 ∧ + P.relOut = roundRelE Φ m₀ m₁ bound rBound 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 + relIn := roundRelE Φ m₀ m₁ bound rBound K φF b 0 + relOut := roundRelE Φ m₀ m₁ bound rBound 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) + (relIn := roundRelE Φ m₀ m₁ bound rBound K φF b 0) + (relOut := roundRelE Φ m₀ m₁ bound rBound 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.1.append (roundPackage Φ m₀ m₁ bound rBound 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)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundsChainAux Φ m₀ m₁ bound rBound b init impl K φF count).1 /-- The loop's input seam is the round-`0` relation (the seam pin for composing after the sumcheck bridge). -/ theorem roundsChain_relIn (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundsChain Φ m₀ m₁ bound rBound b init impl K φF count).relIn = + roundRelE Φ m₀ m₁ bound rBound K φF b 0 := + (roundsChainAux Φ m₀ m₁ bound rBound b init impl K φF count).2.1 /-- The loop's output seam is the round-`count` relation (the seam pin for composing with the final-evaluation step). -/ theorem roundsChain_relOut (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundsChain Φ m₀ m₁ bound rBound b init impl K φF count).relOut = + roundRelE Φ m₀ m₁ bound rBound K φF b count := + (roundsChainAux Φ m₀ m₁ bound rBound b init impl K φF count).2.2 end Protocol diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean index 96f3471863..9c77823358 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck.lean @@ -6,55 +6,45 @@ Authors: Tobias Rothmann, Pablo Martín Vinuelas import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Reduction /-! -# Hachi Zero-Check (Figure 5 / corrected Lemma 10) +# Hachi Zero-Check (Figure 5 / 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. +`eq̃`-batched range constraints and `α`-evaluated linear constraints — to evaluations at derived +points. -## ⚠ The paper's Lemma 10 is repaired here +## Deviation from the paper's Lemma 10 -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. The formal counterexample to the paper's -argument is `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`; the specification boundary -is recorded in `docs/kb/audits/noz26-zero-check-lemma10.md`. +The paper's Lemma 10 argues extraction from uniform vector challenges, but a coordinate-wise +family of accepting transcripts only certifies that `H` vanishes on an axis cross, which for +`m ≥ 2` does not imply `H ≡ 0`. `ZeroCheck/Reduction.lean` instead draws two scalar seeds +`(ρ₀, ρ_α)` and derives the evaluation points along the Kronecker curves +`κ_m(ρ) = (ρ, ρ², ρ⁴, …)`, where root counting determines a multilinear polynomial. The +counterexample to the uniform-challenge argument is +`LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`, and the deviation is recorded in +`docs/kb/audits/noz26-zero-check-lemma10.md`. ## Folder structure -* `ZeroCheck/Constraints.lean` — the **shared constraint encoding** (Eqs. (21)–(23)): the table - `w̃`, the batched polynomials `H₀`/`H_α` (genuine `MvPolynomial.MLE`s, so their multilinearity - is `sorry`-free), the sumcheck polynomials `F_{0,τ₀}`/`F_{α,τ₁}` with their degree pins, the - Kronecker point `kroneckerPoint`, `hypercubeSum`, and `roundRel`/`roundRelE`. Consumed by both - this zero-check *and* the sumcheck round loop (`Sumcheck/`), so it sits at the shared base of the - batched-sumcheck machinery. The `M̃_α` contraction `hAlphaEvals` is now **concrete** — the - `α`-evaluated per-row lift defect (row-encoded into the `m₁`-cube), so `H_α` is a genuine - batched polynomial with meaningful coefficients (`hAlphaEvals_rowPoint`, axiom-clean). The table - entry function `wTable` is now **concrete** too (it reads the committed `z`/`ρ` coefficients - directly, so `H₀` non-vacuously tests shortness of the committed data). Only the range-side - soundness (`H₀ ≡ 0 ⇒ liftShort`) and the sumcheck-polynomial stubs remain F5. -* `ZeroCheck/Batch.lean` — the zero-round **batching bridge** (entry head): reinterprets the lift's - per-row residual claims as the two `MvPolynomial` identities `H₀ ≡ 0 ∧ H_α ≡ 0` - (`relBatched`/`relBatchedE`, Eqs. (22)–(23)); the un-batching pull-back - `mem_relLiftE_of_relBatchedE` (`relBatchedE → relLiftE`, arity pin `n ≤ 2 ^ m₁`) is - **proof-`sorry`-free** (via `MLE_eq_zero_iff` + `hAlphaEvals_rowPoint`; the residual `sorryAx` is - only the `wTable`/`H₀` conjunct in its statement type). -* `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. The CWSS theorem `zeroCheck_coordinateWiseSpecialSound` (`relBatchedE - → relZeroCheckE`, `k = D = zeroCheckD m₀ m₁`) is **`sorry`-free and now axiom-clean** — its - escape/collision/root-counting extraction is complete, and with the encodings `hAlphaEvals`/ - `wTable` now concrete, `#print axioms` reports no `sorryAx` (only the ambient - `propext`/`Classical.choice`/`Quot.sound`; the `Classical.choice` is from the classical-choice - branch selection in `buildWitnessE`, the documented constructivity caveat). The Kronecker kernel - `arm_eq_zero_of_family` is likewise axiom-clean. +* `ZeroCheck/Constraints.lean` — the constraint encoding (Eqs. (21)–(23)): the table `w̃`, the + batched polynomials `H₀`/`H_α` (as `MvPolynomial.MLE`s), the sumcheck summands + `F_{0,τ₀}`/`F_{α,τ₁}` with their per-variable degrees, `kroneckerPoint`, `hypercubeSum`, and the + per-round relation `roundRel`/`roundRelE`. Shared between this zero-check and the sumcheck rounds + (`Sumcheck/`). +* `ZeroCheck/Batch.lean` — the zero-round batching bridge: reinterprets the lift's per-row claims + as the two identities `H₀ ≡ 0 ∧ H_α ≡ 0` (`relBatched`/`relBatchedE`, Eqs. (22)–(23)). The + pull-back `mem_relLiftE_of_relBatchedE` (`relBatchedE → relLiftE`) recovers the per-row equation + via `MLE_eq_zero_iff` and `hAlphaEvals_rowPoint`, under the arity bound `n ≤ 2 ^ m₁`. +* `ZeroCheck/Reduction.lean` — Hachi Figure 5 / Lemma 10: one challenge round carrying the seed + pair `(ρ₀, ρ_α) ∈ F²`, reducing the identities to point evaluations at the derived Kronecker + points. The coordinate-wise special soundness theorem + `zeroCheck_coordinateWiseSpecialSound` reduces `relBatchedE` to `relZeroCheckE` with + `k = D = zeroCheckD m₀ m₁`, its extraction handling escapes, weak-binding collisions, and + Kronecker root counting (`arm_eq_zero_of_family`). 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` (`openCore`, row 6). +Its output relation `relZeroCheckE` is the input of the sumcheck bridge in `Sumcheck/`, and the +chain is composed in `Composition.lean`. ## References diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean index 8ce9ba8cd3..22162dc935 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Batch.lean @@ -6,30 +6,25 @@ Authors: Tobias Rothmann, Pablo Martín Vinuelas import ArkLib.Commitments.Functional.Hachi.ZeroCheck.Constraints /-! - # Batching bridge — Hachi Eqs. (22)–(23) — escape-threaded (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`), and `w̃` short. - - 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. The `liftShort` conjunct (resolution option 2) is preserved verbatim - from `relLift`; it is the precondition of the weak-binding escape (`LiftCom.collision_mem`) - invoked by the zero-check's extraction. - - * **extraction direction** (the pull-back `mem_relLiftE_of_relBatchedE`, **axiom-clean**): - the only nontrivial conjunct is the per-row equation, recovered from `H_α ≡ 0` by non-degeneracy - of the `eq̃` basis (`MvPolynomial.MLE_eq_zero_iff`) followed by the row-encoding faithfulness - lemma `hAlphaEvals_rowPoint`. The `K.com`, `liftShort` and bound-sanity conjuncts are carried - **verbatim** by `relBatched`; in particular the *range* identity `H₀ ≡ 0` and the norm-parameter - hypotheses `2b ≤ q+1`, `b−1 ≤ bound` play **no role** in this pull-back (shortness is asserted - directly), so they are not assumed. The only genuine hypothesis is the row-encoding arity pin - `hn : n ≤ 2 ^ m₁`. (With `hAlphaEvals`/`wTable` now concrete, no `sorryAx` remains.) + # Batching bridge — Hachi Eqs. (22)–(23) + + A zero-round reduction between two readings of the lift's claims: + + * `relLiftE` — `w̃` opens `t`, the per-row `α`-evaluated constraints hold, `w̃` is short + (`RingSwitch/Reduction.lean`); + * `relBatchedE` — `w̃` opens `t`, the batched polynomials `H₀^{w̃}` and `H_α^{w̃}` are identically + zero (Eqs. (22)–(23), `ZeroCheck/Constraints.lean`), and `w̃` is short. + + The statement and witness are unchanged (`ReduceClaim` at `mapStmt := id`); only the reading of + the claims changes, which separates the batching algebra from the Kronecker root counting of the + zero-check. The `liftShort` conjunct is carried through unchanged, since it is the shortness + precondition of the commitment's weak binding (`LiftCom.collision_mem`) used later by the + zero-check's extractor. + + The reduction's content is the pull-back `mem_relLiftE_of_relBatchedE` from `relBatchedE` to + `relLiftE`: the per-row equation is recovered from `H_α ≡ 0` via `MvPolynomial.MLE_eq_zero_iff` + and `hAlphaEvals_rowPoint`, and the remaining conjuncts are carried over directly. Its only + hypothesis is the row-encoding arity bound `n ≤ 2 ^ m₁`. ## References @@ -45,47 +40,44 @@ 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 (m₀ m₁ : ℕ) (bound rBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} -/-- **The batched relation** (Hachi Eqs. (22)–(23) as polynomial identities): `w̃` opens `t`, is -short (`liftShort`), 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)) +/-- The batched relation (Hachi Eqs. (22)–(23) as polynomial identities): `w̃` opens `t`, is short +(`liftShort`), the range polynomial `H₀^{w̃}` and the linear-constraint polynomial `H_α^{w̃}` are +both identically zero, and `bound ≤ rlin.bound`. This is the zero-check's input relation. -/ +def relBatched (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) : Set (LiftStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.2.1 ∧ - liftShort Φ bound ρBound p.2 ∧ + liftShort Φ bound rBound p.2 ∧ 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)) +/-- `relBatched` extended with the escape branch (`.inr e` requires `e ∈ K.esc`); the zero-check's +input relation. -/ +def relBatchedE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (relBatched Φ m₀ m₁ bound rBound K φF b).withEscape K.esc omit [NeZero q] [IsCyclotomic Φ] in -/-- **Un-batching pull-back** (the bridge's `hRel`): the batched identities imply the lift's -per-row claims. Escapes pass through. - -The only nontrivial conjunct is the per-row equation, recovered from `H_α ≡ 0`: by -`MvPolynomial.MLE_eq_zero_iff` every Boolean-point coefficient `hAlphaEvals` vanishes, and by -`hAlphaEvals_rowPoint` the coefficient at `rowPoint i` is exactly row `i`'s `α`-evaluated lift -defect, so the row equation of `relLift` follows. The `K.com`, `liftShort` and bound-sanity -conjuncts are carried verbatim by `relBatched` — in particular the *range* identity `H₀ ≡ 0` and -the norm-parameter hypotheses `2b ≤ q+1`, `b-1 ≤ bound` play **no role** in this pull-back -(shortness is already asserted directly), so they are not assumed. The arity pin `hn : n ≤ 2 ^ m₁` -is the batching cube's row-encoding requirement. -/ +/-- The batched identities imply the lift's per-row claims; escapes pass through. + +The per-row equation is recovered from `H_α ≡ 0`: by `MvPolynomial.MLE_eq_zero_iff` every +Boolean-point coefficient `hAlphaEvals` vanishes, and by `hAlphaEvals_rowPoint` the coefficient at +`rowPoint i` is row `i`'s `α`-evaluated lift defect, giving the row equation of `relLift`. The +`K.com`, `liftShort`, and bound conjuncts are shared between the two relations. The range identity +`H₀ ≡ 0` is not needed here, since shortness is asserted directly. The hypothesis `hn : n ≤ 2 ^ m₁` +is the row-encoding bound of the batching cube. -/ theorem mem_relLiftE_of_relBatchedE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) (hn : n ≤ 2 ^ m₁) (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 + (h : (X, w) ∈ relBatchedE Φ m₀ m₁ bound rBound K φF b) : + (X, w) ∈ relLiftE Φ bound rBound K φF := by rcases w with w | e · -- real witness: only the per-row equation needs work simp only [relBatchedE, Set.mem_withEscape_inl, relBatched, Set.mem_setOf_eq] at h @@ -100,11 +92,11 @@ theorem mem_relLiftE_of_relBatchedE · -- escape: statement-independent pass-through simpa only [relBatchedE, relLiftE, Set.mem_withEscape_inr] using h -/-- **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). -/ +/-- The batching bridge packaged as a `CWSSPackage`: a zero-round `ReduceClaim` at `mapStmt := id` +reducing `relLiftE` to `relBatchedE` with no soundness error, its correctness supplied by +`mem_relLiftE_of_relBatchedE`. -/ def batchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) (hn : n ≤ 2 ^ m₁) : CWSSPackage init impl (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) @@ -112,14 +104,14 @@ def batchPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbCom (!p[] : ProtocolSpec 0) where verifier := ReduceClaim.verifier oSpec id struct := CWSSStructure.ofIsEmpty - relIn := relLiftE Φ bound ρBound K φF - relOut := relBatchedE Φ m₀ m₁ bound ρBound K φF b + relIn := relLiftE Φ bound rBound K φF + relOut := relBatchedE Φ m₀ m₁ bound rBound 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) + (relIn := relLiftE Φ bound rBound K φF) + (relOut := relBatchedE Φ m₀ m₁ bound rBound 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 hn stmtIn witOut h) + mem_relLiftE_of_relBatchedE Φ m₀ m₁ bound rBound K φF b hn stmtIn witOut h) end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean index c0222670cd..1b8059aacd 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Constraints.lean @@ -8,57 +8,49 @@ import ArkLib.Data.MvPolynomial.LinearMvExtension import ArkLib.Data.MvPolynomial.Multilinear /-! - # Constraint encoding — Hachi Eqs. (21)–(23) — escape-threaded (sumcheck-track milestone F5/F6) + # Constraint encoding — Hachi Eqs. (21)–(23) - Definitions only (no protocol): the shared constraint-encoding layer consumed by the batching - bridge (`ZeroCheck/Batch.lean`), the zero-check round (`ZeroCheck/Reduction.lean`), the sumcheck - rounds, and the final-evaluation step. All declarations live in the §4.3 chain's namespace - `ArkLib.Lattices.Ajtai.InnerOuter`, over the **concrete** lifted witness `LiftedWitness Φ μ n` - and the abstract weak-binding commitment `LiftCom` of `RingSwitch/Reduction.lean`, so this layer - composes directly into the escape-threaded opening chain (`Composition.lean`). + The constraint-encoding layer of the Hachi §4.3 sumcheck: the table `w̃` (Eq. (21)), the two + batched constraint polynomials `H₀` and `H_α` (Eqs. (23) and (22)), the sumcheck summands + `F_{0,τ₀}` and `F_{α,τ₁}`, and the Kronecker challenge curve. These definitions are consumed by + the batching bridge (`ZeroCheck/Batch.lean`), the zero-check round (`ZeroCheck/Reduction.lean`), + the sumcheck rounds, and the final-evaluation step. Everything is stated over the lifted witness + `LiftedWitness Φ μ n` and the weak-binding commitment `LiftCom` of `RingSwitch/Reduction.lean`. ## The table `w̃` (Eq. (21)) - The committed lifted witness `(z, ρ)` is re-read as an `F`-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; `m₁` is the row-batching arity (`2 ^ m₁ ≥ n` rows of the lifted system, the arity pin - `hAlphaEvals`/`rowPoint` require). The `M̃_α` contraction `hAlphaEvals` is now **concrete** — the - `α`-evaluated per-row lift defect, row-encoded into the `m₁`-cube (`hAlphaEvals_rowPoint`, - axiom-clean) — so `H_α ≡ 0` genuinely characterizes "every lifted row vanishes at `α`" (consumed - by the batching bridge). The table entry function `wTable` is now **concrete** too — it reads the - committed `z`/`ρ` coefficients directly (decoding the `m₀`-cube to `row := idx / d`, - `col := idx % d`), so `H₀ ≡ 0` is a genuine (non-vacuous) shortness statement on the committed - data. Both `hZero`/`hAlpha` are built via the real multilinear extension `MvPolynomial.MLE`, so - their multilinearity (`hZero_degreeOf_le`/`hAlpha_degreeOf_le` — the hypothesis of the corrected - Lemma 10's Kronecker interpolation) is **`sorry`-free**, and the whole zero-check is now - **axiom-clean**. Matching `H₀ ≡ 0` to `liftShort`'s two bounds `(bound, ρBound)` (the range-side - soundness step) and the sumcheck-polynomial stubs remain F5. + `wTable` re-reads the committed pair `(z, r)` as an `F`-valued function on the `m₀`-cube: the + rows are the `Zq`-coefficient vectors of the witness entries `zⱼ ∈ Rq` followed by those of the + quotients `rᵢ`, and the columns are the `d` coefficient positions (`d = deg Φ.φ`). The arity + `m₀` satisfies `2 ^ m₀ ≥ (μ + n)·d`; `m₁` is the row-batching arity, with `2 ^ m₁ ≥ n`. ## 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). + * `hAlpha` (Eq. (22)): the `eq̃`-batched linear-constraint polynomial + `H_α(τ) = ∑ᵢ eq̃(τ, i)·(∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ) − yᵢ(α))`, multilinear in `m₁` + variables. `H_α ≡ 0` holds 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` holds iff every table entry lies in `[−(b−1), b−1]`, assuming + `2b − 1 < q` so that field roots correspond to centered integer representatives. - ## The sumcheck polynomials and degree pins (design R8) + Both are built as multilinear extensions (`MvPolynomial.MLE`), hence have degree `≤ 1` in each + variable. - `F_{0,τ₀}` 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_{α,τ₁}` has per-variable - degree `≤ 2`. Everything downstream is degree-parametric (`roundDegZero`/`roundDegAlpha`). + ## The sumcheck summands - ## The Kronecker point (Lemma 10 repair) + `F_{0,τ₀}` (`sumcheckPolyZero`) sums over the cube to `H₀(τ₀)` and has per-variable degree `2b`; + `F_{α,τ₁}` (`sumcheckPolyAlpha`) sums to `H_α(τ₁) + zcTargetAlpha` and has per-variable degree + `≤ 2`. These are the polynomials the sumcheck rounds operate on. - `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** - (`LinearMvExtension.powAlgHom_eq_zero_iff`), so univariate root counting is information-complete - — the engine of the corrected zero-check (`ZeroCheck/Reduction.lean`). Re-exported here as the - same map used by the downstream sumcheck files. + ## The Kronecker curve (Fig. 5) + + `kroneckerPoint m ρ = (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})`. A multilinear polynomial in `m` variables, + restricted to this curve, is univariate of degree `< 2^m`, and the restriction is injective on + multilinear polynomials, so such a polynomial is determined by its values along the curve. The + paper's Figure 5 samples the evaluation points `τ₀, τ₁` uniformly over `F^{m₀}` and `F^{m₁}`; + this formalization instead derives them from two scalar seeds `(ρ₀, ρ_α)` along this curve, so + the zero-check reduces `H₀ ≡ 0` and `H_α ≡ 0` to univariate root counting. ## References @@ -77,20 +69,20 @@ variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZM variable {n μ : ℕ} {E : Type} {F : Type} [Field F] variable (m₀ m₁ : ℕ) -/-! ## The Kronecker curve and the corrected soundness parameter -/ +/-! ## The Kronecker curve and the soundness parameter -/ -/-- **The Kronecker point** `κ_m(ρ) := (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})` — the corrected Lemma 10's -challenge-derivation curve. Definitionally the kernel map `LinearMvExtension.kroneckerPoint`, so -the zero-check's root-counting kernel applies verbatim; re-exported here as the name the -downstream sumcheck files use. -/ +/-- The Kronecker curve `κ_m(ρ) = (ρ, ρ², ρ⁴, …, ρ^{2^{m−1}})`, along which a multilinear +polynomial in `m` variables restricts to a univariate polynomial of degree `< 2^m`. A re-export +of `LinearMvExtension.kroneckerPoint` under the name used by the zero-check and sumcheck files. +The zero-check derives its evaluation points along this curve (see the module docstring). -/ @[reducible] def kroneckerPoint (m : ℕ) (ρ : F) : Fin m → F := LinearMvExtension.kroneckerPoint (m := m) ρ -/-- **The corrected Lemma 10 parameter** `D := max(2, 2^{m₀}, 2^{m₁})`: the maximum padded -constraint-table size, i.e. the univariate degree bound of the Kronecker pullbacks (plus the `2` -padding for degenerate zero-arity identities, meeting the CWSS convention `2 ≤ k`). The paper's -`max(2d, 2b-1)` conflates parameters of Lemmas 9 and 11; no value of it repairs the original -challenge encoding. -/ +/-- The number of distinct challenge seeds the zero-check needs per coordinate, +`D := max(2, 2^{m₀}, 2^{m₁})`. `2^{m₀}` and `2^{m₁}` are the degrees of the univariate Kronecker +restrictions of `H₀` and `H_α`, so that many roots determine each polynomial; the floor of `2` +meets the `2 ≤ k` requirement of the coordinate-wise special soundness structure. The paper's +Lemma 10 uses `max(2d, 2b−1)`. -/ def zeroCheckD (m₀ m₁ : ℕ) : ℕ := max 2 (max (2 ^ m₀) (2 ^ m₁)) theorem two_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ≤ zeroCheckD m₀ m₁ := le_max_left _ _ @@ -101,21 +93,21 @@ theorem two_pow_m₀_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ^ m₀ ≤ zeroCheckD m theorem two_pow_m₁_le_zeroCheckD (m₀ m₁ : ℕ) : 2 ^ m₁ ≤ zeroCheckD m₀ m₁ := (le_max_right _ _).trans (le_max_right _ _) -/-- Per-round univariate degree of the range sumcheck (`F_{0,τ₀}`): degree `2b` (pin R8). -/ +/-- Per-round univariate degree of the range sumcheck summand `F_{0,τ₀}`, namely `2b`. -/ def roundDegZero (b : ℕ) : ℕ := 2 * b -/-- Per-round univariate degree of the linear sumcheck (`F_{α,τ₁}`): degree `≤ 2` (pin R8). -/ +/-- Per-round univariate degree of the linear sumcheck summand `F_{α,τ₁}`, namely `2`. -/ def roundDegAlpha : ℕ := 2 -/-! ## The range factor and the table (now concrete; range-side soundness is F5) -/ +/-! ## The range factor and the table -/ -/-- Hachi Eq. (23)'s per-entry range factor `P_b(v) := v·∏_{j=1}^{b-1} (v - j)·(v + j)`: the +/-- Hachi Eq. (23)'s per-entry range factor `P_b(v) = v·∏_{j=1}^{b-1} (v - j)·(v + j)`, the vanishing polynomial of the symmetric range `{-(b-1), …, b-1}`. -/ def rangeProduct (b : ℕ) (v : F) : F := v * ∏ j ∈ Finset.Icc 1 (b - 1), ((v - (j : F)) * (v + (j : F))) -/-- **Root characterization of the range factor** (over a field): `P_b(v) = 0` iff `v` is (the -image of) an integer in the symmetric range `{-(b-1), …, b-1}`. -/ +/-- Over a field, `P_b(v) = 0` iff `v` is the image of an integer in the symmetric range +`{-(b-1), …, b-1}`. -/ theorem rangeProduct_eq_zero_iff {b : ℕ} {v : F} : rangeProduct b v = 0 ↔ ∃ j : ℕ, j ≤ b - 1 ∧ (v = (j : F) ∨ v = -(j : F)) := by unfold rangeProduct @@ -132,19 +124,17 @@ theorem rangeProduct_eq_zero_iff {b : ℕ} {v : F} : rw [mul_eq_zero, sub_eq_zero, add_eq_zero_iff_eq_neg] exact hv -/-- **The Eq. (21) table**: the committed `(z, ρ)` re-read as an `F`-valued function on the -`m₀`-cube. The cube point is decoded (via `finFunctionFinEquiv`) to a flat index `idx`, split into -a `row := idx / d` and `column := idx % d` (`d = deg Φ.φ`); rows `< μ` read the `Zq`-coefficients -of the committed `zⱼ ∈ Rq`, rows `μ ≤ · < μ + n` read the coefficients of the committed quotients -`ρᵢ`, both mapped through the base-field embedding `φF`; all other cube points are zero-padded. - -**The coefficients are read directly** — the range test `H₀` is on the *committed* data, so that -`H₀ ≡ 0 ⇒` every committed coefficient lies in `[−(b−1), b−1]` is a genuine (non-vacuous) shortness -statement. (The paper's base-`b` gadget decomposition is the *honest prover's* pre-commit step to -obtain short pieces; re-decomposing here would make every entry a base-`b` digit, hence trivially -in range, and `H₀` would test nothing.) The `b` argument is retained for signature compatibility -with `hZero`; matching `H₀ ≡ 0` to `liftShort`'s two bounds `(bound, ρBound)` is the range-side -soundness step (F5, out of scope here). -/ +/-- The Eq. (21) table `w̃`: the committed pair `(z, r)` read as an `F`-valued function on the +`m₀`-cube. A cube point is decoded (via `finFunctionFinEquiv`) to a flat index `idx`, split into +`row := idx / d` and `column := idx % d` (`d = deg Φ.φ`). Rows `< μ` return the `Zq`-coefficients +of the witness entries `zⱼ ∈ Rq`; rows `μ ≤ · < μ + n` return the coefficients of the quotients +`rᵢ`; both are mapped through the base-field embedding `φF`, and all remaining cube points return +zero. + +The coefficients are read directly rather than gadget-decomposed, so the range polynomial `H₀` +constrains the committed data itself: `H₀ ≡ 0` says every committed coefficient lies in +`[−(b−1), b−1]`. The `b` argument is unused here and kept for signature compatibility with +`hZero`. -/ noncomputable def wTable (φF : ZMod q →+* F) (_b : ℕ) (w : LiftedWitness Φ μ n) : (Fin m₀ → Fin 2) → F := fun pt => @@ -153,180 +143,176 @@ noncomputable def wTable (φF : ZMod q →+* F) (_b : ℕ) (w : LiftedWitness Φ if hz : idx / d < μ then φF ((w.z ⟨idx / d, hz⟩).1.coeff (idx % d)) else if hr : idx / d - μ < n then - φF ((w.ρ ⟨idx / d - μ, hr⟩).coeff (idx % d)) + φF ((w.r ⟨idx / d - μ, hr⟩).coeff (idx % d)) else 0 -/-- **The `m₁`-cube point encoding row `i : Fin n`** (arity pin `n ≤ 2 ^ m₁`): the inverse image -of `i` under the binary encoding `finFunctionFinEquiv : (Fin m₁ → Fin 2) ≃ Fin (2 ^ m₁)`. Rows -with index `≥ n` are the zero-padding of the batching cube. -/ +/-- The `m₁`-cube point that encodes row `i : Fin n` (requires `n ≤ 2 ^ m₁`): the preimage of `i` +under the binary encoding `finFunctionFinEquiv : (Fin m₁ → Fin 2) ≃ Fin (2 ^ m₁)`. Cube points +encoding an index `≥ n` are the zero-padding of the batching cube. -/ def rowPoint (hn : n ≤ 2 ^ m₁) (i : Fin n) : Fin m₁ → Fin 2 := finFunctionFinEquiv.symm ⟨(i : ℕ), lt_of_lt_of_le i.isLt hn⟩ -/-- The `M̃_α`-contracted per-row value of Eq. (22): the Boolean-point coefficients of `H_α`, -`i ↦ (∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ)) − ŷᵢ(α)`. - -**Given concretely (Lemma-10 design, Option A).** By the "represent the constraints by -polynomials" identity of [NOZ26] §4.3, this `M̃_α`-contraction equals the `α`-evaluated per-row -**defect** of the lift relation `relLift`, -`evalAt α (∑ⱼ Mᵢⱼ·zⱼ) − evalAt α ŷᵢ − evalAt α (X^d+1)·evalAt α ρᵢ`, so we take that defect as -the definition, row-encoded into the `m₁`-cube via `rowPoint` and zero-padded on rows `≥ n`. Its -vanishing at every Boolean point is then exactly the `relLift` row constraint -(`hAlphaEvals_rowPoint`) — the content the batching bridge's un-batching pull-back consumes. The -literal table-contraction form (needed only for the sumcheck *summand* `sumcheckPolyAlpha`) -remains F5 (`wTable`). The `b` argument is retained for signature compatibility with `hAlpha`. -/ -noncomputable def hAlphaEvals (φF : ZMod q →+* F) (_b : ℕ) (s : RlinStatement Φ n μ) (a : F) +/-- The Boolean-point coefficients of `H_α` (Eq. (22)): the `M̃_α`-contracted per-row value +`i ↦ (∑_{u,ℓ} M̃_α(i,u)·w̃(u,ℓ)·α̃(ℓ)) − yᵢ(α)`. + +By the "represent the constraints by polynomials" identity of [NOZ26] §4.3, this contraction +equals the `α`-evaluated per-row defect of the lift relation `relLift`, namely +`evalAt α (∑ⱼ Mᵢⱼ·zⱼ) − evalAt α yᵢ − evalAt α (X^d+1)·evalAt α rᵢ`. It is defined here as that +defect, encoded into the `m₁`-cube via `rowPoint` and zero-padded on rows `≥ n`; the row equation +of `relLift` (Fig. 4) is recovered at the Boolean points via `hAlphaEvals_rowPoint`. The `b` +argument is unused here and kept for signature compatibility with `hAlpha`. -/ +noncomputable def hAlphaEvals (φF : ZMod q →+* F) (_b : ℕ) (s : RlinStatement Φ n μ) (α : F) (w : LiftedWitness Φ μ n) : (Fin m₁ → Fin 2) → F := fun pt => if h : ((finFunctionFinEquiv pt : Fin (2 ^ m₁)) : ℕ) < n then - evalAt φF a (rowSum Φ s w.z ⟨_, h⟩) - - evalAt φF a ((s.yvec ⟨_, h⟩).1.toPoly) - - evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ ⟨_, h⟩) + evalAt φF α (rowSum Φ s w.z ⟨_, h⟩) + - evalAt φF α ((s.yvec ⟨_, h⟩).1.toPoly) + - evalAt φF α Φ.φ.toPoly * evalAt φF α (w.r ⟨_, h⟩) else 0 omit [NeZero q] [IsCyclotomic Φ] in -/-- **Faithfulness of the row encoding**: at the Boolean point `rowPoint i`, the `H_α` -coefficient `hAlphaEvals` is exactly row `i`'s `α`-evaluated lift defect. This is the bridge -between `hAlpha ≡ 0` (via `MLE_eq_zero_iff`) and the per-row `relLift` constraints. -/ -theorem hAlphaEvals_rowPoint (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +/-- At the Boolean point `rowPoint i`, the coefficient `hAlphaEvals` equals row `i`'s +`α`-evaluated lift defect. This links `hAlpha ≡ 0` (via `MLE_eq_zero_iff`) to the per-row +constraints of `relLift`. -/ +theorem hAlphaEvals_rowPoint (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : F) (w : LiftedWitness Φ μ n) (hn : n ≤ 2 ^ m₁) (i : Fin n) : - hAlphaEvals Φ m₁ φF b s a w (rowPoint m₁ hn i) = - evalAt φF a (rowSum Φ s w.z i) - evalAt φF a ((s.yvec i).1.toPoly) - - evalAt φF a Φ.φ.toPoly * evalAt φF a (w.ρ i) := by + hAlphaEvals Φ m₁ φF b s α w (rowPoint m₁ hn i) = + evalAt φF α (rowSum Φ s w.z i) - evalAt φF α ((s.yvec i).1.toPoly) + - evalAt φF α Φ.φ.toPoly * evalAt φF α (w.r i) := by simp only [hAlphaEvals, rowPoint, Equiv.apply_symm_apply, Fin.eta, i.isLt, dif_pos] -/-! ## The batched constraint polynomials (genuine multilinear extensions) -/ +/-! ## The batched constraint polynomials -/ -/-- **Eq. (23)**: the `eq̃`-batched range polynomial as the real multilinear extension of the -per-entry range factor `P_b(w̃(·))` — 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`). -/ +/-- `H₀` (Eq. (23)): the range polynomial, the multilinear extension of the per-entry range factor +`P_b(w̃(·))` in `τ ∈ F^{m₀}`. `H₀ ≡ 0` iff every table entry is a root of the range product, i.e. +lies in `[−(b−1), b−1]` (assuming `2b − 1 < q`). -/ noncomputable def hZero (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₀) F := MLE fun v => rangeProduct b (wTable Φ m₀ φF b w v) -/-- **Eq. (22)**: the `eq̃`-batched linear constraint polynomial as the real multilinear -extension of the `M̃_α`-contracted per-row values; `H_α ≡ 0` iff every lifted row of `relLift` +/-- `H_α` (Eq. (22)): the linear-constraint polynomial, the multilinear extension of the +`M̃_α`-contracted per-row values `hAlphaEvals`. `H_α ≡ 0` iff every lifted row of `relLift` vanishes at `α`. -/ -noncomputable def hAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +noncomputable def hAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : F) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₁) F := - MLE (hAlphaEvals Φ m₁ φF b s a w) + MLE (hAlphaEvals Φ m₁ φF b s α w) omit [NeZero q] [IsCyclotomic Φ] in -/-- `H₀` is multilinear (degree `≤ 1` in each batching variable) — the hypothesis of the -Kronecker pullback degree bound `< 2 ^ m₀`. `sorry`-free (a genuine `MLE`). -/ +/-- `H₀` has degree `≤ 1` in each variable — it is a multilinear extension. This is the bound +that makes its Kronecker restriction univariate of degree `< 2 ^ m₀`. -/ theorem hZero_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) (j : Fin m₀) : (hZero Φ m₀ φF b w).degreeOf j ≤ 1 := MLE_degreeOf _ j omit [NeZero q] [IsCyclotomic Φ] in -/-- `H_α` is multilinear (degree `≤ 1` in each batching variable). `sorry`-free. -/ -theorem hAlpha_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +/-- `H_α` has degree `≤ 1` in each variable — it is a multilinear extension. -/ +theorem hAlpha_degreeOf_le (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : F) (w : LiftedWitness Φ μ n) (j : Fin m₁) : - (hAlpha Φ m₁ φF b s a w).degreeOf j ≤ 1 := + (hAlpha Φ m₁ φF b s α w).degreeOf j ≤ 1 := MLE_degreeOf _ j -/-- `H₀` as an element of the multilinear subtype (the input the Kronecker root-counting kernel -consumes). -/ +/-- `H₀` bundled as an element of the multilinear subtype `restrictDegree _ _ 1`, the form the +zero-check's root-counting argument (`arm_eq_zero_of_family`) takes as input. -/ noncomputable def hZeroML (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) : MvPolynomial.restrictDegree (Fin m₀) F 1 := ⟨hZero Φ m₀ φF b w, (mem_restrictDegree_iff_degreeOf_le _ _).mpr fun j => hZero_degreeOf_le Φ m₀ φF b w j⟩ -/-- `H_α` as an element of the multilinear subtype. -/ -noncomputable def hAlphaML (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +/-- `H_α` bundled as an element of the multilinear subtype `restrictDegree _ _ 1`. -/ +noncomputable def hAlphaML (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : F) (w : LiftedWitness Φ μ n) : MvPolynomial.restrictDegree (Fin m₁) F 1 := - ⟨hAlpha Φ m₁ φF b s a w, - (mem_restrictDegree_iff_degreeOf_le _ _).mpr fun j => hAlpha_degreeOf_le Φ m₁ φF b s a w j⟩ + ⟨hAlpha Φ m₁ φF b s α w, + (mem_restrictDegree_iff_degreeOf_le _ _).mpr fun j => hAlpha_degreeOf_le Φ m₁ φF b s α w j⟩ /-- Evaluation of the multilinear extension of the table `w̃` at a point `a ∈ F^{m₀}`: -`mle[w̃](a) = ∑ᵢ w̃(i)·eq̃(i, a)`. The final-evaluation step's claim currency -(`Sumcheck/FinalEval.lean`). `sorry`-free modulo the `wTable` encoding. -/ +`mle[w̃](a) = ∑ᵢ w̃(i)·eq̃(i, a)`. This is the evaluation claim carried into the final-evaluation +step (`Sumcheck/FinalEval.lean`). -/ noncomputable def wTableMleEval (φF : ZMod q →+* F) (b : ℕ) (w : LiftedWitness Φ μ n) (a : Fin m₀ → F) : F := MvPolynomial.eval a (MLE (wTable Φ m₀ φF b w)) -/-! ## The sumcheck polynomials (sorried F5 content) -/ +/-! ## The sumcheck summands -/ -/-- **`F_{0,τ₀}`** (the range sumcheck summand): satisfies `∑_{x} F_{0,τ₀}(x) = H₀(τ₀)` -(`sum_sumcheckPolyZero`). Per-variable degree `roundDegZero b = 2b`. **Sorried (F5).** -/ +/-- The range sumcheck summand `F_{0,τ₀}`, characterized by `∑_{x} F_{0,τ₀}(x) = H₀(τ₀)` +(`sum_sumcheckPolyZero`) with per-variable degree `roundDegZero b = 2b`. -/ def sumcheckPolyZero (φF : ZMod q →+* F) (b : ℕ) (τ₀ : Fin m₀ → F) (w : LiftedWitness Φ μ n) : MvPolynomial (Fin m₀) F := sorry -/-- **`F_{α,τ₁}`** (the linear sumcheck summand): satisfies -`∑_{x} 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) +/-- The linear sumcheck summand `F_{α,τ₁}`, characterized by +`∑_{x} F_{α,τ₁}(x) = H_α(τ₁) + zcTargetAlpha` (`sum_sumcheckPolyAlpha`) with per-variable degree +`roundDegAlpha = 2`. -/ +def sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : 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. **Sorried (F5).** -/ -def zcTargetAlpha (φF : ZMod q →+* F) (s : RlinStatement Φ n μ) (a : F) +/-- The public initial target of the linear sumcheck, `∑ᵢ eq̃(τ₁, i)·yᵢ(α)`, which the verifier +computes from the statement alone. -/ +def zcTargetAlpha (φF : ZMod q →+* F) (s : RlinStatement Φ n μ) (α : F) (τ₁ : Fin m₁ → F) : F := sorry -/-- Partial hypercube sum: `hypercubeSum H i cs = ∑_{x ∈ {0,1}^{m₀ − i}} H(cs, x)`. **Sorried -(F5).** -/ +/-- Partial sum of `H` over the trailing cube coordinates: `hypercubeSum H i cs = +∑_{x ∈ {0,1}^{m₀ − i}} H(cs, x)`, where `cs` fixes the first `i` coordinates. -/ def hypercubeSum (H : MvPolynomial (Fin m₀) F) (i : ℕ) (cs : Fin i → F) : F := sorry -/-- The sum of `F_{0,τ₀}` over the cube is `H₀(τ₀)`. **Sorried (F5).** -/ +/-- The full-cube sum of the range summand `F_{0,τ₀}` equals `H₀(τ₀)`. -/ 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`. **Sorried (F5).** -/ -theorem sum_sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (a : F) +/-- The full-cube sum of the linear summand `F_{α,τ₁}` equals `H_α(τ₁) + zcTargetAlpha`. -/ +theorem sum_sumcheckPolyAlpha (φF : ZMod q →+* F) (b : ℕ) (s : RlinStatement Φ n μ) (α : 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 + hypercubeSum m₀ (sumcheckPolyAlpha Φ m₀ m₁ φF b s α τ₁ w) 0 (fun j => j.elim0) = + MvPolynomial.eval τ₁ (hAlpha Φ m₁ φF b s α w) + zcTargetAlpha Φ m₁ φF s α τ₁ := 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 (the challenge is the seed pair; the batching -points `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)` are derived deterministically). -/ +/-- The zero-check's output statement: the lift statement extended by the two challenge seeds +`(ρ₀, ρ_α)`. The batching points are derived from them along the Kronecker curve, +`τ₀ = κ_{m₀}(ρ₀)` and `τ_α = κ_{m₁}(ρ_α)`. -/ structure ZeroCheckStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) where - /-- The `R^lin` statement (carrying the public `M`, `yvec`, `bound`). -/ + /-- The `R^lin` statement, carrying the public `M`, `yvec`, and `bound`. -/ rlin : RlinStatement Φ n μ - /-- The `w̃`-commitment from the lift stage. -/ + /-- The commitment to `w̃` from the lift stage. -/ t : TCom - /-- The HMZ25 evaluation challenge `α` from the lift stage. -/ + /-- The ring-switching evaluation challenge `α` from the lift stage ([HMZ25], Fig. 4). -/ α : F - /-- The Kronecker seed `ρ₀` of the range zero-check. -/ + /-- The seed `ρ₀` from which the range check's evaluation point is derived. -/ seed₀ : F - /-- The Kronecker seed `ρ_α` of the linear zero-check. -/ + /-- The seed `ρ_α` from which the linear check's evaluation point is derived. -/ 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^{(α)})`. -/ +/-- The statement after `i` sumcheck rounds: the zero-check statement, the `i` challenges drawn so +far, and the current range and linear sumcheck targets. -/ structure RoundStatement (Φ : CyclotomicModulus (ZMod q)) (TCom F : Type) (n μ : ℕ) (i : ℕ) where - /-- The zero-check statement (public data, commitment, `α`, Kronecker seeds). -/ + /-- The zero-check statement (public data, commitment, `α`, and the two seeds). -/ zc : ZeroCheckStatement Φ TCom F n μ - /-- The sumcheck challenges so far. -/ + /-- The sumcheck challenges drawn 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 : ℕ) +variable (bound rBound : ℕ) -/-- **The per-round seam relation** of the paired sumcheck ([NOZ26] Lemma 11): `w̃` opens `t`, is -short (`liftShort` — the weak-binding escape's precondition, threaded through every seam; -resolution option 2 of the audit doc), and both partial-hypercube-sum claims at the current -challenge prefix equal the current targets. The public sanity conjunct `bound ≤ rlin.bound` -threads the global norm parameter back to the `R^lin` statement bound. -/ -def roundRel (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- The per-round relation of the paired sumcheck ([NOZ26] Lemma 11): `w̃` opens `t`, is short +(`liftShort`, the shortness precondition of the commitment's weak binding), and both +partial-hypercube-sum claims at the current challenge prefix equal the current targets. The final +conjunct `bound ≤ rlin.bound` ties the global norm parameter to the statement's declared bound. -/ +def roundRel (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) (i : ℕ) : Set (RoundStatement Φ K.TCom F n μ i × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.zc.t ∧ - liftShort Φ bound ρBound p.2 ∧ + liftShort Φ bound rBound p.2 ∧ hypercubeSum m₀ (sumcheckPolyZero Φ m₀ φF b (kroneckerPoint m₀ p.1.zc.seed₀) p.2) i p.1.challenges = p.1.target₀ ∧ hypercubeSum m₀ @@ -334,10 +320,11 @@ def roundRel (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound) p.2) i p.1.challenges = p.1.targetα ∧ bound ≤ p.1.zc.rlin.bound} -/-- Escape-threaded per-round seam relation. -/ -def roundRelE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- `roundRel` extended with the escape branch: on `.inl w` it is `roundRel`, and on `.inr e` it +requires `e ∈ K.esc`. -/ +def roundRelE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (roundRel Φ m₀ m₁ bound rBound K φF b i).withEscape K.esc end ArkLib.Lattices.Ajtai.InnerOuter diff --git a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean index 8d685f995d..873b66d47f 100644 --- a/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean +++ b/ArkLib/Commitments/Functional/Hachi/ZeroCheck/Reduction.lean @@ -8,53 +8,43 @@ import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.ChallengeR import ArkLib.OracleReduction.Security.CoordinateWiseSpecialSoundness.Package /-! - # Zero-check — Hachi Figure 5 / **corrected** Lemma 10 — escape-threaded (milestone F6) + # Zero-check — Hachi Figure 5 / Lemma 10 One challenge round reducing the batched polynomial identities `H₀ ≡ 0 ∧ H_α ≡ 0` - (`relBatchedE`, `ZeroCheck/Batch.lean`) to their evaluations at random points; the two scalar - evaluation claims then seed the sumcheck (`Sumcheck/Bridge.lean`). Escape-threaded and stated - over the concrete `LiftedWitness Φ μ n` / weak-binding `LiftCom`, so it composes into the - §4.3 opening chain (`Composition.lean`, row 6). - - ## ⚠ The paper's Lemma 10 is not provable as stated — this file implements the repair - - A coordinate-wise star of accepting transcripts with the paper's **uniform vector challenges** - `(τ₀, τ_α) ∈ F^{m₀} × F^{m₁}` only certifies that a multilinear `H` vanishes on the *axis - cross* through the star's center, and for ≥ 2 challenge variables cross-vanishing does not - imply `H ≡ 0` (counterexample `(t₁-a)(t₂-b)`, - `LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`). No choice of the paper's parameter - helps. - - **Adopted repair (one round, Kronecker curve):** the verifier's challenge is a pair of *scalar - seeds* `(ρ₀, ρ_α) ∈ F²`, and the evaluation points are derived on the **Kronecker curves** - `τ₀ := κ_{m₀}(ρ₀)`, `τ_α := κ_{m₁}(ρ_α)`. On the curve a multilinear `H` pulls back to the - univariate `powAlgHom H` of degree `< 2^m`, and the pullback is injective on multilinears - (`LinearMvExtension.powAlgHom_eq_zero_iff`), so univariate root counting is information-complete: - `D = zeroCheckD m₀ m₁ = max(2, 2^{m₀}, 2^{m₁})` distinct seeds per coordinate identify the - polynomial. Only the challenge *distribution* is restricted to the two curves (error scale - `D/|F|`). **This is the one place the formalization deliberately changes the paper's protocol to - repair its proof.** The algebraic kernel lives in - `ArkLib/Data/MvPolynomial/LinearMvExtension.lean`; the generic one-round CWSS engine is - `OracleReduction/…/CoordinateWiseSpecialSoundness/ChallengeRound.lean`. - - ## Corrected Hachi Lemma 10 — coordinate-wise special soundness - - `zeroCheck_coordinateWiseSpecialSound` is **`sorry`-free**: from `2D − 1` accepting transcripts - whose seed pairs form a special-sound family, the tree extractor (`buildWitnessE`) either - - 1. finds a branch carrying a downstream **escape** `.inr e` → passes it through; - 2. finds two branches carrying **distinct short openings** of the shared `t` → the weak-binding - escape `K.escOfCollision` (`K.collision_mem`, Hachi Remark 2 / Lemma 7 — the `liftShort` - conjunct of `relZeroCheck` supplies the shortness precondition); or - 3. fixes one common opening `w̃` and, per coordinate, uses the family's `D` distinct seeds as + (`relBatchedE`, `ZeroCheck/Batch.lean`) to evaluations at derived points; the two scalar + evaluation claims then seed the sumcheck (`Sumcheck/Bridge.lean`). It is stated over the lifted + witness `LiftedWitness Φ μ n` and the weak-binding `LiftCom`, and composes into the §4.3 opening + chain (`Composition.lean`). + + ## Deviation from the paper's Lemma 10 + + The paper's Figure 5 draws uniform vector challenges `(τ₀, τ_α) ∈ F^{m₀} × F^{m₁}`. A + coordinate-wise family of accepting transcripts then only certifies that a multilinear `H` + vanishes on the axis cross through the family's center, which for two or more variables does not + imply `H ≡ 0` — e.g. `(t₁ - a)(t₂ - b)` vanishes on the cross through `(a, b)` without being + zero (`LinearMvExtension.exists_nonzero_vanishing_on_axis_cross`). So the argument for Lemma 10 + as stated does not go through. + + This formalization instead draws a pair of scalar seeds `(ρ₀, ρ_α) ∈ F²` and derives the + evaluation points along the Kronecker curves `τ₀ = κ_{m₀}(ρ₀)`, `τ_α = κ_{m₁}(ρ_α)`. Restricted + to such a curve, a multilinear `H` becomes univariate of degree `< 2^m`, and this restriction is + injective on multilinear polynomials (`LinearMvExtension.powAlgHom_eq_zero_iff`), so + `D = zeroCheckD m₀ m₁ = max(2, 2^{m₀}, 2^{m₁})` distinct seeds per coordinate determine `H`. The + algebraic core is in `ArkLib/Data/MvPolynomial/LinearMvExtension.lean`, and the generic one-round + soundness engine is `OracleReduction/…/CoordinateWiseSpecialSoundness/ChallengeRound.lean`. + + ## Coordinate-wise special soundness + + `zeroCheck_coordinateWiseSpecialSound`: from `2D − 1` accepting transcripts whose seed pairs form + a special-sound family, the extractor (`buildWitnessE`) does one of the following. + + 1. Some branch carries an escape `.inr e`: pass it through. + 2. Two branches carry distinct short openings of the shared `t`: return the weak-binding escape + `K.escOfCollision` (`K.collision_mem`, Hachi Remark 2 / Lemma 7; the `liftShort` conjunct of + `relZeroCheck` supplies the required shortness). + 3. All branches share one opening `w̃`: per coordinate, the family's `D` distinct seeds are `≥ 2^m` Kronecker roots of the multilinear identity (`arm_eq_zero_of_family`), giving - `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0` — i.e. `relBatchedE` via `.inl w̃`. - - The theorem is now **axiom-clean**: with the encodings `wTable`/`hAlphaEvals` made concrete, - `#print axioms zeroCheck_coordinateWiseSpecialSound` reports no `sorryAx` — only the ambient - `propext`/`Classical.choice`/`Quot.sound` (the `Classical.choice` is the documented - constructivity caveat: `buildWitnessE` selects branch witnesses by classical choice). The - standalone kernel `arm_eq_zero_of_family` is axiom-clean. + `H₀^{w̃} ≡ 0` and `H_α^{w̃} ≡ 0`, i.e. membership in `relBatchedE` via `.inl w̃`. ## References @@ -70,18 +60,17 @@ open CoordinateWise CoordinateWise.ChallengeRound /-! ## Wire format and CWSS structure -/ -/-- The zero-check's wire format: one verifier challenge carrying the **Kronecker seed pair** -`(ρ₀, ρ_α)` as a `Fin 2 → F`, so the generic one-round challenge engine (`ChallengeRound`) -applies verbatim. -/ +/-- The zero-check's wire format: one verifier challenge carrying the seed pair `(ρ₀, ρ_α)` as a +`Fin 2 → F`, matching the generic one-round challenge engine `ChallengeRound`. -/ @[reducible] def pSpecZeroCheck (F : Type) : ProtocolSpec 1 := ChallengeRound.pSpec F 2 instance instSampleableTypeChallengePSpecZeroCheck {F : Type} [SampleableType F] : ∀ i, SampleableType ((pSpecZeroCheck F).Challenge i) := inferInstance -/-- **The corrected Lemma 10 CWSS structure**: the seed-pair challenge decomposes into `ℓ = 2` -scalar coordinates over `F`, with soundness parameter `k = D = zeroCheckD m₀ m₁`. Star arity -`2·(D−1)+1 = 2D−1`. -/ +/-- The coordinate-wise special soundness structure for the zero-check: the seed-pair challenge +has `ℓ = 2` scalar coordinates over `F`, with soundness parameter `k = D = zeroCheckD m₀ m₁`, +giving a family of `2·(D−1)+1 = 2D−1` transcripts. -/ def zeroCheckStructure (F : Type) (m₀ m₁ : ℕ) : CWSSStructure (pSpecZeroCheck F) := chalStructure F 2 (zeroCheckD m₀ m₁) (by norm_num) (two_le_zeroCheckD m₀ m₁) @@ -90,7 +79,7 @@ section Protocol variable {q : ℕ} [NeZero q] [Fact (Nat.Prime q)] [BEq (ZMod q)] [LawfulBEq (ZMod q)] (Φ : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ] variable {n μ : ℕ} {E : Type} {F : Type} [Field F] -variable (m₀ m₁ : ℕ) (bound ρBound : ℕ) +variable (m₀ m₁ : ℕ) (bound rBound : ℕ) variable {ι : Type} {oSpec : OracleSpec ι} {σ : Type} open MvPolynomial @@ -101,9 +90,9 @@ def zcMapStmt {TCom : Type} (stmt : LiftStatement Φ TCom F n μ) (seeds : Fin 2 ZeroCheckStatement Φ TCom F n μ := ⟨stmt.1, stmt.2.1, stmt.2.2, seeds 0, seeds 1⟩ -/-- 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`. -/ +/-- The zero-check verifier (Figure 5): a pass-through that extends the lift statement by the two +seeds read from the challenge. 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 @@ -124,33 +113,34 @@ def zeroCheckProver {TCom : Type} : | ⟨0, _⟩ => fun st => pure fun c => (st, c) output := fun ⟨⟨stmt, wit⟩, c⟩ => pure (zcMapStmt Φ stmt c, wit) -/-- **The zero-check's output relation** (corrected Figure 5 residual claims): `w̃` opens `t`, -is short (`liftShort` — the weak-binding escape's precondition; resolution option 2), and both -batched constraint polynomials vanish **at the derived Kronecker points**. -/ -def relZeroCheck (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- The zero-check's output relation (Figure 5's residual claims): `w̃` opens `t`, is short +(`liftShort`), and both batched constraint polynomials vanish at the points derived from the +seeds — `H₀` at `κ_{m₀}(ρ₀)` and `H_α` at `κ_{m₁}(ρ_α)`. -/ +def relZeroCheck (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) : Set (ZeroCheckStatement Φ K.TCom F n μ × LiftedWitness Φ μ n) := {p | K.com p.2 = p.1.t ∧ - liftShort Φ bound ρBound p.2 ∧ + liftShort Φ bound rBound p.2 ∧ 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)) +/-- `relZeroCheck` extended with the escape branch (`.inr e` requires `e ∈ K.esc`); the input +relation of the sumcheck bridge. -/ +def relZeroCheckE (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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 + (relZeroCheck Φ m₀ m₁ bound rBound K φF b).withEscape K.esc -/-! ## The Kronecker root-counting arm (axiom-clean kernel) -/ +/-! ## The Kronecker root-counting step -/ -/-- **One arm of corrected Lemma 10**: if a special-sound family of seed pairs (parameter -`D ≥ 2^m`) has, at coordinate `i`, all its seeds `ρ` satisfying `H(κ_m(ρ)) = 0` for one fixed -multilinear `H`, then `H ≡ 0`. The family supplies `D ≥ 2^m` distinct seeds at coordinate `i` -(`IsSpecialSoundFamily.exists_coord_finset`); each is a root of the univariate Kronecker pullback -of degree `< 2^m`, so the pullback is zero and Kronecker injectivity kills `H` +/-- If a special-sound family of seed pairs (parameter `D ≥ 2^m`) has, at coordinate `i`, every +seed `ρ` satisfying `H(κ_m(ρ)) = 0` for one fixed multilinear `H`, then `H ≡ 0`. The family +supplies `D ≥ 2^m` distinct seeds at coordinate `i` (`IsSpecialSoundFamily.exists_coord_finset`); +each is a root of the univariate Kronecker restriction of degree `< 2^m`, so that restriction is +zero and injectivity gives `H = 0` (`LinearMvExtension.multilinear_eq_zero_of_kronecker_roots`). -/ theorem arm_eq_zero_of_family {m D : ℕ} (hm : 2 ^ m ≤ D) (fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) @@ -164,12 +154,12 @@ theorem arm_eq_zero_of_family {m D : ℕ} (hm : 2 ^ m ≤ D) rw [← hj] exact hroots j -/-! ## The escape-threaded witness assembler -/ +/-! ## The witness assembler -/ -/-- Combine two branch responses of a *differing* pair into an escape: pass through either -branch's `.inr` escape, or route a collision of two distinct openings to `K.escOfCollision`. -Always outputs a `.inr` (its `relBatchedE`-membership is `collideOrPass_mem_relBatchedE`). -/ -def collideOrPass (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) +/-- Combine two distinct branch responses into an escape: pass through either branch's `.inr` +escape, or turn a collision of two distinct openings into `K.escOfCollision`. Always returns an +escape (`.inr`); its `relBatchedE`-membership is `collideOrPass_mem_relBatchedE`. -/ +def collideOrPass (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (a c : LiftedWitness Φ μ n ⊕ E) : LiftedWitness Φ μ n ⊕ E := match a, c with | Sum.inr e, _ => Sum.inr e @@ -177,20 +167,20 @@ def collideOrPass (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρB | Sum.inl wa, Sum.inl wc => Sum.inr (K.escOfCollision wa wc) open Classical in -/-- The zero-check witness assembler (corrected Lemma 10's three-case extraction), the `mkWitness` -argument of the generic `ChallengeRound` extractor: +/-- The zero-check witness assembler, passed as the `mkWitness` argument of the generic +`ChallengeRound` extractor: -* if all `2D − 1` branch responses equal branch 0's, output that response (a common opening — or a +* if all `2D − 1` branch responses equal branch 0's, return that response (a common opening, or a passed-through escape if branch 0 is one); -* otherwise some branch differs from branch 0: `collideOrPass` produces the escape (pass-through - or weak-binding collision). -/ +* otherwise some branch differs from branch 0, and `collideOrPass` produces an escape (a + pass-through or a weak-binding collision). -/ noncomputable def buildWitnessE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (D : ℕ) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (D : ℕ) (_stmt : LiftStatement Φ K.TCom F n μ) (_fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) (resp : Fin (2 * (D - 1) + 1) → (LiftedWitness Φ μ n ⊕ E)) : LiftedWitness Φ μ n ⊕ E := - if h : ∃ j, resp j ≠ resp 0 then collideOrPass Φ bound ρBound K (resp h.choose) (resp 0) + if h : ∃ j, resp j ≠ resp 0 then collideOrPass Φ bound rBound K (resp h.choose) (resp 0) else resp 0 @@ -198,16 +188,16 @@ omit [NeZero q] [IsCyclotomic Φ] in /-- `collideOrPass a c` lands in `relBatchedE` (always as an escape) provided `a ≠ c` and each of `a`, `c` is either a `K.esc` escape or a short opening of the shared commitment `stmt.t`. -/ theorem collideOrPass_mem_relBatchedE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) (stmt : LiftStatement Φ K.TCom F n μ) (a c : LiftedWitness Φ μ n ⊕ E) (hac : a ≠ c) (hesc_a : ∀ e, a = Sum.inr e → e ∈ K.esc) - (hopen_a : ∀ w, a = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w) + (hopen_a : ∀ w, a = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound rBound w) (hesc_c : ∀ e, c = Sum.inr e → e ∈ K.esc) - (hopen_c : ∀ w, c = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w) : - (stmt, collideOrPass Φ bound ρBound K a c) ∈ relBatchedE Φ m₀ m₁ bound ρBound K φF b := by + (hopen_c : ∀ w, c = Sum.inl w → K.com w = stmt.2.1 ∧ liftShort Φ bound rBound w) : + (stmt, collideOrPass Φ bound rBound K a c) ∈ relBatchedE Φ m₀ m₁ bound rBound K φF b := by rcases a with wa | ea <;> rcases c with wc | ec <;> simp only [collideOrPass, relBatchedE, Set.mem_withEscape_inr] - · -- both openings: a genuine weak-binding collision + · -- both openings: a weak-binding collision obtain ⟨hca, hsa⟩ := hopen_a wa rfl obtain ⟨hcc, hsc⟩ := hopen_c wc rfl have hne : wa ≠ wc := fun heq => hac (by rw [heq]) @@ -219,26 +209,26 @@ theorem collideOrPass_mem_relBatchedE -- `[IsCyclotomic Φ]`/`[NeZero q]` are needed to synthesize the `wTable`/`Rq` instances inside -- `hZeroML`/`hAlphaML`, but the linter's usage analysis misses instance-synth-only section vars. set_option linter.unusedSectionVars false in -/-- **The witness assembler is correct** (the math content of corrected Lemma 10): at every -special-sound family of `relZeroCheckE`-accepting branches, `buildWitnessE` lands in -`relBatchedE`. -/ +/-- Correctness of the witness assembler: for any special-sound family of `relZeroCheckE`-accepting +branches, `buildWitnessE` returns a witness in `relBatchedE`. This is the extraction step of the +zero-check's coordinate-wise special soundness. -/ theorem buildWitnessE_mem_relBatchedE - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) (φF : ZMod q →+* F) (b : ℕ) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) {D : ℕ} (hm₀ : 2 ^ m₀ ≤ D) (hm₁ : 2 ^ m₁ ≤ D) (stmt : LiftStatement Φ K.TCom F n μ) (fam : Fin (2 * (D - 1) + 1) → (Fin 2 → F)) (resp : Fin (2 * (D - 1) + 1) → (LiftedWitness Φ μ n ⊕ E)) - (hrel : ∀ j, (zcMapStmt Φ stmt (fam j), resp j) ∈ relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) + (hrel : ∀ j, (zcMapStmt Φ stmt (fam j), resp j) ∈ relZeroCheckE Φ m₀ m₁ bound rBound K φF b) (hfam : IsSpecialSoundFamily 2 D fam) : - (stmt, buildWitnessE Φ bound ρBound K D stmt fam resp) ∈ - relBatchedE Φ m₀ m₁ bound ρBound K φF b := by + (stmt, buildWitnessE Φ bound rBound K D stmt fam resp) ∈ + relBatchedE Φ m₀ m₁ bound rBound K φF b := by classical -- per-branch facts pulled from `relZeroCheckE` membership have hesc : ∀ (j) (e : E), resp j = Sum.inr e → e ∈ K.esc := by intro j e hje have := hrel j; rw [hje, relZeroCheckE, Set.mem_withEscape_inr] at this; exact this have hopen : ∀ (j) (w : LiftedWitness Φ μ n), resp j = Sum.inl w → - K.com w = stmt.2.1 ∧ liftShort Φ bound ρBound w := by + K.com w = stmt.2.1 ∧ liftShort Φ bound rBound w := by intro j w hjw have := hrel j; rw [hjw, relZeroCheckE, Set.mem_withEscape_inl] at this simp only [relZeroCheck, Set.mem_setOf_eq] at this @@ -247,7 +237,7 @@ theorem buildWitnessE_mem_relBatchedE by_cases h : ∃ j, resp j ≠ resp 0 · -- some branch differs from branch 0 → `collideOrPass` produces an escape rw [dif_pos h] - exact collideOrPass_mem_relBatchedE Φ m₀ m₁ bound ρBound K φF b stmt + exact collideOrPass_mem_relBatchedE Φ m₀ m₁ bound rBound K φF b stmt (resp h.choose) (resp 0) h.choose_spec (hesc h.choose) (hopen h.choose) (hesc 0) (hopen 0) · -- all branches equal branch 0 → common opening (or common escape) @@ -278,34 +268,33 @@ theorem buildWitnessE_mem_relBatchedE exact hesc 0 e0 hr0 omit [NeZero q] in -/-- **Corrected Hachi Lemma 10 (CWSS of the zero-check, Figure 5, Kronecker-curve challenges), -`sorry`-free.** The one-round seed-pair verifier is coordinate-wise special sound for -`zeroCheckStructure` (`(ℓ, k) = (2, D)`, `D = zeroCheckD m₀ m₁`), reducing `relBatchedE` to -`relZeroCheckE`. Assembled by `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness`; the whole -of corrected Lemma 10 thereby reduces to `buildWitnessE_mem_relBatchedE`. -/ +/-- Coordinate-wise special soundness of the zero-check (Hachi Figure 5 / Lemma 10). The one-round +seed-pair verifier is coordinate-wise special sound for `zeroCheckStructure` +(`(ℓ, k) = (2, D)`, `D = zeroCheckD m₀ m₁`), reducing `relBatchedE` to `relZeroCheckE`. Assembled +by `ChallengeRound.coordinateWiseSpecialSound_of_mkWitness` from the extraction step +`buildWitnessE_mem_relBatchedE`. -/ theorem zeroCheck_coordinateWiseSpecialSound (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φ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) := + (relBatchedE Φ m₀ m₁ bound rBound K φF b) + (relZeroCheckE Φ m₀ m₁ bound rBound K φF b) := coordinateWiseSpecialSound_of_mkWitness init impl (by norm_num) (two_le_zeroCheckD m₀ m₁) (zeroCheckVerifier Φ) (fun stmt seeds => zcMapStmt Φ stmt seeds) (fun _ _ => rfl) - (relBatchedE Φ m₀ m₁ bound ρBound K φF b) (relZeroCheckE Φ m₀ m₁ bound ρBound K φF b) - (buildWitnessE Φ bound ρBound K (zeroCheckD m₀ m₁)) + (relBatchedE Φ m₀ m₁ bound rBound K φF b) (relZeroCheckE Φ m₀ m₁ bound rBound K φF b) + (buildWitnessE Φ bound rBound K (zeroCheckD m₀ m₁)) (fun stmt fam resp hrel hfam => - buildWitnessE_mem_relBatchedE Φ m₀ m₁ bound ρBound K φF b + buildWitnessE_mem_relBatchedE Φ m₀ m₁ bound rBound K φF b (two_pow_m₀_le_zeroCheckD m₀ m₁) (two_pow_m₁_le_zeroCheckD m₀ m₁) stmt fam resp hrel hfam) -/-- **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 zero-check packaged as a `CWSSPackage` (Hachi Figure 5 / Lemma 10): the one-round seed-pair +verifier with the `(ℓ, k) = (2, D)` structure, reducing `relBatchedE` to `relZeroCheckE`. -/ noncomputable def zeroCheckPackage (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) - (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound ρBound)) + (K : LiftCom (LiftedWitness Φ μ n) E (liftShort Φ bound rBound)) (φF : ZMod q →+* F) (b : ℕ) : CWSSPackage init impl (LiftStatement Φ K.TCom F n μ) (LiftedWitness Φ μ n ⊕ E) @@ -313,10 +302,10 @@ noncomputable def zeroCheckPackage (init : ProbComp σ) (pSpecZeroCheck F) where verifier := zeroCheckVerifier (oSpec := oSpec) Φ struct := zeroCheckStructure F m₀ m₁ - relIn := relBatchedE Φ m₀ m₁ bound ρBound K φF b - relOut := relZeroCheckE Φ m₀ m₁ bound ρBound K φF b + relIn := relBatchedE Φ m₀ m₁ bound rBound K φF b + relOut := relZeroCheckE Φ m₀ m₁ bound rBound K φF b isPure := ⟨fun stmt tr => zcMapStmt Φ stmt (tr.challenges ⟨0, rfl⟩), fun _ _ => rfl⟩ - isCWSS := zeroCheck_coordinateWiseSpecialSound Φ m₀ m₁ bound ρBound init impl K φF b + isCWSS := zeroCheck_coordinateWiseSpecialSound Φ m₀ m₁ bound rBound init impl K φF b end Protocol From 11597858185c2190784888c607e00a899250a1f5 Mon Sep 17 00:00:00 2001 From: ErVinuelas Date: Mon, 20 Jul 2026 19:29:21 +0200 Subject: [PATCH 20/21] fix[Hachi]: reconcile openCore with branch's batchPackage API after main merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge of main brought in the #650 skeleton's Composition.lean (which calls batchPackage with hq2 : 2*b ≤ q+1 and hb : b-1 ≤ bound) but kept this branch's sorry-free Batch.lean, whose batchPackage instead takes hn : n₀ ≤ 2^m₁ (shortness asserted directly in relBatched rather than derived from H₀ ≡ 0). The two disagreed, breaking the build. Adapt openCore/openingChain/hachi_iteration_coordinateWiseSpecialSound back to the hn API and restore the explicit (b := b) in the theorem (b is no longer pinned by a hypothesis). Also drop mem_relLiftE_of_relBatchedE from the module header's sorry inventory: it is now proved sorry-free (#print axioms: propext, Classical.choice, Quot.sound only). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Functional/Hachi/Composition.lean | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ArkLib/Commitments/Functional/Hachi/Composition.lean b/ArkLib/Commitments/Functional/Hachi/Composition.lean index 778f77184c..7d6d1457bb 100644 --- a/ArkLib/Commitments/Functional/Hachi/Composition.lean +++ b/ArkLib/Commitments/Functional/Hachi/Composition.lean @@ -100,9 +100,8 @@ seam a home for the `w̃`-commitment's weak-binding break (design G1; `E` abstra `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 +Lemma 9 (`lift_coordinateWiseSpecialSound`), the F5 encodings (`Constraints.lean`), 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`). @@ -234,7 +233,7 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT [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 ≤ γ) : + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) : CWSSPackage init impl (PolyEvalStatement 𝓜(q, α) innerRows messageDigits outerRows innerDigits dRows m r) (QuadEvalWitness 𝓜(q, α) innerRows (2 ^ m) messageDigits (2 ^ r) innerDigits ⊕ E) @@ -250,7 +249,7 @@ noncomputable def openCore (init : ProbComp σ) (impl : QueryImpl oSpec (StateT 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 ▷ + batchPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b hn ▷ zeroCheckPackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b ▷ sumcheckBridgePackage 𝓜(q, α) m₀ m₁ γ ρBound init impl K φF b @@ -272,7 +271,7 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta [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 ≤ γ) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} @@ -300,7 +299,7 @@ noncomputable def openingChain (init : ProbComp σ) (impl : QueryImpl oSpec (Sta roundsSpec F b (mLow + κ)) ++ₚ pSpecFinalEval F).Challenge i) := ProtocolSpec.instSampleableTypeChallengeAppend (h₁ := i₁) (h₂ := instSampleableTypeChallengePSpecFinalEval) - (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hq2 hb).toGuarded.append + (((openCore (m₀ := mLow + κ) (m₁ := m₁) init impl hq5 hκ hτ K φF hd hn).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 @@ -322,7 +321,7 @@ theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) [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 ≤ γ) + (hd : 0 < (𝓜(q, α)).φ.natDegree) (hn : n₀ ≤ 2 ^ m₁) (zpow : Fin (2 ^ κ) → F) (Φ' : CyclotomicModulus (ZMod q)) [IsCyclotomic Φ'] {innerRows' messageDigits' outerRows' innerDigits' dRows' m' r' : ℕ} @@ -330,16 +329,16 @@ theorem hachi_iteration_coordinateWiseSpecialSound (init : ProbComp σ) innerDigits' dRows') (reinterpretCom : K.TCom → Commitment Φ' outerRows') (base' : ZMod q) (βSq' γ' κ' : ℕ) : - ((openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) init impl hq5 hκ - hτ K φF hd hq2 hb zpow Φ' pp' reinterpretCom base' βSq' + ((openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl hq5 hκ + hτ K φF hd hn 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 + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl + hq5 hκ hτ K φF hd hn 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 + (openingChain (zDigits := zDigits) (ω := ω) (mLow := mLow) (m₁ := m₁) (b := b) init impl hq5 hκ hτ + K φF hd hn zpow Φ' pp' reinterpretCom base' βSq' γ' κ').isCWSS end OpeningChain From 2a14c3c3080934d3f8085b5a248daf75238a2021 Mon Sep 17 00:00:00 2001 From: ErVinuelas Date: Mon, 20 Jul 2026 19:56:38 +0200 Subject: [PATCH 21/21] fix[Hachi]: add NOZ26 BibTeX entry for the Hachi paper KB page CI's knowledge-base lint (scripts/kb/lint.py) requires every paper page in docs/kb/papers/ to have a matching key in blueprint/src/references.bib. The branch added docs/kb/papers/NOZ26.md (bibkey NOZ26) but no NOZ26 entry, so the check failed with "Paper page without matching BibTeX key". Add the NOZ26 ePrint entry (2026/156). Author names and metadata verified against the official eprint page: Ngoc Khanh Nguyen, George O'Rourke, Jiapeng Zhang. Co-Authored-By: Claude Opus 4.8 (1M context) --- blueprint/src/references.bib | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blueprint/src/references.bib b/blueprint/src/references.bib index f56087f560..0ed7c5d56d 100644 --- a/blueprint/src/references.bib +++ b/blueprint/src/references.bib @@ -249,6 +249,15 @@ @misc{Jo26 url = {https://eprint.iacr.org/2026/891} } +@misc{NOZ26, + title = {Hachi: Efficient Lattice-Based Multilinear Polynomial Commitments over + Extension Fields}, + author = {Nguyen, Ngoc Khanh and O'Rourke, George and Zhang, Jiapeng}, + howpublished = {Cryptology {ePrint} Archive, Paper 2026/156}, + year = {2026}, + url = {https://eprint.iacr.org/2026/156} +} + @article{listdecoding, title={Algorithmic results in list decoding}, author={Guruswami, Venkatesan and others},