#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