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.

143 lines
4.8 KiB

  1. #lang ivy1.7
  2. include order # this is a file from the standard library (`ivy/ivy/include/1.7/order.ivy`)
  3. isolate round = {
  4. type this
  5. individual minus_one:this
  6. relation succ(R1:round, R2:round)
  7. action incr(i:this) returns (j:this)
  8. specification {
  9. # to simplify verification, we treat rounds as an abstract totally ordered set with a successor relation.
  10. instantiate totally_ordered(this)
  11. property minus_one < 0
  12. property succ(X,Z) -> (X < Z & ~(X < Y & Y < Z))
  13. after incr {
  14. ensure succ(i,j)
  15. }
  16. }
  17. implementation {
  18. # here we prove that the abstraction is sound.
  19. interpret this -> int # rounds are integers in the Tendermint specification.
  20. definition minus_one = 0-1
  21. definition succ(R1,R2) = R2 = R1 + 1
  22. implement incr {
  23. j := i+1;
  24. }
  25. }
  26. }
  27. instance node : iterable # nodes are a set with an order, that can be iterated over (see order.ivy in the standard library)
  28. relation well_behaved(N:node) # whether a node is well-behaved or not. NOTE: Used only in the proof and the Byzantine model; Nodes do know know who is well-behaved and who is not.
  29. isolate proposers = {
  30. # each round has a unique proposer in Tendermint. In order to avoid a
  31. # function from round to node (which makes verification more difficult), we
  32. # abstract over this function using a relation.
  33. relation is_proposer(N:node, R:round)
  34. export action get_proposer(r:round) returns (n:node)
  35. specification {
  36. property is_proposer(N1,R) & is_proposer(N2,R) -> N1 = N2
  37. after get_proposer {
  38. ensure is_proposer(n,r);
  39. }
  40. }
  41. implementation {
  42. function f(R:round):node
  43. definition f(r:round) = <<<r % `node.size`>>>
  44. definition is_proposer(N,R) = N = f(R)
  45. implement get_proposer {
  46. n := f(r);
  47. }
  48. }
  49. }
  50. isolate value = { # the type of values
  51. type this
  52. relation valid(V:value)
  53. individual nil:value
  54. specification {
  55. property ~valid(nil)
  56. }
  57. implementation {
  58. interpret value -> bv[2]
  59. definition nil = <<< -1 >>> # let's say nil is -1
  60. definition valid(V) = V ~= nil
  61. }
  62. }
  63. object nset = { # the type of node sets
  64. type this # a set of N=3f+i nodes for 0<i<=3
  65. relation member(N:node, S:nset) # set-membership relation
  66. relation is_quorum(S:nset) # intent: sets of cardinality at least 2f+i+1
  67. relation is_blocking(S:nset) # intent: at least f+1 nodes
  68. export action insert(s:nset, n:node) returns (t:nset)
  69. export action empty returns (s:nset)
  70. implementation {
  71. # NOTE: this is not checked at all by Ivy; it is however useful to generate C++ code and run it for debugging purposes
  72. <<< header
  73. #include <set>
  74. #include <exception>
  75. namespace hash_space {
  76. template <typename T>
  77. class hash<std::set<T> > {
  78. public:
  79. size_t operator()(const std::set<T> &s) const {
  80. hash<T> h;
  81. size_t res = 0;
  82. for (const T &e : s)
  83. res += h(e);
  84. return res;
  85. }
  86. };
  87. }
  88. >>>
  89. interpret nset -> <<< std::set<`node`> >>>
  90. definition member(n:node, s:nset) = <<< `s`.find(`n`) != `s`.end() >>>
  91. definition is_quorum(s:nset) = <<< 3*`s`.size() > 2*`node.size` >>>
  92. definition is_blocking(s:nset) = <<< 3*`s`.size() > `node.size` >>>
  93. implement empty {
  94. <<<
  95. >>>
  96. }
  97. implement insert {
  98. <<<
  99. `t` = `s`;
  100. `t`.insert(`n`);
  101. >>>
  102. }
  103. <<< encode `nset`
  104. std::ostream &operator <<(std::ostream &s, const `nset` &a) {
  105. s << "{";
  106. for (auto iter = a.begin(); iter != a.end(); iter++) {
  107. if (iter != a.begin()) s << ", ";
  108. s << *iter;
  109. }
  110. s << "}";
  111. return s;
  112. }
  113. template <>
  114. `nset` _arg<`nset`>(std::vector<ivy_value> &args, unsigned idx, long long bound) {
  115. throw std::invalid_argument("Not implemented"); // no syntax for nset values in the REPL
  116. }
  117. >>>
  118. }
  119. }
  120. object classic_bft = {
  121. relation quorum_intersection
  122. private {
  123. definition [quorum_intersection_def] quorum_intersection = forall Q1,Q2. exists N. well_behaved(N) & nset.member(N, Q1) & nset.member(N, Q2) # every two quorums have a well-behaved node in common
  124. }
  125. }
  126. trusted isolate accountable_bft = {
  127. # this is our baseline assumption about quorums:
  128. private {
  129. property [max_2f_byzantine] exists N . well_behaved(N) & nset.member(N,Q) # every quorum has a well-behaved member
  130. }
  131. }