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.

133 lines
3.8 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. isolate net = {
  27. export action recv(dst:node,v:msg)
  28. action send(src:node,dst:node,v:msg)
  29. # Note that the `recv` action is exported, meaning that it can be called
  30. # non-deterministically by the environment any time it is enabled. In other
  31. # words, a packet that is in flight can be received at any time. In this
  32. # sense, the network is fully asynchronous. Moreover, there is no
  33. # requirement that a given message will be received at all.
  34. # The state of the network consists of all the packets that have been
  35. # sent so far, along with their destination.
  36. relation sent(V:msg, N:node)
  37. after init {
  38. sent(V, N) := false
  39. }
  40. before send {
  41. sent(v,dst) := true
  42. }
  43. before recv {
  44. require sent(v,dst) # only sent messages can be received.
  45. }
  46. }
  47. # The network shim sits on top of the network and, upon receiving a message,
  48. # calls the appropriate protocol handler. It also exposes a `broadcast` action
  49. # that sends to all nodes.
  50. isolate shim = {
  51. # In order not repeat the same code for each handler, we use a handler
  52. # module parameterized by the type of message it will handle. Below we
  53. # instantiate this module for the 3 types of messages of Tendermint
  54. module handler(p_kind) = {
  55. action handle(dst:node,m:msg)
  56. object spec = {
  57. before handle {
  58. assert sent(m,dst) & m.m_kind = p_kind
  59. }
  60. }
  61. }
  62. instance proposal_handler : handler(msg_kind.proposal)
  63. instance prevote_handler : handler(msg_kind.prevote)
  64. instance precommit_handler : handler(msg_kind.precommit)
  65. relation sent(M:msg,N:node)
  66. action broadcast(src:node,m:msg)
  67. action send(src:node,dst:node,m:msg)
  68. specification {
  69. after init {
  70. sent(M,D) := false;
  71. }
  72. before broadcast {
  73. sent(m,D) := true
  74. }
  75. before send {
  76. sent(m,dst) := true
  77. }
  78. }
  79. # Here we give an implementation of it that satisfies its specification:
  80. implementation {
  81. implement net.recv(dst:node,m:msg) {
  82. if m.m_kind = msg_kind.proposal {
  83. call proposal_handler.handle(dst,m)
  84. }
  85. else if m.m_kind = msg_kind.prevote {
  86. call prevote_handler.handle(dst,m)
  87. }
  88. else if m.m_kind = msg_kind.precommit {
  89. call precommit_handler.handle(dst,m)
  90. }
  91. }
  92. implement broadcast { # broadcast sends to all nodes, including the sender.
  93. var iter := node.iter.create(0);
  94. while ~iter.is_end
  95. invariant net.sent(M,D) -> sent(M,D)
  96. {
  97. var n := iter.val;
  98. call net.send(src,n,m);
  99. iter := iter.next;
  100. }
  101. }
  102. implement send {
  103. call net.send(src,dst,m)
  104. }
  105. private {
  106. invariant net.sent(M,D) -> sent(M,D)
  107. }
  108. }
  109. } with net, node # to prove that the shim implementation satisfies the shim specification, we rely on the specification of net and node.