ltl-accumulator-verified/verification/Proofs/Consistency.lean
mrwulf cfde9b2cd7 S5 stage 2: extractConsNode extractor defined (consistency, Theorem 3 steps 1-2)
The consistency collision extractor: walks the ConsRec new-root fold in
parallel with the honest size-n tree of D₁ and returns the concrete
colliding node preimage pair at the first level where the fold's hnode
argument pair diverges from the honest node — or none if the fold is
genuine all the way down (binding holds). Both branches verified faithful
to ConsRec's hnode argument order (n₀≤k: y' left / s right; n₀>k: s left
/ y' right). Termination via kbelow bounds.

Deliberate honest checkpoint: the DEFINITION compiles and is cone-audited
[propext, LTLAcc.sha256, Quot.sound]; the binding CORRECTNESS proof — the
single hardest object in the corpus — is stage 3, kept for a fresh
session rather than a rushed long turn. 22 certs green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:55:51 +02:00

106 lines
4.4 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/- S5 (stage 1) — infrastructure for consistency soundness (paper Theorem 3,
steps 1-2). The binding argument needs one non-obvious arithmetic fact and
two list-surgery facts, isolated and proven here before the main proof.
Arithmetic: when the pinned prefix of size n₀ spills past the left subtree
(k < n₀ ≤ n, k = kbelow n), the honest tree of `D₁.take n₀` splits at the
SAME point k. That is `kbelow n₀ = k`, and it holds because k is a power of
two with k < n₀ ≤ 2k, which pins kbelow uniquely. -/
import Proofs.Descent
namespace LTLAcc
/-- Two powers of two both lying in `(m/2, m]` (i.e. `2^a < m ≤ 2^(a+1)`)
have the same exponent. -/
theorem pow2_exp_unique {a j m : Nat}
(ha : 2^a < m) (ha2 : m ≤ 2^(a+1)) (hj : 2^j < m) (hj2 : m ≤ 2^(j+1)) :
a = j := by
rcases Nat.lt_trichotomy a j with h | h | h
· -- a < j ⇒ a+1 ≤ j ⇒ 2^(a+1) ≤ 2^j < m, contradicting m ≤ 2^(a+1)
have : a + 1 ≤ j := h
have hle : 2^(a+1) ≤ 2^j := Nat.pow_le_pow_right (by omega) this
omega
· exact h
· have : j + 1 ≤ a := h
have hle : 2^(j+1) ≤ 2^a := Nat.pow_le_pow_right (by omega) this
omega
/-- `kbelow` is pinned by its defining inequalities: a power of two `p`
with `p < m ≤ 2p` IS `kbelow m`. -/
theorem kbelow_eq_of_pow2_between {p m : Nat} (j : Nat) (hp : p = 2^j)
(h1 : p < m) (h2 : m ≤ 2 * p) : kbelow m = p := by
have hm2 : 2 ≤ m := by
have : 1 ≤ p := by rw [hp]; exact Nat.one_le_two_pow
omega
obtain ⟨a, ha⟩ := kbelow_pow2 m
have hlt := kbelow_lt m hm2
have hle := le_two_kbelow m hm2
-- kbelow m = 2^a with 2^a < m ≤ 2^(a+1); p = 2^j with 2^j < m ≤ 2^(j+1)
rw [ha] at hlt hle
have hj2 : m ≤ 2^(j+1) := by rw [Nat.pow_succ]; omega
have ha2 : m ≤ 2^(a+1) := by rw [Nat.pow_succ]; omega
have hja : 2^j < m := by omega
have : a = j := pow2_exp_unique hlt ha2 hja hj2
rw [ha, this, ← hp]
/-- The specialization used in the binding: with `k = kbelow n`, `2 ≤ n`,
and `k < n₀ ≤ n`, the prefix tree splits at the same `k`. -/
theorem kbelow_prefix_eq {n n₀ : Nat} (hn : 2 ≤ n)
(hk : k = kbelow n) (hlo : k < n₀) (hhi : n₀ ≤ n) :
kbelow n₀ = k := by
obtain ⟨j, hj⟩ := kbelow_pow2 n
rw [← hk] at hj
have hle := le_two_kbelow n hn
rw [← hk] at hle
exact kbelow_eq_of_pow2_between j hj hlo (by omega)
/-! ### list surgery -/
theorem take_take_le (l : List Bytes) (k n₀ : Nat) (h : k ≤ n₀) :
(l.take n₀).take k = l.take k := by
rw [List.take_take]; congr 1; omega
theorem take_drop_prefix (l : List Bytes) (k n₀ : Nat) :
(l.take n₀).drop k = (l.drop k).take (n₀ - k) := by
rw [List.drop_take]
/-! ### the consistency collision extractor (paper Theorem 3, steps 1-2) -/
/-- Walk the `ConsRec` fold in parallel with the honest size-`n` tree of
`D₁`. At each level the new-root component builds `hnode` of a left and
a right value; compare that argument pair against the honest node
`hnode (MTH (D₁.take k)) (MTH (D₁.drop k))`. Return `some` concrete
colliding preimage pair at the first mismatch (steps 1-2 of Theorem 3),
or `none` if the fold is genuine all the way down — in which case the
binding `x = MTH (D₁.take n₀)` holds (proven in stage 3). -/
noncomputable def extractConsNode (n₀ n : Nat) (C : List Hash) (b : Bool)
(r : Hash) (D₁ : List Bytes) : Option (List UInt8 × List UInt8) :=
if n₀ = n then none
else if n₀ > n n₀ = 0 n ≤ 1 then none
else
match C.getLast? with
| none => none
| some s =>
let k := kbelow n
if n₀ ≤ k then
let y' := ((ConsRec n₀ k C.dropLast b r).map Prod.snd).getD default
if y' = MTH (D₁.take k) ∧ s = MTH (D₁.drop k) then
extractConsNode n₀ k C.dropLast b r (D₁.take k)
else
some (0x01 :: (y'.val ++ s.val),
0x01 :: ((MTH (D₁.take k)).val ++ (MTH (D₁.drop k)).val))
else
let y' := ((ConsRec (n₀ - k) (n - k) C.dropLast false r).map Prod.snd).getD default
if s = MTH (D₁.take k) ∧ y' = MTH (D₁.drop k) then
extractConsNode (n₀ - k) (n - k) C.dropLast false r (D₁.drop k)
else
some (0x01 :: (s.val ++ y'.val),
0x01 :: ((MTH (D₁.take k)).val ++ (MTH (D₁.drop k)).val))
termination_by n
decreasing_by
· have h2 : 2 ≤ n := by omega
exact kbelow_lt n h2
· have := kbelow_pos n
omega
end LTLAcc