|
1 | 1 | From Perennial.program_proof Require Import grove_prelude.
|
2 | 2 | From Goose.github_com.mit_pdos.pav Require Import merkle.
|
3 | 3 |
|
4 |
| -From Perennial.program_proof.pav Require Import classes misc cryptoffi cryptoutil merkle_internal. |
| 4 | +From Perennial.program_proof.pav Require Import classes misc cryptoffi cryptoutil. |
5 | 5 | From Perennial.program_proof Require Import std_proof.
|
6 | 6 |
|
| 7 | +Section internal. |
| 8 | +Context `{!heapGS Σ}. |
| 9 | + |
| 10 | +Inductive tree : Type := |
| 11 | + (* Cut only exists for proof checking trees. *) |
| 12 | + | Cut : list w8 → tree |
| 13 | + | Empty : tree |
| 14 | + | Leaf : list w8 → tree |
| 15 | + | Interior : list tree → tree. |
| 16 | + |
| 17 | +Fixpoint has_entry (tr : tree) (id : list w8) (node : tree) : Prop := |
| 18 | + match tr with |
| 19 | + | Cut _ => False |
| 20 | + | Empty => id = [] ∧ tr = node |
| 21 | + | Leaf val => id = [] ∧ tr = node |
| 22 | + | Interior children => |
| 23 | + match id with |
| 24 | + | [] => False |
| 25 | + | pos :: rest => |
| 26 | + ∃ child, children !! Z.to_nat (word.unsigned pos) = Some child ∧ has_entry child rest node |
| 27 | + end |
| 28 | + end. |
| 29 | + |
| 30 | +(* The core invariant that defines the recursive hashing structure of merkle trees. *) |
| 31 | +Fixpoint is_node_hash (tr : tree) (hash : list w8) : iProp Σ := |
| 32 | + match tr with |
| 33 | + | Cut hash' => ⌜hash = hash' ∧ length hash' = 32%nat⌝ |
| 34 | + | Empty => is_hash [W8 0] hash |
| 35 | + | Leaf val => is_hash (val ++ [W8 1]) hash |
| 36 | + | Interior children => |
| 37 | + ∃ (child_hashes : list (list w8)), |
| 38 | + let map_fn := (λ c h, is_node_hash c h) in |
| 39 | + ([∗ list] child_fn;hash' ∈ (map_fn <$> children);child_hashes, |
| 40 | + child_fn hash') ∗ |
| 41 | + is_hash (concat child_hashes ++ [W8 2]) hash |
| 42 | + end%I. |
| 43 | + |
| 44 | +#[global] |
| 45 | +Instance is_node_hash_timeless tr hash : Timeless (is_node_hash tr hash). |
| 46 | +Proof. Admitted. |
| 47 | + |
| 48 | +#[global] |
| 49 | +Instance is_node_hash_persistent tr hash : Persistent (is_node_hash tr hash). |
| 50 | +Proof. Admitted. |
| 51 | + |
| 52 | +Lemma node_hash_len tr hash : |
| 53 | + is_node_hash tr hash -∗ ⌜length hash = 32%nat⌝. |
| 54 | +Proof. |
| 55 | + iIntros "Htree". |
| 56 | + destruct tr. |
| 57 | + { iDestruct "Htree" as "[%Heq %Hlen]". naive_solver. } |
| 58 | + 1-2: iDestruct (is_hash_len with "Htree") as "%Hlen"; naive_solver. |
| 59 | + { |
| 60 | + iDestruct "Htree" as (ch) "[_ Htree]". |
| 61 | + by iDestruct (is_hash_len with "Htree") as "%Hlen". |
| 62 | + } |
| 63 | +Qed. |
| 64 | + |
| 65 | +Lemma concat_len_eq {A : Type} sz (l1 l2 : list (list A)) : |
| 66 | + concat l1 = concat l2 → |
| 67 | + (Forall (λ l, length l = sz) l1) → |
| 68 | + (Forall (λ l, length l = sz) l2) → |
| 69 | + 0 < sz → |
| 70 | + l1 = l2. |
| 71 | +Proof. |
| 72 | + intros Heq_concat Hlen_l1 Hlen_l2 Hsz. |
| 73 | + apply (f_equal length) in Heq_concat as Heq_concat_len. |
| 74 | + do 2 rewrite concat_length in Heq_concat_len. |
| 75 | + generalize dependent l2. |
| 76 | + induction l1 as [|a1]; destruct l2 as [|a2]; simpl; |
| 77 | + intros Heq_concat Hlen_l2 Heq_concat_len; |
| 78 | + decompose_Forall; eauto with lia. |
| 79 | + apply (f_equal (take (length a1))) in Heq_concat as Htake_concat. |
| 80 | + apply (f_equal (drop (length a1))) in Heq_concat as Hdrop_concat. |
| 81 | + rewrite <-H in Htake_concat at 2. |
| 82 | + rewrite <-H in Hdrop_concat at 2. |
| 83 | + do 2 rewrite take_app_length in Htake_concat. |
| 84 | + do 2 rewrite drop_app_length in Hdrop_concat. |
| 85 | + rewrite Htake_concat. |
| 86 | + apply (app_inv_head_iff [a2]). |
| 87 | + apply IHl1; eauto with lia. |
| 88 | +Qed. |
| 89 | + |
| 90 | +(* TODO: is this necessary? can we just apply node_hash_len when necessary? *) |
| 91 | +Lemma sep_node_hash_len ct ch : |
| 92 | + ([∗ list] t;h ∈ ct;ch, is_node_hash t h) -∗ |
| 93 | + ⌜Forall (λ l, length l = 32%nat) ch⌝. |
| 94 | +Proof. |
| 95 | + iIntros "Htree". |
| 96 | + iDestruct (big_sepL2_impl _ (λ _ _ h, ⌜length h = 32%nat⌝%I) with "Htree []") as "Hlen_sepL2". |
| 97 | + { |
| 98 | + iIntros "!>" (???) "_ _ Hhash". |
| 99 | + by iDestruct (node_hash_len with "Hhash") as "Hlen". |
| 100 | + } |
| 101 | + iDestruct (big_sepL2_const_sepL_r with "Hlen_sepL2") as "[_ %Hlen_sepL1]". |
| 102 | + iPureIntro. |
| 103 | + by apply Forall_lookup. |
| 104 | +Qed. |
| 105 | + |
| 106 | +Lemma disj_empty_leaf digest v : |
| 107 | + is_node_hash Empty digest -∗ |
| 108 | + is_node_hash (Leaf v) digest -∗ |
| 109 | + False. |
| 110 | +Proof. |
| 111 | + iIntros "Hempty Hleaf". |
| 112 | + iDestruct (is_hash_inj with "Hempty Hleaf") as "%Heq". |
| 113 | + iPureIntro. |
| 114 | + destruct v as [|a]; [naive_solver|]. |
| 115 | + (* TODO: why doesn't list_simplifier or solve_length take care of this? *) |
| 116 | + apply (f_equal length) in Heq. |
| 117 | + rewrite length_app in Heq. |
| 118 | + simpl in *. |
| 119 | + lia. |
| 120 | +Qed. |
| 121 | + |
| 122 | +(* Ownership of a logical merkle tree. *) |
| 123 | +Definition own_node_except' (recur : option (list w8) -d> loc -d> tree -d> iPropO Σ) : option (list w8) -d> loc -d> tree -d> iPropO Σ := |
| 124 | + (λ path ptr_tr tr, |
| 125 | + match tr with |
| 126 | + (* We should never have cuts in in-memory trees. *) |
| 127 | + | Cut _ => False |
| 128 | + | Empty => True |
| 129 | + | Leaf val => |
| 130 | + ∃ sl_val hash sl_hash, |
| 131 | + "Hval" ∷ own_slice_small sl_val byteT (DfracOwn 1) val ∗ |
| 132 | + "Hptr_val" ∷ ptr_tr ↦[node :: "val"] (slice_val sl_val) ∗ |
| 133 | + "Hhash" ∷ own_slice_small sl_hash byteT (DfracOwn 1) hash ∗ |
| 134 | + "Hsl_hash" ∷ ptr_tr ↦[node :: "hash"] (slice_val sl_hash) ∗ |
| 135 | + "#His_hash" ∷ match path with |
| 136 | + | None => is_node_hash tr hash |
| 137 | + | _ => True |
| 138 | + end |
| 139 | + | Interior children => |
| 140 | + ∃ hash sl_hash sl_children ptr_children sl_val, |
| 141 | + "Hhash" ∷ own_slice_small sl_hash byteT (DfracOwn 1) hash ∗ |
| 142 | + "Hsl_children" ∷ own_slice_small sl_children ptrT (DfracOwn 1) ptr_children ∗ |
| 143 | + "Hptr_val" ∷ ptr_tr ↦[node :: "val"] (slice_val sl_val) ∗ (* XXX: keeping this to ensure ptr_tr is not null *) |
| 144 | + "Hptr_children" ∷ ptr_tr ↦[node :: "Children"] (slice_val sl_children) ∗ |
| 145 | + "Hchildren" ∷ |
| 146 | + ([∗ list] idx ↦ child;ptr_child ∈ children;ptr_children, |
| 147 | + match path with |
| 148 | + | Some (a :: path) => if decide (uint.nat a = idx) then ▷ recur (Some path) ptr_child child |
| 149 | + else ▷ recur None ptr_child child |
| 150 | + | None => ▷ recur None ptr_child child |
| 151 | + | Some [] => ▷ recur None ptr_child child |
| 152 | + end |
| 153 | + ) ∗ |
| 154 | + "#His_hash" ∷ match path with |
| 155 | + | None => is_node_hash tr hash |
| 156 | + | _ => True |
| 157 | + end |
| 158 | + end)%I. |
| 159 | + |
| 160 | +Local Instance own_node_except'_contractive : Contractive own_node_except'. |
| 161 | +Proof. Admitted. |
| 162 | + |
| 163 | +Definition own_node_except : option (list w8) → loc → tree → iProp Σ := fixpoint own_node_except'. |
| 164 | + |
| 165 | +Lemma own_node_except_unfold path ptr obj : |
| 166 | + own_node_except path ptr obj ⊣⊢ (own_node_except' own_node_except) path ptr obj. |
| 167 | +Proof. |
| 168 | + apply (fixpoint_unfold own_node_except'). |
| 169 | +Qed. |
| 170 | + |
| 171 | +Definition tree_to_map (tr : tree) : gmap (list w8) (list w8) := |
| 172 | + let fix traverse (tr : tree) (acc : gmap (list w8) (list w8)) (id : list w8) := |
| 173 | + match tr with |
| 174 | + | Cut _ => acc |
| 175 | + | Empty => acc |
| 176 | + | Leaf val => <[id:=val]>acc |
| 177 | + | Interior children => |
| 178 | + (* Grab all entries from the children, storing the ongoing id. *) |
| 179 | + (foldr |
| 180 | + (λ child (pIdxAcc:(Z * gmap (list w8) (list w8))), |
| 181 | + let acc' := traverse child pIdxAcc.2 (id ++ [W8 pIdxAcc.1]) |
| 182 | + in (pIdxAcc.1 + 1, acc')) |
| 183 | + (0, acc) children |
| 184 | + ).2 |
| 185 | + end |
| 186 | + in traverse tr ∅ []. |
| 187 | + |
| 188 | +Lemma own_node_except_mono path2 path1 root tr : |
| 189 | + match path1, path2 with |
| 190 | + | Some a, Some b => prefix a b |
| 191 | + | None, _ => True |
| 192 | + | _, _ => False |
| 193 | + end → |
| 194 | + own_node_except path1 root tr -∗ |
| 195 | + own_node_except path2 root tr. |
| 196 | +Proof. |
| 197 | +Admitted. |
| 198 | + |
| 199 | +End internal. |
| 200 | + |
7 | 201 | Section external.
|
8 | 202 | Context `{!heapGS Σ}.
|
9 | 203 |
|
@@ -153,7 +347,7 @@ Section local_defs.
|
153 | 347 | Context `{!heapGS Σ}.
|
154 | 348 | Definition own (ptr : loc) (obj : t) : iProp Σ :=
|
155 | 349 | ∃ sl_Val sl_Dig sl_Proof,
|
156 |
| - "HVal" ∷ ptr ↦[GetReply :: "Val"] (slice_val sl_Val) ∗ |
| 350 | + "HVal" ∷ ptr ↦[GetReply :: "val"] (slice_val sl_Val) ∗ |
157 | 351 | "Hptr_Val" ∷ own_slice_small sl_Val byteT (DfracOwn 1) obj.(Val) ∗
|
158 | 352 | "HDig" ∷ ptr ↦[GetReply :: "Digest"] (slice_val sl_Dig) ∗
|
159 | 353 | "Hptr_Dig" ∷ own_slice_small sl_Dig byteT (DfracOwn 1) obj.(Digest) ∗
|
@@ -201,7 +395,90 @@ Lemma wp_Tree_Put ptr_tr entries sl_id id sl_val val d0 d1 :
|
201 | 395 | else
|
202 | 396 | "Htree" ∷ own_merkle ptr_tr entries
|
203 | 397 | }}}.
|
204 |
| -Proof. Admitted. |
| 398 | +Proof. |
| 399 | + iIntros (?) "H HΦ". iNamed "H". |
| 400 | + wp_rec. |
| 401 | + wp_pures. |
| 402 | + iDestruct (own_slice_small_sz with "Hid") as %Hsz_id. |
| 403 | + wp_apply wp_slice_len. |
| 404 | + wp_pures. |
| 405 | + wp_if_destruct. |
| 406 | + { (* return an error. *) |
| 407 | + wp_pures. |
| 408 | + replace (slice.nil) with (slice_val Slice.nil) by done. |
| 409 | + iApply "HΦ". |
| 410 | + iModIntro. iFrame. iPureIntro. |
| 411 | + word. |
| 412 | + } |
| 413 | + wp_rec. |
| 414 | + wp_pures. |
| 415 | + iNamed "Htree". |
| 416 | + wp_loadField. |
| 417 | + wp_pures. |
| 418 | + |
| 419 | + (* weaken to loop invariant. *) |
| 420 | + iDestruct (own_node_except_mono (Some id) with "[$]") as "Hnode". |
| 421 | + { done. } |
| 422 | + |
| 423 | + wp_bind (if: _ then _ else _)%E. |
| 424 | + wp_apply (wp_wand _ _ _ (λ _, |
| 425 | + ∃ (root : loc) tr, |
| 426 | + "Hroot" ∷ ptr_tr ↦[Tree::"root"] #root ∗ |
| 427 | + "Hnode" ∷ own_node_except (Some id) root tr ∗ |
| 428 | + "%Htree" ∷ ⌜ tree_to_map tr = entries ⌝ |
| 429 | + )%I |
| 430 | + with "[Hnode Hroot]"). |
| 431 | + { |
| 432 | + wp_if_destruct. |
| 433 | + 2:{ iModIntro. iFrame "∗%". } |
| 434 | + wp_rec. |
| 435 | + wp_apply wp_NewSlice. |
| 436 | + iIntros (?) "Hsl". |
| 437 | + wp_pures. wp_apply wp_allocStruct. |
| 438 | + { val_ty. } |
| 439 | + |
| 440 | + (* establish that tr = Empty *) |
| 441 | + iDestruct (own_node_except_unfold with "Hnode") as "Hnode". |
| 442 | + rewrite /own_node_except'. |
| 443 | + destruct tr. |
| 444 | + { done. } |
| 445 | + 2:{ iNamed "Hnode". |
| 446 | + by iDestruct (struct_field_pointsto_not_null with "Hptr_val") as %Hn. |
| 447 | + } |
| 448 | + 2:{ iNamed "Hnode". |
| 449 | + by iDestruct (struct_field_pointsto_not_null with "Hptr_val") as %Hn. |
| 450 | + } |
| 451 | + iClear "Hnode". |
| 452 | + iIntros (?) "Hnode". |
| 453 | + iDestruct (struct_fields_split with "Hnode") as "Hnode". |
| 454 | + iNamed "Hnode". |
| 455 | + wp_storeField. |
| 456 | + iFrame. |
| 457 | + iExists Empty. |
| 458 | + iSplitL; last done. |
| 459 | + by iApply own_node_except_unfold. |
| 460 | + } |
| 461 | + iIntros (?) "HifInv". |
| 462 | + wp_pures. clear v. |
| 463 | + wp_apply wp_ref_of_zero. |
| 464 | + { done. } |
| 465 | + iIntros (nodePath_ptr) "HnodePath_ptr". |
| 466 | + wp_pures. |
| 467 | + iNamed "HifInv". |
| 468 | + wp_loadField. |
| 469 | + wp_load. |
| 470 | + rewrite zero_slice_val. |
| 471 | + wp_apply (wp_SliceAppend (V:=loc) with "[]"). |
| 472 | + { iApply own_slice_zero. } |
| 473 | + iIntros (?) "HnodePath". |
| 474 | + simpl. |
| 475 | + wp_store. |
| 476 | + wp_apply wp_ref_to. |
| 477 | + { val_ty. } |
| 478 | + iIntros (pathIdx_ptr) "HpathIdx". |
| 479 | + wp_pures. |
| 480 | + wp_apply (wp_forUpto' with "[$HpathIdx]"). |
| 481 | +Admitted. |
205 | 482 |
|
206 | 483 | Lemma wp_Tree_Get ptr_tr entries sl_id id d0 :
|
207 | 484 | {{{
|
|
0 commit comments