include relarray
include order
include udp
instance node : iterable
isolate nset = {
type this
alias t = this
type index
relation member(N:node, S:t)
relation majority(S:t)
action emptyset returns (s:nset)
action add(s:nset, n : node) returns (s:nset)
specification {
after emptyset {
ensure ~member(N, s)
}
after add {
ensure member(N,s) <-> (member(N , old s) | N = n)
}
}
private {
invariant [majorities_intersect]
(majority(S) & majority(T)) -> exists N. member(N,S) & member(N,T)
}
implementation {
interpret index -> int
function card(S:nset) : index
instance arr : array(index,node)
destructor repr(N:nset) : arr
individual all : nset
definition member(N,S) = exists I. 0 <= I & I < repr(S).end & repr(S).value(I, N)
definition majority(S) = card(S) + card(S) > card(all)
after init {
card(S) := 0;
all.repr := arr.empty;
var i := node.iter.create(0);
while ~i.is_end
invariant (forall N.~(member(N,S) & member(N,T))) ->
card(S) + card(T) <= card(all)
invariant node.iter.done(N,i) <-> member(N,all)
invariant card(S) >= 0
{
all.repr := all.repr.append(i.val);
card(S) := card(S) + 1 if member(i.val, S) else card(S);
i := i.next
};
}
implement emptyset {
s.repr := arr.empty()
}
implement add {
if ~member(n, s) {
s.repr := s.repr.append(n)
}
}
}
}
with node
isolate toy_protocol = {
relation voted(N:node,M:node)
relation isleader(N:node)
var quorum : nset
specification {
after init {
voted(N,M) := false;
isleader(N) := false;
}
invariant [one_leader]
isleader(N) & isleader(M) -> N = M
invariant [one_vote_per_node]
voted(L, N ) & voted(L, M ) -> N = M
invariant [leader_has_majority]
isleader(N) ->
nset.majority(quorum) &
forall M. nset.member(M , quorum) -> voted(M,N)
action vote(v : node, n : node) = {
require ~voted(v,N);
voted(v, n) := true
}
action become_leader(n : node, s : nset) = {
require nset.majority(s) & (nset.member(N,s) -> voted(N, n));
isleader(n) := true;
quorum := s
}
}
}
with nset, nset.majorities_intersect
object msg_kind = {
type this = {request_vote, vote, leader}
}
object msg = {
type this = struct {
kind : msg_kind,
src : node,
vote : node
}
}
instance net : udp_simple(node,msg)
isolate shim = {
module handler(p_kind) = {
action handle(dst:node,m:msg)
specification {
before handle {
require sent(m,dst) & m.kind = p_kind
}
}
}
relation sent(M:msg,N:node)
instance request_vote : handler(msg_kind.request_vote)
instance vote : handler(msg_kind.vote)
instance leader : handler(msg_kind.leader)
action bcast(src:node,m:msg)
specification {
after init {
sent(M,D) := false;
}
before bcast {
sent(m,D) := true;
}
}
implementation {
action debug_sending(src:node,dst:node,m:msg)
action debug_recving(dst:node,m:msg)
implement net.recv(dst:node,m:msg) {
call debug_recving(dst,m);
if m.kind = msg_kind.request_vote {
call request_vote.handle(dst,m)
}
else if m.kind = msg_kind.vote {
call vote.handle(dst,m)
}
else if m.kind = msg_kind.leader {
call leader.handle(dst,m)
}
}
implement bcast {
var iter := node.iter.create(0);
while ~iter.is_end
invariant net.spec.sent(M,D) -> sent(M,D)
{
var n := iter.val;
call debug_sending(src,n,m);
call net.send(src,n,m);
iter := iter.next;
}
}
}
private {
invariant net.spec.sent(M,D) -> sent(M,D)
}
} with net, node
isolate toy_system = {
relation alreadyvoted(N:node)
function voters(N:node) : nset
specification {
invariant [safe] forall M1:msg, M2:msg.
shim.sent(M1,N1) & M1.kind = msg_kind.leader
& shim.sent(M2,N2) & M2.kind = msg_kind.leader
-> M1.src = M2.src
}
after init (self : node) {
alreadyvoted(self) := false;
voters(self) := nset.emptyset()
}
action request_vote(self : node) = {
var msg : msg;
msg.kind := msg_kind.request_vote;
msg.src := self;
call shim.bcast(self,msg);
if ~alreadyvoted(self) {
alreadyvoted(self) := true;
voters(self) := voters(self).add(self);
call toy_protocol.vote(self,self) # ghost
}
}
implement shim.request_vote.handle(self : node, req : msg) {
if ~alreadyvoted(self) {
alreadyvoted(self) := true;
var reply : msg;
reply.kind := msg_kind.vote;
reply.src := self;
reply.vote := req.src;
call shim.bcast(self,reply);
call toy_protocol.vote(self,req.src)
}
}
implement shim.vote.handle(self : node, req : msg) {
if req.vote = self {
voters(self) := voters(self).add(req.src);
if nset.majority(voters(self)) {
var reply : msg;
reply.kind := msg_kind.leader;
reply.src := self;
call shim.bcast(self,reply);
call toy_protocol.become_leader(self, voters(self)) # ghost
}
}
}
private {
invariant forall M:msg. shim.sent(M,N) & M.kind = msg_kind.vote -> toy_protocol.voted(M.src, M.vote)
invariant shim.sent(M) & M.kind = msg_kind.request -> toy_protocol.voted(M.src,M.src)
invariant nset.member(N1 ,voters(N2)) -> toy_protocol.voted(N1,N2)
invariant forall M:msg. shim.sent(M,N) & M.kind = msg_kind.leader -> toy_protocol.isleader(M.src)
invariant toy_protocol.voted(N1,N2) -> alreadyvoted(N1)
}
} with nset, toy_protocol, shim
export toy_system.request_vote
import shim.debug_sending
import shim.debug_recving
extract iso_impl(self:node) = toy_system(self),shim(self),net(self),nset,node
schema trans1 = {
type t
function x : t
function z : t
property x = X & z = X -> x = z
}
schema trans2 = {
type t
function x : t
property x = X & Y = X -> x = Y
}
schema cong1 = {
type t
type u
function x : t
function f(X:t) : u
property x = X -> f(x) = f(X)
}
schema cong2 = {
type t1
type t2
type u
function x1 : t1
function x2 : t2
function f(X1:t1,X2:t2) : u
property x1 = X1 & x2 = X2 -> f(x1,x2) = f(X1,X2)
}