From mathcomp Require Import all_ssreflect.
From mathcomp Require Import finmap multiset.
From mathcomp Require Import zify.
From Coq Require Import Reals Relation_Definitions Relation_Operators.
From mathcomp Require Import boolp Rstruct.
From Algorand Require Import fmap_ext algorand_model.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Open Scope mset_scope.
Open Scope fmap_scope.
Open Scope fset_scope.
Safety helper definitions and results
This module contains helper functions and lemmas
used when proving safety of the transition system.
Ltac finish_case :=
simpl;
solve[
repeat first[
reflexivity|
eassumption|
split|
eexists]].
Gather all the unfoldings we might want for working with transitions into
a hint database for use with autounfold.
Create HintDb utransition_unfold discriminated.
#[
export]
Hint Unfold
propose_result propose_ok repropose_ok no_propose_ok
softvote_result softvote_new_ok softvote_repr_ok no_softvote_ok
certvote_result certvote_ok no_certvote_ok
nextvote_result nextvote_val_ok nextvote_open_ok nextvote_stv_ok no_nextvote_ok
certvote_timeout_ok
set_softvotes certvote_ok certvote_result
set_nextvotes_open adv_period_open_ok adv_period_open_result
set_nextvotes_val adv_period_val_ok adv_period_val_result
set_certvotes certify_ok certify_result
vote_msg deliver_nonvote_msg_result :
utransition_unfold.
Create HintDb gtransition_unfold discriminated.
#[
export]
Hint Unfold
tick_ok tick_update tick_users
delivery_result
step_result
is_partitioned recover_from_partitioned
is_unpartitioned make_partitioned
corrupt_user_result :
gtransition_unfold.
Arguments delivery_result :
clear implicits.
Generic path lemmas
Dropping elements from a path still results in a path.
Lemma path_drop T (
R:
rel T)
x p (
H:
path R x p)
n:
match drop n p with
|
List.nil =>
true
|
List.cons x'
p' =>
path R x'
p'
end.
Proof.
by elim:
n x p H=> [|
n IHn]
x [|
a l /
andP []
H_path];
last apply IHn.
Qed.
Lemma path_drop'
T (
R:
rel T)
x p (
H:
path R x p)
n:
match drop n (
x::
p)
with
| [::] =>
true
| [::
x' &
p'] =>
path R x'
p'
end.
Proof.
elim:
n x p H=> [|
n IHn]
x p H_path //=.
move/
IHn: {
IHn}
H_path.
destruct n;
simpl.
by destruct p;[|
move/
andP => []].
rewrite -
add1n -
drop_drop.
by destruct (
drop n p);[|
destruct l;[|
move/
andP => []]].
Qed.
Predicate path still holds after taking n elements.
Lemma path_prefix :
forall T R p (
x:
T)
n,
path R x p ->
path R x (
take n p).
Proof.
induction p;[
done|].
move => /=
x n /
andP [
Hr Hpath].
destruct n.
done.
simpl;
apply /
andP;
by auto.
Qed.
Proposition does not hold initially but holds for last element implies there
must be a point in the path where it becomes true.
Lemma path_steps :
forall {
T} (
R :
rel T)
x0 p,
path R x0 p ->
forall (
P :
pred T),
~~
P x0 ->
P (
last x0 p) ->
exists n,
match drop n (
x0 ::
p)
with
|
x1 ::
x2 ::
_ => ~~
P x1 &&
P x2
|
_ =>
false
end.
Proof.
clear.
intros T R x0 p H_path P H_x0.
revert p H_path.
induction p using last_ind.
*
simpl.
intros _ H_b.
exfalso.
revert H_b.
apply /
negP.
assumption.
*
rewrite last_rcons.
rewrite rcons_path.
move => /
andP [
H_path H_step]
H_x.
destruct (
P (
last x0 p))
eqn:
H_last.
+
destruct IHp as [
n'
Hind];[
done..|].
exists n'.
destruct n';
simpl in Hind |- *.
destruct p;[
by exfalso|
assumption].
destruct (
ltnP n' (
size p));[|
by (
rewrite drop_oversize in Hind)].
rewrite drop_rcons;[
destruct (
drop n'
p)
as [|? [|]];[
done..|
tauto]|].
apply ltnW;
assumption.
+
clear IHp.
exists (
size p).
destruct (
size p)
eqn:
H_size.
simpl.
rewrite (
size0nil H_size)
in H_last |- *.
simpl.
by apply/
andP.
simpl.
rewrite (
drop_nth x0).
rewrite <-
cats1, <-
H_size.
rewrite drop_size_cat;[|
reflexivity].
rewrite nth_cat.
rewrite H_size ltnSn.
change n with (
n.+1.-1).
rewrite <-
H_size,
nth_last,
H_last.
simpl.
assumption.
rewrite size_rcons H_size.
rewrite ltnS.
apply leqnSn.
Qed.
Generic multiset lemmas
Element x in mset of seq iff x is in seq.
Lemma in_seq_mset (
T :
choiceType) (
x :
T) (
s :
seq T):
(
x \
in seq_mset s) = (
x \
in s).
Proof.
The number of elements in the preimage of f w.r.t. b and multiset m is
the same as applying map_mset on f and m and then b.
Lemma map_mset_count {
A B :
choiceType} (
f:
A ->
B) (
m : {
mset A}) :
forall (
b:
B), (
count (
preim f (
pred1 b))
m) = (
map_mset f m)
b.
Proof.
Element membership w.r.t. preimage is preserved by map_mset on the multiset m.
Lemma map_mset_has {
A B :
choiceType} (
f:
A ->
B) (
m : {
mset A}) :
forall (
b:
pred B),
has b (
map_mset f m) =
has (
preim f b)
m.
Proof.
The support of a multiset is unique when viewed as a sequence.
Lemma finsupp_mset_uniq (
T:
choiceType) (
A:{
mset T}):
uniq (
finsupp A).
Proof.
The sequence of a subset of a multiset is equal to the subset's finite support modulo reordering.
Lemma msubset_finsupp (
T:
choiceType) (
A B: {
mset T}):
(
A `<=`
B)%
mset ->
perm_eq (
finsupp A) [
seq i <-
finsupp B |
i \
in A].
Proof.
Summing up elements in a multiset subset is the same as taking sequence length.
Lemma msubset_size_sum (
T:
choiceType) (
A B: {
mset T}):
(
A `<=`
B)%
mset ->
\
sum_(
i <-
finsupp B)
A i =
size A.
Proof.
The size of a unioned multiset is sum of the size of its components.
Lemma mset_add_size (
T:
choiceType) (
A B : {
mset T}):
size (
A `+`
B) = (
size A +
size B)%
nat.
Proof.
The size of msetn n x is n for any x.
Lemma msetn_size (
T:
choiceType)
n (
x:
T):
size (
msetn n x) =
n.
Proof.
The subset of a multiset has smaller size.
Lemma msubset_size (
T:
choiceType) (
A B : {
mset T}):
(
A `<=`
B)%
mset ->
size A <=
size B.
Proof.
If msets are equal after adding seqs to mset A, this implies seqs have the same elements.
Lemma msetD_seq_mset_perm_eq (
T:
choiceType) (
A: {
mset T}) (
l l':
seq T):
A `+`
seq_mset l =
A `+`
seq_mset l' ->
perm_eq l l'.
Proof.
Generic sequence lemmas
Lemma in_memP (
T :
eqType) (
x :
T)
l :
reflect (
List.In x l) (
in_mem x (
mem l)).
Proof.
apply iffP with (
P :=
x \
in l).
-
by case: (
x \
in l) => //;
constructor.
-
elim:
l => [|
h l IH] //=.
rewrite inE;
case/
orP;
first by move/
eqP=>->;
left.
by move/
IH =>
mem_x;
right.
-
elim:
l => [|
h l IH] //=;
case.
by move =>->;
rewrite inE;
apply/
orP;
left.
by move/
IH;
rewrite inE =>
mem_x;
apply/
orP;
right.
Qed.
Lemma take_rcons T :
forall (
s :
seq T) (
x :
T),
take (
size s) (
rcons s x) =
s.
Proof.
elim => //=; last by move => a l IH x; rewrite IH. Qed.
Lemma perm_eq_cons1P (
T :
eqType) (
s :
seq T) (
a :
T) :
reflect (
s = [::
a]) (
perm_eq s [::
a]).
Proof.
case:
s => [|
x s];
first by rewrite /
perm_eq /= ?
eqxx;
constructor.
case:
s => [|
y s].
apply: (
iffP idP).
rewrite /
perm_eq /= ?
eqxx.
move/
andP => [
Ht Ht'].
move:
Ht.
case Hax: (
a ==
x) => //.
by move/
eqP:
Hax =>->.
by move =>->;
apply perm_refl.
apply: (
iffP idP) => //.
set s1 := [::
_ &
_].
set s2 := [::
_].
move =>
Hpr.
by have Hs:
size s1 =
size s2 by apply perm_size.
Qed.
Lemmas relating seqs and sets
A set derived from an empty seq is the empty set.
Lemma set_nil :
forall (
T :
finType), [
set x in [::]] = @
set0 T.
Proof.
by move => T. Qed.
The cardinality of seq as set is size of the unduplicated seq.
Lemma finseq_size :
forall (
T :
finType) (
s:
seq T), #|
s| =
size (
undup s).
Proof.
move=>
T s.
rewrite -
cardsE.
elim:
s => //=;
first by rewrite set_nil cards0.
move =>
x s IH.
rewrite set_cons /=.
case:
ifP => //=.
move =>
xs.
suff Hsuff:
x |: [
set x0 in s] = [
set x in s]
by rewrite Hsuff.
apply/
setP =>
y.
rewrite in_setU1.
case Hxy: (
y ==
x) => //.
rewrite /=
inE.
by move/
eqP:
Hxy=>->.
move/
negP/
negP =>
Hx.
by rewrite cardsU1 /=
inE Hx /=
add1n IH.
Qed.
A set derived using filter in seq and filter directly have same size.
Lemma imfset_filter_size_lem (
A B :
choiceType) (
f :
A ->
B) (
sq :
seq A) (
P :
A ->
bool):
#|` [
fset x |
x in [
seq f x |
x <-
sq &
P x]]| = #|` [
fset f x |
x in sq &
P x]|.
Proof.
Generic definitions
Turn pred on UState into pred on GState - assumed false if uid not present.
Definition upred uid (
P :
pred UState) :
pred GState :=
fun g =>
match g.(
users).[?
uid]
with
|
Some u =>
P u
|
None =>
false
end.
Turn pred on UState into pred on GState - assumed true if uid not present.
Definition upred'
uid (
P :
pred UState) :
pred GState :=
fun g =>
match g.(
users).[?
uid]
with
|
Some u =>
P u
|
None =>
true
end.
Lemmas about the step of a user state
step_le is equivalent to step_leb.
Lemma step_leP:
forall s1 s2,
reflect (
step_le s1 s2) (
step_leb s1 s2).
Proof.
step_lt is equivalent to step_ltb.
Lemma step_ltP:
forall s1 s2,
reflect (
step_lt s1 s2) (
step_ltb s1 s2).
Proof.
Weaken step: less-than implies less-than-or-equal.
Lemma step_ltW a b:
step_lt a b ->
step_le a b.
Proof.
clear.
destruct a as [[? ?] ?],
b as [[? ?]?].
unfold step_lt,
step_le;
intuition.
Qed.
Transitivitity of step_le.
Lemma step_le_trans a b c:
step_le a b ->
step_le b c ->
step_le a c.
Proof.
destruct a as [[? ?] ?],
b as [[? ?]?],
c as [[? ?]?].
unfold step_le.
intros H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
eapply leq_trans;
eassumption.
Qed.
Transitivity of step_lt.
Lemma step_lt_trans a b c:
step_lt a b ->
step_lt b c ->
step_lt a c.
Proof.
destruct a as [[? ?] ?],
b as [[? ?]?],
c as [[? ?]?].
unfold step_lt.
intros H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
eapply ltn_trans;
eassumption.
Qed.
a < b and b <= c implies a < c.
Lemma step_lt_le_trans a b c:
step_lt a b ->
step_le b c ->
step_lt a c.
Proof.
destruct a as [[? ?] ?],
b as [[? ?]?],
c as [[? ?]?].
unfold step_lt,
step_le.
intros H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
eapply leq_trans;
eassumption.
Qed.
a <= b and b < c implies a < c.
Lemma step_le_lt_trans a b c:
step_le a b ->
step_lt b c ->
step_lt a c.
Proof.
destruct a as [[? ?] ?],
b as [[? ?]?],
c as [[? ?]?].
unfold step_lt,
step_le.
intros H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
destruct H_ab as [
H_ab|[->
H_ab]];
destruct H_bc as [
H_bc|[->
H_bc]];[
left..|];
[
eapply ltn_trans;
eassumption|
assumption|
assumption|];
right;
split;[
reflexivity|];
clear -
H_ab H_bc.
eapply leq_ltn_trans;
eassumption.
Qed.
step is not less than itself.
Lemma step_lt_irrefl r p s: ~
step_lt (
r,
p,
s) (
r,
p,
s).
Proof.
rewrite /
step_lt =>
Hrp;
case:
Hrp;
rewrite ltnn //.
by case => ?;
rewrite ltnn;
case => //;
case.
Qed.
step is less than or equal to itself.
Lemma step_le_refl step:
step_le step step.
Proof.
clear;
unfold step_le;
intuition.
Qed.
ustate_after and step_le are equivalent.
Lemma ustate_after_iff_step_le u1 u2:
step_le (
step_of_ustate u1) (
step_of_ustate u2)
<->
ustate_after u1 u2.
Proof.
unfold ustate_after;
destruct u1,
u2;
simpl.
clear;
tauto.
Qed.
Transitivity of ustate_after.
Lemma ustate_after_transitive :
forall us1 us2 us3,
ustate_after us1 us2 ->
ustate_after us2 us3 ->
ustate_after us1 us3.
Proof.
Lemmas about tick functions
tick_ok_users function as a predicate.
Lemma tick_ok_usersP :
forall increment (
g :
GState),
reflect
(
forall (
uid :
UserId) (
h :
uid \
in domf g.(
users)),
user_can_advance_timer increment g.(
users).[
h])
(
tick_ok_users increment g).
Proof.
move =>
increment g.
exact:
allfP.
Qed.
Domain of users unchanged after tick.
Lemma tick_users_domf :
forall increment pre,
domf pre.(
users) =
domf (
tick_users increment pre).
Proof.
move =>
increment pre.
by rewrite -
updf_domf.
Qed.
tick_users at uid results in user_advance_timer.
Lemma tick_users_upd :
forall increment pre uid (
h :
uid \
in domf pre.(
users)),
(
tick_users increment pre).[?
uid] =
Some (
user_advance_timer increment pre.(
users).[
h]).
Proof.
move =>
increment pre uid h.
by rewrite updf_update.
Qed.
tick_users results in None if the user not in the domain of the pre-state.
Lemma tick_users_notin :
forall increment pre uid (
h :
uid \
notin domf pre.(
users)),
(
tick_users increment pre).[?
uid] =
None.
Proof.
Lemmas about merge_msgs_deadline and send_broadcasts
A message in merge_msgs_deadline is either already in the mailbox or
it is a member of the messages being merged.
Lemma in_merge_msgs :
forall d (
msg:
Msg)
now msgs mailbox,
(
d,
msg) \
in merge_msgs_deadline now msgs mailbox ->
msg \
in msgs \/ (
d,
msg) \
in mailbox.
Proof.
send_broadcasts definition.
Lemma send_broadcastsE now targets prev_msgs msgs:
send_broadcasts now targets prev_msgs msgs =
updf prev_msgs targets (
fun _ =>
merge_msgs_deadline now msgs).
Proof.
send_broadcasts at uid results in merge_msgs_deadline.
Lemma send_broadcasts_in :
forall (
msgpool :
MsgPool)
now uid msgs targets
(
h :
uid \
in msgpool) (
h' :
uid \
in targets),
(
send_broadcasts now targets msgpool msgs).[?
uid] =
Some (
merge_msgs_deadline now msgs msgpool.[
h]).
Proof.
send_brodcasts results in None if uid is not in the domain of msgpool.
Lemma send_broadcast_notin :
forall (
msgpool :
MsgPool)
now uid msgs targets
(
h :
uid \
notin domf msgpool),
(
send_broadcasts now targets msgpool msgs).[?
uid] =
None.
Proof.
send_brodcasts at uid results in msgpool at uid if uid is in msgpool,
but uid is not in targets of send_broadcasts function.
Lemma send_broadcast_notin_targets :
forall (
msgpool :
MsgPool)
now uid msgs targets
(
h :
uid \
in msgpool) (
h' :
uid \
notin targets),
(
send_broadcasts now targets msgpool msgs).[?
uid] =
msgpool.[?
uid].
Proof.
send_broadcast contains msg but original mailbox does not, msg must be in l.
Lemma broadcasts_prop
uid (
msg:
Msg) (
l:
seq Msg)
time (
targets : {
fset UserId}) (
mailboxes'
mailboxes : {
fmap UserId -> {
mset R *
Msg}}):
(
odflt mset0 mailboxes'.[?
uid] `<=`
odflt mset0 mailboxes.[?
uid])%
mset ->
match (
send_broadcasts time targets mailboxes'
l).[?
uid]
with
|
Some msg_mset =>
has (
fun p :
R *
Msg =>
p.2 ==
msg)
msg_mset
|
None =>
false
end ->
~~
match mailboxes.[?
uid]
with
|
Some msg_mset =>
has (
fun p :
R *
Msg =>
p.2 ==
msg)
msg_mset
|
None =>
false
end ->
msg \
in l.
Proof.
clear.
move =>
H_sub H_pre H_post.
have: ~~
has (
fun p:
R *
Msg =>
p.2 ==
msg) (
odflt mset0 mailboxes.[?
uid])
by case:
fndP H_post;[|
rewrite enum_mset0].
move {
H_post} =>
H_post.
rewrite send_broadcastsE in H_pre.
case:
mailboxes'.[?
uid]/
fndP =>
H_mb';
[|
by move:
H_pre;
rewrite not_fnd //;
congr (
uid \
notin _):
H_mb';
apply updf_domf].
have: ~~
has (
fun p:
R *
Msg =>
p.2 ==
msg) (
mailboxes'.[
H_mb'])
by apply/
contraNN:
H_post => /
hasP /= [
x H_in H_x];
apply/
hasP;
exists x;[
apply (
msubset_subset H_sub);
rewrite in_fnd|].
move {
H_post} =>
H_post.
move: {
mailboxes H_sub}
H_pre.
case:(
uid \
in targets)/
boolP=>
H_tgt;
[|
by apply contraTT;
rewrite updf_update'].
rewrite updf_update {
targets H_tgt}// => /
hasP /=[[
d msg']
H_in /
eqP /=
H_msg].
move:
H_msg H_in=> {
msg'}-> /
in_merge_msgs [//|
H_in].
move/
negP in H_post;
contradict H_post.
by apply /
hasP;
exists (
d,
msg).
Qed.
Definition of onth, lemmas about onth and step
onth: option returning nth element of seq if n small enough.
Definition onth {
T :
Type} (
s :
seq T) (
n :
nat) :
option T :=
ohead (
drop n s).
onth results in Some if the index is small.
Lemma onth_size :
forall T (
s:
seq T)
n x,
onth s n =
Some x ->
n <
size s.
Proof.
If onth of a prefix is not None then onth of the original sequence is the same.
Lemma onth_from_prefix T (
s:
seq T)
k n x:
onth (
take k s)
n =
Some x ->
onth s n =
Some x.
Proof.
onth equal to Some x implies nth element is x.
Lemma onth_nth T (
s:
seq T)
ix x:
onth s ix =
Some x -> (
forall x0,
nth x0 s ix =
x).
Proof.
onth equal to Some x means x is in the sequence.
Lemma onth_in (
T:
eqType) (
s:
seq T)
ix x:
onth s ix =
Some x ->
x \
in s.
Proof.
onth equal to Some x means that the last element of the prefixed sequence is x.
Lemma onth_take_last T (
s:
seq T)
n x:
onth s n =
some x ->
forall x0,
last x0 (
take n.+1
s) =
x.
Proof.
Predicates true for all elements of a sequence are true for elements returned by onth.
Lemma all_onth T P s: @
all T P s ->
forall ix x,
onth s ix =
Some x ->
P x.
Proof.
x in s implies onth (the index of x in s) is x.
Lemma onth_index (
T :
eqType) (
x :
T) (
s :
seq T):
x \
in s ->
onth s (
index x s) =
Some x.
Proof.
at_step holds for nth element with P and nth element is g, then P g holds.
Lemma at_step_onth n (
path :
seq GState) (
P :
pred GState):
at_step n path P ->
forall g,
onth path n =
Some g ->
P g.
Proof.
unfold at_step,
onth.
case Hdrop: (
drop n path) => [|
a l] //=.
by move =>
HP g;
case =><-.
Qed.
If nth element is g and P g holds, then at_step holds for n and P.
Lemma onth_at_step n (
path :
seq GState)
g:
onth path n =
Some g ->
forall (
P :
pred GState),
P g ->
at_step n path P.
Proof.
unfold at_step,
onth.
case Hdrop: (
drop n path) => [|
a l] //=.
by case =><-.
Qed.
onth is true at ix implies onth is true at truncated trace for size of new trace minus one.
Lemma onth_take_some :
forall (
trace:
seq GState)
ix g,
onth trace ix =
Some g ->
onth (
take ix.+1
trace) (
size (
take ix.+1
trace)).-1 =
Some g.
Proof.
move =>
trace ix g H_onth.
clear -
H_onth.
unfold onth.
erewrite drop_nth with (
x0:=
g).
simpl.
rewrite nth_last.
erewrite onth_take_last with (
x:=
g);
try assumption.
trivial.
destruct trace.
inversion H_onth.
intuition.
Qed.
Lemmas about step_in_path_at
step_in_path_at implies a global transition.
Lemma transition_from_path
g0 states ix (
H_path:
is_trace g0 states)
g1 g2
(
H_step :
step_in_path_at g1 g2 ix states):
g1 ~~>
g2.
Proof.
unfold step_in_path_at in H_step.
destruct states.
inversion H_path.
destruct H_path as [
H_g0 H_path];
subst.
have {
H_path} :=
path_drop'
H_path ix.
destruct (
drop ix (
g ::
states));[
done|].
destruct l;[
done|].
destruct H_step as [-> ->].
simpl.
by move/
andP => [] /
asboolP.
Qed.
step_in_path_at with same index must be with same states.
Lemma step_ix_same trace ix g1 g2:
step_in_path_at g1 g2 ix trace ->
forall g3 g4,
step_in_path_at g3 g4 ix trace ->
g3 =
g1 /\
g4 =
g2.
Proof.
clear.
unfold step_in_path_at.
destruct (
drop ix trace)
as [|? [|]];(
tauto ||
intuition congruence).
Qed.
A step in path at n from g1 to g2 means g1 is at index n.
Lemma step_in_path_onth_pre {
g1 g2 n path} (
H_step :
step_in_path_at g1 g2 n path)
:
onth path n =
Some g1.
Proof.
unfold step_in_path_at in H_step.
unfold onth.
destruct (
drop n path)
as [|? []];
destruct H_step.
rewrite H;
reflexivity.
Qed.
step_in_path_at from g1 to g2 with n means g2 is at index n+1.
Lemma step_in_path_onth_post {
g1 g2 n path} (
H_step :
step_in_path_at g1 g2 n path)
:
onth path n.+1 =
Some g2.
Proof.
step_in_path_at of truncated path implies step_in_path_at of original path.
Lemma step_in_path_prefix (
g1 g2 :
GState)
n k (
path :
seq GState) :
step_in_path_at g1 g2 n (
take k path)
->
step_in_path_at g1 g2 n path.
Proof.
revert k path;
induction n.
intros k path;
destruct path;[
done|];
destruct k;[
done|];
destruct path;[
done|];
destruct k;
done.
intros k path.
destruct k.
clear;
intro;
exfalso;
destruct path;
assumption.
unfold step_in_path_at.
destruct path.
done.
simpl.
apply IHn.
Qed.
step_in_path_at implies step_in_path_at of truncated path provided the truncation
is sufficiently long.
Lemma step_in_path_take (
g1 g2 :
GState)
n (
path :
seq GState) :
step_in_path_at g1 g2 n path
->
step_in_path_at g1 g2 n (
take n.+2
path).
Proof.
revert path;
induction n.
intro path.
destruct path;[
done|];
destruct path;
done.
intros path.
unfold step_in_path_at.
destruct path.
done.
simpl.
apply IHn.
Qed.
Lemmas on reset_msg_delays and reset_user_msg_delays
Reset_deadline in reset_user_msg_delays if m is in msgs.
Lemma reset_msg_delays_fwd :
forall (
msgs : {
mset R *
Msg}) (
m :
R *
Msg),
m \
in msgs ->
forall now, (
reset_deadline now m \
in reset_user_msg_delays msgs now).
Proof.
If m was in reset_user_msg_delays then the deadline must have been reset.
Lemma reset_user_msg_delays_rev (
now :
R) (
msgs : {
mset R *
Msg}) (
m:
R*
Msg):
m \
in reset_user_msg_delays msgs now ->
exists d0,
m =
reset_deadline now (
d0,
m.2) /\ (
d0,
m.2) \
in msgs.
Proof.
The domain of msgpool is unchanged after reset_msg_delays.
Lemma reset_msg_delays_domf :
forall (
msgpool :
MsgPool)
now,
domf msgpool =
domf (
reset_msg_delays msgpool now).
Proof.
by move =>
msgpool pre;
rewrite -
updf_domf. Qed.
reset_msg_delays at uid results in reset_user_msg_delays.
Lemma reset_msg_delays_upd :
forall (
msgpool :
MsgPool)
now uid (
h :
uid \
in domf msgpool),
(
reset_msg_delays msgpool now).[?
uid] =
Some (
reset_user_msg_delays msgpool.[
h]
now).
Proof.
move =>
msgpool now uid h.
have Hu :=
updf_update _ h.
have Hu' :=
Hu (
domf msgpool)
_ h.
by rewrite Hu'.
Qed.
reset_msg_delays results in None if the user is not in the domain of the message pool.
Lemma reset_msg_delays_notin :
forall (
msgpool :
MsgPool)
now uid
(
h :
uid \
notin domf msgpool),
(
reset_msg_delays msgpool now).[?
uid] =
None.
Proof.
Definitions and lemmas for sent and forged messages
Definition user_sent sender (
m :
Msg) (
pre post :
GState) :
Prop :=
exists (
ms :
seq Msg),
m \
in ms
/\ ((
exists d incoming,
related_by (
lbl_deliver sender d incoming ms)
pre post)
\/ (
related_by (
lbl_step_internal sender ms)
pre post)).
Definition user_forged (
msg:
Msg) (
g1 g2:
GState) :=
related_by (
lbl_forge_msg (
msg_sender msg) (
msg_round msg) (
msg_period msg) (
msg_type msg) (
msg_ev msg))
g1 g2.
Definition user_sent_at ix path uid msg :=
exists g1 g2,
step_in_path_at g1 g2 ix path
/\
user_sent uid msg g1 g2.
A user who sends a message must be in both pre and post states.
Lemma user_sent_in_pre {
sender m pre post} (
H :
user_sent sender m pre post):
sender \
in pre.(
users).
Proof.
by case: H => [msgs [H_mem [[d [recv H_step]] | H_step]]];
case: H_step => [key_ustate [ustate_post H_step]].
Qed.
Lemma user_sent_in_post {
sender m pre post} (
H :
user_sent sender m pre post):
sender \
in post.(
users).
Proof.
destruct H as [
msgs [
H_mem [[
d [
recv H_step]] |
H_step]]];
simpl in H_step.
-
by destruct H_step as [
key_ustate [
ustate_post [
H_sender [
H_corrupt H_step]]]];
destruct H_step as [
key_mailbox [
H_recv H_post]];
subst post;
unfold delivery_result,
step_result;
destruct pre;
simpl;
clear;
change (
sender \
in domf (
users.[
sender <-
ustate_post]));
rewrite dom_setf;
apply fset1U1.
-
by destruct H_step as [
key_ustate [
ustate_post [
H_corrupt [
H_sender H_post]]]];
subst post;
unfold delivery_result,
step_result;
destruct pre;
simpl;
clear;
change (
sender \
in domf (
users.[
sender <-
ustate_post]));
rewrite dom_setf;
apply fset1U1.
Qed.
The step of a user in the pre-state who sends message is same as msg_step.
Lemma utransition_label_start uid msg g1 g2 :
user_sent uid msg g1 g2 ->
forall u,
g1.(
users).[?
uid] =
Some u ->
(
step_of_ustate u) = (
msg_step msg).
Proof.
unfold user_sent.
move => [
sent [
msg_in H_trans]]
u H_u.
case:
msg msg_in =>
mtype v r p uid_m msg_in.
case:
H_trans => [[
d [
body H_recv]]|
H_step].
* {
destruct H_recv as (
key_ustate &
ustate_post &
H_step &
H_honest
&
key_mailbox &
H_msg_in_mailbox & ->).
destruct g1;
simpl in * |- *.
unfold step_of_ustate.
rewrite in_fnd in H_u.
injection H_u;
clear H_u.
intros <-.
remember (
ustate_post,
sent)
as ustep_out in H_step.
destruct H_step;
injection Hequstep_out;
clear Hequstep_out;
intros <- <-;
try (
exfalso;
exact (
notF msg_in)).
move:
msg_in;
rewrite inE;
case/
eqP => [
eq_type eq_ev eq_p eq_r eq_s];
subst.
unfold certvote_ok in H;
decompose record H;
clear H.
revert H0.
clear;
unfold pre',
valid_rps;
autounfold with utransition_unfold;
simpl;
clear.
move => [-> [-> ->]];
reflexivity.
}
* {
destruct H_step as (
key_user &
ustate_post &
H_honest &
H_step & ->).
destruct g1;
simpl in * |- *.
rewrite in_fnd in H_u;
injection H_u;
clear H_u.
intro H.
rewrite ->
H in * |- *.
clear key_user users H.
move/
in_memP:
msg_in =>
msg_in.
clear -
msg_in H_step.
unfold step_of_ustate.
remember (
ustate_post,
sent)
as ustep_out in H_step;
destruct H_step;
injection Hequstep_out;
clear Hequstep_out;
intros <- <-;
let rec use_mem H :=
first [
exfalso;
exact H
|
destruct H as [
H|
H];[
injection H as <- <- <- <- <-|
use_mem H]]
in use_mem msg_in;
autounfold with utransition_unfold in H;
decompose record H;
clear H;
match goal with
| [
H:
valid_rps _ _ _ _ |-
_] =>
unfold valid_rps in H;
move:
H
| [
H:
advancing_rp _ _ _ |-
_] =>
unfold advancing_rp in H;
move :
H
end;
clear;
simpl;
move=> [-> [-> ->]];
reflexivity.
}
Qed.
The step of a user in post-state who sends a message is greater than the message step.
Lemma utransition_label_end :
forall uid msg g1 g2,
user_sent uid msg g1 g2 ->
forall u,
g2.(
users).[?
uid] =
Some u ->
step_lt (
msg_step msg) (
step_of_ustate u).
Proof.
move =>
uid msg g1 g2.
unfold user_sent.
move => [
sent [
msg_in H_trans]]
u H_u.
case:
msg msg_in =>
mtype v r p uid_m /=
msg_in.
case:
H_trans => [[
d [
body H_recv]]|
H_step].
* {
destruct H_recv as (
key_ustate &
ustate_post &
H_step &
H_honest
&
key_mailbox &
H_msg_in_mailbox & ->).
revert H_u.
rewrite fnd_set eq_refl.
case => {
u}<-.
destruct g1;
cbn -[
in_mem mem eq_op]
in * |- *.
remember (
ustate_post,
sent)
as ustep_out in H_step.
destruct H_step;
injection Hequstep_out;
clear Hequstep_out;
intros <- <-;
try (
exfalso;
exact (
notF msg_in));
match type of msg_in with
|
is_true (
_ \
in [::
_]) =>
unfold in_mem in msg_in;
simpl in msg_in;
rewrite Bool.orb_false_r in msg_in;
apply (
elimT eqP)
in msg_in;
injection msg_in;
clear msg_in;
intros -> -> -> -> ->
end.
autounfold with utransition_unfold in H.
decompose record H.
revert H0;
subst pre';
clear;
unfold valid_rps;
destruct pre;
simpl.
by intuition.
}
* {
destruct H_step as (
key_user &
ustate_post &
H_honest &
H_step & ->).
revert H_u.
rewrite fnd_set eq_refl.
case => {
u}<-.
destruct g1;
cbn -[
in_mem mem eq_op]
in * |- *.
move/
in_memP:
msg_in =>
msg_in.
clear -
msg_in H_step.
remember (
ustate_post,
sent)
as ustep_out in H_step.
destruct H_step;
injection Hequstep_out;
clear Hequstep_out;
intros <- <-;
let rec use_mem H :=
first [
exfalso;
exact H
|
destruct H as [
H|
H];[
injection H as <- <- <- <- <-|
use_mem H]]
in use_mem msg_in;
autounfold with utransition_unfold in H;
decompose record H;
clear H;
match goal with
| [
H:
valid_rps _ _ _ _ |-
_] =>
move:
H;
unfold valid_rps
| [
H:
advancing_rp _ _ _ |-
_] =>
move:
H;
unfold advancing_rp
end;
clear;
destruct pre;
simpl;
clear;
move=> [-> [->
H_step_val]];
repeat (
split ||
right);
by rewrite addn1 ltnSn.
}
Qed.
Definitions for receiving messages
Definition step_at path ix lbl :=
exists g1 g2,
step_in_path_at g1 g2 ix path /\
related_by lbl g1 g2.
Definition msg_received uid msg_deadline msg path :
Prop :=
exists n ms,
step_at path n
(
lbl_deliver uid msg_deadline msg ms).
Definition received_next_vote u voter round period step value path :
Prop :=
exists d,
msg_received u d (
match value with
|
Some v =>
mkMsg Nextvote_Val (
next_val v step)
round period voter
|
None =>
mkMsg Nextvote_Open (
step_val step)
round period voter
end)
path.
Labeling transitions
All global transitions have some label.
Lemma transitions_labeled:
forall g1 g2,
g1 ~~>
g2 <->
exists lbl,
related_by lbl g1 g2.
Proof.
split.
+
destruct 1;
simpl.
exists (
lbl_tick increment);
finish_case.
destruct pending as [
deadline msg];
exists (
lbl_deliver uid deadline msg sent);
finish_case.
exists (
lbl_step_internal uid sent);
finish_case.
exists (
lbl_exit_partition);
finish_case.
exists (
lbl_enter_partition);
finish_case.
exists (
lbl_corrupt_user uid);
finish_case.
exists (
lbl_replay_msg uid);
finish_case.
exists (
lbl_forge_msg sender r p mtype mval);
finish_case.
+
destruct 1
as [[]
Hrel];
simpl in Hrel;
case:
Hrel.
*
by move =>
Htick ->;
eapply step_tick;
eassumption.
*
move =>
x [
H_uid [
ustate_post [
H_ustep Hg2]]].
move:
Hg2 => [
key_mailbox [
Hg1 Hg2]].
by subst g2;
eapply step_deliver_msg;
eassumption.
*
move =>
x [
H_uid [
ustate_post [
H_corrupt Hg2]]].
by subst g2;
eapply step_internal;
eassumption.
*
move =>
H_part H_g2.
by subst g2;
eapply step_exit_partition;
eassumption.
*
move =>
H_part H_g2.
by subst g2;
eapply step_enter_partition;
eassumption.
*
move =>
H_in [
H_corrupt H_g2].
by subst g2;
eapply step_corrupt_user;
eassumption.
*
move =>
H_in [
msg [
H_corrupt [
H_msg H_g2]]].
by subst g2;
eapply step_replay_msg;
eassumption.
*
move =>
H_in [
s0 [
H_keys [
H_comm [
H_match H_g2]]]].
by subst g2;
eapply step_forge_msg;
eassumption.
Qed.
Internal transitions change the state - used in transition_label_unique.
Lemma internal_not_noop :
forall s pre post l,
s #
pre ~> (
post,
l) ->
pre <>
post.
Proof.
move =>
s pre post l Hst;
inversion Hst;
subst.
all:
try (
autounfold with utransition_unfold in H2;
decompose record H2;
clear H2;
match goal with [
H :
valid_rps _ _ _ _ |-
_] =>
destruct H as [
_ [
_ H_s]];
contradict H_s;
rewrite H_s;
simpl;
clear
end;
try discriminate;
by rewrite addn1 => /
esym /
n_Sn).
autounfold with utransition_unfold in H3;
decompose record H3;
clear H3.
destruct H1 as [
_ [
_ H_s]];
contradict H_s;
rewrite H_s;
simpl;
clear.
by rewrite addn1 => /
esym /
n_Sn.
Qed.
delivery_result decreases mailbox size - used in transition_label_unique.
Lemma deliver_analysis1:
forall g uid upost r m l
(
key_mbox :
uid \
in g.(
msg_in_transit)),
(
r,
m) \
in g.(
msg_in_transit).[
key_mbox] ->
let g2 :=
delivery_result g uid key_mbox (
r,
m)
upost l in
(
forall uid2,
(
size (
odflt mset0 (
g2.(
msg_in_transit).[?
uid2]))
<
size (
odflt mset0 (
g.(
msg_in_transit).[?
uid2]))) <->
uid =
uid2)
/\ [
mset (
r,
m)] =(
odflt mset0 (
g.(
msg_in_transit).[?
uid])
`\`
odflt mset0 (
g2.(
msg_in_transit).[?
uid]))%
mset.
Proof.
Message transitions with same resulting state has the same output messages -
used in transition_label_unique.
Lemma utransition_msg_result_analysis uid upre m upost l l'
(
H_step:
uid #
upre;
m ~> (
upost,
l))
(
H_step':
uid #
upre;
m ~> (
upost,
l')) :
l =
l'.
Proof.
delivery_result on a global state is the same means the message delivery
transitions are equal - used in transition_label_unique.
Lemma deliver_deliver_lbl_unique :
forall g uid uid'
upost upost'
r r'
m m'
l l'
(
key_state :
uid \
in g.(
users)) (
key_mbox :
uid \
in g.(
msg_in_transit))
(
key_state' :
uid' \
in g.(
users)) (
key_mbox' :
uid' \
in g.(
msg_in_transit)),
~
g.(
users).[
key_state].(
corrupt) ->
(
r,
m) \
in g.(
msg_in_transit).[
key_mbox] ->
uid #
g.(
users).[
key_state] ;
m ~> (
upost,
l) ->
~
g.(
users).[
key_state'].(
corrupt) ->
(
r',
m') \
in g.(
msg_in_transit).[
key_mbox'] ->
uid' #
g.(
users).[
key_state'] ;
m' ~> (
upost',
l') ->
delivery_result g uid key_mbox (
r,
m)
upost l =
delivery_result g uid'
key_mbox' (
r',
m')
upost'
l' ->
uid =
uid' /\
r =
r' /\
m =
m' /\
l =
l'.
Proof.
clear.
move =>
g uid uid'
upost upost'
r r'
m m'
l l'
key_state key_mbox key_state'
key_mbox'
H_honest H_pending H_step H_honest'
H_pending'
H_step'
H_results.
have Fact1 :=
deliver_analysis1 upost l H_pending.
have Fact1' :=
deliver_analysis1 upost'
l'
H_pending'.
have {
Fact1 Fact1'}[
H_uids H_pendings] :
uid =
uid' /\ [
mset (
r,
m)] = [
mset (
r',
m')].
{
move:
H_results Fact1 Fact1' => <-.
set g2 :=
delivery_result g uid key_mbox (
r,
m)
upost l.
cbv zeta.
clearbody g2.
clear.
move => [
H_uid H_pending] [
H_uid'
H_pending'].
have H:
uid =
uid by reflexivity.
rewrite <-
H_uid,
H_uid'
in H;
subst uid'.
by split;[|
rewrite H_pending -
H_pending'].
}
subst uid'.
have {
H_pendings}[
H_r H_m]: (
r,
m) = (
r',
m')
by apply/
mset1P;
rewrite -
H_pendings mset11.
subst r'
m'.
repeat (
split;[
reflexivity|]).
have H:
upost =
upost'
by move: (
f_equal (
fun g =>
g.(
users).[?
uid])
H_results);
rewrite !
fnd_set eq_refl;
clear;
congruence.
subst upost'.
rewrite (
bool_irrelevance key_state'
key_state)
in H_step'.
move: (
g.(
users)[`
key_state])
H_step H_step' =>
upre.
clear.
apply utransition_msg_result_analysis.
Qed.
Message delivery transitions cannot be the same as internal transitions -
used in transition_label_unique.
Lemma deliver_internal_False :
forall g uid uid'
upost upost'
r m l l'
(
key_state :
uid \
in g.(
users)) (
key_mbox :
uid \
in g.(
msg_in_transit))
(
key_state' :
uid' \
in g.(
users)),
~
g.(
users).[
key_state].(
corrupt) ->
(
r,
m) \
in g.(
msg_in_transit).[
key_mbox] ->
uid #
g.(
users).[
key_state] ;
m ~> (
upost,
l) ->
~
g.(
users).[
key_state'].(
corrupt) ->
uid' #
g.(
users).[
key_state'] ~> (
upost',
l') ->
delivery_result g uid key_mbox (
r,
m)
upost l =
step_result g uid'
upost'
l' ->
False.
Proof.
clear.
move =>
g uid uid'
upost upost'
r m l l'
key_state key_mbox key_state'.
move =>
H_honest H_msg H_step H_honest'
H_step'.
rewrite/
delivery_result /= /
step_result /= /
RecordSet.set /=.
set us1 :=
_.[
uid <-
_].
set us2 :=
_.[
uid' <-
_].
set sb1 :=
send_broadcasts _ _ _ _.
set sb2 :=
send_broadcasts _ _ _ _.
move =>
Heq.
have Hus:
us2 =
us1 by move:
Heq;
move: (
us1) (
us2) =>
us3 us4;
case.
have Hsb:
sb1 =
sb2 by case:
Heq.
clear Heq.
case Hueq: (
uid' ==
uid);
last first.
move/
eqP:
Hueq =>
Hueq.
have Hus1c:
us2.[?
uid'] =
us1.[?
uid']
by rewrite Hus.
move:
Hus1c.
rewrite 2!
fnd_set.
case:
ifP;
case:
ifP.
-
by move/
eqP.
-
move =>
_ _ Hpost.
suff Hsuff: (
users g) [`
key_state'] =
upost'.
move:
H_step'.
by move/
internal_not_noop.
apply sym_eq in Hpost.
move:
Hpost.
rewrite in_fnd.
by case.
-
by move /
eqP.
-
by move =>
_ /
eqP.
move/
eqP:
Hueq =>
Hueq.
move:
Hsb.
rewrite /
sb1 /
sb2 Hueq.
move:
H_msg.
clear.
set fs := [
fset _ |
_ in _].
set sb1 :=
send_broadcasts _ _ _ _.
set sb2 :=
send_broadcasts _ _ _ _.
move =>
H_msg Hsb.
have Hsb12:
sb1.[?
uid] =
sb2.[?
uid]
by rewrite Hsb.
move:
Hsb12.
rewrite send_broadcast_notin_targets;
first rewrite send_broadcast_notin_targets //.
-
rewrite fnd_set.
case:
ifP;
last by move/
eqP.
move =>
_.
rewrite in_fnd;
case.
move:
H_msg.
set ms := (
msg_in_transit _ _).
move =>
H_msg.
move/
msetP =>
Hms.
move: (
Hms (
r,
m)).
rewrite msetB1E.
case Hrm: ((
r,
m) == (
r,
m));
last by move/
eqP:
Hrm.
rewrite /=.
move:
H_msg.
rewrite -
mset_neq0.
move/
eqP.
case: (
ms (
r,
m)) => //.
move =>
n _.
by lia.
-
rewrite in_fsetE /=.
apply/
negP.
case/
andP =>
Hf.
case/
negP:
Hf.
by rewrite in_fsetE.
-
rewrite 2!
in_fsetE.
by apply/
orP;
left.
-
rewrite in_fsetE /=
in_fsetE.
apply/
negP.
case/
andP =>
Hf.
by case/
negP:
Hf.
Qed.
Internal transitions with equal post-states with the same messages sent must
have sent the messages in the same order - used in transition_label_unique.
Lemma utransition_result_perm_eq uid upre upost l l' :
uid #
upre ~> (
upost,
l) ->
uid #
upre ~> (
upost,
l') ->
perm_eq l l' ->
l =
l'.
Proof.
move =>
Htr Htr'
Hpq.
case Hs: (
size l') => [|
n].
move:
Hs Hpq.
move/
size0nil =>->.
by move/
perm_nilP.
case Hn:
n => [|
n'].
move:
Hs.
rewrite Hn.
destruct l' => //=.
case Hl': (
size l') => //.
move:
Hpq.
move/
size0nil:
Hl' =>->.
by move/
perm_eq_cons1P.
move:
Hs.
rewrite Hn =>
Hl'.
have Heq:
size l =
size l'
by apply perm_size.
move:
Heq.
rewrite Hl' =>
Hl.
have Hll':
size l' >= 2
by rewrite Hl'.
have Hll:
size l >= 2
by rewrite Hl.
clear n n'
Hn Hl'
Hl.
inversion Htr;
inversion Htr';
subst;
simpl in *;
try by [].
move:
Hpq.
set m1 :=
mkMsg Proposal _ _ _ _.
set m2 :=
mkMsg Block _ _ _ _.
set s1 := [::
_;
_].
set s2 := [::
_;
_].
move =>
Hpm.
have Hm1:
m1 \
in s2.
rewrite -(
perm_mem Hpm) /=
inE.
by apply/
orP;
left.
have Hm2:
m2 \
in s2.
rewrite -(
perm_mem Hpm) /=
inE.
apply/
orP;
right.
by rewrite inE.
move:
Hm1 Hm2.
rewrite inE.
move/
orP;
case;
last by rewrite inE.
rewrite /
s2.
move/
eqP =><-.
rewrite inE.
move/
orP;
case;
first by move/
eqP.
rewrite inE.
by move/
eqP =><-.
Qed.
Rule out the possibility that a step that counts as one user sending a
message cannot also count as a send from a different user or different message.
Lemma transition_label_unique :
forall lbl lbl2 g1 g2,
related_by lbl g1 g2 ->
related_by lbl2 g1 g2 ->
match lbl with
|
lbl_deliver _ _ _ _ =>
match lbl2 with
|
lbl_deliver _ _ _ _ =>
lbl2 =
lbl
|
lbl_step_internal _ _=>
lbl2 =
lbl
|
_ =>
True
end
|
lbl_step_internal _ _=>
match lbl2 with
|
lbl_deliver _ _ _ _ =>
lbl2 =
lbl
|
lbl_step_internal _ _=>
lbl2 =
lbl
|
_ =>
True
end
|
_ =>
True
end.
Proof.
move =>
lbl lbl2 g1 g2.
destruct lbl eqn:
H_lbl;
try done.
+
rewrite /=.
move => [
key_ustate [
ustate_post [
H_step H]]].
move:
H => [
Hcorrupt [
key_mailbox [
Hmsg Hg2]]].
rewrite Hg2 {
Hg2} /
related_by.
destruct lbl2;
try done.
*
move => [
key_ustate' [
ustate_post' [
H_step' [
Hcorrupt' [
key_mailbox' [
Hmsg'
Heq]]]]]].
eapply deliver_deliver_lbl_unique in Heq;
eauto.
move:
Heq => [
Hs [
Hr [
Hm Hl]]].
by rewrite Hs Hr Hm Hl.
*
move => [
key_user [
ustate_post' [
Hcorrupt' [
H_step'
Heq]]]].
by eapply deliver_internal_False in Heq;
eauto.
+
rewrite /=.
move => [
key_ustate [
ustate_post [
H_corrupt [
Hstep Hres]]]].
rewrite Hres {
Hres} /
related_by.
destruct lbl2;
try done.
*
move => [
key_ustate' [
upost' [
H_step' [
H_corrupt' [
key_mbox [
Hmsg Heq]]]]]].
apply sym_eq in Heq.
by eapply deliver_internal_False in Heq;
eauto.
*
move => [
key_user [
ustate_post0 [
Hcorrupt0 [
Htr0 Heq]]]].
case Hs: (
s ==
s0);
last first.
move/
eqP:
Hs =>
Hs.
move:
Heq.
rewrite /
step_result /= /
step_result /=.
set us1 :=
_.[
_ <-
_].
set us2 :=
_.[
_ <-
_].
move =>
Heq.
have Hus:
us1 =
us2 by move:
Heq;
move: (
us1) (
us2) =>
us3 us4;
case.
clear Heq.
have Hus1c:
us1.[?
s0] =
us2.[?
s0]
by rewrite Hus.
move:
Hus1c.
rewrite 2!
fnd_set.
case:
ifP => [|
_];
first by move/
eqP =>
Hs';
case:
Hs.
case:
ifP => [
_|];
last by move/
eqP.
rewrite in_fnd;
case =>
Hg.
by apply internal_not_noop in Htr0.
move/
eqP:
Hs =>
Hs.
move:
Heq.
rewrite -
Hs.
rewrite /
step_result /= /
step_result /=.
set us1 :=
_.[
_ <-
_].
set us2 :=
_.[
_ <-
_].
set mh1 := (
_ `+`
seq_mset _)%
mset.
set mh2 := (
_ `+`
seq_mset _)%
mset.
move =>
Heq.
have Hus:
us1 =
us2 by move:
Heq;
move: (
us1) (
us2) =>
us3 us4;
case.
have Hus1c:
us1.[?
s0] =
us2.[?
s0]
by rewrite Hus.
move:
Hus1c.
rewrite 2!
fnd_set -
Hs.
case:
ifP;
last by move/
eqP.
move =>
_;
case =>
Hustate.
have Hmh:
mh1 =
mh2 by case:
Heq.
clear Heq Hcorrupt0.
move:
key_user Htr0.
rewrite -
Hs -
Hustate =>
key_user.
rewrite -(
eq_getf key_ustate) =>
Hstep'.
move:
Hmh.
rewrite /
mh1 /
mh2.
move/
msetD_seq_mset_perm_eq =>
Hprm.
suff Hsuff:
l =
l0 by rewrite Hsuff.
move:
Hstep Hstep'
Hprm.
exact:
utransition_result_perm_eq.
Qed.
User transition lemmas, destructing post state
Message transition on uid results in message sent by uid.
Lemma utransition_msg_sender_good uid u msg result:
uid #
u ;
msg ~>
result ->
forall m,
m \
in result.2 ->
uid =
msg_sender m.
Proof.
clear.
by destruct 1 => /=
m /
in_memP /=;
intuition;
subst m.
Qed.
Internal transition on uid results in message sent by uid.
Lemma utransition_internal_sender_good uid u result:
uid #
u ~>
result ->
forall m,
m \
in result.2 ->
uid =
msg_sender m.
Proof.
clear.
by destruct 1 => /=
m /
in_memP /=;
intuition;
subst m.
Qed.
Definitions of user honesty
User is honest at step (r,p,s).
Definition honest_at_step (
r p s:
nat)
uid (
path :
seq GState) :=
exists n,
match onth path n with
|
None =>
False
|
Some gstate =>
match gstate.(
users).[?
uid]
with
|
None =>
False
|
Some ustate => ~
ustate.(
corrupt)
/\ (
r,
p,
s) = (
step_of_ustate ustate)
end
end.
User is honest in round r and period p.
Definition honest_in_period (
r p:
nat)
uid path :=
exists n,
match @
onth GState path n with
|
None =>
False
|
Some gstate =>
match gstate.(
users).[?
uid]
with
|
None =>
False
|
Some ustate =>
~
ustate.(
corrupt) /\
ustate.(
round) =
r /\
ustate.(
period) =
p
end
end.
User is honest at all points <= step in the path.
Definition honest_during_step (
step:
nat *
nat *
nat)
uid (
path :
seq GState) :=
all (
upred'
uid (
fun u =>
step_leb (
step_of_ustate u)
step ==> ~~
u.(
corrupt)))
path.
Preserving honesty through transitions
Internal user transitions preserves corrupt flag.
Lemma utransition_internal_preserves_corrupt uid pre post sent:
uid #
pre ~> (
post,
sent) ->
pre.(
corrupt) =
post.(
corrupt).
Proof.
set result:=(post,sent). change post with (result.1). clearbody result.
destruct 1;reflexivity.
Qed.
Message transitions preserve corrupt flag.
Lemma utransition_msg_preserves_corrupt uid msg pre post sent:
uid #
pre ;
msg ~> (
post,
sent) ->
pre.(
corrupt) =
post.(
corrupt).
Proof.
set result:=(
post,
sent).
change post with (
result.1).
clearbody result.
destruct 1;
try reflexivity.
+
unfold deliver_nonvote_msg_result;
simpl.
by destruct msg,
msg_ev,
msg_type.
Qed.
The sender of a message is honest in the pre-state.
Lemma user_sent_honest_pre uid msg g1 g2
(
H_send:
user_sent uid msg g1 g2):
(
g1.(
users)[`
user_sent_in_pre H_send]).(
corrupt) =
false.
Proof.
move: (
user_sent_in_pre H_send) =>
H_in.
case:
H_send => [
ms [
H_in_ms [[
d [
inc H_ustep]]|
H_ustep]]].
-
case:
H_ustep => [
H_uid [
ustate_post [
H_ustep [
H_corrupt [
key_mailbox [
H_mail H_g2]]]]]].
apply/
negP.
contradict H_corrupt;
move:
H_corrupt.
by rewrite (
bool_irrelevance H_in H_uid).
-
case:
H_ustep => [
H_uid [
ustate_post [
H_corrupt [
H_ustep H_g2]]]].
apply/
negP;
contradict H_corrupt;
move:
H_corrupt.
by rewrite (
bool_irrelevance H_in H_uid).
Qed.
The sender of message is honest in the post-state.
Lemma user_sent_honest_post uid msg g1 g2
(
H_send:
user_sent uid msg g1 g2):
(
g2.(
users)[`
user_sent_in_post H_send]).(
corrupt) =
false.
Proof.
The sender of a message is honest in the period of the message.
Lemma user_honest_in_from_send ix trace uid msg
(
H_vote:
user_sent_at ix trace uid msg):
let: (
r,
p,
_) :=
msg_step msg in
honest_in_period r p uid trace.
Proof.
Propagating honesty
Propagate honest_during_step backwards.
Lemma honest_during_le s1 s2 uid trace:
step_le s1 s2 ->
honest_during_step s2 uid trace ->
honest_during_step s1 uid trace.
Proof.
Propagate user_honest backwards through transitions.
Lemma honest_backwards_gstep :
forall (
g1 g2 :
GState),
GTransition g1 g2 ->
forall uid,
user_honest uid g2 ->
user_honest uid g1.
Proof.
user_honest at the last state implies user_honest in all states in the path.
Lemma honest_last_all uid g0 p (
H_path :
is_trace g0 p):
user_honest uid (
last g0 p) ->
all (
user_honest uid) (
g0 ::
p).
Proof.
Arguments honest_last_all uid [
g0] [
p].
Honesty is monotone.
Lemma honest_monotone uid g1 g2:
greachable g1 g2 ->
user_honest uid g2 ->
user_honest uid g1.
Proof.
move => [
p H_path H_last]
H_honest2.
subst g2.
pose proof (
honest_last_all uid H_path H_honest2).
by move:
H => /
andP [].
Qed.
Lemmas on manipulation of traces
A non-empty prefix of a trace is a trace.
Lemma is_trace_prefix :
forall trace g0 n,
is_trace g0 trace ->
n > 0 ->
is_trace g0 (
take n trace).
Proof.
clear.
induction trace;[
done|].
destruct n.
done.
simpl.
unfold is_trace.
move => [
H_g0 H_path]
_.
split;[
done|
by apply path_prefix].
Qed.
Dropping elements from a trace still results in a trace.
Lemma is_trace_drop g0 g0'
trace trace' (
H_trace:
is_trace g0 trace)
n:
drop n trace =
g0' ::
trace' ->
is_trace g0' (
g0' ::
trace').
Proof.
move =>
H_drop.
destruct trace.
inversion H_trace.
destruct H_trace as [
H_g0 H_trace];
subst.
eapply path_drop'
with (
n:=
n)
in H_trace.
unfold is_trace.
destruct n.
by rewrite drop0 in H_trace;
rewrite drop0 in H_drop;
inversion H_drop;
subst.
by rewrite H_drop in H_trace.
Qed.
If some predicate is not true initially and then becomes true for some state g_p,
this means there must have been a step from g1 to g2 where it became true.
Lemma path_gsteps_onth
g0 trace (
H_path :
is_trace g0 trace)
ix_p g_p (
H_g_p :
onth trace ix_p =
Some g_p):
forall (
P :
pred GState),
~~
P g0 ->
P g_p ->
exists n g1 g2,
step_in_path_at g1 g2 n trace /\ ~~
P g1 /\
P g2.
Proof.
destruct trace;[
by contradict H_path|].
move:
H_path => [
H_g0 H_path];
subst g.
move=>
P H_NPg0 H_Pg.
have H_path' :=
path_prefix ix_p H_path.
destruct ix_p as [|
ix_p];
first by exfalso;
move:
H_g_p H_NPg0;
case => ->;
rewrite H_Pg.
change (
onth trace ix_p =
Some g_p)
in H_g_p.
pose proof (
path_steps H_path'
H_NPg0).
have H_size_trace :=
onth_size H_g_p.
rewrite -
nth_last nth_take size_takel // (
onth_nth H_g_p)
in H.
specialize (
H H_Pg).
move:
H;
clear -
H_NPg0;
move => [
n H].
exists n.
unfold step_in_path_at.
destruct (
drop n (
g0 ::
take ix_p.+1
trace))
as [|
x l]
eqn:
H_eq;[|
destruct l];[
done..|].
rewrite -[
g0::
trace](
cat_take_drop ix_p.+2).
move/
andP in H;
exists x,
g;
split;[|
assumption].
rewrite drop_cat.
case:
ifP;[
rewrite H_eq;
done|].
move => /
negP /
negP /=;
rewrite ltnS -
ltnNge =>
H_oversize.
by rewrite drop_oversize //
in H_eq.
Qed.
Preservation of users
Global transitions preserve the set of active users.
Lemma gtrans_preserves_users:
forall gs1 gs2,
gs1 ~~>
gs2 ->
domf gs1.(
users) =
domf gs2.(
users).
Proof.
move =>
gs1 gs2.
elim => //.
-
move =>
increment pre Htick.
by rewrite -
tick_users_domf.
-
move =>
pre uid msg_key pending Hpending key_ustate ustate_post sent Hcorrupt Huser /=.
by rewrite mem_fset1U //.
-
move =>
pre uid ustate_key Hcorrupt ustate_post sent Huser /=.
by rewrite mem_fset1U //.
-
move =>
pre uid ustate_key Hcorrupt /=.
by rewrite mem_fset1U //.
Qed.
Lemma gtrans_domf_users:
forall gs1 gs2,
gs1 ~~>
gs2 ->
domf gs1.(
users) `<=`
domf gs2.(
users).
Proof.
Transitions do not decrease step-of-ustate
A one-step user-level transition never decreases round-period-step.
Lemma utr_rps_non_decreasing_msg :
forall uid m us1 us2 ms,
uid #
us1 ;
m ~> (
us2,
ms) ->
ustate_after us1 us2.
Proof.
A one-step user-level transition never decreases round-period-step.
Lemma utr_rps_non_decreasing_internal :
forall uid us1 us2 ms,
uid #
us1 ~> (
us2,
ms) ->
ustate_after us1 us2.
Proof.
move =>
uid us1 us2 ms utrH.
inversion_clear utrH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
-
elim:
H =>
tH [
vH [
vbH [
svH oH]]].
elim:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
rewrite addn1.
by subst.
-
case:
H =>
tH [
vH [
vbH [
svH oH]]].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
rewrite addn1.
by subst.
-
move:
H0 =>
v'
H.
case:
H =>
tH [
vH [
vbH [
svH oH]]].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
rewrite addn1.
by subst.
-
case:
H =>
H [
vH cH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
rewrite addn1.
by subst.
-
case:
H =>
tH [
vH oH].
case:
vH =>
rH [
pH sH].
unfold ustate_after => /=.
do 2! [
right].
do 2! [
split;
auto].
by rewrite sH.
Qed.
A one-step global transition never decreases round-period-step of any user.
Lemma gtr_rps_non_decreasing :
forall g1 g2 uid us1 us2,
g1 ~~>
g2 ->
g1.(
users).[?
uid] =
Some us1 ->
g2.(
users).[?
uid] =
Some us2 ->
ustate_after us1 us2.
Proof.
move =>
g1 g2 uid us1 us2.
elim => //.
-
move =>
increment pre Htick.
move =>
Hu.
case Hd: (
uid \
in domf (
users pre));
last first.
by move/
negP/
negP:
Hd =>
Hd;
move:
Hu;
rewrite not_fnd.
rewrite tick_users_upd //.
case =><-;
move:
Hu.
rewrite in_fnd;
case =>->.
rewrite /
user_advance_timer /= /
ustate_after /=.
by case:
ifP => //=;
right;
right.
-
move =>
pre uid0.
move =>
msg_key [
r m]
Hpend key_ustate ustate_post sent Hloc.
move/
utr_rps_non_decreasing_msg =>
Hst.
case Huid_eq: (
uid ==
uid0).
move/
eqP:
Huid_eq =>->.
rewrite in_fnd //;
case =><-.
rewrite fnd_set /=.
have ->: (
uid0 ==
uid0)
by apply/
eqP.
by case =><-.
move =>
Hus.
rewrite fnd_set /=
Huid_eq Hus.
by case =>->;
right;
right.
-
move =>
pre uid0.
move =>
ustate_key Hloc ustate_post sent.
move/
utr_rps_non_decreasing_internal =>
Hst.
case Huid_eq: (
uid ==
uid0).
move/
eqP:
Huid_eq =>->.
rewrite in_fnd //;
case =><-;
rewrite fnd_set /=.
have ->: (
uid0 ==
uid0)
by apply/
eqP.
by case =><-.
move =>
Hus.
rewrite fnd_set /=
Huid_eq Hus.
by case =>->;
right;
right.
-
move =>
pre Hpre.
rewrite /= -/
users =>
Hus1.
by rewrite Hus1;
case =>->;
right;
right.
-
move =>
pre Hpre.
rewrite /= -/
users =>
Hus1.
by rewrite Hus1;
case =>->;
right;
right.
-
move =>
pre uid0 ustate_key.
move =>
Hcorrupt Hst;
move:
Hst Hcorrupt.
case Huid_eq: (
uid ==
uid0).
move/
eqP:
Huid_eq =>->.
rewrite in_fnd //.
rewrite fnd_set /=.
have ->: (
uid0 ==
uid0)
by apply/
eqP.
rewrite -/(
users pre).
by case =>-> =>
Hcorrupt;
case =><-;
right;
right.
rewrite fnd_set /=
Huid_eq -/(
users pre).
by move =>-> =>
Hcorrupt;
case =>->;
right;
right.
-
move =>
pre uid0.
move =>
ustate_key m Hc Hm.
rewrite /= =>->;
case =>->.
by right;
right.
-
move =>
pre sender.
move =>
sender_key r p s mtype mval Hhave Hcomm Hmatch.
rewrite /= =>->;
case =>->.
by right;
right.
Qed.
Generalization of non-decreasing round-period-step results to paths.
Lemma greachable_rps_non_decreasing :
forall g1 g2 uid us1 us2,
greachable g1 g2 ->
g1.(
users).[?
uid] =
Some us1 ->
g2.(
users).[?
uid] =
Some us2 ->
ustate_after us1 us2.
Proof.
move =>
g1 g2 uid us1 us2.
case =>
gtrace Hpath Hlast.
destruct gtrace.
inversion Hpath.
destruct Hpath as [
H_g0 Hpath];
subst g.
elim:
gtrace g1 g2 uid us1 us2 Hpath Hlast => //=.
move =>
g1 g2 uid us1 us2 Htr ->->;
case =>->.
by right;
right.
move =>
g gtrace IH.
move =>
g1 g2 uid us1 us2.
move/
andP => [
Htrans Hpath]
Hlast Hg1 Hg2.
move/
asboolP:
Htrans =>
Htrans.
case Hg: (
users g).[?
uid] => [
u|].
have IH' :=
IH _ _ _ _ _ Hpath Hlast Hg Hg2.
have Haft :=
gtr_rps_non_decreasing Htrans Hg1 Hg.
move:
Haft IH'.
exact:
ustate_after_transitive.
move/
gtrans_domf_users:
Htrans =>
Hdomf.
case Hd: (
uid \
in domf (
users g1));
last first.
by move/
negP/
negP:
Hd =>
Hd;
move:
Hg1;
rewrite not_fnd.
move/
idP:
Hd =>
Hd.
move:
Hdomf.
move/
fsubsetP =>
Hsub.
move:
Hd;
move/
Hsub =>
Hdom.
move:
Hg.
by rewrite in_fnd.
Qed.
Monotonicity and preservation lemmas
Softvotes are monotone over internal user transitions.
Lemma softvotes_utransition_internal:
forall uid pre post msgs,
uid #
pre ~> (
post,
msgs) ->
forall r p, {
subset pre.(
softvotes) (
r,
p) <=
post.(
softvotes) (
r,
p)}.
Proof.
move => uid pre post msgs step r p.
remember (post,msgs) as result eqn:H_result;
destruct step;case:H_result => [? ?];subst;done.
Qed.
Softvotes are monotone over user message transitions.
Lemma softvotes_utransition_deliver:
forall uid pre post m msgs,
uid #
pre ;
m ~> (
post,
msgs) ->
forall r p,
{
subset pre.(
softvotes) (
r,
p) <=
post.(
softvotes) (
r,
p)}.
Proof.
move =>
uid pre post m msgs step r p.
remember (
post,
msgs)
as result eqn:
H_result.
destruct step;
case:
H_result => [? ?];
subst.
all:
destruct pre;
simpl;
autounfold with utransition_unfold.
all:
repeat match goal with [ |-
context C[
match ?
b with _ =>
_ end]] =>
destruct b end.
all:
move =>
x H_x //.
-
rewrite fsfun_withE.
case Hrp: (
_ ==
_) => //.
by move/
eqP:
Hrp;
case =><--<-;
rewrite mem_undup.
-
rewrite fsfun_withE.
case Hrp: (
_ ==
_) => //.
move/
eqP:
Hrp;
case =><--<-.
by rewrite in_cons mem_undup H_x orbT.
-
rewrite fsfun_withE.
case Hrp: (
_ ==
_) => //.
by move/
eqP:
Hrp;
case =><--<-;
rewrite mem_undup.
-
rewrite fsfun_withE.
case Hrp: (
_ ==
_) => //.
move/
eqP:
Hrp;
case =><--<-.
by rewrite in_cons mem_undup H_x orbT.
Qed.
Softvotes are monotone over global transitions.
Lemma softvotes_gtransition g1 g2 (
H_step:
g1 ~~>
g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
exists u2,
g2.(
users).[?
uid] =
Some u2 /\
forall r p, {
subset u1.(
softvotes) (
r,
p) <=
u2.(
softvotes) (
r,
p)}.
Proof.
clear -
H_step =>
u1 H_u1.
have H_in1: (
uid \
in g1.(
users))
by rewrite -
fndSome H_u1.
have H_in1':
g1.(
users)[`
H_in1] =
u1 by rewrite in_fnd in H_u1;
case:
H_u1.
destruct H_step;
simpl users;
autounfold with gtransition_unfold;
try (
rewrite fnd_set;
case H_eq:(
uid ==
uid0);
[
move/
eqP in H_eq;
subst uid0|]);
try (
eexists;
split;[
eassumption|
done]);
first rewrite updf_update //;
(
eexists;
split;[
eauto|]);
try by intuition auto.
*
move =>
r p v Hv.
rewrite H_in1' /
user_advance_timer.
by match goal with [ |-
context C[
match ?
b with _ =>
_ end]] =>
destruct b end.
*
move:
H1.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
exact:
softvotes_utransition_deliver.
*
move:
H0.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
exact:
softvotes_utransition_internal.
*
by rewrite ?(
eq_getf _ H_in1) /=
H_in1' =>
r p v Hv.
Qed.
Softvotes are monotone between reachable states.
Lemma softvotes_monotone g1 g2 (
H_reach:
greachable g1 g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
forall u2,
g2.(
users).[?
uid] =
Some u2 ->
forall r p, {
subset u1.(
softvotes) (
r,
p) <=
u2.(
softvotes) (
r,
p)}.
Proof.
clear -
H_reach.
move =>
u1 H_u1 u2 H_u2.
destruct H_reach as [
trace H_path H_last].
destruct trace.
inversion H_path.
destruct H_path as [
H_g0 H_path].
subst g.
move:
g1 H_path H_last u1 H_u1.
induction trace.
*
simpl.
by move =>
g1 _ <-
u1;
rewrite H_u2{
H_u2};
case => ->
r p v Hv.
*
cbn [
path last] =>
g1 /
andP [/
asboolP H_step H_path]
H_last u1 H_u1 r p v Hv.
specialize (
IHtrace a H_path H_last).
have [
umid [
H_umid H_sub]] :=
softvotes_gtransition H_step H_u1.
specialize (
H_sub r p).
specialize (
IHtrace umid H_umid r p).
apply IHtrace.
exact:
H_sub.
Qed.
The weight of softvotes is monotone between reachable states.
Lemma soft_weight_monotone g1 g2 (
H_reach:
greachable g1 g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
forall u2,
g2.(
users).[?
uid] =
Some u2 ->
forall v r p,
soft_weight v u1 r p <=
soft_weight v u2 r p.
Proof.
Blocks are monotone over internal user transitions.
Lemma blocks_utransition_internal:
forall uid pre post msgs,
uid #
pre ~> (
post,
msgs) ->
forall r, {
subset pre.(
blocks)
r <=
post.(
blocks)
r}.
Proof.
move => uid pre post msgs step r.
remember (post,msgs) as result eqn:H_result;
destruct step;case:H_result => [? ?];subst;done.
Qed.
Blocks are monotone over user message transition.
Lemma blocks_utransition_deliver:
forall uid pre post m msgs,
uid #
pre ;
m ~> (
post,
msgs) ->
forall r, {
subset pre.(
blocks)
r <=
post.(
blocks)
r}.
Proof.
move =>
uid pre post m msgs step r;
remember (
post,
msgs)
as result eqn:
H_result;
destruct step;
case:
H_result => [? ?];
subst;
destruct pre;
simpl;
autounfold with utransition_unfold;
repeat match goal with [ |-
context C[
match ?
b with _ =>
_ end]] =>
destruct b
|
_ =>
progress simpl end;
try (
by apply subxx_hint);
try (
by move =>
x H_x).
-
rewrite fsfun_withE =>
b Hb.
case Hr: (
r ==
r0) => //;
move/
eqP:
Hr =><-.
by rewrite mem_undup.
-
rewrite fsfun_withE =>
b Hb.
case Hr: (
r ==
r0) => //;
move/
eqP:
Hr =><-.
by rewrite in_cons mem_undup Hb orbT.
Qed.
Blocks are monotone over global transition.
Lemma blocks_gtransition g1 g2 (
H_step:
g1 ~~>
g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
exists u2,
g2.(
users).[?
uid] =
Some u2 /\
forall r, {
subset u1.(
blocks)
r <=
u2.(
blocks)
r}.
Proof.
clear -
H_step =>
u1 H_u1.
have H_in1: (
uid \
in g1.(
users))
by rewrite -
fndSome H_u1.
have H_in1':
g1.(
users)[`
H_in1] =
u1 by rewrite in_fnd in H_u1;
case:
H_u1.
destruct H_step;
simpl users;
autounfold with gtransition_unfold;
try (
rewrite fnd_set;
case H_eq:(
uid ==
uid0);
[
move/
eqP in H_eq;
subst uid0|]);
try (
eexists;
split;[
eassumption|
done]);
first rewrite updf_update //;
(
eexists;
split;[
eauto|]);
try by intuition auto.
*
move =>
r p Hp.
rewrite H_in1' /
user_advance_timer.
by match goal with [ |-
context C[
match ?
b with _ =>
_ end]] =>
destruct b end.
*
move:
H1.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
exact:
blocks_utransition_deliver.
*
move:
H0.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
exact:
blocks_utransition_internal.
*
by rewrite ?(
eq_getf _ H_in1)
H_in1' =>
r p Hp.
Qed.
Blocks are monotone between reachable states.
Lemma blocks_monotone g1 g2 (
H_reach:
greachable g1 g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
forall u2,
g2.(
users).[?
uid] =
Some u2 ->
forall r, {
subset u1.(
blocks)
r <=
u2.(
blocks)
r}.
Proof.
clear -
H_reach.
move =>
u1 H_u1 u2 H_u2.
destruct H_reach as [
trace H_path H_last].
destruct trace.
inversion H_path.
destruct H_path as [
H_g0 H_path].
subst g.
move:
g1 H_path H_last u1 H_u1.
induction trace.
*
simpl.
by move =>
g1 _ <-
u1;
rewrite H_u2{
H_u2};
case => ->
r p Hp.
*
cbn [
path last] =>
g1 /
andP [/
asboolP H_step H_path]
H_last u1 H_u1 r p Hp.
specialize (
IHtrace a H_path H_last).
have [
umid [
H_umid H_sub]] :=
blocks_gtransition H_step H_u1.
specialize (
H_sub r).
specialize (
IHtrace umid H_umid r).
apply IHtrace.
exact:
H_sub.
Qed.
Starting values are preserved over internal user transition.
Lemma stv_utransition_internal:
forall uid pre post msgs,
uid #
pre ~> (
post,
msgs) ->
pre.(
round) =
post.(
round) ->
pre.(
period) =
post.(
period) ->
forall p,
pre.(
stv).[?
p] =
post.(
stv).[?
p].
Proof.
move => uid pre post msgs step.
remember (post,msgs) as result eqn:H_result;
destruct step;case:H_result => [? ?];subst;done.
Qed.
Starting values are preserved by message user transitions.
Lemma stv_utransition_deliver:
forall uid pre post m msgs,
uid #
pre ;
m ~> (
post,
msgs) ->
pre.(
round) =
post.(
round) ->
pre.(
period) =
post.(
period) ->
forall p,
pre.(
stv).[?
p] =
post.(
stv).[?
p].
Proof.
move =>
uid pre post m msgs step H_round H_period.
remember (
post,
msgs)
as result eqn:
H_result;
destruct step;
case:
H_result => [? ?];
subst;
try by (
destruct pre;
simpl;
autounfold with utransition_unfold;
done).
* {
exfalso;
move:
H_period;
clear;
destruct pre;
simpl;
clear.
rewrite -[
period in period =
_]
addn0.
move/
addnI.
done.
}
* {
exfalso;
move:
H_period;
clear;
destruct pre;
simpl;
clear.
rewrite -[
period in period =
_]
addn0.
move/
addnI.
done.
}
* {
exfalso;
unfold certify_ok in H;
decompose record H;
clear H.
move:
H0.
rewrite /
advancing_rp H_round;
clear;
simpl.
by case =>[|[]];[
rewrite ltnNge leq_addr
|
rewrite -[
r in _ =
r]
addn0;
move/
addnI].
}
* {
clear.
unfold deliver_nonvote_msg_result.
by destruct msg,
msg_ev,
msg_type.
}
Qed.
Starting values are preserved by global transitions.
Lemma stv_gtransition g1 g2 (
H_step:
g1 ~~>
g2)
uid:
forall u1,
g1.(
users).[?
uid] =
Some u1 ->
exists u2,
g2.(
users).[?
uid] =
Some u2 /\
(
u1.(
round) =
u2.(
round) ->
u1.(
period) =
u2.(
period) ->
forall p,
u1.(
stv).[?
p] =
u2.(
stv).[?
p]).
Proof.
clear -
H_step =>
u1 H_u1.
have H_in1: (
uid \
in g1.(
users))
by rewrite -
fndSome H_u1.
have H_in1':
g1.(
users)[`
H_in1] =
u1 by rewrite in_fnd in H_u1;
case:
H_u1.
destruct H_step;
simpl users;
autounfold with gtransition_unfold;
try (
rewrite fnd_set;
case H_eq:(
uid ==
uid0);
[
move/
eqP in H_eq;
subst uid0|]);
try (
eexists;
split;[
eassumption|
done]);
first rewrite updf_update //;
(
eexists;
split;[
reflexivity|]).
*
rewrite H_in1' /
user_advance_timer.
by match goal with [ |-
context C[
match ?
b with _ =>
_ end]] =>
destruct b end.
*
move:
H1.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
apply stv_utransition_deliver.
*
move:
H0.
rewrite ?(
eq_getf _ H_in1)
H_in1'.
apply stv_utransition_internal.
*
rewrite ?(
eq_getf _ H_in1)
H_in1'.
done.
Qed.
Starting values are preserved between reachable states.
Lemma stv_forward
g1 g2 (
H_reach :
greachable g1 g2)
uid u1 u2:
g1.(
users).[?
uid] =
Some u1 ->
g2.(
users).[?
uid] =
Some u2 ->
u1.(
round) =
u2.(
round) ->
u1.(
period) =
u2.(
period) ->
forall p,
u1.(
stv).[?
p] =
u2.(
stv).[?
p].
Proof.
clear -
H_reach.
move =>
H_u1 H_u2 H_r H_p.
destruct H_reach as [
trace H_path H_last].
destruct trace.
inversion H_path.
destruct H_path as [
H_g0 H_path].
subst g.
move:
g1 H_path H_last u1 H_u1 H_r H_p.
induction trace.
*
simpl.
by move =>
g1 _ <-
u1;
rewrite H_u2{
H_u2};
case => ->.
*
cbn [
path last] =>
g1 /
andP [/
asboolP H_step H_path]
H_last u1 H_u1 H_r H_p p.
assert (
H_reach :
greachable a g2).
by eapply ex_intro2 with (
a::
trace);
unfold is_trace.
specialize (
IHtrace a H_path H_last).
have [
umid [
H_umid H_sub]] :=
stv_gtransition H_step H_u1.
specialize (
IHtrace umid H_umid).
have H_le_u1_umid :=
gtr_rps_non_decreasing H_step H_u1 H_umid.
have H_le_umid_u2 :=
greachable_rps_non_decreasing H_reach H_umid H_u2.
have H_r':
u1.(
round) =
umid.(
round). {
move:
H_r H_p H_le_u1_umid H_le_umid_u2.
unfold ustate_after.
destruct u1,
umid,
u2;
simpl;
clear;
intros;
subst.
by intuition lia.
}
have H_p':
u1.(
period) =
umid.(
period). {
move:
H_r H_p H_le_u1_umid H_le_umid_u2.
unfold ustate_after.
destruct u1,
umid,
u2;
simpl;
clear;
intros;
subst.
by intuition lia.
}
specialize (
H_sub H_r'
H_p').
rewrite H_sub.
clear H_sub.
apply IHtrace;
congruence.
Qed.
Lemmas for deducing reachability between global states
If the index of g1 is less than the index of g2, and both are in a
trace, this implies g2 is reachable from g1.
Lemma at_greachable
g0 states (
H_path:
is_trace g0 states)
ix1 ix2 (
H_le :
ix1 <=
ix2)
g1 (
H_g1 :
onth states ix1 =
Some g1)
g2 (
H_g2 :
onth states ix2 =
Some g2) :
greachable g1 g2.
Proof.
step_in_path_at from pre to post and pre2 to post2
implies pre2 reachable from post.
Lemma steps_greachable
g0 path (
H_path :
is_trace g0 path)
ix ix2 (
H_lt :
ix <
ix2)
pre post (
H_step :
step_in_path_at pre post ix path)
pre2 post2 (
H_step2 :
step_in_path_at pre2 post2 ix2 path):
greachable post pre2.
Proof.
Lemmas about order of indices in a trace
If the step of a user is smaller in g1 than g2, this
implies that the index of g1 is less than the index of g2.
Lemma order_ix_from_steps g0 trace (
H_path:
is_trace g0 trace):
forall ix1 g1,
onth trace ix1 =
Some g1 ->
forall ix2 g2,
onth trace ix2 =
Some g2 ->
forall uid (
key1:
uid \
in g1.(
users)) (
key2:
uid \
in g2.(
users)),
step_lt (
step_of_ustate (
g1.(
users)[`
key1])) (
step_of_ustate (
g2.(
users)[`
key2])) ->
ix1 <
ix2.
Proof.
step of msg_step for msg1 smaller than msg2 implies index of msg1 less than index of msg2.
Lemma order_sends g0 trace (
H_path:
is_trace g0 trace)
uid
ix1 msg1 (
H_send1:
user_sent_at ix1 trace uid msg1)
ix2 msg2 (
H_send2:
user_sent_at ix2 trace uid msg2):
step_le (
msg_step msg1) (
msg_step msg2) ->
ix1 <=
ix2.
Proof.
Additional lemmas about honesty
Honest at all points less than or equal to step implies honest at g1.
Lemma user_honest_from_during g0 trace (
H_path:
is_trace g0 trace):
forall ix g1,
onth trace ix =
Some g1 ->
forall uid (
H_in :
uid \
in g1.(
users)),
honest_during_step (
step_of_ustate (
g1.(
users)[`
H_in]))
uid trace ->
user_honest uid g1.
Proof.
Honest at all (r,p,s) less than step of u implies honest_during (r,p,s).
Lemma honest_during_from_ustate trace g0 (
H_path :
is_trace g0 trace):
forall ix g,
onth trace ix =
Some g ->
forall uid u,
g.(
users).[?
uid] =
Some u ->
~~
u.(
corrupt) ->
forall r p s,
step_lt (
r,
p,
s) (
step_of_ustate u) ->
honest_during_step (
r,
p,
s)
uid trace.
Proof.
Honest_during (r,p,s), u is at index of n in trace,
and step of u less than or equal to (r,p,s) implies user_honest_at n.
Lemma honest_at_from_during r p s uid trace:
honest_during_step (
r,
p,
s)
uid trace ->
forall g0 (
H_path:
is_trace g0 trace),
forall n g,
onth trace n =
Some g ->
forall u,
g.(
users).[?
uid] =
Some u ->
step_le (
step_of_ustate u) (
r,
p,
s) ->
user_honest_at n trace uid.
Proof.
User honest during step means user sends a message.
Lemma honest_during_from_sent
g0 trace (
H_path:
is_trace g0 trace)
ix uid mty mval r p (
H_send:
user_sent_at ix trace uid (
mkMsg mty mval r p uid)):
honest_during_step (
msg_step (
mkMsg mty mval r p uid))
uid trace.
Proof.
User sends message at r,p and msg_step <= (r,p,s) means user is honest at r,p.
Lemma honest_in_from_during_and_send:
forall r p s uid trace,
honest_during_step (
r,
p,
s)
uid trace ->
forall g0 (
H_path :
is_trace g0 trace),
forall ix g1 g2,
step_in_path_at g1 g2 ix trace ->
forall mt v,
user_sent uid (
mkMsg mt v r p uid)
g1 g2 ->
step_le (
msg_step (
mkMsg mt v r p uid)) (
r,
p,
s) ->
honest_in_period r p uid trace.
Proof.