Oct 13, 2016 - programming languages Haskell, OCaml and Scheme, we can extract our verified checkers to one of these lan
Verification of Certifying Distributed Programs Case Study: Leader Election Samira Akili
Kim V¨ollinger
Humboldt University of Berlin
[email protected]
Humboldt University of Berlin
[email protected]
Abstract
1.
A certifying program produces in addition to each output a witness that certifies the output’s correctness. An accompanying checker program checks whether the computed witness is correct. Such a checker is usually simpler than the original program, and its verification is often feasible while the verification of the original program is too costly. By verifying the checker and by giving a machine-checked proof that the witness certifies the output’s correctness, we get formal instance correctness, i.e. proofs that particular outputs are correct. This verification method to obtain formal instance correctness was demonstrated on sequential programs. In contrast, we are concerned with the correctness of distributed programs, and certifying distributed programs behave differently; for instance, the output and its witness are distributed. In this paper, we present a verification method to obtain formal instance correctness for one class of certifying distributed programs. Moreover, we demonstrate our method on the leader election problem using the theorem prover Coq.
A major problem in software engineering is assuring the quality of software. Well-known methods are testing and formal verification. While testing does not cover all inputs, formal verification is often too costly. We consider certifying programs – a formal method that is, on the one hand, more rigorous than testing, and on the other hand, less costly than formal verification. A certifying program verifies the correctness of its output at runtime. The idea is to adapt the underlying algorithm of a program at design time to protect a user of this program not only against a faulty implementation but also against a faulty algorithm and a faulty execution. To this end, a certifying program produces in addition to each output a witness that certifies the output’s correctness. Since the witness is computed by the untrusted program itself, a simple checker program checks whether the computed witness is correct. Certifying variants of numerous sequential programs have been developed (McConnell et al. 2011). Moreover, Rizkallah developed a method for the verification of certifying sequential programs to achieve formal instance correctness – a proof that a particular instance (input-output pair) is correct. To this end, she combines certifying programs with program verification and theorem proving (Rizkallah 2015); she does program verification for the checker and gives a machinechecked proof that the witness indeed certifies the output’s correctness. Rizkallah demonstrated her method on sequential programs from the LEDA library (Rizkallah 2015). In contrast, we are concerned with the correctness of distributed programs. Certifying distributed programs behave differently to certifying sequential programs; for instance, the witness is distributed over a system and checked by many checkers. Hence, we have to adapt Rizkallahs verification method. In this paper, we present a verification method to achieve formal instance correctness for one class of certifying distributed programs. Moreover, we demonstrate our method on the leader election problem in networks (i.e. the components of a network have to elect a leader among them) using the theorem prover Coq for program verification and theorem proving. The whole formalization is available on GitHub.
Categories and Subject Descriptors D.1.3 [PROGRAMMING TECHNIQUES]: Concurrent Programming—Distributed programming; D.2.4 [SOFTWARE ENGINEERING]: Software/Program Verification—Correctness proofs; F.3.1 [LOGICS AND MEANINGS OF PROGRAMS ]: Specifying and Verifying and Reasoning about Programs —Pre- and postconditions Keywords certifying program, distributed program, verification, formal instance correctness, interactive theorem prover, Coq, leader election
Introduction
[Copyright notice will appear here once ’preprint’ option is removed.]
Verification of Certifying Distributed Programs
1
2016/10/13
In more detail, our contributions are:
input x
• The definition of a class of certifying distributed pro-
grams (Section 3.3). This class is particularly interesting since the witness is computed and checked distributively. Hence, the certification itself is distributed.
certifying program
output y witness w
checker: accept γ(x,y,w)? reject
Figure 1. A certifying program accompanied by its checker.
• A verification method to obtain formal instance correct-
ness for the defined class of certifying distributed programs (Section 3.4).
We recap certifying sequential programs and Rizkallah’s verification method to obtain formal instance correctness for certifying sequential programs in Section 2. In Section 3, we discuss how to apply the concept of certifying sequential programs to distributed programs. We give one approach to apply the concept resulting in a class of certifying distributed algorithms. For this class, we introduce a verification method to obtain formal instance correctness. In Section 4, we demonstrate our verification method on the leader election problem in networks. We present related work in Section 5, and we draw our conclusions and discuss future work in Section 6.
the postcondition ψ. Thus, the input-output pair satisfies the specification. For a triple (x, y, w) ∈ γ, we call w a witness for ψ(x, y). A (correct) certifying program produces in addition to each output a witness such that the witness property is satisfied. However, the idea is that a user of a certifying program does not have to trust the program. That is why an accompanying checker program decides the witness predicate. If the certifying program does not work correctly (i.e. it produces an incorrect output or witness), the witness predicate does not hold and thereby the checker rejects. Hence, a certifying program protects its user not only against a faulty implementation but also against a faulty algorithm and a faulty execution. Figure 1 sums up the concept of a certifying program. However, the user has to trust the checker and to believe/understand that the witness property holds. As a consequence, the checker should be so simple that its correctness can be established easily. Moreover, the witness property should have a proof that is either simple enough for the user to understand or carried out in a way that the user believes its correctness.
2.
2.1.1
• A certifying variant of solving the leader election prob-
lem in networks (Section 4.1). • A case study where we apply our verification method
to certifying leader election in order to obtain formal instance correctness (Section 4.2). 1.1
Structure of this Paper
Preliminaries
In this section, we recap certifying sequential programs, and Rizkallah’s verification method to obtain formal instance correctness for certifying sequential programs. 2.1
As an example, we consider the problem of deciding if a graph is bipartite, i.e. if its vertices can be divided in two partitions so that each edge has a vertex in each of both partitions. We assume a program that decides if a graph is bipartite. For a particular graph G (shown in Figure 2), it decides that G is not bipartite. A certifying variant of this program additionally produces an odd cycle in G as a witness. The witness predicate holds if the witness is a cycle of odd length contained in G, and it can easily be decided by a checker program. The witness predicate has the witness property: an odd cycle contained in G proves that G is not bipartite since an odd cycle is not bipartite itself. For another graph H (shown in Figure 2), the certifying program decides that H is bipartite. In this case, the witness is a bipartition. The witness predicate holds if the witness is indeed a bipartition of H, and it can easily be decided by a checker program. The witness property follows from the definition of a bipartite graph.
Certifying Sequential Programs
The idea of a certifying program is to adapt the underlying algorithm of a program such that the algorithm verifies the correctness of its output at runtime. Hence, a certifying program is an implementation of a certifying algorithm. Usually, the notion of a certifying algorithm is used. Since we want to verify actual outputs by the certifying program, we predominantly use the notion of a certifying program in this paper. We assume a program that takes an input x from a set X and produces an output y from a set Y . The specification of the program is given by two predicates, a precondition φ ⊆ X and a postcondition ψ ⊆ X × Y . Let W be a set of potential witnesses. A witness predicate for the specification is a predicate γ ⊆ X × Y × W with the witness property: ∀x, y, w : φ(x) ∧ γ(x, y, w) −→ ψ(x, y)
2.1.2
Existence of a Witness
There is always a certifying variant of a program, for instance, with a witness that is the computation itself. In general, this is not a good witness, since the witness property is hard to prove; the computation only proves that the output is
(1)
If an instance x, y, w satisfies the precondition φ and the witness predicate γ, then the input-output pair (x, y) satisfies Verification of Certifying Distributed Programs
Example: Deciding Bipartiteness
2
2016/10/13
G:
distributed algorithms to solve problems associated with distributing a computation over a distributed system such as coordination, communication or synchronization of the components. To give some examples, there are distributed algorithms to elect a leader, find a consensus or identify a substructure of the system such as a tree (Raynal 2013). An implementation of a distributed algorithm is a distributed program.
H:
Figure 2. The non-bipartite graph G with the witness being an odd cycle marked in red, and the bipartite graph H with the witness being a bipartition indicated by two colors.
3.2
The distributed setting has all the challenges of the sequential setting. Additionally, it has its own specific challenges (Peleg 2000, Section 1.3). That is why we consider it to be worthwhile to develop certifying variants of distributed programs. While non-termination is considered a fault in sequential programs, some distributed programs should run continuously, e.g. communication protocols. Certification of nonterminating programs poses questions such as when should a non-terminating program compute a witness; and when should a checker of a non-terminating program check a witness. For terminating distributed programs, each component holds its output after termination. Hence, the output of the distributed program is distributed over the distributed system. This output’s distribution leads to questions such as should there be a witness for each component or one witness for the whole system; or should there be one checker or several. Hence, there is not only one way of applying the concept of a certifying sequential program to distributed programs. In previous work, we gave a certifying distributed program for the shortest path problem in networks as an example (V¨ollinger and Reisig 2015).
correct for the input if the program is correct. In fact, proving the witness property becomes program verification then. The challenge is to find “good” witnesses. 2.2
Verification of Certifying Sequential Programs
The user of a certifying program has to believe/understand that the witness property holds, and to trust its accompanying checker. Rizkallah suggests to use a theorem prover to give a machine-checked proof for the witness property, and to verify the checker, i.e. verifying that the checker checks the witness predicate. A checker is usually much simpler than the original program, and its verification is often feasible while the verification of the original program is too costly. By this combination of certifying algorithms with theorem proving and program verification, we achieve formal instance correctness for instances for which the checker accepts. Rizkallah demonstrated her method on programs from the LEDA library – a library for combinatorial and geometric computing. Some of the LEDA-programs are already certifying and they come with checkers written in C++. She used the theorem prover Isabelle to give a machine-checked proof of the witness property and the verifier VCC for the checker verification (Rizkallah 2015).
3.
3.3
Distributed Programs
A distributed system consists of computing components (e.g. processors, processes, computers) that can communicate with each other by shared memory or message-passing channels. A distributed algorithm describes for each component an algorithm such that all components together solve one problem using communication. For instance, we know Verification of Certifying Distributed Programs
A Class of Certifying Distributed Programs
For defining our class C of certifiying distributed programs, we focus on networks (i.e. distributed systems with messagepassing channels) that are static (i.e. components and channels do not leave the system) and asynchronous (i.e. no global clock exists), and on distributed programs that terminate. After termination, each component holds its local output and the global output of the distributed program is the collectivity of the local outputs. Our approach is to make such a distributed algorithm certifying by making it compute many local witnesses that together prove the global output’s correctness. The local witnesses are computed and checked distributively at runtime. Hence, we present certifying distributed programs where the certification is itself distributed. We can represent a network by a graph G that is a finite directed graph with a vertex set V = {1, 2, ..., n} and an edge set E that is symmetric. Each vertex presents a component and two directed edges (i, j) and (j, i) present a bidirectional channel between the components i and j. We call such a graph G a network graph.
Verification Method for a Class of Certifying Distributed Programs
In this section, we give a verification method to obtain formal instance correctness for one class of certifying distributed programs. First, we explain what we consider to be a distributed program and continue with the challenges of applying the concept of certifying sequential programs to distributed programs. Subsequently, we define a class of certifying distributed programs. For this class, we give a verification method to obtain formal instance correctness. 3.1
Certifying Distributed Programs
3
2016/10/13
Let G be a network graph. Let X, Y and W be sets containing potential local inputs, local outputs and local witnesses, respectively. A certifying distributed program p of class C computes for a global input x ∈ X n a global output y ∈ Y n , and in addition, a global witness w ∈ W n . Hence, each component i ∈ V computes for a local input xi ∈ X a local output yi ∈ Y and additionally a local witness wi ∈ W . The specification of p is given by two predicates, a (global) precondition φ ⊆ X n and a (global) postcondition ψ ⊆ X n × Y n × W n . A global witness predicate for the specification is a predicate Γ ⊆ X n ×Y n ×W n with the (global) witness property: ∀x, y, w : φ(x) ∧ Γ(x, y, w) −→ ψ(x, y)
If we solve these proof obligations, we obtain formal instance correctness for each instance (x, y) for which all local checkers accept: Theorem 1. Let G = (V, E) be a network graph with |V | = n. Let X, Y and W be sets. Let φ ⊆ X n be a precondition and ψ ⊆ X n × Y n × W n a postcondition. Let Γ ⊆ X n × Y n × W n be a global witness predicate for φ and ψ, and let γ ⊆ X × Y × W be a local witness predicate for φ, Γ, and G. Let (x, y, w) ∈ X n × Y n × W n . Let c be a local checker deciding γ. Assuming x ∈ φ, if ci accepts on (xi , yi , wi ) for all i ∈ V , then (x, y) ∈ ψ. Proof. (xi , yi , wi ) ∈ γ for all i ∈ V since c decides γ. Since γ is a local witness predicate and x ∈ φ, it follows from the composition property that (x, y, w) ∈ Γ. From Γ being a global witness predicate and x ∈ φ, it follows by the witness property that (x, y) ∈ ψ.
(2)
For a triple (x, y, w) ∈ Γ, we call w a global witness. A local witness predicate is a predicate γ ⊆ X × Y × W with the composition property:
Notice that the program p itself is not mentioned in the theorem. The machine-checked proofs and the verified local checkers can indeed be combined with any program whose input, output and witness have an according type. However, there may be no instance for which all local checkers accept while, in contrast, for a correct program the checkers accept for every instance. Moreover, the reader may wonder why we do not prove: if (x, y) ∈ ψ, then there exists a global witness w such that (x, y, w) ∈ Γ. This property is desirable from the point of view of someone who makes a program certifying. In fact, with such a proof, we would reason about the correctness of p. However, we do not want to establish the correctness of p but to achieve formal instance correctness for p. Verifying formal instance correctness is another problem than verifying programs.
∀x, y, w : φ(x) ∧ (∀i ∈ V γ(xi , yi , wi )) −→ Γ(x, y, w) (3) For a triple (xi , yi , wi ) ∈ γ(xi , yi , wi ), we call wi a local witness of component i. For the class C, the global witness predicate Γ of p is distributively checked in the way that each component i has a local checker that decides whether (xi , yi , wi ) ∈ γ. Since the global witness is computed and checked distributively, class C contains certifying distributed programs that are particularly distributed. 3.3.1
Existence of a Witness for Class C
Analogously to certifying sequential programs, there is always a certifying variant of a terminating distributed program such that the program is in the class C. For instance, with the local witness of a component being its computation and complete history of its communication. Again, proving the witness property becomes program verification. Hence, as for certifying sequential programs, the challenge is to find “good” witnesses. 3.4
3.5
We use the interactive theorem prover Coq (INRIA) for theorem proving in order to solve the proof obligations witness property and composition property, and for program verification in order to solve the proof obligation correctness of the local checkers. Coq provides its user with interactive proof methods by implementing three languages. The first one is Gallina – a program specification and higher-level language based on the calculus of inductive constructions that itself combines a higher-order logic and a richly-typed functional programming language. Gallina implements dependend types and only allows structural recursion. Hence, every program halts and Gallina is not turing-complete. The LTac language lets the user define its own proof methods. However, Coq already has some proof automations with its implemented decision and semi-decision algorithms, and its standard library. The Vernacular language has commands to define functions, predicates or theorems, to change to proof mode, to
Verification Method for Class C
Let p be a certifying distributed program of class C. In order to obtain formal instance correctness for p, we have to solve the following proof obligations: • Witness Property: We have to prove the implication (2). • Composition Property: We have to prove the implica-
tion (3). • Correctness of the Local Checkers: We have to prove
that the local checker ci of each component i checks the local witness predicate γ(xi , yi , wi ), assuming the precondition φ(x) holds, i.e. 1. If φ(x), then ci halts. 2. If φ(x) and (xi , yi , wi ) ∈ γ, then ci accepts. 3. If φ(x) and (xi , yi , wi ) ∈ / γ, then ci rejects. Verification of Certifying Distributed Programs
Using the Interactive Theorem Prover Coq
4
2016/10/13
2
1
machine-check a developed proof, or to extract a program written in Gallina to a turing-complete language like Haskell or Objective Caml. The architecture of Coq is so that there is only a small kernel of typing rules building the trust base of Coq.
4
3
5
3.5.1
Graph Theory in Coq.
We use the graph library Graph Basics (Duprat 2001) that defines basic concepts of graph theory such as undirected graphs, trees or connectivity. The purpose of this library is to express mathematical and computational aspects of graph theory in the same formalism. To the best of our knowledge, there is no other graph library for Coq.
4.
local witness predicate for 6: 6 - leader6 = 6 - distance6 = distance2 +1 - distance6 > 0 - parent6 = 2 - leader2 = leader3 = leader5= leader 6
Figure 3. A network graph with six components. A spanning tree, the global witness, is highlighted in red. On the right side, for the components 2 and 6, the properties required to satisfy local witness predicates are listed.
Case Study: Leader Election • distanceparenti (the distance that i’s parent has from its
As a case study, we consider the leader election problem: all components of a network have to elect exactly one of them as a leader. Usually, a leader is elected for coordination purposes. There are various distributed algorithms that solve leader election. For instance, Lynch gives an asynchronous leader election algorithm for a network of arbitrary topology and components that have unique identifiers (Lynch 1996). In this section, we give a certification for leader election algorithms such that they belong to class C (see Section 3.3). As a proof-of-concept, we demonstrate our verification method to obtain formal instance correctness (see Section 3.4) on certifying leader election using Coq. 4.1
local witness predicate for 2: - leader2 = 2 - distance2 = 0 - parent2 = 2 - leader1 = leader4 = leader6 = leader2
elected leader) and • leaderj for all neighbors j of i (the elected leaders of i’s
neighbors). The global witness w is a vector with the local witnesses as entries; by the global witness predicate, we can tell that the global witness is a spanning tree in G rooted at the elected leader. The global witness predicate Γ(x, y, w) holds • if there exists l ∈ V (“the elected leader and root of the
spanning tree”) with leaderl = l such that distancel = 0, parentl = l and
Certifying Leader Election
leaderj = l for all neighbors j of l,
The problem of leader election is solved if all components of a network agree on exactly one of them as a leader. Thus, in order to verify the global output’s correctness, we have to certify that the leader exists and that all components agree on the leader. We use a spanning tree with the leader as its root to certify the leader’s existence. By checking whether neighbors agree on their elected leader, we certify agreement of the components. On a high level, the specification of leader election is as follows. Let G = (V, E) be a network graph with |V | = n. The local input xi of a component i is i’s neighborhood. i.e. i’s neighboring components and i’s channels. The local output yi of a component i is leaderi (i’s elected leader). The global input x is a vector with the local inputs as entries, and analogously, the global output y is a vector with the local outputs as entries. The precondition φ(x) states that each local input xi is the neighborhood of i in accordance to G. The postcondition ψ(x, y) states that there exists l ∈ V with leaderi = l for all i ∈ V . On a high level, the certification of leader election is as follows. The local witness wi of a component i consists of
and • if for all i ∈ V with leaderi 6= i it holds that
distancei > 0, parenti is a neighbor of i, distancei = distanceparenti + 1 and leaderj = leaderi for all neighbors j of i The witness property states that if all components agree with their neighbors on the leader then all components agree on exactly one leader, and if this elected leader is the root of a spanning tree then the leader exists in the network. The local witness predicate γ is a disjunction with one clause stating all the properties required by the global witness predicate for the elected leader and another clause stating the properties for all the other components. The local checker ci of a component i decides whether (xi , yi , wi ) ∈ γ. The composition property states that if the local witness predicate holds for all components then the global witness predicate holds. As an example, Figure 3 shows a specific network graph and a spanning tree as the global witness in this network.
• distancei (i’s distance from its elected leader), • parenti (i’s parent in the spanning tree), Verification of Certifying Distributed Programs
5
2016/10/13
Moreover, for two components the properties required to satisfy their local witness predicates are listed. For the purpose of this paper, it is not important how a component computes its local witness. However, there are distributed algorithms to compute a spanning tree and Lynch even gives a leader election algorithm that uses a spanning tree (Lynch 1996). 4.2
predicate: gamma root is the local witness predicate of the elected leader and gamma i is the local witness predicate of all other components. We can associate each component i with the values leaderi , parenti and distancei . From a global perspective of the network, we can canonically define the functions leader, parent and distance that each maps a component i to its corresponding value. We use these functions in our formalization in order to reason about their properties. We define the signatures of these functions in Coq as follows:
Verification in Coq
In order to obtain formal instance correctness for certifying leader election, we solve the proof obligations composition property, witness property and correctness of the local checkers. We start by presenting our formalization of the network graph (Section 4.2.1). We continue with the prove of the composition property (Section 4.2.2) and the witness property (Section 4.2.3). We explain the implementation and verification of the local checkers (Section 4.2.4). Finally, we sum up our results (Section 4.2.5). The structure of this section follows our formalization in Coq. We use Coq Sections to structure our work in the way that one Section relies on the results of the previous Sections. 4.2.1
Variable leader : Component -> Component. Variable parent : Component -> Component. Variable distance : Component -> nat.
To instantiate the functions, we added additional clauses to the local witness predicates which we marked with (*x*). For instance, we require the mapping between the component i and its function value parent i by stating the equation parent i = parent i. In Coq, we formalized the local witness predicates as follows: Definition gamma_i (i:Component)(leader_i:Component)(distance_i:nat) (parent_i:Component) (leader_parent_i:Component) (distance_parent_i:nat)(leader_neighbors:C_list) :Prop := leader i i /\ parent i = parent_i /\ (*x*) a (A_ends i parent_i) /\ a (A_ends parent_i i) /\ distance i = distance_i /\ (*x*) distance_parent_i = distance parent_i /\ distance_i = distance_parent_i + 1 /\ leader i = leader_parent_i /\ (*x*) leader_parent_i = leader (parent i) /\ (forall (k:Component), In k leader_neighbors -> k = leader i) /\ (forall (c:Component), In c (neighbors g i) -> In (leader c) leader_neighbors) .
Network Formalization
Our network graph formalization relies on the undirected, connected graph Connected provided by the GraphBasics library. The network graph is built upon a set of Components and a set of Arcs (undirected edges). A vertex of the graph is a component with a unique identifier. In Coq, our network formalization is as follows: Definition Component := Vertex. Definition C_set := U_set Component. Variable a: A_set. Variable v: C_set. Variable g: Connected v a.
Definition gamma_root (i:Component)(leader_i:Component)(distance_i:nat) (parent_i:Component)(leader_neighbors:C_list) :Prop := leader i = leader_i /\ (*x*) leader_i = i /\ parent i = parent_i /\ (*x*) parent_i = i /\ distance i = distance_i /\ (*x*) distance_i = 0 /\ (forall (k:Component), In k leader_neighbors -> k = leader i) /\ (forall (c:Component) , In c (neighbors g i) -> In (leader c) leader_neighbors).
Definition Component_index (c : Component):nat := match c with | index x => x end.
We construct the local input xi of a component i in such a way that x satisfies the precondition. Hence, for the following formalization of the composition property and the witness property, we assume that the precondition holds. 4.2.2
Composition Property
In this section, we give an overview of the formalization of the local witness predicates as well as of the proof of the composition property in Coq. The composition property states that if the precondition holds and the local witness predicate holds for each component, then the global witness predicate is satisfied. The local witness predicate is a disjunction in which one clause applies for the elected leader and the other clause for all other components (see Section 4.1). Moreover, the elected leader is the root of a spanning tree. In Coq, we formalized each clause of the local witness predicate as a single Verification of Certifying Distributed Programs
The local witness predicate only holds for all components in the way that there is one component (root) satisfying the witness predicate gamma root and all other components satisfying the witness predicate gamma i. Suppose there is more than one component fulfilling the gamma root predicate. Then there is more than one component that has elected itself as a leader. Since the graph is connected, there is a path between every component. Hence, there must be a path between two components that have a different leader. If we follow the path, there must be a pair 6
2016/10/13
of components that contradicts the property that all neighbors agree on their leader. Suppose there is no root and all components fulfill the witness predicate gamma i. Then every component has a parent that is not itself. As there is always an edge between a component and its parent, there are as much edges as components in the subgraph that is induced by the parent function. Hence, this subgraph has a cycle. Within a cycle the distance property is violated. As a consequence, we fix one component of the set of components as root.
Axiom parent_root : parent root = root. Axiom parent_exists : forall (x :Component) (prop: v x), v (parent x). Axiom parent_arc: forall (c k:Component)(prop: v c)(prop2: v k), parent c = k -> a (A_ends c k) /\ a (A_ends k c). Axiom distance_root : distance root = 0 . Axiom distance_prop : forall (x:Component)(prop :v x), x root -> distance x = distance (parent x) + 1.
We formalize the witness property in Coq as follows: Theorem global_witness_property: exists (l : Component), v l -> forall (x:Component)(prop1: v x), leader x = l.
Variable root : Component. Variable rootprop : v root.
In order to prove the witness property, we prove some additional properties. We define an inductive type Connection: A Connection is an undirected path between two vertices, consisting of edges that are induced by the parent function. We formalize Connection in Coq as follows:
We formalize the composition property in Coq as follows: Theorem composition_property: forall (leader_root parent_root: Component) ( distance_root : nat)(leader_neighbors_root : C_list), (gamma_root root leader_root distance_root parent_root leader_neighbors_root) ->
Inductive Connection: Vertex -> Vertex -> A_list -> nat -> Set := | self : forall x:Vertex, v x -> Connection x x A_nil 0 | step : forall (x y z : Vertex)(el : A_list)(n:nat), Connection y z el n -> v x -> parent x = y -> a (A_ends x (parent x)) -> a (A_ends (parent x) x) -> Connection x z ((A_ends (parent x) x ) :: el) (S n).
forall (x:Component)(prop1: v x)(prop2: x root) (leader_i parent_i leader_parent_i :Component ) (distance_i distance_parent_i: nat)(leader_neighbors_i : C_list), (gamma_i x leader_i distance_i parent_i leader_parent_i distance_parent_i leader_neighbors_i ) -> distance root = 0 /\ distance x = distance (parent x) + 1 /\ leader root = root /\ leader x x /\ leader x = leader (parent x) /\ parent root = root /\ v (parent x) /\ a (A_ends x (parent x)) /\ a (A_ends (parent x) x) /\ (forall (c:Component), In c (neighbors g x) -> leader c = leader x) /\ (forall (c:Component), In c (neighbors g root) -> leader c = leader root).
A Connection is constructed from a parent to its child, and it has a length. We need the direction as well as the length for our proof of the witness property. Moreover, we define the function parent iteration which takes a component c and a natural number n as parameters and recursively applies the parent function ntimes on c. Fixpoint parent_iteration (n: nat) (c: Component) : Component:= match n with | 0 => c |(S n) => parent (parent_iteration n c) end.
The proof of the composition property in Coq is straightforward and only uses syntactic rewriting. 4.2.3
Witness Property
We call a component that can be obtained by the application of the parent iteration function on c, c’s ancestor. The proof of the witness property rests upon three central lemmata. The first one is:
In this section, we give an overview of the Coq proof of the witness property. The witness property states that if the precondition and the global witness predicate holds, then leader election is solved; the spanning tree witnesses the existence of a leader and by the agrrement between neighbors, there can only be one leader. At the beginning of the Section Witness Property, we state that the global predicate holds:
Lemma path_to_root: forall (n:nat) (x:Component) (prop1 : v x), distance x = n -> {el : A_list & Connection x root el n }.
This lemma states that there is a Connection between every component and the root. We conduct a proof by induction on the distance of a component which is also the length of the Connection. The base case follows from the assumptions. For the induction step, we assume a Connection co between root and the parent of a component x with length n. By definition of Connection, co can be extended by the edge between x0 s parent and x to a Connection
Axiom leader_root : leader root = root. Axiom leader_prop : forall (x:Component)(prop :v x), x root -> leader x x Axiom leader_prop_2 : forall (x:Component)(prop :v x), leader x = leader (parent x). Axiom leader_neighbors_prop : forall (x:Component)(prop :v x)(c:Component), In c (neighbors g x) -> leader c = leader x.
Verification of Certifying Distributed Programs
7
2016/10/13
co0 . By definition, the length of co0 is n + 1 which equals the distance of x. The second central lemma is:
The local checker also needs the local input xi of its component i. The local input of a component i is the neighborhood of i in accordance to the network Graph G (see Section 4.1). We formalize the local input of a checker as the Record local input. Furthermore, we define the function construct local input that takes a component i and a graph g as input and generates the local input of i in accordance to g. We also give the function construct checker that takes a graph g as input and returns a list of local checkers, one for each component of g, whereas each local checker ci is initialized with the local input xi of its component i.
Lemma parent_is_leader : forall (n:nat)(x y: Component)(prop1: v x) (prop2:v y), leader x = leader (parent_iteration n x).
This lemma states that a component x agrees with all its ancestors on the leader. We conduct a proof by induction on the argument n of the parent iteration function. The proof is similar to the proof presented above. The third central lemma is: Lemma parent_transitive_is_root : forall (n:nat) (x: Component)(prop1:v x), n = distance x -> {el : A_list & Connection x root el (distance x) } -> root = (parent_iteration (distance x) x) .
Record local_input: Set := mk_local_input { i : Component; neighbors : C_list; }.
This lemma states that if there is a Connection between a component and root, then root is ancestor of the component. By induction, we can establish that if a Connection exists from component x to component y with length n, then x is the result of applying the parent iteration function n-times on y. Since we already proved that there is a Connection between every component and root, we conclude that root is an ancestor of each component. We prove the witness property by case analysis. For the first case, we have to prove that root is the leader of root which follows from the assumptions. For the second case, we have to prove that all other components have root as their leader. We first use the lemma parent is leader to reformulate the goal. Consequently, we have to prove that the leader of each ancestor of each component is root. Using the lemma parent transitive is root, we establish that each component has root as its ancestor. As root has itself as leader and all other components have the leader of their ancestors as leader, we conclude that root is leader of each component. 4.2.4
Definition construct_local_input (g: Connected v a) (c: Component) : mk_local_input c (neighbors g c).
Definition construct_checker (g: Connected v a ) : list (checker_input -> bool) := map checker (map (construct_local_input g) ( CV_list v a g)).
We implement the local checker in Coq as follows: Definition checker (l: local_input) (c : checker_input) : bool := (((negb (beq c.(leader_i) l.(i))) && beq c.(leader_i) c.(leader_parent_i)) && beq_nat c.(distance_i) ( c.(distance_parent_i)+1 ) && In_bool c.(parent_i) l.(neighbors)) && forallb_neigbors c.(leader_neighbors) c.(leader_i) || beq c.(leader_i) l.(i) && beq_nat c.(distance_i) 0 && beq c.(parent_i) l.(i) && forallb_neigbors c.(leader_neighbors) c.(leader_i).
Note that the checker accepts if a disjunction holds. The clauses of the disjunction represent the local witness predicates: gamma root for the root of the spanning tree and gamma i for the other components. To verify the correctness of a local checker ci , we have to show that ci accepts if and only if the local witness predicate holds for the component i. We formalize the correctness of the checker as follows:
Correctness of the Local Checkers
In this section, we present our implementation and verification of the local checkers. As our certifying leader election belongs to class C, every component has a local checker. The local checker of a component i decides the local witness predicate γ(xi , yi , wi ). Hence, in order to decide the local witness predicate, the checker needs i’s local input xi , i’s local output yi and i’s local witness wi . For our implementation of the local checker ci , we combined yi and wi as this is the input the local checker ci gets from its component i. We encapsulate both, yi and wi , in the Record checker input as follows:
Theorem checker_correctness (l: local_input) (c: checker_input): (leaderconsistency l.(i) c.(leader_parent_i)) (distanceconsistency l.(i) c.(distance_parent_i)) (componentconsistency l.(i) c.(leader_i) c.(parent_i) c.(distance_i) (neighborsconsistency l.(i) c.(leader_neighbors_i))) (checker l c = true) ((gamma_i l.(i) c.(leader_i) c.(distance_i) c.(parent_i) c.(leader_parent_i) c.(distance_parent_i) c.(leader_neighbors_i)
Record checker_input : Set := mk_checker_input { leader_i : Component; distance_i : nat; parent_i : Component; distance_parent_i : nat; leader_parent_i : Component; leader_neighbors_i : C_list; }.
Verification of Certifying Distributed Programs
local_input :=
/\ /\ /\ ->
\/
gamma_root l.(i) c.(leader_i) c.(distance_i) c.(parent_i) c.(leader_neighbors_i) )).
The local witnesses of two neighboring components have to be consistent, i.e. the local witnesses match on corre8
2016/10/13
sponding values. For example, if a component i chooses a component j as its parent (parenti = j), the value of distanceparenti equals distancej . Actually the local checker has to check the consistency in the neighborhood. However, we did not implement the consistency check as part of the local checker since this would include communication of the checkers during the checking. We formalize the consistency check as an assumption for the checker verification using the helper predicates leaderconsistency, distanceconsistency, neighborsconsistency and componentconsistency. The proof of the checker correctness is straightforward and uses syntactic rewriting. To verify the local checker’s correctness, we also have to show that the checker halts. However, Coq only allows structural recursion and as a consequence, every program is guaranteed to halt. Since Coq allows program extraction to the functional programming languages Haskell, OCaml and Scheme, we can extract our verified checkers to one of these languages. 4.2.5
Some techniques for making a distributed program selfstabilizing share similarities to our approach of making a distributed program certifying. The idea of self-stabilization is that a system in a faulty state stabilizes itself to a correct state. To this end, the components of a system have to detect that the system’s state is faulty whereby local detection is desired. As a consequence, there are some similarities to proof labeling schemes (Korman et al. 2010) as well, since if there exists a (silent) self-stabilizing program, then there exists a proof labeling scheme for that program and vice versa (Blin et al. 2014). In contrast, we separate the checking from the computation, rely on witnesses, and integrate proofs for the witness property and the composition property. Distributed programs have been verified in Coq, but to the best of our knowledge, there is no verification method to obtain formal instance correctness for distributed programs.
6.
Since verification of a distributed program is often too costly, we consider investigating verification methods to obtain formal instance correctness to be worthwhile. For this purpose, we consider certifying programs. A checker of a certifying program is usually simpler than the original program, and its verification is often feasible while the verification of the original program is too costly. By verifying the checker and by giving a machine-checked proof that the witness certifies the output’s correctness, we get formal instance correctness. This verification method to obtain formal instance correctness was demonstrated on sequential programs. In contrast, we are concerned with the correctness of distributed programs. Distributed programs behave differently from sequential programs, since for instance, the global output is distributed over a network. In this paper, we defined a class of certifying distributed programs (Section 3.3). Moreover, we presented a certifying variant of the leader election problem in networks (Section 4.1). The defined class is particularly interesting since the certification itself is distributed – the global witness is computed and checked distributively. Since the global witness is also distributed over the network and since we rely on a local witness predicate in order to check a global property distributively, we have to adapt Rizkallah’s verification method to obtain formal instance correctness. We presented a verification method to obtain formal instance correctness for the defined class of certifying distributed programs (Section 3.4). For our verification method, we build upon Rizkallah’s verification method to obtain formal instance correctness for certifying sequential programs. As a proof of concept, we demonstrated our verification method on the leader election problem in networks using the interactive theorem prover Coq. While Rizkallah used the interactive theorem prover Isabelle for theorem proving and the verifier VCC for program verification for her case studies, we decided on using Coq for both tasks. By using
Formal Instance Correctness
We solved the proof obligations composition property, witness property and correctness of the local checkers for certifying leader election using Coq. Thus, we achieved formal instance correctness for certifying leader election.
5.
Related Work
Literature offers more than 100 certifying algorithms ((Arkoudas and Rinard 2005), (Bruce et al. 2009), (Corneil et al. 2013), (Heggernes and Kratsch 2007), (Nikolopoulos and Palios 2012), (Kaplan and Nussbaum 2009), (Hung and Chang 2011), (Necula and Lee 1998), (Zhang et al. 2014), (Sullivan and Masson 1991), (Bright and Sullivan 1995), (Bright et al. 1997), (Glesner 2003), (McConnell 2004), (Hell and Huang), (Mehlhorn and N¨aher 1998), (Finkler and Mehlhorn 1999)). A theory of certifying sequential algorithms along with several examples and further reading is given in (McConnell et al. 2011). Some of these certifying sequential algorithms are implemented in the industrial-level library LEDA (Library for Efficient Data Structures and Algorithms) (Mehlhorn and N¨aher 1999) – a library for combinatorial and geometric computing. In addition, Rizkallah developed a verification method to achieve formal instance correctness for certifying sequential programs ((Rizkallah 2015),(Alkassar et al. 2014), (Alkassar et al. 2011), (Noschinski et al. 2014)). She demonstrated her verification method on some programs from the LEDA library. However, all this work was done for sequential and not for distributed programs. To the best of our knowledge, there is no certifying distributed algorithm apart from the one we published in a previous paper (V¨ollinger and Reisig 2015). Verification of Certifying Distributed Programs
Conclusion and Future Work
9
2016/10/13
only one tool, we reduce the trust base. Moreover, we have less proof obligations since we do not have to prove that a formalization made in one tool is correctly transferred to another tool. In order to evaluate our verification method, some more case studies would be interesting. We expect that the library Graph Basics would be also helpful for the verification of other certifying distributed programs. However, the library does not offer graphs with weighted edges. Since weighted edges are necessary for the formalization of computing shortest paths in a network, it could be an interesting extension. A first step in this direction is the definition of the inductive type Connection that adds the concept of the length of a path. Moreover, we expect that the formalization of the spanning tree as a global witness can be reused for further case studies, since a lot of our certifying distributed programs rely on a spanning tree. Another interesting direction would be to investigate other classes of certifying distributed programs and to find a verification method to obtain formal instance correctness for these classes. Of special interest are certifying distributed programs that do not terminate.
U. Finkler and K. Mehlhorn. Checking priority queues. In Proceedings of the Tenth Annual ACM-SIAM Symposium on Discrete Algorithms, SODA ’99, pages 901–902, Philadelphia, PA, USA, 1999. Society for Industrial and Applied Mathematics. ISBN 0-89871-434-6.
References
A. Korman, S. Kutten, and D. Peleg. Proof labeling schemes. Distributed Computing, 22(4):215–233, 2010.
S. Glesner. Program checking with certificates: Separating correctness-critical code. In K. Araki, S. Gnesi, and D. Mandrioli, editors, FME 2003: Formal Methods, volume 2805 of Lecture Notes in Computer Science, pages 758–777. Springer Berlin Heidelberg, 2003. ISBN 978-3-540-40828-4. P. Heggernes and D. Kratsch. Linear-time certifying recognition algorithms and forbidden induced subgraphs. Nordic Journal of Computing, 14(1):87–108, Jan. 2007. ISSN 1236-6064. P. Hell and J. Huang. Certifying lexbfs recognition algorithms for proper interval graphs and proper interval bigraphs. SIAM Journal on Discrete Mathematics, 18:554–570. R.-W. Hung and M.-S. Chang. An efficient certifying algorithm for the hamiltonian cycle problem on circular-arc graphs. Theoretical Computer Science, 412(39):5351 – 5373, 2011. INRIA. The coq proof assistant. URL http://coq.inria. fr/. H. Kaplan and Y. Nussbaum. Certifying algorithms for recognizing proper circular-arc graphs and unit circular-arc graphs. Discrete Applied Mathematics, 157(15):3216 – 3230, 2009.
E. Alkassar, S. B¨ohme, K. Mehlhorn, and C. Rizkallah. Verification of certifying computations. In G. Gopalakrishnan and S. Qadeer, editors, Computer Aided Verification, volume 6806 of Lecture Notes in Computer Science, pages 67–82. Springer, 2011.
N. A. Lynch. Distributed Algorithms. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 1996. ISBN 1558603484.
E. Alkassar, S. B¨ohme, K. Mehlhorn, and C. Rizkallah. A framework for the verification of certifying computations. Journal of Automated Reasoning, 52(3):241–273, 2014.
R. M. McConnell. A certifying algorithm for the consecutive-ones property. In Proceedings of the Fifteenth Annual ACM-SIAM Symposium on Discrete Algorithms, SODA ’04, pages 768–777, Philadelphia, PA, USA, 2004. Society for Industrial and Applied Mathematics. ISBN 0-89871-558-X.
K. Arkoudas and M. C. Rinard. Deductive runtime certification. Electronic Notes in Theoretical Computer Science, 113:45–63, 2005.
R. M. McConnell, K. Mehlhorn, S. N¨aher, and P. Schweitzer. Certifying algorithms. Computer Science Review, 5:119–161, 2011.
L. Blin, P. Fraigniaud, and B. Patt-Shamir. On Proof-Labeling Schemes versus Silent Self-stabilizing Algorithms, pages 18–32. Springer International Publishing, Cham, 2014. ISBN 978-3319-11764-5.
K. Mehlhorn and S. N¨aher. From algorithms to working programs: On the use of program checking in leda. Lecture Notes in Computer Science, 1450:84–93, 1998.
J. D. Bright and G. F. Sullivan. On-line error monitoring for several data structures. Twenty-Fifth International Symposium on Fault-Tolerant Computing, FTCS-25. Digest of Papers, 1995.
K. Mehlhorn and S. N¨aher. LEDA: A platform for combinatorial and geometric computing. Cambridge University Press, 1999. ISBN 978-0-521-12956-5.
J. D. Bright, G. F. Sullivan, and G. M. Masson. A formally verified sorting certifier. IEEE Trans. Comput., 46(12):1304–1312, 1997.
G. C. Necula and P. Lee. The design and implementation of a certifying compiler. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming Language Design and Implementation, PLDI ’98, pages 333–344, New York, NY, USA, 1998. ACM. ISBN 0-89791-987-4.
D. Bruce, C. T. Hoang, and J. Sawada. A certifying algorithm for 3-colorability of p5-free graphs. In The 20th International Symposium on Algorithms and Computation (ISAAC 2009), LNCS 5878, 2009.
S. D. Nikolopoulos and L. Palios. An o(nm)-time certifying algorithm for recognizing hhd-free graphs. Theoretical Computer Science, 452:117 – 131, 2012.
D. G. Corneil, B. Dalton, and M. Habib. Ldfs-based certifying algorithm for the minimum path cover problem on cocomparability graphs. SIAM Journal on Computing, 42(3):792–807, 2013.
L. Noschinski, C. Rizkallah, and K. Mehlhorn. Verification of certifying computations through autocorres and simpl. In J. M. Badger and K. Y. Rozier, editors, NASA Formal Methods, volume 8430 of Lecture Notes in Computer Science, pages
J. Duprat. A coq toolkit for graph theory, 2001. Rapport de recherche. Ecole Normale Superieur de Lyon.
Verification of Certifying Distributed Programs
10
2016/10/13
46–61. Springer International Publishing, 2014. ISBN 978-3319-06199-3. D. Peleg. Distributed Computing: A Locality-Sensitive Approach. Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2000. ISBN 0-89871-464-8. M. Raynal. Distributed Algorithms for Message-Passing Systems. Springer Berlin Heidelberg, 2013. ISBN 978-3-642-38122-5. C. Rizkallah. Verification of Program Computations. PhD thesis, 2015. G. F. Sullivan and G. M. Masson. Certification trails for data structures. Digest of the 21st Symposium on Fault-Tolerant Computing, pages 240–247, 1991. K. V¨ollinger and W. Reisig. Certification of distributed algorithms solving problems with optimal substructure. In R. Calinescu and B. Rumpe, editors, Software Engineering and Formal Methods - 13th International Conference, SEFM 2015, York, UK, September 7-11, 2015. Proceedings, volume 9276 of Lecture Notes in Computer Science, pages 190–195. Springer, 2015. ISBN 978-3-319-22968-3. Y. Zhang, C. Papamanthou, and J. Katz. Alitheia: Towards practical verifiable graph processing. In Proceedings of the 2014 ACM SIGSAC Conference on Computer and Communications Security, CCS ’14, pages 856–867, New York, NY, USA, 2014. ACM. ISBN 978-1-4503-2957-6.
Verification of Certifying Distributed Programs
11
2016/10/13