-
Notifications
You must be signed in to change notification settings - Fork 218
Expand file tree
/
Copy pathSailfish.tla
More file actions
200 lines (179 loc) · 9.98 KB
/
Sailfish.tla
File metadata and controls
200 lines (179 loc) · 9.98 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
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
----------------------------- MODULE Sailfish -----------------------------
(**************************************************************************************)
(* This is a high-level specification of the Sailfish and Sailfish++ (also called *)
(* signature-free Sailfish) algorithms. At the level of abstraction of this *)
(* specification, the differences between the two algorithms are not visible. *)
(**************************************************************************************)
EXTENDS Integers, FiniteSets, Sequences
CONSTANTS
N \* The set of all nodes
, F \* The set of Byzantine nodes
, R \* The set of rounds
, IsQuorum(_) \* Whether a set is a quorum (i.e. cardinality >= n-f)
, IsBlocking(_) \* Whether a set is a blocking set (i.e. cardinality >= f+1)
, Leader(_) \* operator mapping each round to its leader
, GST \* the first round in which the system is synchronous
ASSUME \E n \in R : R = 1..n \* rounds start at 1; 0 is used as default placeholder
INSTANCE BlockDag \* Import definitions related to DAGs of blocks
(**************************************************************************************)
(* Now we specify the algorithm in the PlusCal language. *)
(**************************************************************************************)
(*--algorithm Sailfish {
variables
vs = {Genesis}, \* the vertices of the DAG
es = {}; \* the edges of the DAG
define {
dag == <<vs, es>>
NoLeaderVoteQuorum(r, vertices, add) ==
LET NoLeaderVote == {v \in vertices : LeaderVertex(r-1) \notin Children(dag, v)}
IN IsQuorum({Node(v) : v \in NoLeaderVote} \cup add)
}
process (correctNode \in N \ F)
variables
round = 0, \* current round; 0 means the node has not started execution
log = <<>>; \* delivered log
{
l0: while (TRUE) {
if (round = 0) { \* start the first round r=1
round := 1;
vs := vs \cup {<<self, 1>>};
es := es \cup {<<<<self, 1>>, Genesis>>}
}
else { \* start a round r>1
with (r \in {r \in R : r > round})
with (deliveredVertices \in SUBSET {v \in vs : Round(v) = r-1}) {
\* we enter a round only if we have a quorum of vertices:
await IsQuorum({Node(v) : v \in deliveredVertices});
\* if this is after GST, we must have all correct vertices:
await r >= GST => (N \ F) \subseteq {Node(v) : v \in deliveredVertices};
\* enter round r:
round := r;
\* if the r-1 leader does not reference the r-2 leader,
\* then we must be sure the r-2 leader cannot commit:
await LeaderVertex(r-1) \in deliveredVertices =>
\/ LeaderVertex(r-2) \in Children(dag, LeaderVertex(r-1))
\/ NoLeaderVoteQuorum(r-1, deliveredVertices, {});
\* if we are the leader, then we must include the r-1 leader or
\* have evidence it cannot commit:
if (Leader(r) = self)
await \/ LeaderVertex(r-1) \in deliveredVertices
\/ NoLeaderVoteQuorum(r, {v \in vs : Round(v) = r}, {self});
\* create a new vertex:
with (newV = <<self, r>>) {
vs := vs \cup {newV};
es := es \cup {<<newV, pv>> : pv \in deliveredVertices};
};
\* commit if there is a quorum of votes for the leader of r-2:
if (r > 2)
with (votesForLeader = {pv \in deliveredVertices : <<pv, LeaderVertex(r-2)>> \in es})
if (IsQuorum({Node(pv) : pv \in votesForLeader}))
log := Linearize(dag, LeaderVertex(r-2))
}
}
}
}
(**************************************************************************************)
(* Next comes our model of Byzantine nodes. Because the real protocol *)
(* disseminates DAG vertices using reliable broadcast, Byzantine nodes cannot *)
(* equivocate and cannot deviate much from the protocol (lest their messages *)
(* be ignored). *)
(**************************************************************************************)
process (byzantineNode \in F)
{
l0: while (TRUE) {
with (r \in R)
with (newV = <<self, r>>) {
when newV \notin vs; \* no equivocation
if (r = 1) {
vs := vs \cup {newV};
es := es \cup {<<newV, Genesis>>}
}
else
with (delivered \in SUBSET {v \in vs : Round(v) = r-1}) {
await IsQuorum({Node(v) : v \in delivered}); \* ignored otherwise
vs := vs \cup {newV};
es := es \cup {<<newV, pv>> : pv \in delivered}
}
}
}
}
}*)
\* BEGIN TRANSLATION (chksum(pcal) = "c16dfa43" /\ chksum(tla) = "9cdbd4f5")
\* Label l0 of process correctNode at line 42 col 9 changed to l0_
VARIABLES vs, es
(* define statement *)
dag == <<vs, es>>
NoLeaderVoteQuorum(r, vertices, add) ==
LET NoLeaderVote == {v \in vertices : LeaderVertex(r-1) \notin Children(dag, v)}
IN IsQuorum({Node(v) : v \in NoLeaderVote} \cup add)
VARIABLES round, log
vars == << vs, es, round, log >>
ProcSet == (N \ F) \cup (F)
Init == (* Global variables *)
/\ vs = {Genesis}
/\ es = {}
(* Process correctNode *)
/\ round = [self \in N \ F |-> 0]
/\ log = [self \in N \ F |-> <<>>]
correctNode(self) == IF round[self] = 0
THEN /\ round' = [round EXCEPT ![self] = 1]
/\ vs' = (vs \cup {<<self, 1>>})
/\ es' = (es \cup {<<<<self, 1>>, Genesis>>})
/\ log' = log
ELSE /\ \E r \in {r \in R : r > round[self]}:
\E deliveredVertices \in SUBSET {v \in vs : Round(v) = r-1}:
/\ IsQuorum({Node(v) : v \in deliveredVertices})
/\ r >= GST => (N \ F) \subseteq {Node(v) : v \in deliveredVertices}
/\ round' = [round EXCEPT ![self] = r]
/\ LeaderVertex(r-1) \in deliveredVertices =>
\/ LeaderVertex(r-2) \in Children(dag, LeaderVertex(r-1))
\/ NoLeaderVoteQuorum(r-1, deliveredVertices, {})
/\ IF Leader(r) = self
THEN /\ \/ LeaderVertex(r-1) \in deliveredVertices
\/ NoLeaderVoteQuorum(r, {v \in vs : Round(v) = r}, {self})
ELSE /\ TRUE
/\ LET newV == <<self, r>> IN
/\ vs' = (vs \cup {newV})
/\ es' = (es \cup {<<newV, pv>> : pv \in deliveredVertices})
/\ IF r > 2
THEN /\ LET votesForLeader == {pv \in deliveredVertices : <<pv, LeaderVertex(r-2)>> \in es'} IN
IF IsQuorum({Node(pv) : pv \in votesForLeader})
THEN /\ log' = [log EXCEPT ![self] = Linearize(dag, LeaderVertex(r-2))]
ELSE /\ TRUE
/\ log' = log
ELSE /\ TRUE
/\ log' = log
byzantineNode(self) == /\ \E r \in R:
LET newV == <<self, r>> IN
/\ newV \notin vs
/\ IF r = 1
THEN /\ vs' = (vs \cup {newV})
/\ es' = (es \cup {<<newV, Genesis>>})
ELSE /\ \E delivered \in SUBSET {v \in vs : Round(v) = r-1}:
/\ IsQuorum({Node(v) : v \in delivered})
/\ vs' = (vs \cup {newV})
/\ es' = (es \cup {<<newV, pv>> : pv \in delivered})
/\ UNCHANGED << round, log >>
Next == (\E self \in N \ F: correctNode(self))
\/ (\E self \in F: byzantineNode(self))
Spec == Init /\ [][Next]_vars
\* END TRANSLATION
(**************************************************************************************)
(* Basic type invariant: *)
(**************************************************************************************)
TypeOK ==
/\ \A v \in vs \ {<<>>} :
/\ Node(v) \in N /\ Round(v) \in Nat \ {0}
/\ \A c \in Children(dag, v) : Round(c) = Round(v) - 1
/\ \A e \in es :
/\ e = <<e[1],e[2]>>
/\ {e[1], e[2]} \subseteq vs
/\ \A n \in N \ F : round[n] \in Nat
(**************************************************************************************)
(* Next we define the safety and liveness properties *)
(**************************************************************************************)
Agreement == \A n1,n2 \in N \ F : Compatible(log[n1], log[n2])
Liveness == \A r \in R : r >= GST /\ Leader(r) \notin F =>
\A n \in N \ F : round[n] >= r+2 =>
\E i \in DOMAIN log[n] : log[n][i] = LeaderVertex(r)
===========================================================================