Built with Alectryon, running Coq+SerAPI v8.12.0+0.12.0. Coq sources are in this panel; goals and messages will appear in the other. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus.
Require Import POCS. Require Import TwoDiskAPI. Require Import Common.OneDiskAPI.
ReplicatedDisk provides a single-disk API on top of two disks, handling disk
failures with replication.
Your job will be to implement the core of the recovery procedure for the
replicated disk, and prove that the entire implementation is correct using your
recovery procedure. This lab is split into several parts. Unlike in previous
labs, the exercises do not proceed in order in the file; you'll first write
specifications and admit the correctness proof, and then you'll come back and
finish the proof.
Module ReplicatedDisk (td : TwoDiskAPI) <: OneDiskAPI. (* EXERCISE (4a): implement read *) Definition read (a:addr) : proc block := Ret block0. Definition write (a:addr) (b:block) : proc unit := _ <- td.write d0 a b; _ <- td.write d1 a b; Ret tt. Definition size : proc nat := msz <- td.size d0; match msz with | Working sz => Ret sz | Failed => msz <- td.size d1; match msz with | Working sz => Ret sz | Failed => Ret 0 end end.
sizeInit computes the size during initialization; it may return None if
the sizes of the underlying disks differ.
Definition sizeInit : proc (option nat) := sz1 <- td.size d0; sz2 <- td.size d1; match sz1 with | Working sz1 => match sz2 with | Working sz2 => if sz1 == sz2 then Ret (Some sz1) else Ret None | Failed => Ret (Some sz1) end | Failed => match sz2 with | Working sz2 => Ret (Some sz2) | Failed => Ret None end end. (* Recursively initialize block a and below. For simplicity, we make the disks match by setting every block to [block0]. *) Fixpoint init_at (a:nat) : proc unit := match a with | 0 => Ret tt | S a => _ <- td.write d0 a block0; _ <- td.write d1 a block0; init_at a end. (* Initialize every disk block *) Definition init' : proc InitResult := size <- sizeInit; match size with | Some sz => _ <- init_at sz; Ret Initialized | None => Ret InitFailed end. Definition init := then_init td.init init'.
forall (A B : Type) (P : A * B -> Prop), (exists (a : A) (b : B), P (a, b)) -> exists p : A * B, P pforall (A B : Type) (P : A * B -> Prop), (exists (a : A) (b : B), P (a, b)) -> exists p : A * B, P prepeat deex; eauto. Qed. (* The [simplify] tactic performs a number of steps that should simplify and clean up your goals. *) Ltac simplify := repeat match goal with | |- forall _, _ => intros | _ => deex | _ => destruct_tuple | [ u: unit |- _ ] => destruct u | |- _ /\ _ => split; [ solve [auto] | ] | |- _ /\ _ => split; [ | solve [auto] ] | |- exists (_: disk*_), _ => apply exists_tuple2 | _ => progress simpl in * | _ => progress safe_intuition | _ => progress subst | _ => progress autorewrite with upd in * end. (* The [finish] tactic tries a number of techniques to solve the goal. *) Ltac finish := repeat match goal with | _ => solve_false | _ => congruence | _ => solve [ intuition ] | _ => (* if we can solve all the side conditions automatically, then it's safe to run descend and create existential variables *) descend; (intuition eauto); lazymatch goal with | |- proc_spec _ _ _ _ => idtac | _ => fail end end. Ltac step := step_proc; simplify; finish.A, B:TypeP:A * B -> PropH:exists (a : A) (b : B), P (a, b)exists p : A * B, P p
Specifications and proofs about our implementation of the replicated disk API,
without considering our recovery.
*These intermediate specifications separate reasoning about the
implementations from recovery behavior.
forall state : TwoDiskBaseAPI.State, two_disks_are state missing missing -> Falseforall state : TwoDiskBaseAPI.State, two_disks_are state missing missing -> Falsedestruct state; unfold missing; simpl; intuition auto. Qed. Hint Resolve both_disks_not_missing : false.forall state : TwoDiskBaseAPI.State, get_disk d0 state ?|= missing /\ get_disk d1 state ?|= missing -> Falseforall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), two_disks_are state missing F -> two_disks_are state P Fforall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), two_disks_are state missing F -> two_disks_are state P Fdestruct state; unfold missing; simpl; intuition auto. Qed.forall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), get_disk d0 state ?|= missing /\ get_disk d1 state ?|= F -> get_disk d0 state ?|= P /\ get_disk d1 state ?|= Fforall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), two_disks_are state F missing -> two_disks_are state F Pforall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), two_disks_are state F missing -> two_disks_are state F Pdestruct state; unfold missing; simpl; intuition auto. Qed. Hint Resolve missing0_implies_any : core. Hint Resolve missing1_implies_any : core. (* EXERCISE (4a): prove this specification for read *)forall (state : TwoDiskBaseAPI.State) (P F : disk -> Prop), get_disk d0 state ?|= F /\ get_disk d1 state ?|= missing -> get_disk d0 state ?|= F /\ get_disk d1 state ?|= Pforall a : addr, proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : block) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) /\ diskGet d a =?= r; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) |}) (read a) td.recover td.abstrforall a : addr, proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : block) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) /\ diskGet d a =?= r; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) |}) (read a) td.recover td.abstr(* Hint: use step instead of step_proc to take advantage of the new automation in this lab. *)forall a : addr, proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : block) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) /\ diskGet d a =?= r; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d) (eq d) |}) (Ret block0) td.recover td.abstrAdmitted. Hint Resolve read_int_ok : core. (* EXERCISE (4a): complete and prove this specification for write *)a:addra0:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq a0) (eq a0)Lexec:Marker "post" (Ret block0)diskGet a0 a =?= block0forall (a : addr) (b : block), proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : unit) (state' : TwoDiskBaseAPI.State) => r = tt /\ two_disks_are state' (eq (diskUpd d a b)) (eq (diskUpd d a b)); recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (write a b) td.recover td.abstrforall (a : addr) (b : block), proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : unit) (state' : TwoDiskBaseAPI.State) => r = tt /\ two_disks_are state' (eq (diskUpd d a b)) (eq (diskUpd d a b)); recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (write a b) td.recover td.abstrAdmitted. Hint Resolve write_int_ok : core. (* EXERCISE (4a): prove this specification for size *)forall (a : addr) (b : block), proc_spec (fun (d : disk) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d) (eq d); post := fun (r : unit) (state' : TwoDiskBaseAPI.State) => r = tt /\ two_disks_are state' (eq (diskUpd d a b)) (eq (diskUpd d a b)); recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (_ <- td.write d0 a b; _ <- td.write d1 a b; Ret tt) td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state' : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state' (eq d_0) (eq d_1); recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d_0) (eq d_1) |}) size td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state' : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state' (eq d_0) (eq d_1); recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d_0) (eq d_1) |}) size td.recover td.abstrAdmitted. Hint Resolve size_int_ok : core. (* We provide a proof for [init]. *) Definition equal_after a (d_0 d_1: disk) := diskSize d_0 = diskSize d_1 /\ forall a', a <= a' -> diskGet d_0 a' = diskGet d_1 a'.proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state' : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state' (eq d_0) (eq d_1); recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d_0) (eq d_1) |}) (msz <- td.size d0; match msz with | Working sz => Ret sz | Failed => msz0 <- td.size d1; match msz0 with | Working sz => Ret sz | Failed => Ret 0 end end) td.recover td.abstrforall n m : nat, n <= m -> n = m \/ S n <= m /\ n <> mforall n m : nat, n <= m -> n = m \/ S n <= m /\ n <> mlia. Qed.n, m:natH:n <= mn = m \/ S n <= m /\ n <> mforall (a : nat) (d_0 d_1 : disk) (b : block), equal_after (S a) d_0 d_1 -> equal_after a (diskUpd d_0 a b) (diskUpd d_1 a b)forall (a : nat) (d_0 d_1 : disk) (b : block), equal_after (S a) d_0 d_1 -> equal_after a (diskUpd d_0 a b) (diskUpd d_1 a b)a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'diskSize (diskUpd d_0 a b) = diskSize (diskUpd d_1 a b)a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'a':natH1:a <= a'diskGet (diskUpd d_0 a b) a' = diskGet (diskUpd d_1 a b) a'autorewrite with upd; eauto.a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'diskSize (diskUpd d_0 a b) = diskSize (diskUpd d_1 a b)a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'a':natH1:a <= a'diskGet (diskUpd d_0 a b) a' = diskGet (diskUpd d_1 a b) a'd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0diskGet (diskUpd d_0 a' b) a' = diskGet (diskUpd d_1 a' b) a'a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'a':natH1:S a <= a'H2:a <> a'diskGet (diskUpd d_0 a b) a' = diskGet (diskUpd d_1 a b) a'd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0diskGet (diskUpd d_0 a' b) a' = diskGet (diskUpd d_1 a' b) a'd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0l:a' < diskSize d_0Some b = Some bd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0n:~ a' < diskSize d_0None = Noneassert (a' < diskSize d_1) by congruence; autorewrite with upd; auto.d_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0l:a' < diskSize d_0Some b = Some bassert (~a' < diskSize d_1) by congruence; autorewrite with upd; auto.d_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1a':natH0:forall a'0 : nat, S a' <= a'0 -> diskGet d_0 a'0 = diskGet d_1 a'0n:~ a' < diskSize d_0None = Noneautorewrite with upd; eauto. Qed. Hint Resolve equal_after_diskUpd : core.a:natd_0, d_1:diskb:blockH:diskSize d_0 = diskSize d_1H0:forall a' : nat, S a <= a' -> diskGet d_0 a' = diskGet d_1 a'a':natH1:S a <= a'H2:a <> a'diskGet (diskUpd d_0 a b) a' = diskGet (diskUpd d_1 a b) a'forall a : nat, proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrforall a : nat, proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after 0 d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret tt) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after (S a) d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (_ <- td.write d0 a block0; _ <- td.write d1 a block0; init_at a) td.recover td.abstrstep.proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after 0 d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret tt) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after (S a) d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (_ <- td.write d0 a block0; _ <- td.write d1 a block0; init_at a) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1r:DiskResult unitLexec:Marker "after" (td.write d0 a block0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' (eq (diskUpd d_0 a block0)) (eq d_1) | Failed => two_disks_are state' missing (eq d_1) end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (_ <- td.write d1 a block0; init_at a) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1r:DiskResult unitLexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:match r with | Working _ => two_disks_are state0 (eq (diskUpd d_0 a block0)) (eq d_1) | Failed => two_disks_are state0 missing (eq d_1) endexists (a0 : disk) (b : disk -> Prop), (Marker "precondition" (td.write d1 a block0) -> two_disks_are state0 b (eq a0)) /\ (unit -> forall state' : TwoDiskBaseAPI.State, two_disks_are state' b (eq (diskUpd a0 a block0)) /\ a < diskSize a0 \/ two_disks_are state' b (eq a0) -> Marker "recovered condition" (td.write d1 a block0) -> True) /\ (forall r : DiskResult unit, Marker "after" (td.write d1 a block0) -> proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' b (eq (diskUpd a0 a block0)) | Failed => two_disks_are state' b missing end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstr)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1v:unitLexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 (eq (diskUpd d_0 a block0)) (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' (eq (diskUpd d_0 a block0)) (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state' (eq (diskUpd d_0 a block0)) missing end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' missing (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state' missing missing end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1v:unitLexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 (eq (diskUpd d_0 a block0)) (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' (eq (diskUpd d_0 a block0)) (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state' (eq (diskUpd d_0 a block0)) missing end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrdestruct r; simplify; finish.a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 (eq (diskUpd d_0 a block0)) (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:match r with | Working _ => two_disks_are state1 (eq (diskUpd d_0 a block0)) (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state1 (eq (diskUpd d_0 a block0)) missing endexists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working _ => two_disks_are state' missing (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state' missing missing end; post := fun (_ : unit) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstra:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)r:DiskResult unitLexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:match r with | Working _ => two_disks_are state1 missing (eq (diskUpd d_1 a block0)) | Failed => two_disks_are state1 missing missing endexists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing (eq (diskUpd d_1 a block0))exists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing missingexists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing (eq (diskUpd d_1 a block0))exists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing (eq (diskUpd d_1 a block0))exists b : disk, (two_disks_are state1 (eq (diskUpd d_0 a block0)) (eq b) /\ equal_after a (diskUpd d_0 a block0) b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)finish.a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing (eq (diskUpd d_1 a block0))(two_disks_are state1 (eq (diskUpd d_0 a block0)) (eq (diskUpd d_1 a block0)) /\ equal_after a (diskUpd d_0 a block0) (diskUpd d_1 a block0)) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)finish. Qed. Hint Resolve init_at_ok : core.a:natIHa:proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1) /\ equal_after a d_0 d_1; post := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (init_at a) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)H0:equal_after (S a) d_0 d_1Lexec:Marker "after" (td.write d0 a block0)state0:TwoDiskBaseAPI.StateH1:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.write d1 a block0)state1:TwoDiskBaseAPI.StateH2:two_disks_are state1 missing missingexists a0 b : disk, (two_disks_are state1 (eq a0) (eq b) /\ equal_after a a0 b) /\ (unit -> forall state' : TwoDiskBaseAPI.State, (exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') -> exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1') /\ (unit -> TwoDiskBaseAPI.State -> True -> True)proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : option nat) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ match r with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) sizeInit td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : option nat) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ match r with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) sizeInit td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : option nat) (state' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ match r with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz1 <- td.size d0; sz2 <- td.size d1; match sz1 with | Working sz3 => match sz2 with | Working sz4 => if equal_dec sz3 sz4 then Ret (Some sz3) else Ret None | Failed => Ret (Some sz3) end | Failed => match sz2 with | Working sz3 => Ret (Some sz3) | Failed => Ret None end end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)r:DiskResult natLexec:Marker "after" (td.size d0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working n => two_disks_are state' (eq d_0) (eq d_1) /\ n = diskSize d_0 | Failed => two_disks_are state' missing (eq d_1) end; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz2 <- td.size d1; match r with | Working sz1 => match sz2 with | Working sz3 => if equal_dec sz1 sz3 then Ret (Some sz1) else Ret None | Failed => Ret (Some sz1) end | Failed => match sz2 with | Working sz3 => Ret (Some sz3) | Failed => Ret None end end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)v:natLexec:Marker "after" (td.size d0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_0; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz2 <- td.size d1; match sz2 with | Working sz3 => if equal_dec v sz3 then Ret (Some v) else Ret None | Failed => Ret (Some v) end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing (eq d_1); post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz2 <- td.size d1; match sz2 with | Working sz3 => Ret (Some sz3) | Failed => Ret None end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)v:natLexec:Marker "after" (td.size d0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_0; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz2 <- td.size d1; match sz2 with | Working sz3 => if equal_dec v sz3 then Ret (Some v) else Ret None | Failed => Ret (Some v) end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)r:DiskResult natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working n => two_disks_are state' (eq d_0) (eq d_1) /\ n = diskSize d_1 | Failed => two_disks_are state' (eq d_0) missing end; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) match r with | Working sz2 => if equal_dec (diskSize d_0) sz2 then Ret (Some (diskSize d_0)) else Ret None | Failed => Ret (Some (diskSize d_0)) end td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (if equal_dec (diskSize d_0) v then Ret (Some (diskSize d_0)) else Ret None) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)Lexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) missing; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some (diskSize d_0))) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (if equal_dec (diskSize d_0) v then Ret (Some (diskSize d_0)) else Ret None) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)e:diskSize d_0 = vproc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some (diskSize d_0))) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)n:diskSize d_0 <> vproc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret None) td.recover td.abstrstep.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)e:diskSize d_0 = vproc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some (diskSize d_0))) td.recover td.abstrstep.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)v:natLexec0:Marker "after" (td.size d1)n:diskSize d_0 <> vproc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret None) td.recover td.abstrstep.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 (eq d_0) (eq d_1)Lexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' (eq d_0) missing; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some (diskSize d_0))) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing (eq d_1); post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (sz2 <- td.size d1; match sz2 with | Working sz3 => Ret (Some sz3) | Failed => Ret None end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 missing (eq d_1)r:DiskResult natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := match r with | Working n => two_disks_are state' missing (eq d_1) /\ n = diskSize d_1 | Failed => two_disks_are state' missing missing end; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) match r with | Working sz2 => Ret (Some sz2) | Failed => Ret None end td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 missing (eq d_1)v:natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some v)) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing missing; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret None) td.recover td.abstrstep.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 missing (eq d_1)v:natLexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing (eq d_1) /\ v = diskSize d_1; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret (Some v)) td.recover td.abstrstep. Qed. Hint Resolve sizeInit_ok : core.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" (td.size d0)state0:TwoDiskBaseAPI.StateH0:two_disks_are state0 missing (eq d_1)Lexec0:Marker "after" (td.size d1)proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := two_disks_are state' missing missing; post := fun (r' : option nat) (state'' : TwoDiskBaseAPI.State) => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ match r' with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret None) td.recover td.abstrforall d_0 d_1 : disk, equal_after 0 d_0 d_1 -> d_0 = d_1forall d_0 d_1 : disk, equal_after 0 d_0 d_1 -> d_0 = d_1d_0, d_1:diskH:diskSize d_0 = diskSize d_1H0:forall a' : nat, 0 <= a' -> diskGet d_0 a' = diskGet d_1 a'd_0 = d_1eapply H0; lia. Qed.d_0, d_1:diskH:diskSize d_0 = diskSize d_1H0:forall a' : nat, 0 <= a' -> diskGet d_0 a' = diskGet d_1 a'a:addrdiskGet d_0 a = diskGet d_1 aforall d_0 d_1 : disk, diskSize d_0 = diskSize d_1 -> equal_after (diskSize d_0) d_0 d_1forall d_0 d_1 : disk, diskSize d_0 = diskSize d_1 -> equal_after (diskSize d_0) d_0 d_1d_0, d_1:diskH:diskSize d_0 = diskSize d_1a':natH0:diskSize d_0 <= a'diskGet d_0 a' = diskGet d_1 a'd_0, d_1:diskH:diskSize d_0 = diskSize d_1a':natH0:diskSize d_0 <= a'H1:~ a' < diskSize d_0diskGet d_0 a' = diskGet d_1 a'autorewrite with upd; eauto. Qed. Hint Resolve equal_after_size : core. Hint Resolve equal_after_0_to_eq : core.d_0, d_1:diskH:diskSize d_0 = diskSize d_1a':natH0:diskSize d_0 <= a'H1:~ a' < diskSize d_0H2:~ a' < diskSize d_1diskGet d_0 a' = diskGet d_1 a'proc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) init' td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) init' td.recover td.abstrproc_spec (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (size <- sizeInit; match size with | Some sz => _ <- init_at sz; Ret Initialized | None => Ret InitFailed end) td.recover td.abstrd_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)r:option natLexec:Marker "after" sizeInitproc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ match r with | Some sz => diskSize d_0' = sz /\ diskSize d_1' = sz | None => True end; post := fun (r' : InitResult) (state'' : TwoDiskBaseAPI.State) => match r' with | Initialized => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) match r with | Some sz => _ <- init_at sz; Ret Initialized | None => Ret InitFailed end td.recover td.abstrstep. Qed. Hint Resolve init'_ok : core.d_0, d_1:diskstate:TwoDiskBaseAPI.StateH:two_disks_are state (eq d_0) (eq d_1)Lexec:Marker "after" sizeInitstate0:TwoDiskBaseAPI.Stated_0', d_1':diskH0:two_disks_are state0 (eq d_0') (eq d_1')H2:diskSize d_1' = diskSize d_0'r:unitLexec0:Marker "after" (init_at (diskSize d_0'))proc_spec (fun (_ : unit) (state' : TwoDiskBaseAPI.State) => {| pre := exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ equal_after 0 d_0' d_1'; post := fun (r' : InitResult) (state'' : TwoDiskBaseAPI.State) => match r' with | Initialized => exists d_0' d_1' : disk, two_disks_are state'' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (Ret Initialized) td.recover td.abstr
Recovery implementation.
*We provide the general structure for recovery: essentially, it consists of
a loop around fixup that terminates after either fixing an out-of-sync
disk block or when a disk has failed.
(* [fixup] returns a [RecStatus] to implement early termination in [recovery_at]. *) Inductive RecStatus := (* continue working, nothing interesting has happened *) | Continue (* some address has been repaired (or the recovery has exhausted the addresses) - only one address can be out of sync and thus only it must be recovered. *) (* OR, one of the disks has failed, so don't bother continuing recovery since the invariant is now trivially satisfied *) | RepairDoneOrFailed. (* EXERCISE (4c): implement [fixup], which should perform recovery for a single address and indicate whether to continue or not. *) Definition fixup (a:addr) : proc RecStatus := Ret Continue. (* recursively performs recovery at [a-1], [a-2], down to 0 *) Fixpoint recover_at (a:addr) : proc unit := match a with | 0 => Ret tt | S n => s <- fixup n; match s with | Continue => recover_at n | RepairDoneOrFailed => Ret tt end end. Definition Recover : proc unit := sz <- size; _ <- recover_at sz; Ret tt.
forall (A : Type) (n m : nat) (a a' : A), n < m -> (if lt_dec n m then a else a') = aforall (A : Type) (n m : nat) (a a' : A), n < m -> (if lt_dec n m then a else a') = adestruct (lt_dec n m); auto. Qed.A:Typen, m:nata, a':AH:n < m(if lt_dec n m then a else a') = aforall (d : disk) (a : nat) (v v' : block), a < diskSize d -> diskGet d a =?= v -> diskGet d a =?= v' -> v = v'forall (d : disk) (a : nat) (v v' : block), a < diskSize d -> diskGet d a =?= v -> diskGet d a =?= v' -> v = v'd:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'v = v'd:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'b:blockH2:diskGet d a = Some bv = v'd:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'H2:diskGet d a = Nonev = v'd:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'b:blockH2:diskGet d a = Some bv = v'd:diska:natv, v':blockH:a < diskSize db:blockH0:Some b =?= vH1:Some b =?= v'H2:diskGet d a = Some bv = v'congruence.d:diska:natv, v':blockH:a < diskSize db:blockH0:v = bH1:v' = bH2:diskGet d a = Some bv = v'd:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'H2:diskGet d a = Nonev = v'eapply disk_inbounds_not_none; eauto. Qed. (* To make these specifications precise while also covering both the already * synced and diverged disks cases, we keep track of which input state we're * in from the input and use it to give an exact postcondition. *) Inductive DiskStatus := | FullySynced | OutOfSync (a:addr) (b:block).d:diska:natv, v':blockH:a < diskSize dH0:diskGet d a =?= vH1:diskGet d a =?= v'H2:diskGet d a = NoneFalseforall (d : disk) (a : addr) (b : block), diskGet d a =?= b -> diskUpd d a b = dforall (d : disk) (a : addr) (b : block), diskGet d a =?= b -> diskUpd d a b = ddestruct (diskGet d a) eqn:?; simpl in *; subst; autorewrite with upd; auto. Qed. Hint Rewrite diskUpd_maybe_same using (solve [ auto ]) : upd. Hint Resolve PeanoNat.Nat.lt_neq : core. Hint Resolve disks_eq_inbounds : core. (* EXERCISE (4c): write and admit a specification for [fixup]. *)d:diska:addrb:blockH:diskGet d a =?= bdiskUpd d a b = dforall a : nat, proc_spec (fun '(d, s) (_ : TwoDiskBaseAPI.State) => {| pre := a < diskSize d /\ match s with | FullySynced | _ => True end; post := fun (_ : RecStatus) (_ : TwoDiskBaseAPI.State) => match s with | FullySynced | _ => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => match s with | FullySynced | _ => True end |}) (fixup a) td.recover td.abstr(* EXERCISE (4d): prove [fixup_ok] *) Admitted. Hint Resolve fixup_ok : core. (* Hint Resolve Lt.lt_n_Sm_le. *) (* EXERCISE (4c): specify and prove recover_at *) (* Hint: use [induction a] *)forall a : nat, proc_spec (fun '(d, s) (_ : TwoDiskBaseAPI.State) => {| pre := a < diskSize d /\ match s with | FullySynced | _ => True end; post := fun (_ : RecStatus) (_ : TwoDiskBaseAPI.State) => match s with | FullySynced | _ => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => match s with | FullySynced | _ => True end |}) (fixup a) td.recover td.abstrforall a : addr, proc_spec (fun (_ : unit) (_ : TwoDiskBaseAPI.State) => {| pre := True; post := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (recover_at a) td.recover td.abstrAdmitted. Hint Resolve recover_at_ok : core. (* EXERCISE (4b): write a spec for recovery *) Definition Recover_spec : Specification _ unit unit TwoDiskBaseAPI.State := fun (_:unit) state => {| pre := True; post := fun _ state' => True; recovered := fun _ state' => True; |}. (* EXERCISE (4c): prove recovery correct *)forall a : addr, proc_spec (fun (_ : unit) (_ : TwoDiskBaseAPI.State) => {| pre := True; post := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (recover_at a) td.recover td.abstrproc_spec Recover_spec Recover td.recover td.abstrAdmitted. (* EXERCISE (4b): prove that your recovery specification is idempotent; that is, its recovered condition implies its precondition. *)proc_spec Recover_spec Recover td.recover td.abstridempotent Recover_specidempotent Recover_specforall (a : unit) (state : TwoDiskBaseAPI.State), pre (Recover_spec a state) -> forall (v : unit) (state' : TwoDiskBaseAPI.State), recovered (Recover_spec a state) v state' -> exists a' : unit, pre (Recover_spec a' state') /\ (forall (rv : unit) (state'' : TwoDiskBaseAPI.State), post (Recover_spec a' state') rv state'' -> post (Recover_spec a state) rv state'')Admitted. (* This proof combines your proof that recovery is correct and that its specification is idempotent to show recovery is correct even if re-run on every crash. *)state:TwoDiskBaseAPI.StateH:Truestate':TwoDiskBaseAPI.StateH0:Trueexists _ : unit, True /\ (unit -> TwoDiskBaseAPI.State -> True -> True)proc_loopspec Recover_spec Recover td.recover td.abstrproc_loopspec Recover_spec Recover td.recover td.abstrproc_spec Recover_spec Recover td.recover td.abstridempotent Recover_specapply Recover_rok.proc_spec Recover_spec Recover td.recover td.abstrapply Recover_spec_idempotent. Qed. Hint Resolve Recover_ok : core. Definition recover: proc unit := _ <- td.recover; Recover. (* As the final step in giving the correctness of the replicated disk operations, we prove recovery specs that include the replicated disk Recover function. *) Definition rd_abstraction (state: TwoDiskBaseAPI.State) (d:OneDiskAPI.State) : Prop := two_disks_are state (eq d) (eq d). Definition abstr : Abstraction OneDiskAPI.State := abstraction_compose td.abstr {| abstraction := rd_abstraction; |}.idempotent Recover_specinit_abstraction init recover abstr inited_anyinit_abstraction init recover abstr inited_anyinit_abstraction init recover abstr inited_anyproc_spec (fun (_ : unit) (state : TwoDiskBaseAPI.State) => {| pre := inited_any state; post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists state'' : State, abstraction {| abstraction := rd_abstraction |} state' state'' /\ inited_any state'' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) init' td.recover td.abstrspec_impl (fun '(d_0, d_1) (state : TwoDiskBaseAPI.State) => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (fun (_ : unit) (state : TwoDiskBaseAPI.State) => {| pre := inited_any state; post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists state'' : State, abstraction {| abstraction := rd_abstraction |} state' state'' /\ inited_any state'' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |})a:unitstate:TwoDiskBaseAPI.StateH:pre {| pre := inited_any state; post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists state'' : State, abstraction {| abstraction := rd_abstraction |} state' state'' /\ inited_any state'' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) state) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) state) v state' -> post {| pre := inited_any state; post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists state'' : State, abstraction {| abstraction := rd_abstraction |} state'0 state'' /\ inited_any state'' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |} v state') /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) state) rv state' -> recovered {| pre := inited_any state; post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists state'' : State, abstraction {| abstraction := rd_abstraction |} state'0 state'' /\ inited_any state'' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |} rv state')a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) rv state' -> True)a:unitd_0:diskH:inited_any (OnlyDisk0 d_0)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) rv state' -> True)a:unitd_1:diskH:inited_any (OnlyDisk1 d_1)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) rv state' -> True)a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (BothDisks d_0 d_1)) rv state' -> True)a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)two_disks_are (BothDisks d_0 d_1) (eq d_0) (eq d_1)a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True endunfold two_disks_are; simpl; intuition.a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)two_disks_are (BothDisks d_0 d_1) (eq d_0) (eq d_1)a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True enddestruct v; simplify; finish.a:unitd_0, d_1:diskH:inited_any (BothDisks d_0 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, two_disks_are state' (eq state'') (eq state'') /\ inited_any state'' | InitFailed => True enda:unitd_0:diskH:inited_any (OnlyDisk0 d_0)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk0 d_0)) rv state' -> True)d_0:diskH:inited_any (OnlyDisk0 d_0)two_disks_are (OnlyDisk0 d_0) (eq d_0) (eq d_0)d_0:diskH:inited_any (OnlyDisk0 d_0)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True endunfold two_disks_are; simpl; intuition.d_0:diskH:inited_any (OnlyDisk0 d_0)two_disks_are (OnlyDisk0 d_0) (eq d_0) (eq d_0)d_0:diskH:inited_any (OnlyDisk0 d_0)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True enddestruct v; simplify; finish.d_0:diskH:inited_any (OnlyDisk0 d_0)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, two_disks_are state' (eq state'') (eq state'') /\ inited_any state'' | InitFailed => True enda:unitd_1:diskH:inited_any (OnlyDisk1 d_1)exists a' : disk * disk, pre ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state' : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) /\ (forall (v : InitResult) (state' : TwoDiskBaseAPI.State), post ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) v state' -> match v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True end) /\ (forall (rv : unit) (state' : TwoDiskBaseAPI.State), recovered ((let '(d_0, d_1) := a' in fun state : TwoDiskBaseAPI.State => {| pre := two_disks_are state (eq d_0) (eq d_1); post := fun (r : InitResult) (state'0 : TwoDiskBaseAPI.State) => match r with | Initialized => exists d_0' d_1' : disk, two_disks_are state'0 (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True end; recovered := fun (_ : unit) (_ : TwoDiskBaseAPI.State) => True |}) (OnlyDisk1 d_1)) rv state' -> True)d_1:diskH:inited_any (OnlyDisk1 d_1)two_disks_are (OnlyDisk1 d_1) (eq d_1) (eq d_1)d_1:diskH:inited_any (OnlyDisk1 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True endunfold two_disks_are; simpl; intuition.d_1:diskH:inited_any (OnlyDisk1 d_1)two_disks_are (OnlyDisk1 d_1) (eq d_1) (eq d_1)d_1:diskH:inited_any (OnlyDisk1 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, rd_abstraction state' state'' /\ inited_any state'' | InitFailed => True enddestruct v; simplify; finish. Qed. (* EXERCISE (4b): prove that read, write, and size are correct when combined with your recovery (using your specification but admitted proof). *)d_1:diskH:inited_any (OnlyDisk1 d_1)v:InitResultstate':TwoDiskBaseAPI.StateH0:match v with | Initialized => exists d_0' d_1' : disk, two_disks_are state' (eq d_0') (eq d_1') /\ d_0' = d_1' | InitFailed => True endmatch v with | Initialized => exists state'' : State, two_disks_are state' (eq state'') (eq state'') /\ inited_any state'' | InitFailed => True endforall a : addr, proc_spec (read_spec a) (read a) recover abstrforall a : addr, proc_spec (read_spec a) (read a) recover abstra:addrproc_spec (read_spec a) (read a) recover abstra:addrproc_spec (fun '(_, state2) (state : TwoDiskBaseAPI.State) => {| pre := True /\ rd_abstraction state state2; post := fun (v : block) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ diskGet state2 a =?= v) /\ rd_abstraction state' state2'; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state' state2' |}) (read a) recover td.abstrAdmitted.a:addrstate2:Statestate:TwoDiskBaseAPI.StateH:TrueH0:rd_abstraction state state2exists a'' : disk, two_disks_are state (eq a'') (eq a'') /\ (forall (v : block) (state' : TwoDiskBaseAPI.State), two_disks_are state' (eq a'') (eq a'') /\ diskGet a'' a =?= v -> exists state2' : State, (state2' = state2 /\ diskGet state2 a =?= v) /\ rd_abstraction state' state2') /\ (unit -> forall state' : TwoDiskBaseAPI.State, two_disks_are state' (eq a'') (eq a'') -> exists _ : unit, True /\ (unit -> forall state'' : TwoDiskBaseAPI.State, True -> exists state2' : State, state2' = state2 /\ rd_abstraction state'' state2'))forall (a : addr) (v : block), proc_spec (write_spec a v) (write a v) recover abstrforall (a : addr) (v : block), proc_spec (write_spec a v) (write a v) recover abstra:addrv:blockproc_spec (write_spec a v) (write a v) recover abstra:addrv:blockproc_spec (fun '(_, state2) (state : TwoDiskBaseAPI.State) => {| pre := True /\ rd_abstraction state state2; post := fun (v0 : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (v0 = tt /\ state2' = diskUpd state2 a v) /\ rd_abstraction state' state2'; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 \/ state2' = diskUpd state2 a v) /\ rd_abstraction state' state2' |}) (write a v) recover td.abstrAdmitted.a:addrv:blockstate2:Statestate:TwoDiskBaseAPI.StateH:TrueH0:rd_abstraction state state2exists a'' : disk, two_disks_are state (eq a'') (eq a'') /\ (forall (v0 : unit) (state' : TwoDiskBaseAPI.State), v0 = tt /\ two_disks_are state' (eq (diskUpd a'' a v)) (eq (diskUpd a'' a v)) -> exists state2' : State, (v0 = tt /\ state2' = diskUpd state2 a v) /\ rd_abstraction state' state2') /\ (unit -> TwoDiskBaseAPI.State -> True -> exists _ : unit, True /\ (unit -> forall state'' : TwoDiskBaseAPI.State, True -> exists state2' : State, (state2' = state2 \/ state2' = diskUpd state2 a v) /\ rd_abstraction state'' state2'))proc_spec size_spec size recover abstrproc_spec size_spec size recover abstrproc_spec size_spec size recover abstr(* simplify is a bit too aggressive about existential variables here, so we provide some manual simplification here *)proc_spec (fun '(_, state2) (state : TwoDiskBaseAPI.State) => {| pre := True /\ rd_abstraction state state2; post := fun (v : nat) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v = diskSize state2) /\ rd_abstraction state' state2'; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state' state2' |}) size recover td.abstrforall (a : unit * State) (state : TwoDiskBaseAPI.State), pre ((let '(_, state2) := a in fun state0 : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state0 state2; post := fun (v : nat) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v = diskSize state2) /\ rd_abstraction state' state2'; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state' state2' |}) state) -> exists a'' : disk * disk, pre ((fun '(d_0, d_1) (state0 : TwoDiskBaseAPI.State) => {| pre := two_disks_are state0 (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state' : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state' (eq d_0) (eq d_1); recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq d_0) (eq d_1) |}) a'' state) /\ (forall (v : nat) (state' : TwoDiskBaseAPI.State), post ((fun '(d_0, d_1) (state0 : TwoDiskBaseAPI.State) => {| pre := two_disks_are state0 (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state'0 : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state'0 (eq d_0) (eq d_1); recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => two_disks_are state'0 (eq d_0) (eq d_1) |}) a'' state) v state' -> post ((let '(_, state2) := a in fun state0 : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state0 state2; post := fun (v0 : nat) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v0 = diskSize state2) /\ rd_abstraction state'0 state2'; recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state'0 state2' |}) state) v state') /\ (forall (v : unit) (state' : TwoDiskBaseAPI.State), recovered ((fun '(d_0, d_1) (state0 : TwoDiskBaseAPI.State) => {| pre := two_disks_are state0 (eq d_0) (eq d_1) /\ diskSize d_0 = diskSize d_1; post := fun (r : nat) (state'0 : TwoDiskBaseAPI.State) => r = diskSize d_0 /\ r = diskSize d_1 /\ two_disks_are state'0 (eq d_0) (eq d_1); recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => two_disks_are state'0 (eq d_0) (eq d_1) |}) a'' state) v state' -> exists a' : unit, pre (Recover_spec a' state') /\ (forall (v' : unit) (state'' : TwoDiskBaseAPI.State), post (Recover_spec a' state') v' state'' -> recovered ((let '(_, state2) := a in fun state0 : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state0 state2; post := fun (v0 : nat) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, (... /\ ...) /\ rd_abstraction state'0 state2'; recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state'0 state2' |}) state) v' state''))a:(unit * State)%typestate:TwoDiskBaseAPI.StateH:pre ((let '(_, state2) := a in fun state : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state state2; post := fun (v : nat) (state' : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v = diskSize state2) /\ rd_abstraction state' state2'; recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state' state2' |}) state)exists a0 b : disk, pre {| pre := two_disks_are state (eq a0) (eq b) /\ diskSize a0 = diskSize b; post := fun (r : nat) (state' : TwoDiskBaseAPI.State) => r = diskSize a0 /\ r = diskSize b /\ two_disks_are state' (eq a0) (eq b); recovered := fun (_ : unit) (state' : TwoDiskBaseAPI.State) => two_disks_are state' (eq a0) (eq b) |} /\ (forall (v : nat) (state' : TwoDiskBaseAPI.State), post {| pre := two_disks_are state (eq a0) (eq b) /\ diskSize a0 = diskSize b; post := fun (r : nat) (state'0 : TwoDiskBaseAPI.State) => r = diskSize a0 /\ r = diskSize b /\ two_disks_are state'0 (eq a0) (eq b); recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => two_disks_are state'0 (eq a0) (eq b) |} v state' -> post ((let '(_, state2) := a in fun state : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state state2; post := fun (v0 : nat) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v0 = diskSize state2) /\ rd_abstraction state'0 state2'; recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state'0 state2' |}) state) v state') /\ (forall (v : unit) (state' : TwoDiskBaseAPI.State), recovered {| pre := two_disks_are state (eq a0) (eq b) /\ diskSize a0 = diskSize b; post := fun (r : nat) (state'0 : TwoDiskBaseAPI.State) => r = diskSize a0 /\ r = diskSize b /\ two_disks_are state'0 (eq a0) (eq b); recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => two_disks_are state'0 (eq a0) (eq b) |} v state' -> exists a' : unit, pre (Recover_spec a' state') /\ (forall (v' : unit) (state'' : TwoDiskBaseAPI.State), post (Recover_spec a' state') v' state'' -> recovered ((let '(_, state2) := a in fun state : TwoDiskBaseAPI.State => {| pre := True /\ rd_abstraction state state2; post := fun (v0 : nat) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, (state2' = state2 /\ v0 = diskSize state2) /\ rd_abstraction state'0 state2'; recovered := fun (_ : unit) (state'0 : TwoDiskBaseAPI.State) => exists state2' : State, state2' = state2 /\ rd_abstraction state'0 state2' |}) state) v' state''))Admitted. (* This theorem shows that Ret does not modify the abstract state exposed by the replicated disk; the interesting part is that if recovery runs, then the only effect should be the wipe relation (the trivial relation [no_wipe] in this case) *) (* EXERCISE (4b): prove this theorem using your recovery spec *)u:units:Statestate:TwoDiskBaseAPI.StateH:True /\ rd_abstraction state sexists a b : disk, (two_disks_are state (eq a) (eq b) /\ diskSize a = diskSize b) /\ (forall (v : nat) (state' : TwoDiskBaseAPI.State), v = diskSize a /\ v = diskSize b /\ two_disks_are state' (eq a) (eq b) -> exists state2' : State, (state2' = s /\ v = diskSize s) /\ rd_abstraction state' state2') /\ (unit -> forall state' : TwoDiskBaseAPI.State, two_disks_are state' (eq a) (eq b) -> exists _ : unit, True /\ (unit -> forall state'' : TwoDiskBaseAPI.State, True -> exists state2' : State, state2' = s /\ rd_abstraction state'' state2'))rec_wipe recover abstr no_wiperec_wipe recover abstr no_wipeforall (state0 : TwoDiskBaseAPI.State) (state0' : State) (state : TwoDiskBaseAPI.State), rd_abstraction state0 state0' -> no_wipe state0 state -> exists _ : unit, True /\ (forall state' : TwoDiskBaseAPI.State, unit -> True -> exists state2' : State, no_wipe state0' state2' /\ rd_abstraction state' state2')Admitted. End ReplicatedDisk.state0:TwoDiskBaseAPI.Statestate0':StateH:two_disks_are state0 (eq state0') (eq state0')exists _ : unit, True /\ (forall state' : TwoDiskBaseAPI.State, unit -> True -> exists state2' : State, state2' = state0' /\ two_disks_are state' (eq state2') (eq state2'))