Learning
¤
Module describing an acyclic partial function. The function is built by calling the "set" action. This has preconditions that enforce the required invariants. The function can be accessed using "dom" to query if an element is in the domain of the function, and "get" to get its value. The "get" action has a precondition that the given element must be in the domain.
Because of the invariant that the relation re construct by "set" is an acyclic partial function, we can represent it by its transitive closure "tc", while remainin gin EPR.
¤
module inverse_forest(carrier) = {
relation dom(X:carrier) # domain of the function
relation tc(X:carrier,Y:carrier) # transitive closure of the function
axiom tc(X,X) # Reflexivity
axiom tc(X, Y) & tc(Y, Z) -> tc(X, Z) # transitivity
axiom tc(X, Y) & tc(Y, X) -> X = Y # anti-symmetry
axiom tc(X, Y) & tc(X, Z) -> (tc(Y, Z) | tc(Z, Y)) # linearity
init ~dom(X) & (tc(X,Y) <-> X = Y) #initially empty
action set(x:carrier,y:carrier) = {
assert ~tc(y,x); # can't create a cycle
assert ~dom(x); # can't remove links
dom(x) := true;
tc(X, Y) := tc(X, Y) | tc(X, x) & tc(y, Y)
}
action get(x:carrier) returns (y:carrier) = {
assert dom(x);
assume tc(x,y) & ~(tc(x,X) & tc(X,y) & X ~= x & X ~= y)
}
}
¤
Types, relations and functions describing state of the network
¤
type packet
type host
relation pending(P:packet, S:host, T:host)
individual src(P:packet) : host
individual dst(P:packet) : host
relation link(S:host, T:host)
instantiate route(H:host) : inverse_forest(host)
axiom ~link(X, X) # no self-loops
axiom ~link(X, Y) | link(Y, X) # symmetric
init ~pending(P,S,T)
¤
Protocol description
There is just one action describing the receipt of a pending message by some host.
¤
action receive = {
local p0:packet, sw0:host, sw1:host, sw2:host, s0:host, t0:host {
¤
The action's guard.
t0 := dst(p0);
s0 := src(p0);
assume pending(p0, sw0, sw1) | s0 = sw1 & sw0 = sw1;
¤
Abstract the number of times that the same packet recieved
if (pending(p0,sw0, sw1)) {
pending(p0, sw0, sw1) := *
};
¤
learn: if no route from receiving switch back to source...
if (~route(s0).dom(sw1) & sw0 ~= sw1 & s0 ~= sw1) {
call route(s0).set(sw1,sw0) # create new route
};
¤
forward the packet if dest is not self
if t0 ~= sw1 {
if ~route(t0).dom(sw1) { # if no route to dest...
pending(p0, sw1, Y) := link(sw1, Y) & Y ~= sw0 # flood
}
else {
sw2 := route(t0).get(sw1); # get the out-going link
pending(p0, sw1, sw2) := true # route the packet there
}
}
}
}
export receive
conjecture ~(route(S).dom(V0) & ~route(S).tc(V0, S))
conjecture ~route(V0).dom(V0)
conjecture ~(pending(P, V0, V1) & dst(P) = V0)
conjecture ~(pending(P, V0, V1) & ~route(src(P)).dom(V0) & V0 ~= src(P))
conjecture ~(route(S).tc(V0, V1) & ~V0 = V1 & ~route(S).dom(V0))
conjecture ~(route(S).tc(V0, V1) & ~V0 = V1 & S ~= V1 & ~route(S).dom(V1)) # TODO: needed?
conjecture ~(~V0 = S & route(S).tc(S, V0))