You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
4.1 KiB

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