Home
Coq Proof of Safety for a Two Node Bidirectional Network
In Sukumar Ghosh’s book Distributed Systems: An Algorithmic Approach, an example of a safety property proof is given for a simple network consisting of two nodes communicating bidirectionally via two unidirectional channels. Here I’ll walk through how Ghosh’s informal proof can be translated into a formal proof using Coq.
On page 93, Ghosh poses Example 5.2 as follows:
We can easily define this network in Coq, then prove that the safety property P does indeed hold.
Network Definition
We begin by importing various modules and opening various scopes:
Require Import List String Omega ZArith.
Local Open Scope string_scope.
Local Open Scope list_scope.
Local Open Scope Z_scope.
Then we define a type channel
to be a list of strings, without elaborating any structure that those strings should have:
Definition channel : Type := list string.
Each message will simply be the string literal “Message.”:
Definition msg : string := " ""Message."" ".
Next we need some kind of structure that describes the state of the network, which is comprised of four entities: local variable t
, local variable r
, channel c1
, and channel c2
. There are various ways to do this, but I chose to use Coq’s Record
macro, which generates an Inductive
behind the scenes along with accessor functions corresponding to each entity within:
Record netstate := NetState {t : Z; r : Z; c1 : channel; c2 : channel}.
Now we can define the network’s initial state as described by Ghosh: both local variables are initialized to five, and both channels are empty.
Definition initial := {| t := 5 ; r := 5 ; c1 := nil ; c2 := nil |}.
Network Behavior as an Inductive Relation
With the structure of the network defined, we now need a definition for how the network behaves. We can create one using an inductive relation:
Inductive netEvalR : netstate -> netstate -> Prop :=
First is the initial state, which is trivially related to itself:
| Initial : (netEvalR initial initial)
Next is step 1 as Ghosh defines it: if local variable t
is greater than 0, decrement t
and send a message on channel c1
. Note that we must be more explicit by adding the requirement that the current state st'
must be related to a previous state st
by the evaluation relation: netEvalR st st' ->
. Without this requirement, the relation would hold for states that aren’t reachable from the initial state of the network, which we do not want. In fact, omitting this requirement would make it impossible to write a proof of safety, as our proof will rely on induction and we will need specific constraints on preceding network states that can be used to show that safety properties will carry though to suceeding states.
| Step1 : forall st st',
netEvalR st st' ->
st'.(t) > 0 ->
(netEvalR st' {| t := st'.(t) - 1 ; r := st'.(r) ;
c1 := msg :: st'.(c1) ; c2 := st'.(c2) |})
The other three states proceed similarly, just as Ghosh defines them above:
| Step2 : forall st st',
netEvalR st st' ->
st'.(c2) <> nil ->
(netEvalR st' {| t := st'.(t) + 1 ; r := st'.(r) ;
c1 := st'.(c1) ; c2 := (tl st'.(c2)) |})
| Step3 : forall st st',
netEvalR st st' ->
st'.(c1) <> nil ->
(netEvalR st' {| t := st'.(t) ; r := st'.(r) + 1 ;
c1 := (tl st'.(c1)) ; c2 := st'.(c2) |})
| Step4 : forall st st',
netEvalR st st' ->
st'.(r) > 0 ->
(netEvalR st' {| t := st'.(t) ; r := st'.(r) - 1 ;
c1 := st'.(c1) ; c2 := (msg :: st'.(c2)) |}).
Safety Property, Part 1
Here’s how Ghosh defines the desired safety property for this system:
I’m going to break this in half for ease of proving. The first half consists of the first two terms of Ghosh’s invariant I
, which describe the local variables t
and r
:
Definition safety1 (st' : netstate) : Prop := st'.(t) >= 0 /\ st'.(r) >= 0.
We’ll now prove that this proposition holds for all network states st'
reachable from all previous network states st
:
Lemma safety1_holds: forall st st' : netstate,
(netEvalR st st') ->
safety1 st'.
We start by introducing variables and hypotheses, then splitting the proposition into its respective halves:
Proof.
intros st st' H. split.
The first half asserts that local variable t
is non-negative. We proceed by induction on hypothesis H
, which assumes that st
and st'
are related network states. Induction gives us five subgoals, one for each possible step of the evaluation relation:
- In the initial state, local variable
t
is 5, which is of course >= 0. Useomega
to dispatch this subgoal. - In step 1,
t
is decremented by 1. Sincet
is strictly > 0 in the preceding step of the relation as a condition of step 1 (represented as hypothesisH0
in the context of the proof), we know that after the decrementt
will be >= 0. Useomega
to dispatch. - In step 2,
t
is incremented by 1. By the induction hypothesis,t
is >= 0 prior to the increment, so of courset
is >= 0 after the increment as well. Useomega
to dispatch. - In step 3,
t
is unchanged. By the induction hypothesis we assume thatt
is greater than or equal to 0 in the previous network state, so the same holds whent
is unchanged. Useomega
orassumption
to dispatch. - In step 4,
t
is unchanged as in step 3. Useomega
orassumption
to dispatch.
The same use of induction followed by omega
on all of the subgoals also suffices to prove the second half of the proposition asserting r
is non-negative. Putting it all together, the proof is:
Lemma safety1_holds: forall st st' : netstate,
(netEvalR st st') ->
safety1 st'.
Proof.
intros st st' H. split; induction H; simpl; omega.
Qed.
Safety Property, Part 2
Now we’ll prove the second half of Ghosh’s invariant I
above, defining it in Coq as follows:
Definition safety2 (st' : netstate) : Prop :=
Zlength st'.(c1) + Zlength st'.(c2) + st'.(t) + st'.(r) = 10.
In other words, we want to prove that no matter what state the network is in, the sum of local variable t
, local variable r
and the lengths of channels c1
and c2
is always 10.
We’ll now prove that this proposition holds for all network states st'
reachable from all previous network states st
:
Lemma safety2_holds : forall st st' : netstate,
(netEvalR st st') ->
safety2 st'.
After some basic setup, we proceed by induction on hypothesis H
:
Proof.
intros st st' H. unfold safety2. induction H; simpl.
As above, induction generates five subgoals, one for each step of the evaluation relation:
- In the initial state of the network, local variables
t
andr
are 5, while the lengths of the channels are each 0, thussafety1
trivially holds (usereflexivity
). - In step 1 of the relation, a single message is appended to channel
c1
concommitant with a decrement to the value oft
. First werewrite Zlength_cons
to “extract” the implied addition, thenomega
will cancel it out with the decrement, giving us a goal that matches our induction hypothesis. - In step 2 of the relation, a message is removed from channel
c2
concommitant with an increment to the value oft
. Although it’s obvious to us that these cancel out, we have to do some work to show that the length ofc2
will always decrease by one. To do so,destruct (c2 st')
to get two subgoals:- The first subgoal represents the case of an empty
c2
. But we know this is impossible because a condition of step 2 requires thatc2
not be nil. Dispatch withcontradiction
. - The second subgoals represents a
c2
containing at least one message. The tail of a list(s :: c)
is simply c, which we get withsimpl
. Then werewrite Zlength_cons in IHnetEvalR
to extract the implied addition, which matches up with the increment oft
in the goal, andomega
will do the rest.
- The first subgoal represents the case of an empty
- The flow for step 3 of the relation is the same as that for step 3, only with
(c1 st')
destructed instead of(c2 st')
. - The flow for step 4 of the relation is exactly the same as that for step 1.
The resultant proof that safety2
holds is therefore:
Lemma safety2_holds : forall st st' : netstate,
(netEvalR st st') ->
safety2 st'.
Proof.
intros st st' H. unfold safety2. induction H; simpl;
try reflexivity; (* Initial state *)
try (rewrite Zlength_cons; omega); (* Steps 1 and 4 *)
[destruct (c2 st') | destruct (c1 st')]; (* Steps 2 and 3 *)
try contradiction;
(simpl; rewrite Zlength_cons in IHnetEvalR; omega).
Qed.
Concluding Theorem
The preceding lemmas encapsulate the work required to formally prove that Ghosh’s safety invariant holds for all reachable network states. We can now combine them into a succint concluding theorem:
Theorem safety_holds : forall st st' : netstate,
(netEvalR st st') -> safety1 st' /\ safety2 st'.
Proof.
intros st st' H. split;
[apply (safety1_holds st st') | apply (safety2_holds st st')];
assumption.
Qed.