2026-07-11 10:06:20 +00:00
|
|
|
/- L3 of the accumulator pyramid: the prover-side Path function and
|
|
|
|
|
**Theorem 1 (inclusion completeness)** — honest receipts always verify:
|
|
|
|
|
|
|
|
|
|
Root (hleaf D[m]) m |D| (Path m D) = some (MTH D)
|
|
|
|
|
|
|
|
|
|
No hash property is used anywhere (the theorem is about shapes). -/
|
|
|
|
|
import Proofs.Basic
|
|
|
|
|
|
|
|
|
|
namespace LTLAcc
|
|
|
|
|
|
|
|
|
|
/-- `Path` (paper §5.3): the operator's inclusion path for index `m`.
|
|
|
|
|
Sibling subtree heads, leaf-to-root order (the paper's `‖ [s]`). -/
|
S3/L4-L5: root binding (Lemma 2, Path instance) + Theorem 2, constructive
The crux layer — the statement whose HAND proof once carried the frontier
coverage bug is now kernel-checked.
- gen: hash outputs refactored to Hash = {l : List UInt8 // l.length = 32}.
MECHANIZATION FINDING: the paper's pair-coincidence step ('equal hnode
values of distinct argument pairs are a collision') is load-bearing on
FIXED-WIDTH outputs — with unconstrained byte strings x++s = X++Y does
not split. hnode_preimage_inj (cone: propext) makes this explicit via
List.append_inj on equal-length components. Queued as a half-sentence
for the paper's next cycle.
- HasCollision := ∃ x y, x ≠ y ∧ sha256 x = sha256 y — appears ONLY as a
conclusion, never a hypothesis (no collision-resistance assumed).
- hnode_inj_or_collision / hleaf_inj_or_collision: the per-node dichotomy.
- root_binding: any accepting reconstruction from (v,P) to the honest root
either IS the honest receipt (leaf hash AND full path P = Path m D — case
(ii) pinning every consumed sibling) or exhibits a collision. Motive
quantifies (v,P); induction on Path; k-fold discipline.
- incl_sound (Theorem 2, position binding): accepting a wrong leaf at m
yields a collision. Cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] — the single hash axiom, pinned in check.sh. ALL GREEN.
Also: Root n=1 branch changed from list-match to decidable 'if P = []'
(well-founded unfolding generated a spurious exhaustiveness obligation);
Root_one_cons added. Fable-5 statement-audit passed. LTL untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:21:17 +00:00
|
|
|
noncomputable def Path (m : Nat) (D : List Bytes) : List Hash :=
|
2026-07-11 10:06:20 +00:00
|
|
|
if D.length ≤ 1 then []
|
|
|
|
|
else
|
|
|
|
|
let k := kbelow D.length
|
|
|
|
|
if m < k then Path m (D.take k) ++ [MTH (D.drop k)]
|
|
|
|
|
else Path (m - k) (D.drop k) ++ [MTH (D.take k)]
|
|
|
|
|
termination_by D.length
|
|
|
|
|
decreasing_by
|
|
|
|
|
· simp only [List.length_take]
|
|
|
|
|
have h2 : 2 ≤ D.length := by omega
|
|
|
|
|
have hk := kbelow_lt D.length h2
|
|
|
|
|
omega
|
|
|
|
|
· simp only [List.length_drop]
|
|
|
|
|
have hp := kbelow_pos D.length
|
|
|
|
|
omega
|
|
|
|
|
|
|
|
|
|
/-! ### List helper lemmas (self-contained; no stdlib-name dependence) -/
|
|
|
|
|
|
|
|
|
|
theorem getD_take (l : List Bytes) (k m : Nat) (h : m < k) :
|
|
|
|
|
(l.take k).getD m [] = l.getD m [] := by
|
|
|
|
|
induction l generalizing k m with
|
|
|
|
|
| nil => simp
|
|
|
|
|
| cons a t ih =>
|
|
|
|
|
cases k with
|
|
|
|
|
| zero => omega
|
|
|
|
|
| succ k' =>
|
|
|
|
|
cases m with
|
|
|
|
|
| zero => simp
|
|
|
|
|
| succ m' =>
|
|
|
|
|
simp only [List.take_succ_cons, List.getD_cons_succ]
|
|
|
|
|
exact ih k' m' (by omega)
|
|
|
|
|
|
|
|
|
|
theorem getD_drop (l : List Bytes) (k i : Nat) :
|
|
|
|
|
(l.drop k).getD i [] = l.getD (k + i) [] := by
|
|
|
|
|
induction l generalizing k with
|
|
|
|
|
| nil => simp
|
|
|
|
|
| cons a t ih =>
|
|
|
|
|
cases k with
|
|
|
|
|
| zero => simp
|
|
|
|
|
| succ k' =>
|
|
|
|
|
have hidx : k' + 1 + i = (k' + i) + 1 := by omega
|
|
|
|
|
simp only [List.drop_succ_cons, hidx, List.getD_cons_succ]
|
|
|
|
|
exact ih k'
|
|
|
|
|
|
|
|
|
|
theorem exists_singleton_of_length_one (l : List Bytes)
|
|
|
|
|
(h : l.length = 1) : ∃ d, l = [d] := by
|
|
|
|
|
cases l with
|
|
|
|
|
| nil => simp at h
|
|
|
|
|
| cons a t =>
|
|
|
|
|
cases t with
|
|
|
|
|
| nil => exact ⟨a, rfl⟩
|
|
|
|
|
| cons b u => simp at h
|
|
|
|
|
|
|
|
|
|
/-! ### Equation lemmas -/
|
|
|
|
|
|
|
|
|
|
theorem MTH_single (d : Bytes) : MTH [d] = hleaf d := by
|
|
|
|
|
rw [MTH]; rfl
|
|
|
|
|
|
|
|
|
|
theorem MTH_split (D : List Bytes) (h : 2 ≤ D.length) :
|
|
|
|
|
MTH D = hnode (MTH (D.take (kbelow D.length)))
|
|
|
|
|
(MTH (D.drop (kbelow D.length))) := by
|
|
|
|
|
rw [MTH]
|
|
|
|
|
have h0 : ¬ D.length = 0 := by omega
|
|
|
|
|
have h1 : ¬ D.length = 1 := by omega
|
|
|
|
|
simp only [h0, h1, dite_false]
|
|
|
|
|
|
S3/L4-L5: root binding (Lemma 2, Path instance) + Theorem 2, constructive
The crux layer — the statement whose HAND proof once carried the frontier
coverage bug is now kernel-checked.
- gen: hash outputs refactored to Hash = {l : List UInt8 // l.length = 32}.
MECHANIZATION FINDING: the paper's pair-coincidence step ('equal hnode
values of distinct argument pairs are a collision') is load-bearing on
FIXED-WIDTH outputs — with unconstrained byte strings x++s = X++Y does
not split. hnode_preimage_inj (cone: propext) makes this explicit via
List.append_inj on equal-length components. Queued as a half-sentence
for the paper's next cycle.
- HasCollision := ∃ x y, x ≠ y ∧ sha256 x = sha256 y — appears ONLY as a
conclusion, never a hypothesis (no collision-resistance assumed).
- hnode_inj_or_collision / hleaf_inj_or_collision: the per-node dichotomy.
- root_binding: any accepting reconstruction from (v,P) to the honest root
either IS the honest receipt (leaf hash AND full path P = Path m D — case
(ii) pinning every consumed sibling) or exhibits a collision. Motive
quantifies (v,P); induction on Path; k-fold discipline.
- incl_sound (Theorem 2, position binding): accepting a wrong leaf at m
yields a collision. Cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] — the single hash axiom, pinned in check.sh. ALL GREEN.
Also: Root n=1 branch changed from list-match to decidable 'if P = []'
(well-founded unfolding generated a spurious exhaustiveness obligation);
Root_one_cons added. Fable-5 statement-audit passed. LTL untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:21:17 +00:00
|
|
|
theorem Root_one (v : Hash) (m : Nat) : Root v m 1 [] = some v := by
|
|
|
|
|
rw [Root]; simp
|
|
|
|
|
|
|
|
|
|
theorem Root_one_cons (v : Hash) (m : Nat) (p : Hash) (q : List Hash) :
|
|
|
|
|
Root v m 1 (p :: q) = none := by
|
|
|
|
|
rw [Root]; simp
|
2026-07-11 10:06:20 +00:00
|
|
|
|
|
|
|
|
/-- `Root` at a composite size, left branch (`m < k`). -/
|
S3/L4-L5: root binding (Lemma 2, Path instance) + Theorem 2, constructive
The crux layer — the statement whose HAND proof once carried the frontier
coverage bug is now kernel-checked.
- gen: hash outputs refactored to Hash = {l : List UInt8 // l.length = 32}.
MECHANIZATION FINDING: the paper's pair-coincidence step ('equal hnode
values of distinct argument pairs are a collision') is load-bearing on
FIXED-WIDTH outputs — with unconstrained byte strings x++s = X++Y does
not split. hnode_preimage_inj (cone: propext) makes this explicit via
List.append_inj on equal-length components. Queued as a half-sentence
for the paper's next cycle.
- HasCollision := ∃ x y, x ≠ y ∧ sha256 x = sha256 y — appears ONLY as a
conclusion, never a hypothesis (no collision-resistance assumed).
- hnode_inj_or_collision / hleaf_inj_or_collision: the per-node dichotomy.
- root_binding: any accepting reconstruction from (v,P) to the honest root
either IS the honest receipt (leaf hash AND full path P = Path m D — case
(ii) pinning every consumed sibling) or exhibits a collision. Motive
quantifies (v,P); induction on Path; k-fold discipline.
- incl_sound (Theorem 2, position binding): accepting a wrong leaf at m
yields a collision. Cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] — the single hash axiom, pinned in check.sh. ALL GREEN.
Also: Root n=1 branch changed from list-match to decidable 'if P = []'
(well-founded unfolding generated a spurious exhaustiveness obligation);
Root_one_cons added. Fable-5 statement-audit passed. LTL untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:21:17 +00:00
|
|
|
theorem Root_left (v : Hash) (m n : Nat) (P : List Hash) (s : Hash)
|
2026-07-11 10:06:20 +00:00
|
|
|
(hn : 2 ≤ n) (hm : m < kbelow n) :
|
|
|
|
|
Root v m n (P ++ [s]) = (Root v m (kbelow n) P).map (hnode · s) := by
|
|
|
|
|
have h1 : ¬ n = 1 := by omega
|
|
|
|
|
have h0 : ¬ n = 0 := by omega
|
|
|
|
|
cases hR : Root v m (kbelow n) P with
|
|
|
|
|
| none =>
|
|
|
|
|
rw [Root]; simp [h0, h1, hm, hR]
|
|
|
|
|
| some x =>
|
|
|
|
|
rw [Root]; simp [h0, h1, hm, hR]
|
|
|
|
|
|
|
|
|
|
/-- `Root` at a composite size, right branch (`m ≥ k`). -/
|
S3/L4-L5: root binding (Lemma 2, Path instance) + Theorem 2, constructive
The crux layer — the statement whose HAND proof once carried the frontier
coverage bug is now kernel-checked.
- gen: hash outputs refactored to Hash = {l : List UInt8 // l.length = 32}.
MECHANIZATION FINDING: the paper's pair-coincidence step ('equal hnode
values of distinct argument pairs are a collision') is load-bearing on
FIXED-WIDTH outputs — with unconstrained byte strings x++s = X++Y does
not split. hnode_preimage_inj (cone: propext) makes this explicit via
List.append_inj on equal-length components. Queued as a half-sentence
for the paper's next cycle.
- HasCollision := ∃ x y, x ≠ y ∧ sha256 x = sha256 y — appears ONLY as a
conclusion, never a hypothesis (no collision-resistance assumed).
- hnode_inj_or_collision / hleaf_inj_or_collision: the per-node dichotomy.
- root_binding: any accepting reconstruction from (v,P) to the honest root
either IS the honest receipt (leaf hash AND full path P = Path m D — case
(ii) pinning every consumed sibling) or exhibits a collision. Motive
quantifies (v,P); induction on Path; k-fold discipline.
- incl_sound (Theorem 2, position binding): accepting a wrong leaf at m
yields a collision. Cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] — the single hash axiom, pinned in check.sh. ALL GREEN.
Also: Root n=1 branch changed from list-match to decidable 'if P = []'
(well-founded unfolding generated a spurious exhaustiveness obligation);
Root_one_cons added. Fable-5 statement-audit passed. LTL untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:21:17 +00:00
|
|
|
theorem Root_right (v : Hash) (m n : Nat) (P : List Hash) (s : Hash)
|
2026-07-11 10:06:20 +00:00
|
|
|
(hn : 2 ≤ n) (hm : ¬ m < kbelow n) :
|
|
|
|
|
Root v m n (P ++ [s]) =
|
|
|
|
|
(Root v (m - kbelow n) (n - kbelow n) P).map (hnode s ·) := by
|
|
|
|
|
have h1 : ¬ n = 1 := by omega
|
|
|
|
|
have h0 : ¬ n = 0 := by omega
|
|
|
|
|
cases hR : Root v (m - kbelow n) (n - kbelow n) P with
|
|
|
|
|
| none =>
|
|
|
|
|
rw [Root]; simp [h0, h1, hm, hR]
|
|
|
|
|
| some x =>
|
|
|
|
|
rw [Root]; simp [h0, h1, hm, hR]
|
|
|
|
|
|
|
|
|
|
/-! ### Theorem 1 -/
|
|
|
|
|
|
|
|
|
|
/-- **Theorem 1 (Inclusion completeness)**, paper §6: for every non-empty
|
|
|
|
|
leaf list `D` and every `m < |D|`, the honestly produced receipt
|
|
|
|
|
verifies to the honest root. -/
|
|
|
|
|
theorem incl_complete (m : Nat) (D : List Bytes) (hm : m < D.length) :
|
|
|
|
|
Root (hleaf (D.getD m [])) m D.length (Path m D) = some (MTH D) := by
|
|
|
|
|
induction m, D using Path.induct with
|
|
|
|
|
| case1 m D hle =>
|
|
|
|
|
-- |D| ≤ 1 and m < |D| force D = [d], m = 0
|
|
|
|
|
have h1 : D.length = 1 := by omega
|
|
|
|
|
obtain ⟨d, rfl⟩ := exists_singleton_of_length_one D h1
|
|
|
|
|
have hm0 : m = 0 := by simpa using hm
|
|
|
|
|
subst hm0
|
|
|
|
|
rw [Path]
|
|
|
|
|
simp only [List.length_singleton, if_pos (by omega : (1:Nat) ≤ 1)]
|
|
|
|
|
rw [MTH_single]
|
|
|
|
|
simpa using Root_one (hleaf d) 0
|
|
|
|
|
| case2 m D hgt k hmk ih =>
|
|
|
|
|
have h2 : 2 ≤ D.length := by omega
|
|
|
|
|
have hkeq : k = kbelow D.length := rfl
|
|
|
|
|
have hkl : k < D.length := by rw [hkeq]; exact kbelow_lt D.length h2
|
|
|
|
|
have hmk' : m < kbelow D.length := by rw [← hkeq]; exact hmk
|
|
|
|
|
rw [Path]
|
|
|
|
|
simp only [if_neg hgt, ← hkeq, if_pos hmk]
|
|
|
|
|
rw [Root_left _ _ _ _ _ h2 hmk', ← hkeq]
|
|
|
|
|
have htklen : (D.take k).length = k := by
|
|
|
|
|
simp [List.length_take]; omega
|
|
|
|
|
have ihm : m < (D.take k).length := by omega
|
|
|
|
|
have hrec := ih ihm
|
|
|
|
|
rw [getD_take D k m hmk, htklen] at hrec
|
|
|
|
|
rw [hrec, MTH_split D h2, ← hkeq]
|
|
|
|
|
rfl
|
|
|
|
|
| case3 m D hgt k hmk ih =>
|
|
|
|
|
have h2 : 2 ≤ D.length := by omega
|
|
|
|
|
have hkeq : k = kbelow D.length := rfl
|
|
|
|
|
have hkl : k < D.length := by rw [hkeq]; exact kbelow_lt D.length h2
|
|
|
|
|
have hkp : 0 < k := by rw [hkeq]; exact kbelow_pos D.length
|
|
|
|
|
have hmk' : ¬ m < kbelow D.length := by rw [← hkeq]; exact hmk
|
|
|
|
|
rw [Path]
|
|
|
|
|
simp only [if_neg hgt, ← hkeq, if_neg hmk]
|
|
|
|
|
rw [Root_right _ _ _ _ _ h2 hmk', ← hkeq]
|
|
|
|
|
have hidx : k + (m - k) = m := by omega
|
|
|
|
|
have hget : (D.drop k).getD (m - k) [] = D.getD m [] := by
|
|
|
|
|
rw [getD_drop, hidx]
|
|
|
|
|
have hdplen : (D.drop k).length = D.length - k := by
|
|
|
|
|
simp [List.length_drop]
|
|
|
|
|
have ihm : m - k < (D.drop k).length := by omega
|
|
|
|
|
have hrec := ih ihm
|
|
|
|
|
rw [hget, hdplen] at hrec
|
|
|
|
|
rw [hrec, MTH_split D h2, ← hkeq]
|
|
|
|
|
rfl
|
|
|
|
|
|
S3.5: explicit collision extractor — Theorem 2 made non-vacuous, vacuous forms removed
The S3 Socratic re-audit found incl_sound was kernel-perfect but VACUOUS:
its '... ∨ HasCollision' disjunct (∃ x y, x≠y ∧ sha256 x = sha256 y) is
provable by pigeonhole ALONE (sha256: infinite List UInt8 → finite
32-byte Hash), so the theorem said nothing about forgeries. Even a
data-carrying {p // IsCollision p} disjunct fails (Classical.choice
inhabits it). The only faithful rendering of the paper's 'explicit
algorithm 𝓔' is a NAMED FUNCTION whose correctness is a claim about ITS
OUTPUT.
- extractIncl (m D d P): total function that walks the honest tree and
returns the concrete colliding preimage pair at the first divergence
(a node preimage pair, or the leaf preimage pair at the bottom).
- extractIncl_correct: d ≠ D[m] ∧ accepting-receipt →
IsCollision (extractIncl …).1 (extractIncl …).2. A statement ABOUT the
fixed function's output; pigeonhole cannot discharge it.
ADVERSARIAL CHECK (probe, since removed): proved
¬ IsCollision (extractIncl 0 [[7]] [7] []) — i.e. on a NON-forgery input
the output is provably NOT a collision, so the conclusion is genuinely
false for some inputs ⇒ non-vacuous, choice-proof.
- Removed the vacuous theorems entirely (incl_sound, root_binding,
hnode/hleaf_inj_or_collision, HasCollision def) so no hollow statement
survives in a corpus destined for the log. Kept the real building
blocks (hnode_preimage_inj [propext]; eq_dropLast helper moved to
Completeness; Binding.lean deleted).
extractIncl_correct cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound]. THE button green (14 certs). Fable statement-audit passed.
LTL untouched (12 leaves, bcd15f9d).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 13:31:21 +00:00
|
|
|
/-- A non-empty list is its `dropLast` plus its last element
|
|
|
|
|
(self-contained; no stdlib-name dependence). -/
|
|
|
|
|
theorem eq_dropLast_append_of_getLast? (l : List Hash) (s : Hash)
|
|
|
|
|
(h : l.getLast? = some s) : l = l.dropLast ++ [s] := by
|
|
|
|
|
induction l with
|
|
|
|
|
| nil => simp at h
|
|
|
|
|
| cons a t ih =>
|
|
|
|
|
cases t with
|
|
|
|
|
| nil =>
|
|
|
|
|
simp at h
|
|
|
|
|
subst h
|
|
|
|
|
rfl
|
|
|
|
|
| cons b u =>
|
|
|
|
|
have hh : (b :: u).getLast? = some s := by
|
|
|
|
|
simpa using h
|
|
|
|
|
have := ih hh
|
|
|
|
|
calc a :: b :: u = a :: (b :: u) := rfl
|
|
|
|
|
_ = a :: ((b :: u).dropLast ++ [s]) := by rw [← this]
|
|
|
|
|
_ = (a :: b :: u).dropLast ++ [s] := by simp
|
|
|
|
|
|
revision round 1: address both external reviews (GPT-5.6 + second Claude)
No theorem was wrong; every fix is spec-surface, audit-mechanism, docs,
or harness coverage. Changes:
LEAN (Claude F1, GPT M4):
- acceptIncl: the consumer's inclusion accept (m<n ∧ Root=some r) is now
a named object, not just a theorem hypothesis. Root alone accepts
out-of-range m; acceptIncl pins the guard.
- acceptIncl_complete / acceptIncl_sound: route Thm 1/2 through it.
- extractCons_correct_paper: Thm 3 at the paper's exact quantifiers
(n₀≤n₁, no separate 0<n₀; n₀=0 discharged since D₀=[]=take 0).
SCRIPT (GPT H1/H2, Claude F3):
- Phase 3b: fail-closed audit-surface COVERAGE — every named decl under
Proofs/ and gen/ must be in CONES or a documented EXCLUDE (sha256,
Bytes); anonymous gen instances count-pinned; every CONES key must be
queried by AxiomCheck (no pin-but-never-check). Tested: an
unclassified theorem now makes the button exit 1.
- H2: distinct markers — LEAN GREEN always, ATTESTATION GREEN only when
fidelity actually ran; SKIP/absent-pacta no longer emit the strong
marker. Attestation gate keys on ATTESTATION GREEN.
- Phase 0: orphan-olean guard (every Proofs/*.olean needs a sibling
.lean); deleted 6 orphans; untracked all *.olean/.lake from git and
gitignored them (root cause of the F3 tarball leak).
HARNESS (Claude F1, GPT M3):
- added out-of-range families (m≥n, m>n, n₀>n₁, n₀=0); re-pinned counts
230,271 / 230,016 (match the reviewer's independent RFC difftest
exactly); narrowed 'exhaustive' wording to the tested domain.
DOCS: README stale rows fixed (freeze banner no longer contradicts
table); KNOWN-GAPS gap 3 reworded (general Lemma 2 = specializations),
+gaps 9 (cost), 10 (pin init), 11 (acceptIncl resolved); STATEMENT-MAP
+acceptIncl rows, +Lemma-2-general note, +constant-vs-property
clarification for §10(i).
Button: EXIT 0, coverage complete, ATTESTATION GREEN, 230,271/230,016.
56 pinned cones over an ENFORCED surface. LTL untouched (12, bcd15f9d).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:53:47 +00:00
|
|
|
/-- Completeness through the named acceptance predicate (review F1): the
|
|
|
|
|
honest receipt satisfies `acceptIncl`. -/
|
|
|
|
|
theorem acceptIncl_complete (m : Nat) (D : List Bytes) (hm : m < D.length) :
|
|
|
|
|
acceptIncl (D.getD m []) m D.length (Path m D) (MTH D) :=
|
|
|
|
|
⟨hm, incl_complete m D hm⟩
|
|
|
|
|
|
2026-07-11 10:06:20 +00:00
|
|
|
end LTLAcc
|