github.com/vipernet-xyz/tm@v0.34.24/spec/ivy-proofs/network_shim.ivy (about)

     1  #lang ivy1.7
     2  # ---
     3  # layout: page
     4  # title: Network model and network shim
     5  # ---
     6  
     7  # Here we define a network module, which is our model of the network, and a
     8  # shim module that sits on top of the network and which, upon receiving a
     9  # message, calls the appropriate protocol handler.
    10  
    11  include domain_model
    12  
    13  # Here we define an enumeration type for identifying the 3 different types of
    14  # messages that nodes send.
    15  object msg_kind = { # TODO: merge with step_t
    16      type this = {proposal, prevote, precommit}
    17  }
    18  
    19  # Here we define the type of messages `msg`. Its members are structs with the fields described below.
    20  object msg = {
    21      type this = struct {
    22          m_kind : msg_kind,
    23          m_src  : node,
    24          m_round : round,
    25          m_value : value,
    26          m_vround : round
    27      }
    28  }
    29  
    30  # This is our model of the network:
    31  isolate net = {
    32  
    33      export action recv(dst:node,v:msg)
    34      action send(src:node,dst:node,v:msg)
    35      # Note that the `recv` action is exported, meaning that it can be called
    36      # non-deterministically by the environment any time it is enabled. In other
    37      # words, a packet that is in flight can be received at any time. In this
    38      # sense, the network is fully asynchronous. Moreover, there is no
    39      # requirement that a given message will be received at all.
    40  
    41      # The state of the network consists of all the packets that have been
    42      # sent so far, along with their destination.
    43      relation sent(V:msg, N:node)
    44  
    45      after init {
    46          sent(V, N) := false
    47      }
    48  
    49      before send {
    50          sent(v,dst) := true
    51      }
    52  
    53      before recv {
    54          require sent(v,dst) # only sent messages can be received.
    55      }
    56  }
    57  
    58  # The network shim sits on top of the network and, upon receiving a message,
    59  # calls the appropriate protocol handler. It also exposes a `broadcast` action
    60  # that sends to all nodes.
    61  
    62  isolate shim = {
    63  
    64      # In order not repeat the same code for each handler, we use a handler
    65      # module parameterized by the type of message it will handle. Below we
    66      # instantiate this module for the 3 types of messages of Tendermint
    67      module handler(p_kind) = {
    68          action handle(dst:node,m:msg)
    69          object spec = {
    70              before handle {
    71                  assert sent(m,dst) & m.m_kind = p_kind
    72              }
    73          }
    74      }
    75  
    76      instance proposal_handler : handler(msg_kind.proposal)
    77      instance prevote_handler : handler(msg_kind.prevote)
    78      instance precommit_handler : handler(msg_kind.precommit)
    79  
    80      relation sent(M:msg,N:node)
    81  
    82      action broadcast(src:node,m:msg)
    83      action send(src:node,dst:node,m:msg)
    84  
    85      specification {
    86          after init {
    87              sent(M,D) := false;
    88          }
    89          before broadcast {
    90              sent(m,D) := true
    91          }
    92          before send {
    93              sent(m,dst) := true
    94          }
    95      }
    96  
    97      # Here we give an implementation of it that satisfies its specification:
    98      implementation {
    99  
   100          implement net.recv(dst:node,m:msg) {
   101  
   102              if m.m_kind = msg_kind.proposal {
   103                  call proposal_handler.handle(dst,m)
   104              }
   105              else if m.m_kind = msg_kind.prevote {
   106                  call prevote_handler.handle(dst,m)
   107              }
   108              else if m.m_kind = msg_kind.precommit {
   109                  call precommit_handler.handle(dst,m)
   110              }
   111          }
   112  
   113          implement broadcast { # broadcast sends to all nodes, including the sender.
   114              var iter := node.iter.create(0);
   115              while ~iter.is_end
   116              invariant net.sent(M,D) -> sent(M,D)
   117              {
   118                  var n := iter.val;
   119                  call net.send(src,n,m);
   120                  iter := iter.next;
   121              }
   122          }
   123  
   124          implement send {
   125              call net.send(src,dst,m)
   126          }
   127  
   128          private {
   129              invariant net.sent(M,D) -> sent(M,D)
   130          }
   131      }
   132  
   133  } with net, node # to prove that the shim implementation satisfies the shim specification, we rely on the specification of net and node.