This is an attempt to specify the echo algorithm in electrum.
Do you think, the spec is correct?
Any hints for improving the spec?
/* Echo algorithm for constructing a spanning tree in an undirected
graph starting from a single initiator.
*/
open util/graph[Node]
enum Color {Red, Green}
abstract sig Node {
neighbors: set Node, -- Graph
var parent: lone Node, -- relation for tree
var inbox: set Node, -- inbox for messages from other nodes
var received: set Node,-- already received messages
var color: one Color -- ready = returns message to parent = green
}
one sig INode extends Node {} -- initiator
sig PNode extends Node {} -- paricipants
fact {
noSelfLoops[neighbors] -- irreflexive
undirected[neighbors] -- undirected
rootedAt[neighbors, INode] -- connected with initiator
}
// skip state' = state
pred skip {
parent' = parent
inbox' = inbox
received' = received
color' = color
}
// initial state
pred init {
no parent and no inbox and no received
color = Node -> Red
}
pred broadcast[n: Node, to: set Node] {
all q: to | q.inbox' = q.inbox + n
all unchanged: Node - to | unchanged.inbox' = unchanged.inbox
}
// initial state and broadcast from initiator to all his neighbors
pred initiate {
init
broadcast[INode, INode.neighbors]
parent' = parent
received' = received
color' = color
}
pred broadcast2[n: Node, to: set Node] {
all q: to | q.inbox' = q.inbox + n
all u: (Node - to) | u != n implies u.inbox' = u.inbox else no u.inbox'
}
// forward: msg from future parent forwarded to all other neighbors
pred forward {
some n: PNode, msg: Node | {
no n.parent and msg in n.inbox
parent' = parent ++ n->msg
broadcast2[n, (n.neighbors - msg)]
received' = received ++ n->(n.received + msg)
color' = color
}
}
// received: msg from other neighbor returned
pred received {
some n: Node, msg: Node | {
(one n.parent or n = INode) and msg in n.inbox
parent' = parent
all u: Node | u != n implies u.inbox' = u.inbox else no u.inbox'
received' = received ++ n->(n.received + msg)
color' = color
}
}
// ready: alle msg from other neighbors returned, msg to parent
pred ready {
some n: PNode | {
n.received = n.neighbors and n.color = Red // Ausgangslage
parent' = parent
inbox' = inbox ++ (n.parent->n)
received' = received
color' = color ++ (n->Green)
}
}
// rerminate: initiator got all return msgs
pred terminate {
no PNode or (some INode.neighbors and INode.neighbors = INode.received) // Ausgangslage
parent' = parent
inbox' = inbox
received' = received
color' = color ++ (INode->Green)
}
// echo algorithm with end criteria
fact {
init
always {
initiate or
forward or
received or
ready or
terminate or
skip
}
eventually INode.color = Green
}
run {} for 1 Node
run {} for exactly 2 Node
run {} for exactly 3 Node
run {} for exactly 4 Node, 12 steps
run {} for exactly 5 Node, 15 steps // 200 sec or so on my machine
// check Spannbaum gefunden?
check {eventually tree[~parent]} for 4 Node, 12 steps