multiBftAbstract
include collections
include order
¤
Modules that should probably come from a standard library
¤
¤
Module for axiomatizing a total order
¤
module total_order(r) = {
axiom r(X,X) # Reflexivity
axiom r(X, Y) & r(Y, Z) -> r(X, Z) # Transitivity
axiom r(X, Y) & r(Y, X) -> X = Y # Anti-symmetry
axiom r(X, Y) | r(Y, X) # Totality
}
module total_order2(r) = {
axiom r(X,X) # Reflexivity
axiom r(X, Y) & r(Y, Z) -> r(X, Z) # Transitivity
axiom r(X, Y) & r(Y, X) -> X = Y # Anti-symmetry
axiom r(X, Y) | r(Y, X) # Totality
}
¤
Types, relations and functions describing state of the network
¤
instance replica : iterable
type operation
type view
type slot
type tquorum
individual none: view # null - used for max value on an empty set
individual i0 : slot
isolate creport = {
type this
relation maxv(CR:this, I:slot, R:view)
relation maxo(CR:this, I:slot, V:operation)
relation start(CR:this, I: slot)
relation afterend(CR:this, I: slot)
action set(cr:this, i: slot, r: view, v: operation) returns (cr:this)
action mergecr(cr: this, max_cr: this) returns (max_cr: this)
action match_cr(cr1: this, cr2: this) returns (ret: bool)
specification {
before mergecr {
require start(cr, I) <-> start(max_cr, I)
}
after mergecr {
ensure maxo(max_cr, I, V) & maxv(max_cr, I, R) & maxo(old max_cr, I, V2) & maxv(old max_cr, I, R2) & maxo(cr, I, V3) & maxv(cr, I, R3) -> ((R2 < R3 -> (R = R3 & V = V3)) &
(R2 > R3 -> (R = R2 & V = V2)) &
(R3 > R2 -> (R = R3 & V = V3)) &
((R2 = R3 & R2 ~= none) -> (R = R2 & V = V2)) &
((R2 = R3 & R2 =none) -> R = none)
)
}
after match_cr {
ensure ret <-> begin(cr1) = begin(cr2)
}
property maxv(CR, I, R1) & maxv(CR, I, R2) -> R1 = R2
property maxo(CR, I, V1) & maxo(CR, I, V2) -> V1 = V2
property start(CR, I1) & start(CR, I2) -> I1 = I2
property afterend(CR, I1) & afterend(CR, I2) -> I1 = I2
property start(CR, I) & afterend(CR, I2) -> I <= I2
}
implementation {
interpret slot -> nat
interpret view -> nat
type commit = struct {
maxview : view,
maxop : operation
}
individual empty_commit : commit
object nac = {
invariant [empty_commit_inv] empty_commit.maxview = none
after init { empty_commit.maxview := none }
}
instance arr : array(slot, commit)
destructor repr(CR: this) : arr
destructor begin(CR: this) : slot
definition maxv(CR, I, R) = R = (maxview(repr(CR).value(I - begin(CR))) if (I >= begin(CR) & I - begin(CR) < repr(CR).end) else none)
definition maxo(CR, I, O) = (I >= begin(CR) & I - begin(CR) < repr(CR).end & repr(CR).value(I - begin(CR)).maxop = O)
definition start(CR, I) = begin(CR) = I
definition afterend(CR, I) = begin(CR) + repr(CR).end = I
implement match_cr{
ret := begin(cr1) = begin(cr2);
}
function geti(CR:creport, I:slot) : commit
definition geti(CR,I) = repr(CR).value(I) if I < repr(CR).end else empty_commit
implement mergecr {
var cr_end := begin(cr) + repr(cr).end;
var max_cr_end := begin(max_cr) + repr(max_cr).end;
if cr_end > max_cr_end {
repr(max_cr) := repr(max_cr).resize(cr_end, empty_commit);
max_cr_end := begin(max_cr) + repr(max_cr).end;
};
var i : slot;
i := 0;
while i ~= repr(cr).end
invariant begin(max_cr) = begin(old max_cr)
invariant begin(cr) = begin(old max_cr)
invariant i <= repr(cr).end
invariant max_cr_end = begin(max_cr) + repr(max_cr).end
invariant ~(I < i & geti(cr,I).maxview <= geti(old max_cr,I).maxview & geti(max_cr,I) ~= geti(old max_cr,I))
invariant ~(I < i & geti(old max_cr,I).maxview < geti(cr,I).maxview & geti(max_cr,I) ~= geti(cr,I))
invariant ~(i <= I & geti(max_cr,I) ~= geti(old max_cr,I))
{
if repr(max_cr).value(i).maxview < repr(cr).value(i).maxview {
repr(max_cr) := repr(max_cr).set(i, repr(cr).value(i));
};
i := i + 1;
}
}
}
} with axioms
module relarray(domain,range) = {
type this
alias t = this
action empty returns (a:t)
action create(s:domain,y:range) returns (a:t)
action set(a:t,x:domain,y:range) returns (a:t)
action get(a:t,x:domain) returns (y:range)
action size(a:t) returns (s:domain)
action resize(a:t,s:domain,v:range) returns (a:t)
action append(a:t,v:range) returns (a:t)
¤
Representation
Function "end" gives the end value of an array while value(a,x) gives the value that x maps to in a.
function end(A:t) : domain
relation value(A:t,X:domain,Y:range)
¤
Specification
Notice that get and set have the precondition that x is in [0,end).
object spec = {
property end(X) >= 0
property value(A,X,Y1) & value(A,X,Y2) -> Y1 = Y2
after empty {
assert end(a) = 0
}
before create {
assert 0 <= s
}
after create {
assert end(a) = s & value(a,X,y)
}
before set {
assert 0 <= x & x < end(a)
}
after set {
assert end(a) = end(old a);
assert value(a,x,Y) <-> Y = y;
assert X ~= x -> (value(a,X,Y) <-> value(old a,X,Y))
}
before get {
assert 0 <= x & x < end(a)
}
after get {
assert value(a,x,y)
}
after size {
assert s = end(a)
}
after resize {
assert end(a) = s;
assert 0 <= X & X < end(old a) -> value(a,X,Y) <-> value(old a,X,Y);
assert end(old a) <= X & X < s -> value(a,X,Y) <-> Y = v
}
after append {
assert end(a) > end(old a) & ~(end(old a) < X & X < end(a));
assert 0 <= X & X < end(old a) -> value(a,X,Y) <-> value(old a,X,Y);
assert value(a,end(old a),Y) <-> Y = v
}
}
object impl = {
<<< member
class t_ : std::vector<range> {};
interpret t -> <<< t_ >>>
interpret t -> <<< std::vector<`range`> >>>
definition value(a:t,i:domain,v:range) = <<< (0 <= `i` && `i` < `a`.size()) ? `a`[`i`] == `v`: false >>>
definition end(a:t) = <<< `a`.size() >>>
implement create {
<<<
`a`.resize(`s`);
for (unsigned i = 0; i < `s`; i++)
`a`[i] = y;
>>>
}
implement empty {
<<<
>>>
}
implement set {
<<<
if (0 <= `x` && `x` < (`domain`)`a`.size())
`a`[`x`] = `y`;
>>>
}
implement get {
<<<
if (0 <= `x` && `x` < (`domain`)`a`.size())
`y` = `a`[`x`];
>>>
}
implement size {
<<<
`s` = (`domain`) `a`.size();
>>>
}
implement resize {
<<<
unsigned __old_size = `a`.size();
`a`.resize(`s`);
for (unsigned i = __old_size; i < (unsigned)`s`; i++)
`a`[i] = v;
>>>
}
implement append {
<<<
`a`.push_back(`v`);
>>>
}
<<< impl
std::ostream &operator <<(std::ostream &s, const `t` &a) {
s << '[';
for (unsigned i = 0; i < a.size(); i++) {
if (i != 0)
s << ',';
s << a[i];
}
s << ']';
return s;
}
template <>
`t` _arg<`t`>(std::vector<ivy_value> &args, unsigned idx, int bound) {
ivy_value &arg = args[idx];
if (arg.atom.size())
throw out_of_bounds(idx);
`t` a;
a.resize(arg.fields.size());
for (unsigned i = 0; i < a.size(); i++) {
a[i] = _arg<`range`>(arg.fields,i,0);
}
return a;
}
template <>
void __deser<`t`>(ivy_deser &inp, `t` &res) {
inp.open_list();
while(inp.open_list_elem()) {
res.resize(res.size()+1);
__deser(inp,res.back());
inp.close_list_elem();
}
inp.close_list();
}
template <>
void __ser<`t`>(ivy_ser &res, const `t` &inp) {
int sz = inp.size();
res.open_list(sz);
for (unsigned i = 0; i < (unsigned)sz; i++) {
res.open_list_elem();
__ser(res,inp[i]);
res.close_list_elem();
}
res.close_list();
}
template <>
z3::expr __to_solver(gen& g, const z3::expr& z3val, `t`& val) {
z3::expr z3end = g.apply("`end`",z3val);
z3::expr __ret = z3end == g.int_to_z3(z3end.get_sort(),val.size());
unsigned __sz = val.size();
for (unsigned __i = 0; __i < __sz; ++__i)
__ret = __ret && __to_solver(g,g.apply("`value`",z3val,g.int_to_z3(g.sort("`domain`"),__i)),val[__i]);
return __ret;
}
template <>
void __from_solver<`t`>( gen &g, const z3::expr &v,`t` &res){
`domain` __end;
__from_solver(g,g.apply("`end`",v),__end);
unsigned __sz = (unsigned) __end;
res.resize(__sz);
for (unsigned __i = 0; __i < __sz; ++__i)
__from_solver(g,g.apply("`value`",v,g.int_to_z3(g.sort("`domain`"),__i)),res[__i]);
}
template <>
void __randomize<`t`>( gen &g, const z3::expr &v){
unsigned __sz = rand() % 4;
z3::expr val_expr = g.int_to_z3(g.sort("`domain`"),__sz);
z3::expr pred = g.apply("`end`",v) == val_expr;
g.add_alit(pred);
for (unsigned __i = 0; __i < __sz; ++__i)
__randomize<`range`>(g,g.apply("`value`",v,g.int_to_z3(g.sort("`domain`"),__i)));
}
>>>
}
trusted isolate iso = spec,impl
attribute test = impl
}
trusted isolate nset = {
type this
alias t = this
relation member(N:replica, S:t)
relation majority(S:t)
action emptyset returns (s:t)
action add(s:t, n : replica) returns (s:t)
specification {
after emptyset {
ensure ~member(N, s)
}
after add {
ensure member(N,s) <-> (member(N , old s) | N = n)
}
}
private {
invariant [majorities_intersect]
(majority(S) & majority(T)) -> exists N. is_good(N) & member(N,S) & member(N,T)
}
implementation {
type index
interpret index -> int
function card(S:t) : index
instance arr : relarray(index,replica)
destructor repr(N:nset) : arr
individual all : nset
definition member(N,S) = exists I. 0 <= I & I < repr(S).end & repr(S).value(I, N)
definition majority(S) = card(S) + card(S) + card(S) > card(all) + card(all)
after init {
card(S) := 0;
repr(all) := arr.empty;
var i := replica.iter.create(0);
while ~i.is_end
invariant (forall N.~(member(N,S) & member(N,T))) ->
card(S) + card(T) <= card(all)
invariant replica.iter.done(N,i) <-> member(N,all)
invariant card(S) >= 0
{
repr(all) := repr(all).append(i.val);
card(S) := card(S) + 1 if member(i.val, S) else card(S);
i := i.next
};
}
implement emptyset {
repr(s) := arr.empty()
}
implement add {
if ~member(n, s) {
repr(s) := repr(s).append(n)
}
}
}
}
relation tmember(N:replica, T:tquorum) # N in T
relation view_change_msg(N:replica, R:view, CR:creport)
relation prepare_msg(N:replica, R:view, I:slot, V:operation)
relation commit_msg(N:replica, R:view, I:slot, V:operation)
relation new_view_msg(N:replica, R:view, Q:nset)
relation has_new_view(N:replica, R:view)
relation left_view(N:replica, R:view)
relation last_checkpoint(N:replica, I:slot)
relation current_slot(N:replica, I:slot)
conjecture [checkSlotLesscCurSlot] last_checkpoint(N, I1) & current_slot(N, I2) -> le_slot(I1, I2)
conjecture [lastCheckPFun] last_checkpoint(N, I1) & last_checkpoint(N, I2) -> I1 = I2
conjecture [curSlotPFun] current_slot(N, I1) & current_slot(N, I2) -> I1 = I2
relation l_view_change_msg(N:replica, R:view, SN:replica, CR:creport)
relation l_prepare_msg(N:replica, R:view, SN:replica, I:slot, V:operation)
relation l_commit_msg(N:replica, R:view, SN:replica, I:slot, V:operation)
relation l_new_view_msg(N:replica, R:view, SN:replica, Q:nset)
relation committed(N:replica, R:view, I:slot, V:operation)
relation l_committed(N:replica, I:slot, V:operation)
relation primary(N:replica, R:view)
relation le(X:view, Y:view)
relation le_slot(X:slot, Y:slot)
relation is_good(N:replica) # ghost, saves if N is Byzantine
isolate axioms = {
axiom le(none,R)
axiom le_slot(i0, I)
instantiate total_order(le)
instantiate total_order2(le_slot)
axiom primary(N1, R) & primary(N2, R) -> N1 = N2
}
isolate quorums = {
axiom forall T:tquorum. exists N:replica. tmember(N, T) & is_good(N)
}
isolate protocol(n:replica, r:view) = {
after init {
view_change_msg(N, R, CR) := false;
left_view(N, R) := false;
prepare_msg(N, R, I, V) := false;
commit_msg(N, R, I, V) := false;
committed(N, R, I, V) := false;
new_view_msg(N, R, Q) := false;
has_new_view(N, R) := false;
l_view_change_msg(N, R, SN, CR) := false;
l_prepare_msg(N, R, SN, I, V) := false;
l_commit_msg(N, R, SN, I, V) := false;
l_new_view_msg(N, R, SN, Q) := false;
}
action send_view_change_msg = {
var ok := true;
ok := ok & r ~= none;
ok := ok & ~left_view(n,r);
local cr:creport {
if ok {
cr := vcCalc.maximalCommit(n, r);
view_change_msg(n, r, cr) := true;
l_view_change_msg(n, r, n, cr) := true;
left_view(n, R) := left_view(n, R) | ~le(r, R)
}
}
}
action rcv_view_change_msg(sn:replica, cr:creport) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & view_change_msg(sn, r, cr);
ok := ok & forall I. exists R, V. cr.maxv(I, R) & cr.maxo(I, V) & l_commit_msg(n, R, sn, I, V);
ok := ok & forall I, R. cr.maxv(I, R) -> ~le(r, R);
if ok {
l_view_change_msg(n, r, sn, cr) := true;
}
}
action send_new_view_msg = {
var ok := true;
ok := ok & r ~= none;
ok := ok & primary(n, r);
ok := ok & ~left_view(n, r);
ok := ok & ~has_new_view(n, r);
local cr: creport, q: nset {
if ok {
q := maxCalc.view_change_set(n, r);
if q.majority {
cr := maxCalc.max_cr(n, r);
new_view_msg(n, r, q) := true;
l_new_view_msg(n, r, n, q) := true;
has_new_view(n, r) := true;
prepare_msg(n, r, I, V) := prepare_msg(n, r, I, V) | (exists R. cr.maxv(I,R) & R ~= none & cr.maxo(I, V));
l_prepare_msg(n, r, n, I, V) := l_prepare_msg(n, r, n, I, V) | (exists R. cr.maxv(I,R) & R ~= none & cr.maxo(I,V));
}
}
}
}
action rcv_new_view_msg(p:replica, q:nset) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & ~primary(n, r);
ok := ok & ~left_view(n, r);
ok := ok & ~has_new_view(n, r);
ok := ok & primary(p, r);
ok := ok & new_view_msg(p, r, q);
local cr: creport {
if ok {
q := maxCalc.view_change_set(n, r);
if q.majority {
cr := maxCalc.max_cr(n, r);
assume (forall I, R, V. cr.maxv(I, R) & R ~= none -> l_prepare_msg(n, r, p, I, V)); # can be turned into an if but verification time goes over 2 minuets. #primary sent all required preprepares
l_new_view_msg(n, r, p, q) :=true;
has_new_view(n, r) := true;
prepare_msg(n, r, I, V) := prepare_msg(n, r, I, V) | (exists R. cr.maxv(I,R) & R ~= none & cr.maxo(I, V));
l_prepare_msg(n, r, n, I, V) := l_prepare_msg(n, r, n, I, V) | (exists R. cr.maxv(I,R) & R ~= none & cr.maxo(I,V));
}
}
}
}
action send_preprepare_msg(i:slot, v:operation) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & has_new_view(n, r);
ok := ok & primary(n, r);
ok := ok & ~left_view(n, r) ;#relaxed version of this condition, was: ~left_view(n,r) & (forall R2. le(R2,r) & R2 ~= r -> left_view(n, R2)); # view n equals r
ok := ok & forall V. ~l_prepare_msg(n, r, n, i, V);
if ok {
prepare_msg(n, r, i, v) := true;
l_prepare_msg(n, r, n, i, v) := true;
};
}
action rcv_preprepare_msg(p:replica, i:slot, v:operation) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & primary(p, r);
ok := ok & prepare_msg(p, r, i, v);
if ok {
l_prepare_msg(n, r, p, i, v) := true;
};
}
action send_prepare_msg(p:replica, i:slot, v:operation) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & has_new_view(n, r);
ok := ok & ~primary(n, r);
ok := ok & forall V. ~l_prepare_msg(n, r, n, i, V);
ok := ok & primary(p, r);
ok := ok & l_prepare_msg(n, r, p, i, v);
if ok {
prepare_msg(n, r, i, v) := true;
l_prepare_msg(n, r, n, i, v) := true;
};
}
action rcv_prepare_msg(sn:replica, i:slot, v:operation) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & ~primary(n, r);
ok := ok & prepare_msg(sn, r, i, v);
if ok {
l_prepare_msg(n, r, sn, i, v) := true;
};
}
action send_commit_msg(i:slot, v:operation, q:nset) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & ~left_view(n, r);
ok := ok & q.majority;
ok := ok & forall N:replica. nset.member(N,q) -> l_prepare_msg(n, r, N, i, v);
if ok {
commit_msg(n, r, i, v) := true;
l_commit_msg(n, r, n, i, v) := true;
};
}
action rcv_commit_msg(sn:replica ,i:slot, v:operation, q:nset) = {
var ok := true;
ok := ok & r ~= none;
ok := ok & q.majority;
ok := ok & forall N. nset.member(N,q) -> l_prepare_msg(n, r, N, i, v);
ok := ok & commit_msg(sn, r, i, v);
if ok {
l_commit_msg(n, r, sn, i, v) := true;
};
}
action make_committed(i:slot, v:operation, q:nset) = {
var ok := true;
ok := ok & r ~= none;
ok := q.majority;
ok := ok & forall N. nset.member(N, q) -> commit_msg(N, r, i, v);
if ok {
committed(n, r, i, v) := true;
};
}
################ byzantine actions from here¤
allowing byz to send multiple prepare and preprepare
action byz_send_preprepare_msg(i:slot, v:operation) = {
assume r ~= none;
assume primary(n, r);
assume ~is_good(n);
prepare_msg(n, r, i, v) := true;
}
action byz_send_prepare_msg(i:slot, v:operation) = {
assume r ~= none;
assume ~primary(n, r);
assume ~is_good(n);
prepare_msg(n, r, i, v) := true;
}
action byz_send_commit_msg(i:slot, v:operation, q:nset) = {
assume r ~= none;
assume ~is_good(n);
commit_msg(n, r, i, v) := true;
}
action byz_send_view_change_msg(cr:creport) = {
assume r ~= none;
assume ~is_good(n);
view_change_msg(n, r, cr) := true;
left_view(n, R) := left_view(n, R) | ~le(r, R)
}
action byz_send_new_view_msg(q:nset) = {
assume r ~= none;
assume ~is_good(n);
new_view_msg(n, r, q) := true;
has_new_view(n, r) := true;
}
export send_view_change_msg
export rcv_view_change_msg
export send_new_view_msg
export rcv_new_view_msg
export send_preprepare_msg
export rcv_preprepare_msg
export send_prepare_msg
export rcv_prepare_msg
export send_commit_msg
export rcv_commit_msg
export make_committed
export byz_send_preprepare_msg
export byz_send_prepare_msg
export byz_send_commit_msg
export byz_send_view_change_msg
export byz_send_new_view_msg
conjecture (
committed(N1, I, R1, V1) &
committed(N2, I, R2, V2)
) -> V1 = V2
conjecture is_good(N) & commit_msg(N, R, I, V1) & commit_msg(N, R, I, V2) -> V1 = V2
conjecture is_good(N) & view_change_msg(N, R2, CR) & ~le(R2, R1) & creport.maxv(CR, I, none) -> ~commit_msg(N, R1, I, V2)
conjecture is_good(N) & view_change_msg(N, R, CR) & creport.maxv(CR, I, R2) & R2~= none -> ~le(R, R2)
conjecture is_good(N) & view_change_msg(N, R, CR) & creport.maxv(CR, I, R2) & R2 ~= none & creport.maxo(CR, I, V) -> commit_msg(N, R2, I, V)
conjecture is_good(N) & view_change_msg(N, R, CR) & creport.maxv(CR, I, R2) & R2 ~= none & ~le(R,ROTHER) & ~le(ROTHER, R2) -> ~commit_msg(N, ROTHER, I, VOTHER)
conjecture is_good(N) & view_change_msg(N, R, CR) & creport.maxv(CR, I, RM) & commit_msg(N, RC, I, V) & ~le(R, RC) -> le(RC, RM)
conjecture ~commit_msg(N, none, I, V)
conjecture le(R1, R2) & left_view(N, R2) -> left_view(N, R1)
conjecture view_change_msg(N, R, CR) & ~le(R, R1) -> left_view(N, R1)
conjecture l_commit_msg(O, R, N, I, V) -> commit_msg(N, R, I, V)
conjecture commit_msg(N, R, I, V) & is_good(N) -> l_commit_msg(N, R, N, I, V)
conjecture l_view_change_msg(O, R, N, CR) -> view_change_msg(N, R, CR)
conjecture l_view_change_msg(O, R, N, CR) & creport.maxv(CR, I, R2) & R2~= none -> ~le(R, R2)
conjecture l_view_change_msg(O, R, N, CR) & creport.maxv(CR, I, R2) & R2 ~= none & creport.maxo(CR, I, V) -> l_commit_msg(O, R2, N, I, V)
conjecture l_view_change_msg(O, R, N, CR) & is_good(N) & creport.maxv(CR, I, none) & ~le(R, R2) -> ~commit_msg(N, R2, I, V)
conjecture l_view_change_msg(O, R, N, CR) & is_good(O) & creport.maxv(CR, I, R2) & R2 ~= none -> le(R2, R) & R ~= R2
conjecture view_change_msg(N, R1, CR) & is_good(N) -> l_view_change_msg(N, R1, N, CR)
private {
conjecture is_good(N) & prepare_msg(N, R, I, V1) & prepare_msg(N, R, I, V2) -> V1 = V2
conjecture is_good(N) & prepare_msg(N, R, I, V) & primary(P, R) -> prepare_msg(P, R, I, V)
conjecture forall R,V. (exists N . commit_msg(N, R, I, V) & is_good(N)) -> exists Q. nset.majority(Q) & forall N2. nset.member(N2,Q) -> prepare_msg(N2, R, I, V)
conjecture forall R:view, V:operation. committed(N1, R, I, V) -> exists Q. nset.majority(Q) & forall N:replica. nset.member(N, Q) -> commit_msg(N, R, I, V)
Properties of choosable and prepare_msg conjecture forall R1:view, R2:view, V1:operation, V2:operation, Q:byzquorum. is_good(primary(R2)) & ~le(R2, R1) & prepare_msg(primary(R2), I, R2, V2) & V1 ~= V2 -> exists N:replica. byzmember(N, Q) & is_good(N) & left_view(N, R1) & ~commit_msg(N, I, R1, V1)
conjecture forall R1:view, R2:view, V1:operation, Q:byzquorum. ~le(R2,R1) & available(R2) -> exists N:replica. byzmember(N,Q) & is_good(N) & left_view(N,R1) & ~commit_msg(N,R1,V1)
conjecture ~prepare_msg(N, R, V) & is_good(N) & has_new_view(N ,R) -> available(R)
conjecture forall R1:view, R2:view, V1:operation, Q:byzquorum. ~le(R2, R1) & (exists N2. ~prepare_msg(N2, I, R2, V1) & is_good(N2) & has_new_view(N2 ,R2)) -> exists N:replica. byzmember(N,Q) & is_good(N) & left_view(N,R1) & ~commit_msg(N, I, R1, V1)
conjecture forall R1:view, R2:view, V1:operation. ~le(R2, R1) & (exists N2:replica. ~prepare_msg(N2, R2, I, V1) & is_good(N2) & has_new_view(N2 ,R2)) ->
forall Q:nset, R1:view. nset.majority(Q) -> exists N:replica. nset.member(N, Q) & is_good(N) & left_view(N, R1) & ~commit_msg(N, R1, I, V1)
conjecture is_good(N) & prepare_msg(N, R, I, V) -> has_new_view(N, R)
conjecture is_good(N) & commit_msg(N, R, I, V) -> (exists Q:nset. nset.majority(Q) & forall N1:replica. nset.member(N1, Q) -> prepare_msg(N1, R, I, V))
local messages
conjecture l_prepare_msg(O, R, N, I, V) -> prepare_msg(N, R, I, V)
conjecture prepare_msg(N, R, I, V) & is_good(N) -> l_prepare_msg(N, R, N, I, V)
conjecture l_commit_msg(O, R, N, I, V) -> commit_msg(N, R, I, V)
conjecture commit_msg(N, R, I, V) & is_good(N) -> l_commit_msg(N, R, N, I, V)
conjecture (exists O, N. l_commit_msg(O, R, N, I, V)) -> (exists Q. nset.majority(Q) & forall N2. nset.member(N2, Q) -> prepare_msg(N2, R, I, V))
} #private
} with axioms, quorums, maxCalc(n, r), vcCalc #protocol
isolate checkpoints = {
type state
type hash
relation checkpoint_msg(N:replica, I:slot, H:hash)
relation l_checkpoint_msg(O:replica, N:replica, I:slot, H:hash)
relation state_transfer_msg(N:replica, I:slot, S:state)
relation l_state_transfer_msg(O:replica, N:replica, I:slot, S:state)
relation is_checkpoint(I:slot)
individual s0:state
relation nextState(S1:state, V:operation, S2:state)
relation nextSlot(I1:slot, I2:slot)
function replicaState(N:replica) : state
function stateHash(S:state) : hash
function oracleStateHistory(I:slot) : state
function oracleLog(I:slot) : operation
individual oracleSlot : slot
after init {
checkpoint_msg(N, I, H) := false;
l_committed(N, I, V) := false;
l_checkpoint_msg(O, N, I, H) := false;
state_transfer_msg(N, I, S) := false;
l_state_transfer_msg(O, N, I, S) := false;
last_checkpoint(N, I) := I = i0;
current_slot(N, I) := I = i0;
replicaState(N) := s0;
oracleSlot := i0;
oracleStateHistory(i0) := s0;
}
action advance_state(n:replica, i:slot, i2:slot, v:operation, s2:state) = {
assume current_slot(n, i);
assume nextSlot(i, i2);
assume l_committed(n, i, v);
assume nextState(replicaState(n), v, s2);
current_slot(n, I) := I = i2;
replicaState(n) := s2;
if oracleSlot = i & is_good(n) {
oracleLog(i) := v;
oracleSlot := i2;
oracleStateHistory(i2) := s2;
};
}
action observe_committed(n:replica, i:slot, v:operation) = {
if (exists R. committed(n, R, i, v)) {
l_committed(n, i, v) := true;
};
}
action send_checkpoint(n:replica, i:slot, s:state) = {
if current_slot(n, i) & replicaState(n) = s {
checkpoint_msg(n, i, stateHash(s)) := true;
};
}
action rcv_checkpoint(o:replica, n:replica, i:slot, h:hash) = {
if checkpoint_msg(n, i, h) {
l_checkpoint_msg(o, n, i, h) := true;
};
}
action accept_checkpoint(n:replica, s:replica, i:slot, h:hash, st:state, t:tquorum) = {
var ok := true;
ok := ok & exists I. current_slot(n, I) & le_slot(I, i) & I ~= i;
ok := ok & stateHash(st) = h;
ok := ok & l_state_transfer_msg(n, s, i, st);
ok := ok & forall N1:replica. tmember(N1, t) -> l_checkpoint_msg(n, N1, i, h);
if ok {
current_slot(n, I) := I = i;
replicaState(n) := st;
};
}
action send_state(n:replica, i:slot) = {
if current_slot(n, i) {
state_transfer_msg(n, i, replicaState(n)) := true;
};
}
action rcv_state(o:replica, n:replica, i:slot, s:state) = {
if state_transfer_msg(n, i, s) {
l_state_transfer_msg(o, n, i, s) := true;
}
}
action byz_send_checkpoint(n:replica, i:slot, h:hash) = {
assume ~is_good(n);
checkpoint_msg(n, i, h) := true;
}
action byz_send_state(n:replica, i:slot, s:state) = {
assume ~is_good(n);
state_transfer_msg(n, i, s) := true;
}
export advance_state
export observe_committed
export send_checkpoint
export rcv_checkpoint
export accept_checkpoint
export send_state
export rcv_state
export byz_send_checkpoint
export byz_send_state
conjecture is_good(N1) & is_good(N2) & current_slot(N1, I) & current_slot(N2, I) -> replicaState(N1) = replicaState(N2)
private {
axiom [nextStatePFun] nextState(S1, V, S2) & nextState(S1, V, S22) -> S2 = S22
axiom nextSlot(I1, I2) -> le_slot(I1, I2) & I1 ~= I2
axiom nextSlot(I1, I2) & I3 ~= I2 & le_slot(I3, I2) -> le_slot(I3, I1)
axiom [hashUnique] stateHash(S1) = stateHash(S2) -> S1 = S2
conjecture (l_committed(N, I, V) & is_good(N)) -> (exists R. committed(N, R, I, V))
conjecture l_checkpoint_msg(O, N, I, H) -> checkpoint_msg(N, I, H)
conjecture (checkpoint_msg(N, I, H) & is_good(N) & le_slot(I2, I) & I2 ~= I) -> (exists R:view, V:operation. committed(N, R, I2, V))
conjecture l_committed(N, I, V1) & l_committed(N, I, V2) & is_good(N) -> V1 = V2
conjecture is_good(N) & checkpoint_msg(N, I, H) -> le_slot(I, oracleSlot)
conjecture is_good(N) & current_slot(N, I) -> le_slot(I, oracleSlot)
conjecture le_slot(I, oracleSlot) & I ~= oracleSlot -> exists N, R, V. committed(N , R, I, V)
conjecture le_slot(I2, oracleSlot) & nextSlot(I1, I2) & committed(N, R, I1, V) -> nextState(oracleStateHistory(I1), oracleLog(I1), oracleStateHistory(I2))
conjecture is_good(N) & current_slot(N, I) -> replicaState(N) = oracleStateHistory(I)
conjecture le_slot(I, oracleSlot) & I ~= oracleSlot -> exists N, R. committed(N, R, I, oracleLog(I))
conjecture is_good(N) & checkpoint_msg(N, I, H) & stateHash(S) = H -> oracleStateHistory(I) = S
conjecture l_state_transfer_msg(O, N, I, S) -> state_transfer_msg(N, I, S)
}
} with axioms, quorums, protocol #checkpoints
isolate maxCalc(n:replica, r:view) = {
individual max_cr: creport
individual view_change_set: nset
conjecture forall N:replica. nset.member(N, view_change_set) -> exists CR. l_view_change_msg(n, r, N, CR)
conjecture forall I. exists R. max_cr.maxv(I, R)
conjecture forall I. (exists R. R ~= none & max_cr.maxv(I, R)) -> exists V. max_cr.maxo(I, V)
conjecture forall I.
exists R. max_cr.maxv(I, R) &
(R ~= none ->
exists V:operation, N:replica, CR:creport.
max_cr.maxo(I, V) &
nset.member(N, view_change_set) &
~le(r, R) &
l_view_change_msg(n, r, N, CR) &
creport.maxv(CR, I, R) &
creport.maxo(CR, I, V)
)
conjecture forall I, RM, V.
max_cr.maxv(I, RM) & max_cr.maxo(I, V) & RM ~= none
->
exists N.
nset.member(N, view_change_set) &
l_commit_msg(n, RM, N, I, V)
conjecture max_cr.maxv(I, none) & nset.member(N, view_change_set) & ~le(r, R) & is_good(N) -> ~l_commit_msg(n, R, N, I, V)
conjecture forall I, N, CR, MAXR.
max_cr.maxv(I, none) ->
~( nset.member(N, view_change_set) &
~le(r,MAXR) &
l_view_change_msg(n, r, N, CR) &
creport.maxv(CR, I, MAXR) &
MAXR ~= none
)
conjecture forall I:slot, RM:view, N2:replica, R2:view, CR2:creport.
max_cr.maxv(I, RM) & RM ~= none &
nset.member(N2, view_change_set) &
l_view_change_msg(n, r, N2, CR2) &
creport.maxv(CR2, I, R2) &
~le(r,R2) &
R2 ~= none
->
le(R2, RM)
action update_max_cr(s: replica, cr: creport) = {
require l_view_change_msg(n, r, s, cr);
var ret := creport.match_cr(cr, max_cr);
if ret {
view_change_set := view_change_set.add(s);
max_cr := creport.mergecr(max_cr, cr);
}
}
private {
conjecture forall I, CR. l_view_change_msg(O, R, N, CR) -> exists R2. creport.maxv(CR, I, R2)
conjecture forall I, CR. l_view_change_msg(O, R, N, CR) & creport.maxv(CR, I, R2) & R2 ~= none -> exists V. creport.maxo(CR, I, V)
conjecture l_view_change_msg(n, r, N, CR) & nset.member(N, view_change_set) & creport.maxv(CR, I, R) & max_cr.maxv(I, RM) -> le(R, RM)
conjecture forall I, RM. max_cr.maxv(I, RM) & max_cr.maxo(I, V) & RM ~= none -> exists N, CR. nset.member(N, view_change_set) & l_view_change_msg(n, r, N, CR) & creport.maxv(CR, I, RM) & creport.maxo(CR, I, V)
}
} with protocol,axioms,nset, creport
isolate vcCalc = {
action maximalCommit(n:replica, r:view) returns (cr: creport) = {
assume forall I:slot. (
(cr.maxv(I,none) &
forall MAXR:view,MV:operation. ~(~le(r,MAXR) & l_commit_msg(n, MAXR, n, I,MV))
) |
(exists R, V. cr.maxv(I,R) &
cr.maxo(I,V) &
R ~= none &
~le(r, R) &
l_commit_msg(n, R, n, I, V) &
(forall R2:view,V2:operation. (~le(r,R2) & l_commit_msg(n, R2, n, I, V2)) -> le(R2,R))
)
);
ensure exists R. cr.maxv(I, R);
ensure cr.maxv(I, none) & l_commit_msg(n, R, n, I, V) -> le(r, R);
ensure (exists R. cr.maxv(I, R) & R ~= none) -> (exists V. cr.maxo(I, V));
ensure cr.maxv(I, R) & R ~= none & cr.maxo(I, V) -> l_commit_msg(n, R, n, I, V);
ensure cr.maxv(I, R) & R ~= none & l_commit_msg(n, R2, n, I, V) & ~le(r, R2) -> le(R2, R);
}
} with protocol, axioms