cluster/isabelle/OneSlot.thy (102 lines of code) (raw):

section \<open>One-slot consistency\<close> text \<open>The replicated state machine determines the values that are committed in each of a sequence of \textit{slots}. Each slot runs a logically-separate consensus algorithm which is shown to be consistent here. Further below, the protocol is shown to refine this slot-by-slot model correctly.\<close> text \<open>Consistency is shown to follow from the invariants listed below. Further below, the protocol is shown to preserve these invariants in each step, which means it is not enormously important to understand these in detail.\<close> theory OneSlot imports Preliminaries begin locale oneSlot = (* basic functions *) fixes Q :: "Node set set" fixes v :: "Term \<Rightarrow> Value" (* message-sent predicates *) fixes promised\<^sub>f :: "Node \<Rightarrow> Term \<Rightarrow> bool" fixes promised\<^sub>b :: "Node \<Rightarrow> Term \<Rightarrow> Term \<Rightarrow> bool" fixes proposed :: "Term \<Rightarrow> bool" fixes accepted :: "Node \<Rightarrow> Term \<Rightarrow> bool" fixes committed :: "Term \<Rightarrow> bool" (* other definitions *) fixes promised :: "Node \<Rightarrow> Term \<Rightarrow> bool" defines "promised n t \<equiv> promised\<^sub>f n t \<or> (\<exists> t'. promised\<^sub>b n t t')" fixes prevAccepted :: "Term \<Rightarrow> Node set \<Rightarrow> Term set" defines "prevAccepted t ns \<equiv> {t'. \<exists> n \<in> ns. promised\<^sub>b n t t'}" (* invariants *) assumes Q_intersects: "Q \<frown> Q" assumes promised\<^sub>f: "\<lbrakk> promised\<^sub>f n t; t' < t \<rbrakk> \<Longrightarrow> \<not> accepted n t'" assumes promised\<^sub>b_lt: "promised\<^sub>b n t t' \<Longrightarrow> t' < t" assumes promised\<^sub>b_accepted: "promised\<^sub>b n t t' \<Longrightarrow> accepted n t'" assumes promised\<^sub>b_max: "\<lbrakk> promised\<^sub>b n t t'; t' < t''; t'' < t \<rbrakk> \<Longrightarrow> \<not> accepted n t''" assumes proposed: "proposed t \<Longrightarrow> \<exists> q \<in> Q. (\<forall> n \<in> q. promised n t) \<and> (prevAccepted t q = {} \<or> (\<exists> t'. v t = v t' \<and> maxTerm (prevAccepted t q) \<le> t' \<and> proposed t' \<and> t' < t))" assumes proposed_finite: "finite {t. proposed t}" assumes accepted: "accepted n t \<Longrightarrow> proposed t" assumes committed: "committed t \<Longrightarrow> \<exists> q \<in> Q. \<forall> n \<in> q. accepted n t" lemma (in oneSlot) prevAccepted_proposed: "prevAccepted t ns \<subseteq> {t. proposed t}" using accepted prevAccepted_def promised\<^sub>b_accepted by fastforce lemma (in oneSlot) prevAccepted_finite: "finite (prevAccepted p ns)" using prevAccepted_proposed proposed_finite by (meson rev_finite_subset) lemma (in oneSlot) Q_nonempty: "\<And>q. q \<in> Q \<Longrightarrow> q \<noteq> {}" using Q_intersects by (auto simp add: intersects_def) text \<open>The heart of the consistency proof is property P2b from \textit{Paxos made simple} by Lamport:\<close> lemma (in oneSlot) p2b: assumes "proposed t\<^sub>1" and "committed t\<^sub>2" and "t\<^sub>2 < t\<^sub>1" shows "v t\<^sub>1 = v t\<^sub>2" using assms proof (induct t\<^sub>1 rule: less_induct) case (less t\<^sub>1) hence hyp: "\<And> t\<^sub>1'. \<lbrakk> t\<^sub>1' < t\<^sub>1; proposed t\<^sub>1'; t\<^sub>2 \<le> t\<^sub>1' \<rbrakk> \<Longrightarrow> v t\<^sub>1' = v t\<^sub>2" using le_imp_less_or_eq by blast from `proposed t\<^sub>1` obtain q\<^sub>1 t\<^sub>1' where q\<^sub>1_quorum: "q\<^sub>1 \<in> Q" and q\<^sub>1_promised: "\<And>n. n \<in> q\<^sub>1 \<Longrightarrow> promised n t\<^sub>1" and q\<^sub>1_value: "prevAccepted t\<^sub>1 q\<^sub>1 = {} \<or> (v t\<^sub>1 = v t\<^sub>1' \<and> maxTerm (prevAccepted t\<^sub>1 q\<^sub>1) \<le> t\<^sub>1' \<and> proposed t\<^sub>1' \<and> t\<^sub>1' < t\<^sub>1)" by (meson proposed) from `committed t\<^sub>2` obtain q\<^sub>2 where q\<^sub>2_quorum: "q\<^sub>2 \<in> Q" and q\<^sub>2_accepted: "\<And>n. n \<in> q\<^sub>2 \<Longrightarrow> accepted n t\<^sub>2" using committed by force have "q\<^sub>1 \<inter> q\<^sub>2 \<noteq> {}" using Q_intersects intersects_def less.prems q\<^sub>1_quorum q\<^sub>2_quorum by auto then obtain n where n\<^sub>1: "n \<in> q\<^sub>1" and n\<^sub>2: "n \<in> q\<^sub>2" by auto from n\<^sub>1 q\<^sub>1_promised have "promised n t\<^sub>1" by simp moreover from n\<^sub>2 q\<^sub>2_accepted have "accepted n t\<^sub>2" by simp ultimately obtain t\<^sub>2' where t\<^sub>2': "promised\<^sub>b n t\<^sub>1 t\<^sub>2'" using less.prems(3) promised\<^sub>f promised_def by blast have q\<^sub>1_value: "v t\<^sub>1 = v t\<^sub>1'" "maxTerm (prevAccepted t\<^sub>1 q\<^sub>1) \<le> t\<^sub>1'" "proposed t\<^sub>1'" "t\<^sub>1' < t\<^sub>1" using n\<^sub>1 prevAccepted_def q\<^sub>1_value t\<^sub>2' by auto note `v t\<^sub>1 = v t\<^sub>1'` also have "v t\<^sub>1' = v t\<^sub>2" proof (intro hyp) have p: "maxTerm (prevAccepted t\<^sub>1 q\<^sub>1) \<in> prevAccepted t\<^sub>1 q\<^sub>1" apply (intro maxTerm_mem prevAccepted_finite) using n\<^sub>1 prevAccepted_def t\<^sub>2' by auto show "t\<^sub>1' < t\<^sub>1" "proposed t\<^sub>1'" using q\<^sub>1_value by simp_all have "t\<^sub>2 \<le> t\<^sub>2'" by (meson \<open>accepted n t\<^sub>2\<close> less.prems(3) not_le promised\<^sub>b_max t\<^sub>2') also have "t\<^sub>2' \<le> maxTerm (prevAccepted t\<^sub>1 q\<^sub>1)" using n\<^sub>1 prevAccepted_def t\<^sub>2' prevAccepted_finite by (intro maxTerm_max, auto) also have "... \<le> t\<^sub>1'" using q\<^sub>1_value by simp finally show "t\<^sub>2 \<le> t\<^sub>1'" . qed finally show ?case . qed text \<open>From this, it follows that any two committed values are equal as desired.\<close> lemma (in oneSlot) consistent: assumes "committed t\<^sub>1" and "committed t\<^sub>2" shows "v t\<^sub>1 = v t\<^sub>2" using assms by (metis Q_nonempty accepted all_not_in_conv committed not_less_iff_gr_or_eq p2b) text \<open>It will be useful later to know the conditions under which a value in a term can be committed, which is spelled out here:\<close> lemma (in oneSlot) commit: assumes q_quorum: "q \<in> Q" assumes q_accepted: "\<And>n. n \<in> q \<Longrightarrow> accepted n t\<^sub>0" defines "committed' t \<equiv> committed t \<or> t = t\<^sub>0" shows "oneSlot Q v promised\<^sub>f promised\<^sub>b proposed accepted committed'" by (smt committed'_def Q_intersects oneSlot_axioms oneSlot_def q_accepted q_quorum) end