Ironfleet toy lock refined
An Ivy model of the toy lock example from https://github.com/Microsoft/Ironclad/blob/master/ironfleet/src/Dafny/Distributed/Protocol/Lock/Node.i.dfy
A total order helper module
module total_order(t,r) = {
axiom r(X:t,X) # Reflexivity
axiom r(X:t, Y) & r(Y, Z) -> r(X, Z) # Transitivity
axiom r(X:t, Y) & r(Y, X) -> X = Y # Anti-symmetry
axiom r(X:t, Y) | r(Y, X) # Totality
}
¤
Types, relations and functions describing state of the network
¤
type node
type epoch
type packet
relation (X:epoch <= Y:epoch)
instantiate total_order(epoch, <=)
module counter(t) = {
action incr(input:t) returns (output:t) = {
output := input + 1
}
}
module abs_counter(t,c) = {
action incr(input:t) returns (output:t) = {
assert ~(output <= input)
}
mixin incr after c.incr
}
module abs_formatter = {
These are abstract destructuring functions for packets
individual dst(P:packet) : node # the destination address
individual ep(P:packet) : epoch # the packet's epoch
action mk_transfer(d:node,e:epoch) returns (p:packet) = {
assert dst(p) = d & ep(p) = e
}
}
module abs_low_intf(fmt) = {
relation sent(P:packet) # the set of sent packets
init ~sent(P) # initially, no packets sent
action send(p:packet) = {
sent(p) := true
}
action recv(dst:node) returns(p:packet) = {
assert sent(p) & fmt.dst(p) = dst
}
}
module net_intf(lower,fmt) = {
This is a concrete implementation that sends and receives formatted packets via a lower-level service
The parameter "lower" is the lower-level interface and "fmt" is the packet formatter.
send a transfer message to destination address
action send_transfer(e:epoch, dst:node) = {
local p : packet {
p := fmt.mk_transfer(dst,e);
call lower.send(p)
}
}
action recv_transfer(dst:node) returns (e:epoch) = {
local p : packet {
p := lower.recv(dst);
e := fmt.ep(p)
}
}
conjecture l.sent(P) -> an.transfer(f.ep(P),f.dst(P))
}
module abs_net_intf(net) = {
This is an abstract specification that is not concerned with the actual message format. We will add that as a refinement.
state of abstract network interface
relation transfer(E:epoch, N:node) # the node is the message destination
init ~transfer(E, N)
action send_transfer(e:epoch, dst:node) = {
transfer(e,dst) := true
}
mixin send_transfer after net.send_transfer
action recv_transfer(dst:node) returns (e:epoch) = {
assert transfer(e,dst) # see (*) below
}
mixin recv_transfer after net.recv_transfer
}
module proto(cnt,net) = {
ep(n) is the current epoch of node n
individual ep(N:node) : epoch
relation held(N:node)
individual first:node
init held(X) <-> X=first
init N ~= first -> ep(N) = 0
init ~(ep(first) <= 0)
relation locked(E:epoch, N:node) # the node is the message source
init ~locked(E, N)
¤
Protocol description
¤
release the lock and send a transfer message
action grant(n1:node, n2:node) = {
local e: epoch {
assume held(n1);
e := cnt.incr(ep(n1));
call net.send_transfer(e, n2);
held(n1) := false
}
}
action accept(n:node) = {
local e:epoch {
e := net.recv_transfer(n);
if ~(e <= ep(n)) {
held(n) := true;
ep(n) := e;
locked(e, n) := true
}
}
}
action critical(n:node) = {
assume held(n);
assert X ~= n -> ~held(X)
}
¤
invariant conjectures
no two locked at same epoch (as a sanity check)
conjecture locked(E, N1) & locked(E, N2) -> N1 = N2
conjecture an.transfer(E, N1) & an.transfer(E, N2) -> N1 = N2
conjecture locked(E, N) -> (E <= ep(N))
conjecture held(N) & N ~= M -> ~(ep(N) <= ep(M))
conjecture held(N) & an.transfer(E, M) -> (E <= ep(N))
conjecture an.transfer(E, N) & ~(E <= ep(N)) -> ~(E <= ep(M))
conjecture an.transfer(E, N) & ~(E <= ep(N)) & an.transfer(F, M) -> (F <= E)
}
Notice that the formatter and low interface are currently abstract specifications that could be refined in the future.
instantiate c : counter(epoch)
instantiate f : abs_formatter()
instantiate l : abs_low_intf(f)
instantiate n : net_intf(l,f)
instantiate p : proto(c,n)
interpret epoch -> int
export p.grant
export p.accept
export p.critical
¤
All after this is proof
Overlay the abstract counter specification
instantiate an : abs_net_intf(n)
instantiate ac : abs_counter(epoch,c)
isolate iso_p = p with an,ac
isolate iso_c = c with ac,epoch
isolate iso_n = n with an, l, f