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