datatype Constants = Constants(ids: seq<nat>) {
predicate ValidIdx(i: int) {
ghost predicate UniqueIds() {
(forall i, j | ValidIdx(i) && ValidIdx(j) && ids[i]==ids[j] :: i == j)
datatype Variables = Variables(highest_heard: seq<int>) {
ghost predicate WF(c: Constants)
&& |highest_heard| == |c.ids|
ghost predicate Init(c: Constants, v: Variables)
&& (forall i | c.ValidIdx(i) :: v.highest_heard[i] == -1)
function max(a: int, b: int) : int {
function NextIdx(c: Constants, idx: nat) : nat
if idx + 1 == |c.ids| then 0 else idx + 1
ghost predicate Transmission(c: Constants, v: Variables, v': Variables, srcidx: nat)
&& var dstidx := NextIdx(c, srcidx);
&& var message := max(v.highest_heard[srcidx], c.ids[srcidx]);
&& var dst_new_max := max(v.highest_heard[dstidx], message);
&& v' == v.(highest_heard := v.highest_heard[dstidx := dst_new_max])
datatype Step = TransmissionStep(srcidx: nat)
ghost predicate NextStep(c: Constants, v: Variables, v': Variables, step: Step)
case TransmissionStep(srcidx) => Transmission(c, v, v', srcidx)
ghost predicate Next(c: Constants, v: Variables, v': Variables)
exists step :: NextStep(c, v, v', step)
ghost predicate IsLeader(c: Constants, v: Variables, i: int)
&& v.highest_heard[i] == c.ids[i]
ghost predicate Safety(c: Constants, v: Variables)
forall i, j | IsLeader(c, v, i) && IsLeader(c, v, j) :: i == j
ghost predicate IsChord(c: Constants, v: Variables, start: int, end: int)
&& c.ids[start] == v.highest_heard[end]
ghost predicate Between(start: int, node: int, end: int)
else node < end || start < node
ghost predicate OnChordHeardDominatesId(c: Constants, v: Variables, start: int, end: int)
forall node | Between(start, node, end) && c.ValidIdx(node)
:: v.highest_heard[node] > c.ids[node]
ghost predicate OnChordHeardDominatesIdInv(c: Constants, v: Variables)
| IsChord(c, v, start, end)
:: OnChordHeardDominatesId(c, v, start, end)
ghost predicate Inv(c: Constants, v: Variables)
&& OnChordHeardDominatesIdInv(c, v)
lemma InitImpliesInv(c: Constants, v: Variables)
lemma NextPreservesInv(c: Constants, v: Variables, v': Variables)
var step :| NextStep(c, v, v', step);
var srcidx := step.srcidx;
var dstidx := NextIdx(c, srcidx);
var message := max(v.highest_heard[srcidx], c.ids[srcidx]);
var dst_new_max := max(v.highest_heard[dstidx], message);
| IsChord(c, v', start, end)
ensures OnChordHeardDominatesId(c, v', start, end)
forall node | Between(start, node, end) && c.ValidIdx(node)
ensures v'.highest_heard[node] > c.ids[node]
if v'.highest_heard[end] == v.highest_heard[end] {
assert v' == v; // trigger
} else if v'.highest_heard[end] == c.ids[srcidx] {
} else if v'.highest_heard[end] == v.highest_heard[srcidx] {
assert IsChord(c, v, start, srcidx);
assert IsChord(c, v, start, end);
assert OnChordHeardDominatesIdInv(c, v');
forall i, j | IsLeader(c, v', i) && IsLeader(c, v', j) ensures i == j {
assert IsChord(c, v', i, i); // trigger
assert IsChord(c, v', j, j); // trigger
lemma InvImpliesSafety(c: Constants, v: Variables)