-
Notifications
You must be signed in to change notification settings - Fork 201
/
Copy pathVoteProof.tla
1496 lines (1424 loc) · 68.2 KB
/
VoteProof.tla
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
----------------------------- MODULE VoteProof ------------------------------
(***************************************************************************)
(* This is a high-level consensus algorithm in which a set of processes *)
(* called `acceptors' cooperatively choose a value. The algorithm uses *)
(* numbered ballots, where a ballot is a round of voting. Acceptors cast *)
(* votes in ballots, casting at most one vote per ballot. A value is *)
(* chosen when a large enough set of acceptors, called a `quorum', have *)
(* all voted for the same value in the same ballot. *)
(* *)
(* Ballots are not executed in order. Different acceptors may be *)
(* concurrently performing actions for different ballots. *)
(***************************************************************************)
EXTENDS Integers, NaturalsInduction, FiniteSets, FiniteSetTheorems,
WellFoundedInduction, TLC, TLAPS
CONSTANT Value, \* As in module Consensus, the set of choosable values.
Acceptor, \* The set of all acceptors.
Quorum \* The set of all quorums.
(***************************************************************************)
(* The following assumption asserts that a quorum is a set of acceptors, *)
(* and the fundamental assumption we make about quorums: any two quorums *)
(* have a non-empty intersection. *)
(***************************************************************************)
ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor
/\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {}
THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {}
PROOF BY QA
-----------------------------------------------------------------------------
(***************************************************************************)
(* Ballot is the set of all ballot numbers. For simplicity, we let it be *)
(* the set of natural numbers. However, we write Ballot for that set to *)
(* make it clear what the function of those natural numbers are. *)
(* *)
(* The algorithm and its refinements work with Ballot any set with minimal *)
(* element 0, -1 not an element of Ballot, and a well-founded total order *)
(* < on Ballot \cup {-1} with minimal element -1, and 0 < b for all *)
(* non-zero b in Ballot. In the proof, any set of the form i..j must be *)
(* replaced by the set of all elements b in Ballot \cup {-1} with i \leq b *)
(* \leq j, and i..(j-1) by the set of such b with i \leq b < j. *)
(***************************************************************************)
Ballot == Nat
-----------------------------------------------------------------------------
(***************************************************************************)
(* In the algorithm, each acceptor can cast one or more votes, where each *)
(* vote cast by an acceptor has the form <<b, v>> indicating that the *)
(* acceptor has voted for value v in ballot b. A value is chosen if a *)
(* quorum of acceptors have voted for it in the same ballot. *)
(* *)
(* The algorithm uses two variables, `votes' and `maxBal', both arrays *)
(* indexed by acceptor. Their meanings are: *)
(* *)
(* votes[a] - The set of votes cast by acceptor `a'. *)
(* *)
(* maxBal[a] - The number of the highest-numbered ballot in which `a' *)
(* has cast a vote, or -1 if it has not yet voted. *)
(* *)
(* The algorithm does not let acceptor `a' vote in any ballot less than *)
(* maxBal[a]. *)
(* *)
(* We specify our algorithm by the following PlusCal algorithm. The *)
(* specification Spec defined by this algorithm describes only the safety *)
(* properties of the algorithm. In other words, it specifies what steps *)
(* the algorithm may take. It does not require that any (non-stuttering) *)
(* steps be taken. We prove that this specification Spec implements the *)
(* specification Spec of module Consensus under a refinement mapping *)
(* defined below. This shows that the safety properties of the voting *)
(* algorithm (and hence the algorithm with additional liveness *)
(* requirements) imply the safety properties of the Consensus *)
(* specification. Liveness is discussed later. *)
(***************************************************************************)
(***************************
--algorithm Voting {
variables votes = [a \in Acceptor |-> {}],
maxBal = [a \in Acceptor |-> -1];
define {
(************************************************************************)
(* We now define the operator SafeAt so SafeAt(b, v) is function of the *)
(* state that equals TRUE if no value other than v has been chosen or *)
(* can ever be chosen in the future (because the values of the *)
(* variables votes and maxBal are such that the algorithm does not *)
(* allow enough acceptors to vote for it). We say that value v is safe *)
(* at ballot number b iff Safe(b, v) is true. We define Safe in terms *)
(* of the following two operators. *)
(* *)
(* Note: This definition is weaker than would be necessary to allow a *)
(* refinement of ordinary Paxos consensus, since it allows different *)
(* quorums to "cooperate" in determining safety at b. This is used in *)
(* algorithms like Vertical Paxos that are designed to allow *)
(* reconfiguration within a single consensus instance, but not in *)
(* ordinary Paxos. See *)
(* *)
(* AUTHOR = "Leslie Lamport and Dahlia Malkhi and Lidong Zhou ", *)
(* TITLE = "Vertical Paxos and Primary-Backup Replication", *)
(* Journal = "ACM SIGACT News (Distributed Computing Column)", *)
(* editor = {Srikanta Tirthapura and Lorenzo Alvisi}, *)
(* booktitle = {PODC}, *)
(* publisher = {ACM}, *)
(* YEAR = 2009, *)
(* PAGES = "312--313" *)
(************************************************************************)
VotedFor(a, b, v) == <<b, v>> \in votes[a]
(**********************************************************************)
(* True iff acceptor a has voted for v in ballot b. *)
(**********************************************************************)
DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v)
(************************************************************************)
(* We now define SafeAt. We define it recursively. The nicest *)
(* definition is *)
(* *)
(* RECURSIVE SafeAt(_, _) *)
(* SafeAt(b, v) == *)
(* \/ b = 0 *)
(* \/ \E Q \in Quorum : *)
(* /\ \A a \in Q : maxBal[a] \geq b *)
(* /\ \E c \in -1..(b-1) : *)
(* /\ (c # -1) => /\ SafeAt(c, v) *)
(* /\ \A a \in Q : *)
(* \A w \in Value : *)
(* VotedFor(a, c, w) => (w = v) *)
(* /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) *)
(* *)
(* However, TLAPS does not currently support recursive operator *)
(* definitions. We therefore define it as follows using a recursive *)
(* function definition. *)
(************************************************************************)
SafeAt(b, v) ==
LET SA[bb \in Ballot] ==
(****************************************************************)
(* This recursively defines SA[bb] to equal SafeAt(bb, v). *)
(****************************************************************)
\/ bb = 0
\/ \E Q \in Quorum :
/\ \A a \in Q : maxBal[a] \geq bb
/\ \E c \in -1..(bb-1) :
/\ (c # -1) => /\ SA[c]
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, c, w) => (w = v)
/\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
IN SA[b]
}
(*************************************************************************)
(* There are two possible actions that an acceptor can perform, each *)
(* defined by a macro. In these macros, `self' is the acceptor that is *)
(* to perform the action. The first action, IncreaseMaxBal(b) allows *)
(* acceptor `self' to set maxBal[self] to b if b is greater than the *)
(* current value of maxBal[self]. *)
(*************************************************************************)
macro IncreaseMaxBal(b) {
when b > maxBal[self] ;
maxBal[self] := b
}
(*************************************************************************)
(* Action VoteFor(b, v) allows acceptor `self' to vote for value v in *)
(* ballot b if its `when' condition is satisfied. *)
(*************************************************************************)
macro VoteFor(b, v) {
when /\ maxBal[self] \leq b
/\ DidNotVoteIn(self, b)
/\ \A p \in Acceptor \ {self} :
\A w \in Value : VotedFor(p, b, w) => (w = v)
/\ SafeAt(b, v) ;
votes[self] := votes[self] \cup {<<b, v>>};
maxBal[self] := b
}
(*************************************************************************)
(* The following process declaration asserts that every process `self' *)
(* in the set Acceptor executes its body, which loops forever *)
(* nondeterministically choosing a Ballot b and executing either an *)
(* IncreaseMaxBal(b) action or nondeterministically choosing a value v *)
(* and executing a VoteFor(b, v) action. The single label indicates *)
(* that an entire execution of the body of the `while' loop is performed *)
(* as a single atomic action. *)
(* *)
(* From this intuitive description of the process declaration, one might *)
(* think that a process could be deadlocked by choosing a ballot b in *)
(* which neither an IncreaseMaxBal(b) action nor any VoteFor(b, v) *)
(* action is enabled. An examination of the TLA+ translation (and an *)
(* elementary knowledge of the meaning of existential quantification) *)
(* shows that this is not the case. You can think of all possible *)
(* choices of b and of v being examined simultaneously, and one of the *)
(* choices for which a step is possible being made. *)
(*************************************************************************)
process (acceptor \in Acceptor) {
acc : while (TRUE) {
with (b \in Ballot) {
either IncreaseMaxBal(b)
or with (v \in Value) { VoteFor(b, v) }
}
}
}
}
The following is the TLA+ specification produced by the translation.
Blank lines, produced by the translation because of the comments, have
been deleted.
****************************)
\* BEGIN TRANSLATION
VARIABLES votes, maxBal
(* define statement *)
VotedFor(a, b, v) == <<b, v>> \in votes[a]
DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v)
SafeAt(b, v) ==
LET SA[bb \in Ballot] ==
\/ bb = 0
\/ \E Q \in Quorum :
/\ \A a \in Q : maxBal[a] \geq bb
/\ \E c \in -1..(bb-1) :
/\ (c # -1) => /\ SA[c]
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, c, w) => (w = v)
/\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
IN SA[b]
vars == << votes, maxBal >>
ProcSet == (Acceptor)
Init == (* Global variables *)
/\ votes = [a \in Acceptor |-> {}]
/\ maxBal = [a \in Acceptor |-> -1]
acceptor(self) == \E b \in Ballot:
\/ /\ b > maxBal[self]
/\ maxBal' = [maxBal EXCEPT ![self] = b]
/\ UNCHANGED votes
\/ /\ \E v \in Value:
/\ /\ maxBal[self] \leq b
/\ DidNotVoteIn(self, b)
/\ \A p \in Acceptor \ {self} :
\A w \in Value : VotedFor(p, b, w) => (w = v)
/\ SafeAt(b, v)
/\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
/\ maxBal' = [maxBal EXCEPT ![self] = b]
Next == (\E self \in Acceptor: acceptor(self))
Spec == Init /\ [][Next]_vars
\* END TRANSLATION
-----------------------------------------------------------------------------
(***************************************************************************)
(* To reason about a recursively-defined operator, one must prove a *)
(* theorem about it. In particular, to reason about SafeAt, we need to *)
(* prove that SafeAt(b, v) equals the right-hand side of its definition, *)
(* for b \in Ballot and v \in Value. This is not automatically true for a *)
(* recursive definition. For example, from the recursive definition *)
(* *)
(* Silly[n \in Nat] == CHOOSE v : v # Silly[n] *)
(* *)
(* we cannot deduce that *)
(* *)
(* Silly[42] = CHOOSE v : v # Silly[42] *)
(* *)
(* (From that, we could easily deduce Silly[42] # Silly[42].) *)
(***************************************************************************)
(***************************************************************************)
(* Here is the theorem that essentially asserts that SafeAt(b, v) equals *)
(* the right-hand side of its definition. *)
(***************************************************************************)
THEOREM SafeAtProp ==
\A b \in Ballot, v \in Value :
SafeAt(b, v) <=>
\/ b = 0
\/ \E Q \in Quorum :
/\ \A a \in Q : maxBal[a] \geq b
/\ \E c \in -1..(b-1) :
/\ (c # -1) => /\ SafeAt(c, v)
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, c, w) => (w = v)
/\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)
<1>1. SUFFICES ASSUME NEW v \in Value
PROVE \A b \in Ballot : SafeAtProp!(b, v)
BY Zenon
<1> USE DEF Ballot
<1> DEFINE Def(SA, bb) ==
\/ bb = 0
\/ \E Q \in Quorum :
/\ \A a \in Q : maxBal[a] \geq bb
/\ \E c \in -1..(bb-1) :
/\ (c # -1) => /\ SA[c]
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, c, w) => (w = v)
/\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d)
SA[bb \in Ballot] == Def(SA, bb)
<1>2. \A b : SafeAt(b, v) = SA[b]
BY DEF SafeAt
<1>3. ASSUME NEW n \in Nat, NEW g, NEW h,
\A i \in 0..(n-1) : g[i] = h[i]
PROVE Def(g, n) = Def(h, n)
BY <1>3
<1>4. SA = [b \in Ballot |-> Def(SA, b)]
<2> HIDE DEF Def
<2> QED
BY <1>3, RecursiveFcnOfNat, Isa
<1>5. \A b \in Ballot : SA[b] = Def(SA, b)
<2> HIDE DEF Def
<2> QED
BY <1>4, Zenon
<1>6. QED
BY <1>2, <1>5, Zenon DEF SafeAt
-----------------------------------------------------------------------------
(***************************************************************************)
(* We now define TypeOK to be the type-correctness invariant. *)
(***************************************************************************)
TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)]
/\ maxBal \in [Acceptor -> Ballot \cup {-1}]
(***************************************************************************)
(* We now define `chosen' to be the state function so that the algorithm *)
(* specified by formula Spec conjoined with the liveness requirements *)
(* described below implements the algorithm of module Consensus (satisfies *)
(* the specification LiveSpec of that module) under a refinement mapping *)
(* that substitutes this state function `chosen' for the variable `chosen' *)
(* of module Consensus. The definition uses the following one, which *)
(* defines ChosenIn(b, v) to be true iff a quorum of acceptors have all *)
(* voted for v in ballot b. *)
(***************************************************************************)
ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v)
chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)}
-----------------------------------------------------------------------------
(***************************************************************************)
(* The following lemma is used for reasoning about the operator SafeAt. *)
(* It is proved from SafeAtProp by induction. *)
(***************************************************************************)
LEMMA SafeLemma ==
TypeOK =>
\A b \in Ballot :
\A v \in Value :
SafeAt(b, v) =>
\A c \in 0..(b-1) :
\E Q \in Quorum :
\A a \in Q : /\ maxBal[a] >= c
/\ \/ DidNotVoteIn(a, c)
\/ VotedFor(a, c, v)
<1> SUFFICES ASSUME TypeOK
PROVE SafeLemma!2
OBVIOUS
<1> DEFINE P(b) == \A c \in 0..b : SafeLemma!2!(c)
<1> USE DEF Ballot
<1>1. P(0)
OBVIOUS
<1>2. ASSUME NEW b \in Ballot, P(b)
PROVE P(b+1)
<2>1. /\ b+1 \in Ballot \ {0}
/\ (b+1) - 1 = b
OBVIOUS
<2>2. 0..(b+1) = (0..b) \cup {b+1}
OBVIOUS
<2>3. SUFFICES ASSUME NEW v \in Value,
SafeAt(b+1, v),
NEW c \in 0..b
PROVE \E Q \in Quorum :
\A a \in Q : /\ maxBal[a] >= c
/\ \/ DidNotVoteIn(a, c)
\/ VotedFor(a, c, v)
BY <1>2
<2>4. PICK Q \in Quorum :
/\ \A a \in Q : maxBal[a] \geq (b+1)
/\ \E cc \in -1..b :
/\ (cc # -1) => /\ SafeAt(cc, v)
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, cc, w) => (w = v)
/\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
BY SafeAtProp, <2>3, <2>1, Zenon
<2>5. PICK cc \in -1..b :
/\ (cc # -1) => /\ SafeAt(cc, v)
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, cc, w) => (w = v)
/\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d)
BY <2>4
<2>6. CASE c > cc
BY <2>4, <2>5, <2>6, QA DEF TypeOK
<2>7. CASE c = cc
<3>2. \A a \in Q : maxBal[a] \in Ballot \cup {-1}
BY QA DEF TypeOK
<3>3. \A a \in Q : maxBal[a] \geq c
BY <2>4, <2>7, <3>2
<3>4. \A a \in Q : \/ DidNotVoteIn(a, c)
\/ VotedFor(a, c, v)
BY <2>7, <2>5 DEF DidNotVoteIn
<3>5. QED
BY <3>3, <3>4
<2>8. CASE c < cc
BY <2>8, <1>2, <2>5
<2>9. QED
BY <2>6, <2>7, <2>8
<1>3. \A b \in Ballot : P(b)
BY <1>1, <1>2, NatInduction, Isa
<1>4. QED
BY <1>3
-----------------------------------------------------------------------------
(***************************************************************************)
(* We now define the invariant that is used to prove the correctness of *)
(* our algorithm--meaning that specification Spec implements specification *)
(* Spec of module Consensus under our refinement mapping. Correctness of *)
(* the voting algorithm follows from the the following three invariants: *)
(* *)
(* VInv1: In any ballot, an acceptor can vote for at most one value. *)
(* *)
(* VInv2: An acceptor can vote for a value v in ballot b iff v is *)
(* safe at b. *)
(* *)
(* VInv3: Two different acceptors cannot vote for different values in *)
(* the same ballot. *)
(* *)
(* Their precise definitions are as follows. *)
(***************************************************************************)
VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value :
VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w)
VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value :
VotedFor(a, b, v) => SafeAt(b, v)
VInv3 == \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value :
VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2)
(***************************************************************************)
(* It is obvious, that VInv3 implies VInv1--a fact that we now let TLAPS *)
(* prove as a little check that we haven't made a mistake in our *)
(* definitions. (Actually, we used TLC to check everything before *)
(* attempting any proofs.) We define VInv1 separately because VInv3 is not *)
(* needed for proving safety, only for liveness. *)
(***************************************************************************)
THEOREM VInv3 => VInv1
BY DEF VInv1, VInv3
-----------------------------------------------------------------------------
(***************************************************************************)
(* The following lemma proves that SafeAt(b, v) implies that no value *)
(* other than v can have been chosen in any ballot numbered less than b. *)
(* The fact that it also implies that no value other than v can ever be *)
(* chosen in the future follows from this and the fact that SafeAt(b, v) *)
(* is stable--meaning that once it becomes true, it remains true forever. *)
(* The stability of SafeAt(b, v) is proved as step <1>6 of theorem *)
(* InductiveInvariance below. *)
(* *)
(* This lemma is used only in the proof of theorem VT1 below. *)
(***************************************************************************)
LEMMA VT0 == /\ TypeOK
/\ VInv1
/\ VInv2
=> \A v, w \in Value, b, c \in Ballot :
(b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
<1> SUFFICES ASSUME TypeOK, VInv1, VInv2,
NEW v \in Value, NEW w \in Value
PROVE \A b, c \in Ballot :
(b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
OBVIOUS
<1> P(b) == \A c \in Ballot :
(b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)
<1> USE DEF Ballot
<1>1. P(0)
OBVIOUS
<1>2. ASSUME NEW b \in Ballot, \A i \in 0..(b-1) : P(i)
PROVE P(b)
<2>1. CASE b = 0
BY <2>1
<2>2. CASE b # 0
<3>1. SUFFICES ASSUME NEW c \in Ballot, b > c, SafeAt(b, v), ChosenIn(c, w)
PROVE v=w
OBVIOUS
<3>2. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w)
BY <3>1 DEF ChosenIn
<3>3. PICK QQ \in Quorum,
d \in -1..(b-1) :
/\ (d # -1) => /\ SafeAt(d, v)
/\ \A a \in QQ :
\A x \in Value :
VotedFor(a, d, x) => (x = v)
/\ \A e \in (d+1)..(b-1), a \in QQ : DidNotVoteIn(a, e)
BY <2>2, <3>1, SafeAtProp, Zenon
<3> PICK aa \in QQ \cap Q : TRUE
BY QA
<3>4. c \leq d
BY <3>1, <3>2, <3>3 DEF DidNotVoteIn
<3>5. CASE c = d
BY <3>2, <3>3, <3>4, <3>5
<3>6. CASE d > c
BY <1>2, <3>1, <3>3, <3>4, <3>6
<3>7. QED
BY <3>4, <3>5, <3>6
<2>. QED BY <2>1, <2>2
<1>3. \A b \in Ballot : P(b)
<2>. HIDE DEF P
<2>. QED BY <1>2, GeneralNatInduction, Isa
<1>4. QED
BY <1>3
(***************************************************************************)
(* The following theorem asserts that the invariance of TypeOK, VInv1, and *)
(* VInv2 implies that the algorithm satisfies the basic consensus property *)
(* that at most one value is chosen (at any time). If you can prove it, *)
(* then you understand why the Paxos consensus algorithm allows only a *)
(* single value to be chosen. Note that VInv3 is not needed to prove this *)
(* property. *)
(***************************************************************************)
THEOREM VT1 == /\ TypeOK
/\ VInv1
/\ VInv2
=> \A v, w :
(v \in chosen) /\ (w \in chosen) => (v = w)
<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2,
NEW v, NEW w,
v \in chosen, w \in chosen
PROVE v = w
OBVIOUS
<1>2. v \in Value /\ w \in Value
BY <1>1 DEF chosen
<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w)
BY <1>1 DEF chosen
<1>4. PICK Q \in Quorum, R \in Quorum :
/\ \A a \in Q : VotedFor(a, b, v)
/\ \A a \in R : VotedFor(a, c, w)
BY <1>3 DEF ChosenIn
<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v)
/\ VotedFor(aw, c, w)
BY <1>4, QuorumNonEmpty
<1>6. SafeAt(b, v) /\ SafeAt(c, w)
BY <1>1, <1>2, <1>5, QA DEF VInv2
<1>7. CASE b = c
<2> PICK a \in Q \cap R : TRUE
BY QA
<2>1. /\ VotedFor(a, b, v)
/\ VotedFor(a, c, w)
BY <1>4
<2>2. QED
BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1
<1>8. CASE b > c
BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2
<1>9. CASE c > b
BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2
<1>10. QED
BY <1>7, <1>8, <1>9 DEF Ballot
(***************************************************************************)
(* The rest of the proof uses only the primed version of VT1--that is, the *)
(* theorem whose statement is VT1'. (Remember that VT1 names the formula *)
(* being asserted by the theorem we call VT1.) The formula VT1' asserts *)
(* that VT1 is true in the second state of any transition (pair of *)
(* states). We derive that theorem from VT1 by simple temporal logic, and *)
(* similarly for VT0 and SafeAtProp. *)
(***************************************************************************)
THEOREM SafeAtPropPrime ==
\A b \in Ballot, v \in Value :
SafeAt(b, v)' <=>
\/ b = 0
\/ \E Q \in Quorum :
/\ \A a \in Q : maxBal'[a] \geq b
/\ \E c \in -1..(b-1) :
/\ (c # -1) => /\ SafeAt(c, v)'
/\ \A a \in Q :
\A w \in Value :
VotedFor(a, c, w)' => (w = v)
/\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)'
<1>1. SafeAtProp' BY SafeAtProp, PTL
<1>. QED BY <1>1
LEMMA VT0Prime ==
/\ TypeOK'
/\ VInv1'
/\ VInv2'
=> \A v, w \in Value, b, c \in Ballot :
(b > c) /\ SafeAt(b, v)' /\ ChosenIn(c, w)' => (v = w)
<1>1. VT0' BY VT0, PTL
<1>. QED BY <1>1
THEOREM VT1Prime ==
/\ TypeOK'
/\ VInv1'
/\ VInv2'
=> \A v, w :
(v \in chosen') /\ (w \in chosen') => (v = w)
<1>1. VT1' BY VT1, PTL
<1>. QED BY <1>1
-----------------------------------------------------------------------------
(***************************************************************************)
(* The invariance of VInv2 depends on SafeAt(b, v) being stable, meaning *)
(* that once it becomes true it remains true forever. Stability of *)
(* SafeAt(b, v) depends on the following invariant. *)
(***************************************************************************)
VInv4 == \A a \in Acceptor, b \in Ballot :
maxBal[a] < b => DidNotVoteIn(a, b)
(***************************************************************************)
(* The inductive invariant that we use to prove correctness of this *)
(* algorithm is VInv, defined as follows. *)
(***************************************************************************)
VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4
-----------------------------------------------------------------------------
(***************************************************************************)
(* To simplify reasoning about the next-state action Next, we want to *)
(* express it in a more convenient form. This is done by lemma NextDef *)
(* below, which shows that Next equals an action defined in terms of the *)
(* following subactions. *)
(***************************************************************************)
IncreaseMaxBal(self, b) ==
/\ b > maxBal[self]
/\ maxBal' = [maxBal EXCEPT ![self] = b]
/\ UNCHANGED votes
VoteFor(self, b, v) ==
/\ maxBal[self] \leq b
/\ DidNotVoteIn(self, b)
/\ \A p \in Acceptor \ {self} :
\A w \in Value : VotedFor(p, b, w) => (w = v)
/\ SafeAt(b, v)
/\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}]
/\ maxBal' = [maxBal EXCEPT ![self] = b]
BallotAction(self, b) ==
\/ IncreaseMaxBal(self, b)
\/ \E v \in Value : VoteFor(self, b, v)
(***************************************************************************)
(* When proving lemma NextDef, we were surprised to discover that it *)
(* required the assumption that the set of acceptors is non-empty. This *)
(* assumption isn't necessary for safety, since if there are no acceptors *)
(* there can be no quorums (see theorem QuorumNonEmpty above) so no value *)
(* is ever chosen and the Consensus specification is trivially implemented *)
(* under our refinement mapping. However, the assumption is necessary for *)
(* liveness and it allows us to lemma NextDef for the safety proof as *)
(* well, so we assert it now. *)
(***************************************************************************)
ASSUME AcceptorNonempty == Acceptor # {}
(***************************************************************************)
(* The proof of the lemma itself is quite simple. *)
(***************************************************************************)
LEMMA NextDef ==
TypeOK =>
(Next = \E self \in Acceptor :
\E b \in Ballot : BallotAction(self, b) )
<1> HAVE TypeOK
<1>2. Next = \E self \in Acceptor: acceptor(self)
BY AcceptorNonempty DEF Next, ProcSet
<1>3. @ = NextDef!2!2
BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor
<1>4. QED
BY <1>2, <1>3
-----------------------------------------------------------------------------
(***************************************************************************)
(* We now come to the proof that VInv is an invariant of the *)
(* specification. This follows from the following result, which asserts *)
(* that it is an inductive invariant of the next-state action. This fact *)
(* is used in the liveness proof as well. *)
(***************************************************************************)
THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv'
<1>1. VInv /\ (vars' = vars) => VInv'
BY Isa
DEF VInv, vars, TypeOK, VInv2, VotedFor, SafeAt, DidNotVoteIn, VInv3, VInv4
<1> SUFFICES ASSUME VInv,
NEW self \in Acceptor,
NEW b \in Ballot,
BallotAction(self, b)
PROVE VInv'
BY <1>1, NextDef DEF VInv
<1>2. TypeOK'
<2>1. CASE IncreaseMaxBal(self, b)
BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK
<2>2. CASE \E v \in Value : VoteFor(self, b, v)
BY <2>2 DEF VInv, TypeOK, VoteFor
<2>3. QED
BY <2>1, <2>2 DEF BallotAction
<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value,
VotedFor(a, c, w)
PROVE VotedFor(a, c, w)'
<2>1. CASE IncreaseMaxBal(self, b)
BY <2>1, <1>3 DEF IncreaseMaxBal, VotedFor
<2>2. CASE \E v \in Value : VoteFor(self, b, v)
<3>1. PICK v \in Value : VoteFor(self, b, v)
BY <2>2
<3>2. CASE a = self
<4>1. votes'[a] = votes[a] \cup {<<b, v>>}
BY <3>1, <3>2 DEF VoteFor, VInv, TypeOK
<4>2. QED
BY <1>3, <4>1 DEF VotedFor
<3>3. CASE a # self
<4>1. votes[a] = votes'[a]
BY <3>1, <3>3 DEF VoteFor, VInv, TypeOK
<4>2. QED
BY <1>3, <4>1 DEF VotedFor
<3>4. QED
BY <3>2, <3>3 DEF VoteFor
<2>3. QED
BY <2>1, <2>2 DEF BallotAction
<1>4. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value,
~VotedFor(a, c, w), VotedFor(a, c, w)'
PROVE (a = self) /\ (c = b) /\ VoteFor(self, b, w)
<2>1. CASE IncreaseMaxBal(self, b)
BY <2>1, <1>4 DEF IncreaseMaxBal, VInv, TypeOK, VotedFor
<2>2. CASE \E v \in Value : VoteFor(self, b, v)
<3>1. PICK v \in Value : VoteFor(self, b, v)
BY <2>2
<3>2. a = self
BY <3>1, <1>4 DEF VoteFor, VInv, TypeOK, VotedFor
<3>3. votes'[a] = votes[a] \cup {<<b, v>>}
BY <3>1, <3>2 DEF VoteFor, VInv, TypeOK
<3>4. c = b /\ v = w
BY <1>4, <3>3 DEF VotedFor
<3>5. QED
BY <3>1, <3>2, <3>4
<2>3. QED
BY <2>1, <2>2 DEF BallotAction
<1>5. ASSUME NEW a \in Acceptor
PROVE /\ maxBal[a] \in Ballot \cup {-1}
/\ maxBal'[a] \in Ballot \cup {-1}
/\ maxBal'[a] >= maxBal[a]
BY DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, DidNotVoteIn,
VotedFor, Ballot
<1>6. ASSUME NEW c \in Ballot, NEW w \in Value,
SafeAt(c, w)
PROVE SafeAt(c, w)'
<2> USE DEF Ballot
<2> DEFINE P(i) == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)'
<2>1. P(0)
BY SafeAtPropPrime, 0 .. 0 = {0}, Zenon
<2>2. ASSUME NEW d \in Ballot, P(d)
PROVE P(d+1)
<3>1. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w)
PROVE SafeAt(e, w)'
OBVIOUS
<3>2. CASE e \in 0..d
BY <2>2, <3>1, <3>2
<3>3. CASE e = d+1
<4>. e \in Ballot \ {0}
BY <3>3
<4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q)
BY <3>1, SafeAtProp, Zenon
<4>2. \A aa \in Q : maxBal'[aa] \geq e
BY <1>5, <4>1, QA
<4>3. \E cc \in -1..(e-1) :
/\ (cc # -1) => /\ SafeAt(cc, w)'
/\ \A ax \in Q :
\A z \in Value :
VotedFor(ax, cc, z)' => (z = w)
/\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)'
<5>1. ASSUME NEW cc \in 0..(e-1),
NEW ax \in Q, NEW z \in Value,
VotedFor(ax, cc, z)', ~ VotedFor(ax, cc, z)
PROVE FALSE
<6>1. (ax = self) /\ (cc = b) /\ VoteFor(self, b, z)
BY <5>1, <1>4, QA
<6>2. /\ maxBal[ax] \geq e
/\ maxBal[self] \leq b
BY <4>1, <6>1 DEF VoteFor
<6>. QED BY <3>3, <6>1, <6>2 DEF VInv, TypeOK
<5>2. PICK cc \in -1..(e-1) : SafeAtProp!(e, w)!2!2!(Q)!2!(cc)
BY <4>1
<5>3. ASSUME cc # -1
PROVE /\ SafeAt(cc, w)'
/\ \A ax \in Q : \A z \in Value :
VotedFor(ax, cc, z)' => (z = w)
<6>1. /\ SafeAt(cc, w)
/\ \A ax \in Q :
\A z \in Value : VotedFor(ax, cc, z) => (z = w)
BY <5>2, <5>3
<6>2. SafeAt(cc, w)'
BY <6>1, <5>3, <3>3, <2>2
<6>3. ASSUME NEW ax \in Q, NEW z \in Value, VotedFor(ax, cc, z)'
PROVE z = w
<7>1. CASE VotedFor(ax, cc, z)
BY <6>1, <7>1
<7>2. CASE ~ VotedFor(ax, cc, z)
BY <7>2, <6>3, <5>1, <5>3
<7>3. QED
BY <7>1, <7>2
<6>4. QED
BY <6>2, <6>3
<5>4. ASSUME NEW dd \in (cc+1)..(e-1), NEW ax \in Q,
~ DidNotVoteIn(ax, dd)'
PROVE FALSE
BY <5>2, <5>1, <5>4 DEF DidNotVoteIn
<5>5. QED
BY <5>3, <5>4
<4>4. \/ e = 0
\/ \E Q_1 \in Quorum :
/\ \A aa \in Q_1 : maxBal'[aa] \geq e
/\ \E c_1 \in -1..e - 1 :
/\ c_1 # -1
=> (/\ SafeAt(c_1, w)'
/\ \A aa \in Q_1 :
\A w_1 \in Value :
VotedFor(aa, c_1, w_1)' => w_1 = w)
/\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 :
DidNotVoteIn(aa, d_1)'
BY <4>2, <4>3, <3>3
<4>6. SafeAt(e, w)' <=> <4>4
BY SafeAtPropPrime, <3>3, Zenon
<4>7. QED
BY <4>2, <4>3, <4>6
<3>4. QED
BY <3>2, <3>3
<2>3. \A d \in Ballot : P(d)
BY <2>1, <2>2, NatInduction, Isa
<2>4. QED
BY <2>3, <1>6
<1>7. VInv2'
<2>1. SUFFICES ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW v \in Value,
VotedFor(a, c, v)'
PROVE SafeAt(c, v)'
BY DEF VInv2
<2>2. CASE VotedFor(a, c, v)
BY <1>6, <2>2 DEF VInv, VInv2
<2>3. CASE ~VotedFor(a, c, v)
BY <1>6, <2>1, <2>3, <1>4 DEF VoteFor
<2>4. QED
BY <2>2, <2>3
<1>8. VInv3'
<2>1. ASSUME NEW a1 \in Acceptor, NEW a2\in Acceptor,
NEW c \in Ballot, NEW v1 \in Value, NEW v2 \in Value,
VotedFor(a1, c, v1)',
VotedFor(a2, c, v2)',
VotedFor(a1, c, v1),
VotedFor(a2, c, v2)
PROVE v1 = v2
BY <2>1 DEF VInv, VInv3
<2>2. ASSUME NEW a1 \in Acceptor, NEW a2\in Acceptor,
NEW c \in Ballot, NEW v1 \in Value, NEW v2 \in Value,
VotedFor(a1, c, v1)',
VotedFor(a2, c, v2)',
~ VotedFor(a1, c, v1)
PROVE v1 = v2
<3>1. (a1 = self) /\ (c = b) /\ VoteFor(self, b, v1)
BY <2>2, <1>4
<3>2. CASE a2 = self
<4>1. ~VotedFor(self, b, v2)
BY <3>1 DEF VoteFor, DidNotVoteIn
<4>2. VoteFor(self, b, v2)
BY <2>2, <3>1, <3>2, <4>1, <1>4
<4>. QED BY <3>1, <4>2, <2>2 DEF VotedFor, VoteFor, VInv, TypeOK
<3>3. CASE a2 # self
BY <3>1, <3>3, <2>2 DEF VotedFor, VoteFor, VInv, TypeOK
<3>4. QED
BY <3>2, <3>3
<2>3. QED
BY <2>1, <2>2 DEF VInv3
<1>9. VInv4'
<2>1. SUFFICES ASSUME NEW a \in Acceptor, NEW c \in Ballot,
maxBal'[a] < c,
~ DidNotVoteIn(a, c)'
PROVE FALSE
BY DEF VInv4
<2>2. maxBal[a] < c
BY <1>5, <2>1 DEF Ballot
<2>3. DidNotVoteIn(a, c)
BY <2>2 DEF VInv, VInv4
<2>4. PICK v \in Value : VotedFor(a, c, v)'
BY <2>1 DEF DidNotVoteIn
<2>5. (a = self) /\ (c = b) /\ VoteFor(self, b, v)
BY <1>4, <2>1, <2>3, <2>4 DEF DidNotVoteIn
<2>6. maxBal'[a] = c
BY <2>5 DEF VoteFor, VInv, TypeOK
<2>7. QED
BY <2>1, <2>6 DEF Ballot
<1>10. QED
BY <1>2, <1>7, <1>8, <1>9 DEF VInv
(***************************************************************************)
(* The invariance of VInv follows easily from theorem InductiveInvariance *)
(* and the following result, which is easy to prove with TLAPS. *)
(***************************************************************************)
THEOREM InitImpliesInv == Init => VInv
BY DEF Init, VInv, TypeOK, ProcSet, VInv2, VInv3, VInv4, VotedFor, DidNotVoteIn
(***************************************************************************)
(* The following theorem asserts that VInv is an invariant of Spec. *)
(***************************************************************************)
THEOREM VT2 == Spec => []VInv
BY InitImpliesInv, InductiveInvariance, PTL DEF Spec
-----------------------------------------------------------------------------
(***************************************************************************)
(* The following INSTANCE statement instantiates module Consensus with the *)
(* following expressions substituted for the parameters (the CONSTANTS and *)
(* VARIABLES) of that module: *)
(* *)
(* Parameter of Consensus Expression (of this module) *)
(* ---------------------- --------------------------- *)
(* Value Value *)
(* chosen chosen *)
(* *)
(* (Note that if no substitution is specified for a parameter, the default *)
(* is to substitute the parameter or defined operator of the same name.) *)
(* More precisely, for each defined identifier id of module Consensus, *)
(* this statement defines C!id to equal the value of id under these *)
(* substitutions. *)
(***************************************************************************)
C == INSTANCE Consensus
Refines == C!Spec
(***************************************************************************)
(* The following theorem asserts that the safety properties of the voting *)
(* algorithm (specified by formula Spec) of this module implement the *)
(* consensus safety specification Spec of module Consensus under the *)
(* substitution (refinement mapping) of the INSTANCE statement. *)
(***************************************************************************)
THEOREM VT3 == Spec => C!Spec
<1>1. Init => C!Init
<2> SUFFICES ASSUME Init
PROVE C!Init
OBVIOUS
<2>1. SUFFICES ASSUME NEW v \in chosen
PROVE FALSE
BY DEF C!Init
<2>2. PICK b \in Ballot, Q \in Quorum : \A a \in Q : VotedFor(a, b, v)
BY <2>1 DEF chosen, ChosenIn
<2>3. PICK a \in Q : <<b, v>> \in votes[a]
BY QuorumNonEmpty, <2>2 DEF VotedFor
<2>4. QED
BY <2>3, QA DEF Init
<1>2. VInv /\ VInv'/\ [Next]_vars => [C!Next]_C!vars
<2>. SUFFICES ASSUME VInv, VInv', [Next]_vars
PROVE [C!Next]_C!vars
OBVIOUS
<2>1. CASE vars' = vars
BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor
<2>2. SUFFICES ASSUME NEW self \in Acceptor,
NEW b \in Ballot,
BallotAction(self, b)
PROVE [C!Next]_C!vars
BY <2>1, NextDef DEF VInv
<2>3. ASSUME IncreaseMaxBal(self, b)
PROVE C!vars' = C!vars
BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor
<2>4. ASSUME NEW v \in Value,
VoteFor(self, b, v)
PROVE [C!Next]_C!vars
<3>3. ASSUME NEW w \in chosen
PROVE w \in chosen'
<4>1. PICK c \in Ballot, Q \in Quorum : \A a \in Q : <<c, w>> \in votes[a]
BY <3>3 DEF chosen, ChosenIn, VotedFor
<4>2. SUFFICES ASSUME NEW a \in Q
PROVE <<c, w>> \in votes'[a]
BY DEF chosen, ChosenIn, VotedFor
<4>3. CASE a = self
BY <2>4, <4>1, <4>3 DEF VoteFor, VInv, TypeOK
<4>4. CASE a # self
BY <2>4, <4>1, <4>4, QA DEF VoteFor, VInv, TypeOK
<4>5. QED
BY <4>3, <4>4
<3>1. ASSUME NEW w \in chosen,
v \in chosen'
PROVE w = v
BY <3>3, <3>1, VT1Prime DEF VInv, VInv1, VInv3
<3>2. ASSUME NEW w, w \notin chosen, w \in chosen'
PROVE w = v
<4>2. PICK c \in Ballot, Q \in Quorum : \A a \in Q : <<c, w>> \in votes'[a]
BY <3>2 DEF chosen, ChosenIn, VotedFor
<4>3. PICK a \in Q : <<c, w>> \notin votes[a]
BY <3>2 DEF chosen, ChosenIn, VotedFor
<4>4. CASE a = self
BY <2>4, <4>4, <4>2, <4>3 DEF VoteFor, VInv, TypeOK
<4>5. CASE a # self
BY <2>4, <4>2, <4>3, <4>5, QA DEF VoteFor, VInv, TypeOK
<4>6. QED
BY <4>4, <4>5
<3>. QED
BY <3>3, <3>1, <3>2 DEF C!Next, C!vars
<2>5. QED
BY <2>2, <2>3, <2>4 DEF BallotAction
<1>3. QED
BY <1>1, <1>2, VT2, PTL DEF Spec, C!Spec
-----------------------------------------------------------------------------
(***************************************************************************)
(* Liveness *)
(* *)
(* We now state the liveness property required of our voting algorithm and *)
(* prove that it and the safety property imply specification LiveSpec of *)
(* module Consensus under our refinement mapping. *)