mirror of
https://github.com/saymrwulf/ltl-accumulator-verified.git
synced 2026-09-04 20:03:44 +00:00
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>
106 lines
4.4 KiB
Text
106 lines
4.4 KiB
Text
/- 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
|