-
Notifications
You must be signed in to change notification settings - Fork 217
Expand file tree
/
Copy pathTwoPhase_proof.tla
More file actions
154 lines (146 loc) · 7.7 KB
/
TwoPhase_proof.tla
File metadata and controls
154 lines (146 loc) · 7.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
--------------------------- MODULE TwoPhase_proof --------------------------
(***************************************************************************)
(* TLAPS proofs of TwoPhase.tla theorems: *)
(* *)
(* TPSpec => []TPTypeOK (Band E, directly inductive) *)
(* TPSpec => []TC!TCConsistent (Band M, no conflicting decisions) *)
(* *)
(* TC!TCConsistent says no two RMs end up "committed" and "aborted" *)
(* simultaneously. It is not directly inductive; the strengthening below *)
(* tracks the message-sequencing facts that explain why the TM-broadcast *)
(* "Commit" and "Abort" decisions are mutually exclusive, and how each *)
(* RM's local state correlates with what is on the wire. *)
(* *)
(* The candidate inductive invariant was first validated with Apalache *)
(* (per Konnov/Kuppe/Merz, arXiv:2211.07216 Sec. 3.2) on a finite *)
(* instance with 3 RMs: *)
(* *)
(* TPInit /\ [TPNext]_vars |=0 Inv (initial states satisfy Inv) *)
(* InvInit /\ [TPNext]_vars |=1 Inv (Inv is preserved one step) *)
(* Inv => TCConsistent (Inv implies the goal) *)
(***************************************************************************)
EXTENDS TwoPhase, TLAPS
(***************************************************************************)
(* TPSpec => []TPTypeOK *)
(***************************************************************************)
THEOREM TypeCorrect == TPSpec => []TPTypeOK
<1>1. TPInit => TPTypeOK
BY DEF TPInit, TPTypeOK
<1>2. TPTypeOK /\ [TPNext]_<<rmState, tmState, tmPrepared, msgs>> => TPTypeOK'
<2> SUFFICES ASSUME TPTypeOK,
[TPNext]_<<rmState, tmState, tmPrepared, msgs>>
PROVE TPTypeOK'
OBVIOUS
<2>. USE DEF TPTypeOK, Message
<2>1. CASE TMCommit BY <2>1 DEF TMCommit
<2>2. CASE TMAbort BY <2>2 DEF TMAbort
<2>3. ASSUME NEW rm \in RM, TMRcvPrepared(rm)
PROVE TPTypeOK'
BY <2>3 DEF TMRcvPrepared
<2>4. ASSUME NEW rm \in RM, RMPrepare(rm)
PROVE TPTypeOK'
BY <2>4 DEF RMPrepare
<2>5. ASSUME NEW rm \in RM, RMChooseToAbort(rm)
PROVE TPTypeOK'
BY <2>5 DEF RMChooseToAbort
<2>6. ASSUME NEW rm \in RM, RMRcvCommitMsg(rm)
PROVE TPTypeOK'
BY <2>6 DEF RMRcvCommitMsg
<2>7. ASSUME NEW rm \in RM, RMRcvAbortMsg(rm)
PROVE TPTypeOK'
BY <2>7 DEF RMRcvAbortMsg
<2>8. CASE UNCHANGED <<rmState, tmState, tmPrepared, msgs>>
BY <2>8
<2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7, <2>8 DEF TPNext
<1>. QED BY <1>1, <1>2, PTL DEF TPSpec
(***************************************************************************)
(* TPSpec => []TC!TCConsistent (Band M) *)
(***************************************************************************)
CommitMsg == [type |-> "Commit"]
AbortMsg == [type |-> "Abort"]
PrepMsg(rm) == [type |-> "Prepared", rm |-> rm]
(***************************************************************************)
(* The strengthened inductive invariant. Each conjunct is a fact that *)
(* the protocol's actions preserve and that together imply TCConsistent. *)
(* *)
(* 1. TPTypeOK *)
(* 2. The TM commits at most one decision (mutex on Commit/Abort msgs). *)
(* 3-5. tmState mirrors which decision message has been broadcast. *)
(* 6. tmPrepared only contains RMs that actually sent "Prepared". *)
(* 7. RMs that have a "Prepared" msg in flight are no longer "working". *)
(* 8. "committed" RMs imply CommitMsg has been broadcast. *)
(* 9. CommitMsg in msgs implies every RM had sent "Prepared" first *)
(* (preserved from TMCommit's tmPrepared = RM precondition). *)
(* 10. "aborted" RMs split into two cases: *)
(* - via RMRcvAbortMsg (AbortMsg in msgs), or *)
(* - via RMChooseToAbort (the RM never sent "Prepared"). *)
(***************************************************************************)
Inv ==
/\ TPTypeOK
/\ ~ (CommitMsg \in msgs /\ AbortMsg \in msgs)
/\ tmState = "init" => CommitMsg \notin msgs /\ AbortMsg \notin msgs
/\ tmState = "committed" => CommitMsg \in msgs
/\ tmState = "aborted" => AbortMsg \in msgs
/\ \A rm \in tmPrepared : PrepMsg(rm) \in msgs
/\ \A rm \in RM : PrepMsg(rm) \in msgs => rmState[rm] # "working"
/\ \A rm \in RM : rmState[rm] = "committed" => CommitMsg \in msgs
/\ CommitMsg \in msgs => \A rm \in RM : PrepMsg(rm) \in msgs
/\ \A rm \in RM : rmState[rm] = "aborted" =>
\/ AbortMsg \in msgs
\/ PrepMsg(rm) \notin msgs
LEMMA InvInductive == TPSpec => []Inv
<1>1. TPInit => Inv
BY DEF TPInit, Inv, TPTypeOK, Message, CommitMsg, AbortMsg, PrepMsg
<1>2. Inv /\ [TPNext]_<<rmState, tmState, tmPrepared, msgs>> => Inv'
<2> SUFFICES ASSUME Inv,
[TPNext]_<<rmState, tmState, tmPrepared, msgs>>
PROVE Inv'
OBVIOUS
<2>. USE DEF Inv, TPTypeOK, Message, CommitMsg, AbortMsg, PrepMsg
<2>1. CASE TMCommit
\* tmState : init -> committed; CommitMsg added to msgs.
\* AbortMsg notin msgs in pre-state (tmState=init). tmPrepared = RM
\* gives \A rm \in RM : PrepMsg(rm) \in msgs (via conjunct 6).
BY <2>1 DEF TMCommit
<2>2. CASE TMAbort
\* tmState : init -> aborted; AbortMsg added. CommitMsg notin msgs.
BY <2>2 DEF TMAbort
<2>3. ASSUME NEW rm \in RM, TMRcvPrepared(rm)
PROVE Inv'
\* tmPrepared grows by {rm}, msgs unchanged. PrepMsg(rm) \in msgs is
\* the precondition, so the new tmPrepared still satisfies conjunct 6.
BY <2>3 DEF TMRcvPrepared
<2>4. ASSUME NEW rm \in RM, RMPrepare(rm)
PROVE Inv'
\* rmState[rm] : working -> prepared; PrepMsg(rm) added to msgs.
BY <2>4 DEF RMPrepare
<2>5. ASSUME NEW rm \in RM, RMChooseToAbort(rm)
PROVE Inv'
\* rmState[rm] : working -> aborted, no msg change. PrepMsg(rm)
\* could only have been in msgs if rmState[rm] # "working", but the
\* precondition says it WAS "working", so PrepMsg(rm) \notin msgs;
\* conjunct 10 holds via the second disjunct.
BY <2>5 DEF RMChooseToAbort
<2>6. ASSUME NEW rm \in RM, RMRcvCommitMsg(rm)
PROVE Inv'
\* rmState[rm] becomes "committed"; preconditions: CommitMsg \in msgs.
BY <2>6 DEF RMRcvCommitMsg
<2>7. ASSUME NEW rm \in RM, RMRcvAbortMsg(rm)
PROVE Inv'
\* rmState[rm] becomes "aborted"; preconditions: AbortMsg \in msgs.
BY <2>7 DEF RMRcvAbortMsg
<2>8. CASE UNCHANGED <<rmState, tmState, tmPrepared, msgs>>
BY <2>8
<2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7, <2>8 DEF TPNext
<1>. QED BY <1>1, <1>2, PTL DEF TPSpec
THEOREM Consistency == TPSpec => []TC!TCConsistent
<1>1. Inv => TC!TCConsistent
\* Suppose rmState[rm1] = "aborted" and rmState[rm2] = "committed".
\* From conjunct 8: CommitMsg \in msgs.
\* From conjunct 9: PrepMsg(rm1) \in msgs.
\* From conjunct 2: AbortMsg \notin msgs.
\* From conjunct 10 applied to rm1: AbortMsg \in msgs (false) or
\* PrepMsg(rm1) \notin msgs (false). Contradiction.
BY DEF Inv, TC!TCConsistent, CommitMsg, AbortMsg, PrepMsg
<1>. QED BY <1>1, InvInductive, PTL
============================================================================