Theory BenOr_Proofs
theory BenOr_Proofs
imports Heard_Of.Reduction
  Two_Step_Observing "../HO_Transition_System" BenOr_Defs
begin
subsection ‹Proofs›
type_synonym ben_or_TS_state = "(nat × (process ⇒ (val pstate)))"
consts 
  val0 :: val 
  val1 :: val
text ‹Ben-Or works only on binary values.›
axiomatization where 
  val_exhaust: "v = val0 ∨ v = val1"
  and val_diff: "val0 ≠ val1"
context mono_quorum
begin
definition BenOr_Alg :: "(process, val pstate, val msg)CHOAlgorithm" where
  "BenOr_Alg = CHOAlgorithm.truncate BenOr_M"
definition BenOr_TS ::
  "(round ⇒ process HO) ⇒ (round ⇒ process HO) ⇒ (round ⇒ process) ⇒ ben_or_TS_state TS"
where
  "BenOr_TS HOs SHOs crds = CHO_to_TS BenOr_Alg HOs SHOs (K o crds)"
lemmas BenOr_TS_defs = BenOr_TS_def CHO_to_TS_def BenOr_Alg_def CHOinitConfig_def
  BenOr_initState_def
type_synonym rHO = "nat ⇒ process HO"
definition BenOr_trans_step 
  where
  "BenOr_trans_step HOs SHOs nxt_f snd_f stp ≡ ⋃r μ.
    {((r, cfg), (Suc r, cfg'))|cfg cfg'. two_step r = stp  ∧ (∀p.
      μ p ∈ get_msgs (snd_f r) cfg (HOs r) (SHOs r) p
      ∧ nxt_f r p (cfg p) (μ p) (cfg' p)
    )}"
lemma two_step_less_D:
  "0 < two_step r ⟹ two_step r = Suc 0"
  by(auto simp add: two_step_def)
lemma BenOr_trans:
  "CSHO_trans_alt BenOr_sendMsg (λr p st msgs crd st'. BenOr_nextState r p st msgs st') HOs SHOs crds = 
  BenOr_trans_step HOs SHOs next0 send0 0
  ∪ BenOr_trans_step HOs SHOs  next1 send1 1
  "
proof
  show "CSHO_trans_alt BenOr_sendMsg (λr p st msgs crd. BenOr_nextState r p st msgs) HOs SHOs crds
    ⊆ BenOr_trans_step HOs SHOs next0 send0 0 ∪ BenOr_trans_step HOs SHOs next1 send1 1"
  by(force simp add: CSHO_trans_alt_def BenOr_sendMsg_def BenOr_nextState_def BenOr_trans_step_def 
    K_def dest!: two_step_less_D)
next
  show " BenOr_trans_step HOs SHOs next0 send0 0 ∪
    BenOr_trans_step HOs SHOs next1 send1 1
    ⊆ CSHO_trans_alt BenOr_sendMsg
        (λr p st msgs crd. BenOr_nextState r p st msgs) HOs SHOs crds"
  by(force simp add: CSHO_trans_alt_def BenOr_sendMsg_def BenOr_nextState_def BenOr_trans_step_def)
qed
definition "BenOr_A = CHOAlgorithm.truncate BenOr_M"
subsubsection ‹Refinement›
text ‹Agreement for BenOr only holds if the communication predicates hold›
context
  fixes
    HOs :: "nat ⇒ process ⇒ process set"
    and rho :: "nat ⇒ process ⇒ val pstate"
  assumes comm_global: "BenOr_commGlobal HOs"
  and per_rd: "∀r. BenOr_commPerRd (HOs r)"
  and run: "HORun BenOr_A rho HOs"
begin
definition no_vote_diff where
  "no_vote_diff sc p ≡ vote (sc p) = None ⟶
            (∃q q'. x (sc q) ≠ x (sc q'))"
definition ref_rel :: "(tso_state × ben_or_TS_state)set" where
  "ref_rel ≡ {(sa, (r, sc)).
    r = next_round sa
    ∧ (two_step r = 1 ⟶ r_votes sa = vote o sc)
    ∧ (two_step r = 1 ⟶ (∀p. no_vote_diff sc p))
    ∧ (∀p v. x (sc p) = v ⟶ (∃q. last_obs sa q ∈ {None, Some v}))
    ∧ decisions sa = decide o sc
  }"
lemma HOs_intersect:
  "HOs r p ∩ HOs r' q ≠ {}" using per_rd
  apply(simp add: BenOr_commPerRd_def)
  apply(blast dest: qintersect)
  done
lemma HOs_nonempty:
  "HOs r p ≠ {}" 
  using HOs_intersect
  by blast
lemma vote_origin:
  assumes
  send: "∀p. μ p ∈ get_msgs (send0 r) cfg (HOs r) (HOs r) p"
  and step: "∀p. next0 r p (cfg p) (μ p) (cfg' p)"
  and step_r: "two_step r = 0"
  shows 
    "vote (cfg' p) = Some v ⟷ (∀q ∈ HOs r p. x (cfg q) = v)" 
  using send[THEN spec, where x=p] step[THEN spec, where x=p] step_r HOs_nonempty
  by(auto simp add: next0_def get_msgs_benign send0_def msgRcvd_def o_def restrict_map_def)
  
lemma same_new_vote:
  assumes 
  send: "∀p. μ p ∈ get_msgs (send0 r) cfg (HOs r) (HOs r) p"
  and step: "∀p. next0 r p (cfg p) (μ p) (cfg' p)"
  and step_r: "two_step r = 0"
  obtains v where "∀p w. vote (cfg' p) = Some w ⟶ w = v" 
proof(cases "∃p v. vote (cfg' p) = Some v")
  case True
  assume asm: "⋀v. ∀p w. vote (cfg' p) = Some w ⟶ w = v ⟹ thesis"
  from True obtain p v where "vote (cfg' p) = Some v" by auto
  hence "∀p w. vote (cfg' p) = Some w ⟶ w = v" (is "?LV(v)")
    using vote_origin[OF send step step_r] HOs_intersect
    by(force)
  from asm[OF this] show ?thesis .
qed(auto)
lemma no_x_change:
  assumes
  send: "∀p. μ p ∈ get_msgs (send0 r) cfg (HOs r) (HOs r) p"
  and step: "∀p. next0 r p (cfg p) (μ p) (cfg' p)"
  and step_r: "two_step r = 0"
  shows 
    "x (cfg' p) = x (cfg p)" 
  using send[THEN spec, where x=p] step[THEN spec, where x=p] step_r HOs_nonempty
  by(auto simp add: next0_def get_msgs_benign send0_def msgRcvd_def o_def restrict_map_def)
lemma no_vote:
  assumes 
  send: "∀p. μ p ∈ get_msgs (send0 r) cfg (HOs r) (HOs r) p"
  and step: "∀p. next0 r p (cfg p) (μ p) (cfg' p)"
  and step_r: "two_step r = 0"
  shows
    "no_vote_diff cfg' p" 
    unfolding no_vote_diff_def
proof
  assume
    "vote (cfg' p) = None"
  hence "(∃q q'. x (cfg q) ≠ x (cfg q'))"
   using send[THEN spec, where x=p] step[THEN spec, where x=p] step_r HOs_nonempty 
   apply(clarsimp simp add: next0_def get_msgs_benign send0_def msgRcvd_def o_def restrict_map_def)
   by metis
  thus "(∃q q'. x (cfg' q) ≠ x (cfg' q'))" 
    using no_x_change[OF send step step_r]
    by(simp)
qed
  
lemma step0_ref:
  "{ref_rel} ⋃r S v. tso_round0 r S v, 
    BenOr_trans_step HOs HOs next0 send0 0 {> ref_rel}"
proof(clarsimp simp add: PO_rhoare_defs BenOr_trans_step_def all_conj_distrib)
  fix sa r cfg μ cfg'
  assume
    R: "(sa, (r, cfg)) ∈ ref_rel"
    and step_r: "two_step r = 0" 
    and send: "∀p. μ p ∈ get_msgs (send0 r) cfg (HOs r) (HOs r) p"
    and step: "∀p. next0 r p (cfg p) (μ p) (cfg' p)"
  from R have next_r: "next_round sa = r"
    by(simp add: ref_rel_def)
  from HOs_nonempty send have "∀p. ∃q. q ∈ msgRcvd (μ p)"
    by(fastforce simp add: get_msgs_benign send0_def msgRcvd_def restrict_map_def)
  with step have same_dec: "decide o cfg' = decide o cfg"
    apply(simp add: next0_def o_def)
    by (metis pstate.select_convs(3) pstate.surjective pstate.update_convs(2))
 
  define S where "S = {p. ∃v. vote (cfg' p) = Some v}"
  from same_new_vote[OF send step step_r] 
  obtain v where v: "∀p ∈ S. vote (cfg' p) = Some v"
    by(simp add: S_def) (metis)
  hence vote_const_map: "vote o cfg' = const_map v S"
    by(auto simp add: S_def const_map_def restrict_map_def intro!: ext)
  define sa' where "sa' = sa⦇ next_round := Suc r, r_votes := const_map v S ⦈"
  have "∀p. p ∈ S ⟶ opt_obs_safe (last_obs sa) v"
    using vote_origin[OF send step step_r] R per_rd[THEN spec, of r] v
    apply(clarsimp simp add: BenOr_commPerRd_def opt_obs_safe_def ref_rel_def)
    by metis
    
  hence "(sa, sa') ∈ tso_round0 r S v" using next_r step_r v R 
    vote_origin[OF send step step_r]
    by(auto simp add: tso_round0_def sa'_def all_conj_distrib)
  moreover have "(sa', Suc r, cfg') ∈ ref_rel" using step send v R same_dec step_r next_r
    apply(auto simp add: ref_rel_def sa'_def  two_step_phase_Suc vote_const_map next0_def
      all_conj_distrib no_vote[OF send step step_r])
    by (metis pstate.select_convs(1) pstate.surjective pstate.update_convs(2))
  ultimately show
    "∃sa'. (∃r S v. (sa, sa') ∈ tso_round0 r S v) ∧ (sa', Suc r, cfg') ∈ ref_rel"
    by blast
qed
definition D where
  "D cfg cfg' ≡ {p. decide (cfg' p) ≠ decide (cfg p) }"
lemma decide_origin:
  assumes
  send: "∀p. μ p ∈ get_msgs (send1 r) cfg (HOs r) (HOs r) p"
  and step: "∀p. next1 r p (cfg p) (μ p) (cfg' p)"
  and step_r: "two_step r = Suc 0"
  shows 
    "D cfg cfg' ⊆ {p. ∃v. decide (cfg' p) = Some v ∧ (∀q ∈ HOs r p. vote (cfg q) = Some v)}" 
  using assms
  by(fastforce simp add: D_def next1_def get_msgs_benign send1_def msgRcvd_def o_def restrict_map_def
    x_update_def dec_update_def identicalVoteRcvd_def all_conj_distrib someVoteRcvd_def isVote_def)
  
lemma step1_ref:
  "{ref_rel ∩ (TSO_inv1 ∩ TSO_inv2) × UNIV} ⋃r d_f o_f. tso_round1 r d_f o_f, 
    BenOr_trans_step HOs HOs next1 send1 (Suc 0) {> ref_rel}"
proof(clarsimp simp add: PO_rhoare_defs BenOr_trans_step_def all_conj_distrib)
  fix sa r cfg μ and cfg' :: "process ⇒ val pstate"
  assume
    R: "(sa, (r, cfg)) ∈ ref_rel"
    and step_r: "two_step r = Suc 0" 
    and send: "∀p. μ p ∈ get_msgs (send1 r) cfg (HOs r) (HOs r) p"
    and step: "∀p. next1 r p (cfg p) (μ p) (cfg' p)"
    and ainv: "sa ∈ TSO_inv1"
    and ainv2: "sa ∈ TSO_inv2"
  from R have next_r: "next_round sa = r"
    by(simp add: ref_rel_def)
  define S where "S = {p. ∃v. vote (cfg p) = Some v}"
  from R obtain v where v: "∀p ∈ S. vote (cfg p) = Some v" using ainv step_r
    by(auto simp add: ref_rel_def TSO_inv1_def S_def)
  define Ob where "Ob = {p. x (cfg' p) = v}"
  define o_f where "o_f p = (if S ∈ Quorum then Some v else None)" for p :: process
  define dec_f where "dec_f p = (if p ∈ D cfg cfg' then decide (cfg' p) else None)" for p
  {
    fix p w
    assume "vote (cfg p) = Some w"
    hence "w = v" using v
      by(unfold S_def, auto)
  } note v'=this
    
  have d_guard: "d_guard dec_f (vote ∘ cfg)" using per_rd[THEN spec, of r]
    by(fastforce simp add: d_guard_def locked_in_vf_def quorum_for_def dec_f_def
      BenOr_commPerRd_def dest!: decide_origin[OF send step step_r, THEN subsetD])
  have "dom (vote ∘ cfg) ∈ Quorum ⟶ Ob = UNIV"
  proof(auto simp add: Ob_def)
    fix p
    assume Q: "dom (vote ∘ cfg) ∈ Quorum" (is "?Q ∈ Quorum")
    hence "?Q ∩ HOs r p ≠ {}"  using per_rd[THEN spec, of r]
      by(auto simp add: BenOr_commPerRd_def dest: qintersect)
    hence "someVoteRcvd (μ p) ≠ {}" using send[THEN spec, of p]
      by(force simp add: someVoteRcvd_def get_msgs_benign msgRcvd_def restrict_map_def 
        isVote_def send1_def)
    moreover have "∀q ∈ someVoteRcvd (μ p). ∃x'. μ p q = Some (Vote (Some v))"
       using send[THEN spec, of p]
      by(auto simp add: someVoteRcvd_def get_msgs_benign msgRcvd_def restrict_map_def
        isVote_def send1_def dest: v')
    ultimately show "x (cfg' p) = v" using step[THEN spec, of p]
      by(auto simp add: next1_def x_update_def)
  qed
  note Ob_UNIV=this[rule_format]
  have obs_guard: "obs_guard o_f (vote ∘ cfg)"
    apply(auto simp add: obs_guard_def o_f_def S_def dom_def
      dest: v' Ob_UNIV quorum_non_empty) 
    apply (metis S_def all_not_in_conv  empty_not_quorum v)
    done
  define sa' where "sa' = sa⦇ 
    next_round := Suc (next_round sa)
    , decisions := decisions sa ++ dec_f
    , last_obs := last_obs sa ++ o_f
    ⦈"
  
  have abs_step: "(sa, sa') ∈ tso_round1 r dec_f o_f"  using next_r step_r R d_guard obs_guard
    by(auto simp add: tso_round1_def sa'_def ref_rel_def)
  
  have "∀p. ((decide ∘ cfg) ++ dec_f) p = decide (cfg' p)"
  proof
    fix p
    show "((decide ∘ cfg) ++ dec_f) p = decide (cfg' p)" using step[THEN spec, of p]
      by(auto simp add: dec_f_def D_def next1_def dec_update_def map_add_def)
  qed
  note dec_rel=this[rule_format]
  have "∀p. (∃q. o_f q = None ∧ opt_obsv_state.last_obs sa q = None 
        ∨ (opt_obsv_state.last_obs sa ++ o_f) q = Some (x (cfg' p)))"
  proof(intro allI impI, cases "S ∈ Quorum")
    fix p 
    case True 
    hence "x (cfg' p) = v" using Ob_UNIV
      by(auto simp add: S_def Ob_def dom_def)
    thus "(∃q. o_f q = None ∧ opt_obsv_state.last_obs sa q = None 
        ∨ (opt_obsv_state.last_obs sa ++ o_f) q = Some (x (cfg' p)))"
      using True
      by(auto simp add: o_f_def)
  next
    fix p
    case False
    hence empty: "o_f = Map.empty"
      by(auto simp add: o_f_def)
    from False have "S ≠ UNIV" using UNIV_quorum
      by auto
    then obtain q where q: "vote (cfg q) = None" using False
      by(auto simp add: o_f_def S_def)
    then obtain q1 q2 where 
      "x (cfg q1) ≠ x (cfg q2)" using R step_r
      by(auto simp add: ref_rel_def no_vote_diff_def)
    then obtain q1' q2' where
      "x (cfg q1') = val0"
      "x (cfg q2') = val1"
      by (metis (poly_guards_query) val_exhaust)
    hence "∀v. ∃q. opt_obsv_state.last_obs sa q ∈ {None, Some v}" using R step_r
      apply(auto simp add: ref_rel_def)
      by (metis (poly_guards_query) val_exhaust)
    
    thus "(∃q. o_f q = None ∧ opt_obsv_state.last_obs sa q = None 
        ∨ (opt_obsv_state.last_obs sa ++ o_f) q = Some (x (cfg' p)))" using empty
        by(auto)
  qed
  note obs_rel=this[rule_format]
  have post_rel: 
    "(sa', Suc r, cfg') ∈ ref_rel" using step send next_r R step_r
    by(auto simp add: sa'_def ref_rel_def 
      two_step_phase_Suc dec_rel obs_rel)
    
  from abs_step post_rel show
    "∃sa'. (∃r d_f o_f. (sa, sa') ∈ tso_round1 r d_f o_f) ∧ (sa', Suc r, cfg') ∈ ref_rel"
    by blast
qed
lemma BenOr_Refines_Two_Step_Obs:
  "PO_refines (ref_rel ∩ (TSO_inv1 ∩ TSO_inv2) × UNIV)
    tso_TS (BenOr_TS HOs HOs crds)"
proof(rule refine_using_invariants)
  show "init (BenOr_TS HOs HOs crds) ⊆ ref_rel `` init tso_TS"
    by(auto simp add: BenOr_TS_defs BenOr_HOMachine_def CHOAlgorithm.truncate_def 
      tso_TS_defs ref_rel_def tso_init_def Let_def o_def)
next
  show 
    "{ref_rel ∩ (TSO_inv1 ∩ TSO_inv2) × UNIV} TS.trans tso_TS, 
      TS.trans (BenOr_TS HOs HOs crds) {> ref_rel}"
    apply(simp add: tso_TS_defs BenOr_TS_defs BenOr_HOMachine_def CHOAlgorithm.truncate_def)
    apply(auto simp add: CHO_trans_alt BenOr_trans intro!: step0_ref step1_ref)
    done
qed(auto intro!: TSO_inv1_inductive TSO_inv2_inductive)
subsubsection ‹Termination›
text ‹The full termination proof for Ben-Or is probabilistic, and depends on the state
of the processes, and a "favorable" coin toss, where "favorable" is relative to this state.
As this termination pre-condition is state-dependent, we cannot capture it in an HO 
predicate.
Instead, we prove a variant of the argument, where we assume that there exists a 
round where all the processes hear from the same set of other processes, and all toss the 
same coin.
›
theorem BenOr_termination:
  shows "∃r v. decide (rho r p) = Some v"
proof -
  from comm_global obtain r1 where r1: 
    "∀q. HOs r1 p = HOs r1 q"
    "∀q. (coin r1 p :: val) = coin r1 q"
    "two_step r1 = 1"
    by(simp add: BenOr_commGlobal_def all_conj_distrib, blast)
  from r1 obtain r0 where r1_def: "r1 = Suc r0" and step_r0: "two_step r0 = 0"
    by (cases r1) (auto simp add: two_step_phase_Suc two_step_def mod_Suc)
  define cfg0 where "cfg0 = rho r0"
  define cfg1 where "cfg1 = rho r1"
  define r2 where "r2 = Suc r1"
  define cfg2 where "cfg2 = rho r2"
  define r3 where "r3 = Suc r2"
  define cfg3 where "cfg3 = rho r3"
  define cfg4 where "cfg4 = rho (Suc r3)"
  have step_r2: "two_step r2 = 0" using r1
    by(auto simp add: r2_def two_step_phase_Suc)
  from
    run[simplified HORun_def SHORun_def, THEN CSHORun_step, THEN spec, where x="r0"]
    run[simplified HORun_def SHORun_def, THEN CSHORun_step, THEN spec, where x="r1"]
    run[simplified HORun_def SHORun_def, THEN CSHORun_step, THEN spec, where x="r2"] 
    run[simplified HORun_def SHORun_def, THEN CSHORun_step, THEN spec, where x="r3"]
    obtain μ0 μ1 μ2 μ3 where
    send0: "∀p. μ0 p ∈ get_msgs (send0 r0) cfg0 (HOs r0) (HOs r0) p"
    and step0: "∀p. next0 r0 p (cfg0 p) (μ0 p) (cfg1 p)"
    and send1: "∀p. μ1 p ∈ get_msgs (send1 r1) cfg1 (HOs r1) (HOs r1) p"
    and step1: "∀p. next1 r1 p (cfg1 p) (μ1 p) (cfg2 p)"
    and send2: "∀p. μ2 p ∈ get_msgs (send0 r2) cfg2 (HOs r2) (HOs r2) p"
    and step2: "∀p. next0 r2 p (cfg2 p) (μ2 p) (cfg3 p)"
    and send3: "∀p. μ3 p ∈ get_msgs (send1 r3) cfg3 (HOs r3) (HOs r3) p"
    and step3: "∀p. next1 r3 p (cfg3 p) (μ3 p) (cfg4 p)"
    by(auto simp add: BenOr_A_def BenOr_HOMachine_def 
      two_step_phase_Suc BenOr_nextState_def BenOr_sendMsg_def all_conj_distrib
      CHOAlgorithm.truncate_def step_r0 r1_def r2_def r3_def
      cfg0_def cfg1_def cfg2_def cfg3_def cfg4_def 
      )
  
  let ?v = "x (cfg2 p)"
  from per_rd r1 have xs: "∀q. x (cfg2 q) = ?v"
  proof(cases "∃q w. q ∈ HOs r1 p ∧ vote (cfg1 q) = Some w")
    case True
    then obtain q w where q_w: "q ∈ HOs r1 p ∧ vote (cfg1 q) = Some w"
      by auto
    then have "∀q'. vote (cfg1 q') ∈ {None, Some w}" using  same_new_vote[OF send0 step0 step_r0]
      by (metis insert_iff not_None_eq)
    hence "∀q'. x (cfg2 q') = w"  using step1 send1 q_w
      apply(auto simp add: next1_def all_conj_distrib dec_update_def x_update_def
        get_msgs_benign send1_def isVote_def msgRcvd_def identicalVoteRcvd_def 
        someVoteRcvd_def restrict_map_def)
      by (metis (erased, opaque_lifting) option.distinct(2) option.sel r1(1))
    thus ?thesis
      by auto
  next
    case False
    hence "∀q'. x (cfg2 q') = coin r1 q'" using r1 step1 send1
      apply(auto simp add: next1_def all_conj_distrib dec_update_def x_update_def
        get_msgs_benign send1_def isVote_def msgRcvd_def identicalVoteRcvd_def 
        someVoteRcvd_def restrict_map_def)
      by (metis False)
    thus ?thesis using r1
      by(metis)      
  qed
  
  hence "∀q. vote (cfg3 q) = Some ?v"
    by(simp add: vote_origin[OF send2 step2 step_r2])
  hence "decide (cfg4 p) = Some ?v" using send3[THEN spec, of p] step3[THEN spec, of p] HOs_nonempty
    by(auto simp add: next1_def send1_def get_msgs_benign dec_update_def
      restrict_map_def identicalVoteRcvd_def msgRcvd_def isVote_def)
  thus ?thesis
    by(auto simp add: cfg4_def)
qed
end 
end 
end