Leader tutorial
¤
Leader election tutorial example
See leader_tutorial.md for a description of this example, with instructions on how to verify and run it.
¤
include order
include timeout
include udp
¤
ADT describing a totally ordered datatype
¤
isolate id = {
type this
specification {
instantiate totally_ordered(this)
}
implementation {
interpret this -> bv[32]
}
}
¤
ADT describing a ring topology.
Nodes are totally ordered and provide a get_next action that gets
the next element of the ring in round-robin order.
¤
instance node : iterable
object node = { ...
action get_next(x:this) returns (y:this)
specification {
after get_next {
ensure (y <= X & X <= x) | (x < y & ~ (x < Z & Z < y))
}
}
implementation {
implement get_next {
y := 0 if x = node.max else x.next;
}
}
}
parameter pid(N:node) : id
axiom [pid_injective] pid(N) = pid(M) -> N = M
¤
The abstract model of the protocol has three actions:
- send: this occurs when a node sends its own id
- pass: this occurs when a node receives and id and passes it along
- elect: this occurs when a node declares itself leader
¤
isolate abstract_model = {
action send(n:node,p:id)
action elect(n:node)
specification {
relation sent(V:id,N:node)
after init {
sent(V,N) := false;
}
before send {
require sent(p,n) & p > pid(n) | p = pid(n);
sent(p,n.get_next) := true;
}
before elect {
require sent(pid(n),n);
ensure forall N:node. pid(N) <= pid(n);
}
}
private {
invariant ~(pid(N) < pid(P) & sent(pid(N),N))
invariant ~(L < M & M < N & pid(L) < pid(M) & sent(pid(L),N))
invariant ~(L < M & M < N & pid(M) < pid(N) & sent(pid(M),L))
invariant ~(L < M & M < N & pid(N) < pid(L) & sent(pid(N),M))
}
} with node, id, pid_injective
¤
The high-level service specification
¤
isolate leader = {
action elect(self:node) # called when v is elected leader
specification {
before elect {
ensure pid(self) >= pid(X) # only the max pid can be elected
}
}
implementation {
instantiate net : udp_simple2(node,id)
instantiate timer(N:node) : timeout_sec
var highest(N:node) : id
after init {
highest(N) := pid(N);
}
implement timer.timeout(self:node) {
call abstract_model.send(self,highest(self)); # ghost
call net.send(self,self.get_next,highest(self));
}
implement net.recv(self:node,v:id) {
if v = pid(self) { # Found a leader
call abstract_model.elect(self);
call elect(self);
}
else if v > highest(self) { # pass message to next node
highest(self) := v;
}
}
}
private {
invariant net.sent(V,N) -> abstract_model.sent(V,N)
invariant highest(N) >= pid(N)
invariant highest(N) = pid(N) | abstract_model.sent(highest(N),N)
}
} with node, id, abstract_model
import leader.elect
extract code(n:node) = leader(n), pid(n), node, id