(****************************************************************)
(****************************************************************)
(*** Per-node state transition systems ***)
(* Coordinator transitions *)
(* Changes in the Coordinator state/log triggered upon send *)
Definition cstep_send (cs: CStateT) (to : nid) (d : data) (l : Log) :
CStateT * Log :=
(* Only accept good destinations *)
if to \in pts then
let: (e, s) := cs in
match s with
| CInit =>
if pts == [:: to]
then (e, CWaitPrepResponse d [::], l)
else (e, CSentPrep d [:: to], l)
(* Sending pre-messages *)
| CSentPrep d' tos =>
(* Do not duplicate prepare-requests *)
if perm_eq (to :: tos) pts
(* If all sent, switch to the receiving state *)
then (e, CWaitPrepResponse d' [::], l)
else (e, CSentPrep d' (to :: tos), l)
| CWaitPrepResponse d' res =>
(* Switch into sending commit or abort-messages mode *)
if (perm_eq (map fst res) pts)
then if all (fun r => r) (map snd res)
then if pts == [:: to]
then (e, CWaitAckCommit d' [::], l)
else (e, CSentCommit d' [:: to], l)
else if pts == [:: to]
then (e, CWaitAckAbort d' [::], l)
else (e, CSentAbort  d' [:: to], l)
else (cs, l)
| CSentCommit d' tos =>
(* Sending commit messages *)
if perm_eq (to :: tos) pts
then (e, CWaitAckCommit d' [::], l)
else (e, CSentCommit d' (to :: tos), l)
| CSentAbort d' tos =>
if perm_eq (to :: tos) pts
then (e, CWaitAckAbort d' [::], l)
else (e, CSentAbort d' (to :: tos), l)
| _ => (cs, l)
end
else (cs, l).
Definition c_matches_tag s mtag : bool :=
match  s with
| CWaitPrepResponse _ _ => (mtag == prep_yes) || (mtag == prep_no)
| CWaitAckCommit _ _ => mtag == commit_ack
| CWaitAckAbort _ _ => mtag == abort_ack
| _ => false
end.
(* Changes in the Coordinator state/log triggered upon receive *)
Definition cstep_recv' (cs : CStateT) (from : nid) (mtag : ttag)
(mbody : payload) (l : Log) : CStateT * Log  :=
let: (e, s) := cs in
match s with
| CWaitPrepResponse d' res =>
(* All responses already collected or already received from this participant  *)
if (from \in (map fst res))
then (cs, l)
(* Save result *)
else (e, CWaitPrepResponse d' ((from, mtag == prep_yes) :: res), l)
| CWaitAckCommit d' res =>
if from \in res then (cs, l)
else if (perm_eq (from :: res) pts)
then ((e.+1, CInit), rcons l (true, d'))
else (e, CWaitAckCommit d' (from :: res), l)
| CWaitAckAbort d' res =>
if from \in res then (cs, l)
else if (perm_eq (from :: res) pts)
then ((e.+1, CInit), rcons l (false, d'))
else (e, CWaitAckAbort d' (from :: res), l)
| _ => (cs, l)
end.
Definition cstep_recv (cs: CStateT) (from : nid) (mtag : ttag)
(mbody : payload) (l : Log) : CStateT * Log  :=
if (from \notin pts) then (cs, l)
else let: (e, s) := cs in
(* Ignore messages from irrelevant rounds *)
if (head 0 mbody != e) then (cs, l) else
cstep_recv' cs from mtag mbody l
.
(*  There should be 3 send-transitions for the coordinator:  - send-prepare - send-commit - send-abort  There should be 4 receive-transitions for the coordinator:  - receive-prepare-yes - receive-prepare-no - receive-ack-commit - receive-ack-abort *)
Section CoordinatorGenericSendTransitions.
Notation coh := TPCCoh.
Definition HCn this to := (this == cn /\ to \in pts).
Definition mkLocal {T} (sl : T * Log) := st :-> sl.1 \+ log :-> sl.2.
Variable stag : ttag.
(* Precondition -- this is the way one can define multiple send-transitions *)
Variable prec : CStateT -> nid -> payload -> Prop.
(* Making sure that the precondition is legit *)
Hypothesis cn_prec_safe :
forall this to s m,
HCn this to -> prec s to m -> cohMsg (Msg (TMsg stag m) this to true) s.1.
Definition cn_safe (this n : nid)
(d : dstatelet) (msg : data) :=
HCn this n /\
exists (C : coh d), prec (getStC C) n msg.

Lemma cn_safe_coh this to d m : cn_safe this to d m -> coh d.
Definition cn_step (this to : nid) (d : dstatelet)
(msg : seq nat)
(pf : cn_safe this to d msg) :=
let C := cn_safe_coh pf in
let s := getStC C in
let l := getStL C (cn_this_in (proj1 pf)) in
Some (mkLocal (cstep_send s to (behead msg) l)).