#lang ivy1.7
|
|
|
|
include tendermint
|
|
include abstract_tendermint
|
|
|
|
isolate ghost_ = {
|
|
instantiate abstract_tendermint
|
|
}
|
|
|
|
isolate protocol = {
|
|
instantiate tendermint(ghost_) # here we instantiate the parameter of the tendermint module with `ghost_`; however note that we don't extract any code for `ghost_` (it's not in the list of object in the extract, and it's thus sliced away).
|
|
implementation {
|
|
definition init_val(n:node) = <<< `n`%2 >>>
|
|
}
|
|
# attribute test = impl
|
|
} with ghost_, shim, value, round, proposers
|
|
|
|
# Here we run a simple scenario that exhibits an execution in which nodes make
|
|
# a decision. We do this to rule out trivial modeling errors.
|
|
|
|
# One option to check that this scenario is valid is to run it in Ivy's REPL.
|
|
# For this, first compile the scenario:
|
|
#```ivyc target=repl isolate=code trace=true tendermint_test.ivy
|
|
# Then, run the produced binary (e.g. for 4 nodes):
|
|
#``` ./tendermint_test 4
|
|
# Finally, call the action:
|
|
#``` scenarios.scenario_1
|
|
# Note that Ivy will check at runtime that all action preconditions are
|
|
# satisfied. For example, runing the scenario twice will cause a violation of
|
|
# the precondition of the `start` action, because a node cannot start twice
|
|
# (see `require ~_has_started` in action `start`).
|
|
|
|
# Another possibility would be to run `ivy_check` on the scenario, but that
|
|
# does not seem to work at the moment.
|
|
|
|
isolate scenarios = {
|
|
individual all:nset # will be used as parameter to actions requiring a quorum
|
|
|
|
after init {
|
|
var iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
all := all.insert(iter.val);
|
|
iter := iter.next;
|
|
};
|
|
assert nset.is_quorum(all); # we can also use asserts to make sure we are getting what we expect
|
|
}
|
|
|
|
export action scenario_1 = {
|
|
# all nodes start:
|
|
var iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
call protocol.server.start(iter.val);
|
|
iter := iter.next;
|
|
};
|
|
# all nodes receive the leader's proposal:
|
|
var m:msg;
|
|
m.m_kind := msg_kind.proposal;
|
|
m.m_src := 0;
|
|
m.m_round := 0;
|
|
m.m_value := 0;
|
|
m.m_vround := round.minus_one;
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
call net.recv(iter.val,m);
|
|
iter := iter.next;
|
|
};
|
|
# all nodes prevote:
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
call protocol.server.l_22(iter.val,0);
|
|
iter := iter.next;
|
|
};
|
|
# all nodes receive each other's prevote messages;
|
|
m.m_kind := msg_kind.prevote;
|
|
m.m_vround := 0;
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
var iter2 := node.iter.create(0); # the sender
|
|
while ~iter2.is_end
|
|
{
|
|
m.m_src := iter2.val;
|
|
call net.recv(iter.val,m);
|
|
iter2 := iter2.next;
|
|
};
|
|
iter := iter.next;
|
|
};
|
|
# all nodes precommit:
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
call protocol.server.l_36(iter.val,0,0,all);
|
|
iter := iter.next;
|
|
};
|
|
# all nodes receive each other's pre-commits
|
|
m.m_kind := msg_kind.precommit;
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
var iter2 := node.iter.create(0); # the sender
|
|
while ~iter2.is_end
|
|
{
|
|
m.m_src := iter2.val;
|
|
call net.recv(iter.val,m);
|
|
iter2 := iter2.next;
|
|
};
|
|
iter := iter.next;
|
|
};
|
|
# now all nodes can decide:
|
|
iter := node.iter.create(0);
|
|
while ~iter.is_end
|
|
{
|
|
call protocol.server.l_49_decide(iter.val,0,0,all);
|
|
iter := iter.next;
|
|
};
|
|
}
|
|
|
|
# TODO: add more scenarios
|
|
|
|
} with round, node, proposers, value, nset, protocol, shim, net
|
|
|
|
# extract code = protocol, shim, round, node
|
|
extract code = round, node, proposers, value, nset, protocol, shim, net, scenarios
|