Skip to content

Commit db0f830

Browse files
claudehyperpolymath
authored andcommitted
proof(merkle): Stage 1.2 — root-fold law (root of buildMerkleTree = foldRoot over leaves)
Prove the Merkle root is a deterministic, representation-independent fold over the leaf vector: rootHashWith h (buildMerkleTree hs) = foldRoot h hs, generic over the combiner h (XOR and BLAKE3 alike). - foldRoot: the leaf-only spec of the root (singleton -> element; a 2^(S k) vector splits at 2^k and combines its two sub-folds with h, the same split buildMerkleTree uses). - rootFoldSplit: the inductive step on the already-split halves, so the rootHashWith-of-Node reduction is clean and decoupled from the `with` abstraction (mirrors buildGetLeafSplit; the IH is a higher-order argument). - rootFoldLaw: the theorem; base Refl, step via rootFoldSplit applied to the IH. Pairs with buildGetLeaf to fully characterise buildMerkleTree (leaves + root) and hands the Stage-4 binding argument a leaf-only handle on the root. Machine-checked, axiom-free, total (clean --total build, 19/19). PROOFS.adoc marks Stage 1.2 DONE and adds rootFoldLaw/foldRoot to the proven surface. https://claude.ai/code/session_011z2t8zAxfcCNLJzU7YdpBQ
1 parent 0785a9f commit db0f830

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

docs/PROOFS.adoc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ The core is *clean*: all 19 `ochrance-core` modules carry `%default total` (the
8989
| `Ochrance.Util.VectLemmas` (×5)
9090
| reusable transport/append lemmas: `indexAppendLeft` / `indexAppendRight`,
9191
`indexReplace`, `finToNatReplace`, `splitAtConcat`.
92+
| `rootFoldLaw` + `foldRoot` (Stage 1.2)
93+
| root characterisation: `rootHashWith h (buildMerkleTree hs) = foldRoot h hs` (combiner-generic).
9294
| `roundtripManifest` + sub-codecs (`algoRT`, `modeRT`, `refsRT`, `mpolRT`, …)
9395
| grammar invertibility: `decodeManifest (encodeManifest m) = Just m`.
9496
| `SatisfiesMinimum` / `attestedSatisfiesLax`
@@ -109,9 +111,10 @@ design change or a model downshift mid-stage.
109111
`getLeafHash (buildMerkleTree hs) (finToNat i) = Just (index i hs)`
110112
(`buildGetLeaf` in `Ochrance.Filesystem.MerkleBuild`, on the reusable
111113
`Ochrance.Util.VectLemmas` lemmas). Machine-checked, axiom-free, on `main`.
112-
. *[1.2] Root-fold law*: characterise `rootHashWith h (buildMerkleTree hs)` as a
113-
deterministic fold over `hs` — "the root is a function of the leaves". Pure;
114-
prerequisite of the binding argument.
114+
. *[DONE — 1.2]* Root-fold law: `rootHashWith h (buildMerkleTree hs) = foldRoot h hs`
115+
(`rootFoldLaw` + `foldRoot` in `Ochrance.Filesystem.MerkleBuild`) — the root is a
116+
deterministic, representation-independent fold over the leaves. Combiner-generic;
117+
prerequisite of the binding argument. Machine-checked, axiom-free.
115118
. *[1.3] IO↔pure bridge*: prove `verifyProofIO` / `rootHashBytesIO` compute the
116119
`…With (hashPairBlake3 …)` spec modulo the `Either` allocation-failure plumbing,
117120
against a typed combiner-equality hypothesis — connecting the proven theorem to

ochrance-core/Ochrance/Filesystem/MerkleBuild.idr

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,45 @@ buildGetLeaf {n = S k} hs i with (splitAt (power 2 k)
127127
in rewrite finEq in
128128
rewrite idxEq in
129129
buildGetLeafSplit left right i' buildGetLeaf
130+
131+
--------------------------------------------------------------------------------
132+
-- Root-fold law (Stage 1.2): the root is a deterministic fold over the leaves
133+
--------------------------------------------------------------------------------
134+
135+
||| The Merkle root expressed as a direct fold over the leaf vector, independent
136+
||| of the tree representation: a singleton folds to its element; a `2^(S k)`
137+
||| vector splits into halves (the *same* split `buildMerkleTree` uses) and
138+
||| combines the two sub-folds with `h`. This is the representation-independent
139+
||| *spec* of the root — a pure function of the leaves.
140+
public export
141+
foldRoot : (h : Combiner) -> {n : Nat} -> Vect (power 2 n) HashBytes -> HashBytes
142+
foldRoot h {n = Z} [x] = x
143+
foldRoot h {n = S k} hs =
144+
let hs' : Vect (power 2 k + power 2 k) HashBytes
145+
hs' = replace {p = \x => Vect x HashBytes} (powerTwoSucc k) hs
146+
in case splitAt (power 2 k) hs' of
147+
(l, r) => h (foldRoot h l) (foldRoot h r)
148+
149+
||| Inductive step of the root-fold law on the *already split* halves, so the
150+
||| `rootHashWith`-of-`Node` reduction is clean and decoupled from the `with`
151+
||| abstraction (mirrors `buildGetLeafSplit`). The IH is a higher-order argument.
152+
rootFoldSplit : (h : Combiner) -> {k : Nat} ->
153+
(l, r : Vect (power 2 k) HashBytes) ->
154+
((v : Vect (power 2 k) HashBytes) ->
155+
rootHashWith h (buildMerkleTree {n = k} v) = foldRoot h {n = k} v) ->
156+
rootHashWith h (Node (buildMerkleTree {n = k} l) (buildMerkleTree {n = k} r))
157+
= h (foldRoot h {n = k} l) (foldRoot h {n = k} r)
158+
rootFoldSplit h l r ih = rewrite ih l in rewrite ih r in Refl
159+
160+
||| Root-fold law: the root of the built tree equals the direct leaf fold.
161+
||| `rootHashWith h (buildMerkleTree hs) = foldRoot h hs`. Pairs with
162+
||| `buildGetLeaf` to fully characterise `buildMerkleTree`, and hands the binding
163+
||| argument a leaf-only handle on the root ("the root is a function of the
164+
||| leaves"). The combiner `h` is opaque, so this holds for XOR and BLAKE3 alike.
165+
export
166+
rootFoldLaw : (h : Combiner) -> {n : Nat} -> (hs : Vect (power 2 n) HashBytes) ->
167+
rootHashWith h (buildMerkleTree {n} hs) = foldRoot h {n} hs
168+
rootFoldLaw h {n = Z} [x] = Refl
169+
rootFoldLaw h {n = S k} hs
170+
with (splitAt (power 2 k) (replace {p = \x => Vect x HashBytes} (powerTwoSucc k) hs))
171+
_ | (l, r) = rootFoldSplit h l r (rootFoldLaw h)

0 commit comments

Comments
 (0)