Composite constraint-based Verification of Client-Server Protocols
Giorgio Delzanno(*)  and Tevfik Bultan(**)

(*) DISI Universita' di Genova
(**UCSB University of California at Santa Barbara


Contents
Introduction

Existing constraint technology like Presburger, and Boolean solvers, incorporated in the paradigm of symbolic model checking with rich assertional languages proposed by Kesten et al. at CAV 97 can be successfully applied to the verification of client-server protocols with arbitrary number of clients. Abstract interpretation is the mathematical bridge between protocol specification and constraint-based verification methods working on heterogeneous data like Tevfik Bultan's Action Language verifier of built on top of the Composite Symbolic Library. The method we propose is incomplete but fully automatic and sound for safety and liveness properties. The Action Language verified is, in fact, able to check full CTL temporal specifications. Sufficient conditions for the termination of the resulting verification algorithms can be derived using the general decidability results given by Abdulla et al.

The description of the approach is given in this tech-rep


Tools.

The Action Language Verifier and the Composite Symbolic Library can be downloaded from here.


Experimental results.

As a case-study, we apply the method to check safety and liveness properties for a formal model given through communicating finite-state machines of the directory-based consistency protocol proposed by Steven German.

The description of the protocol is given in the the tech-rep (the same as above).

The specification of the protocol using Action Language verifier is given below.


module main()
  enumerated state {Idle, ServeE, InvE, GrantE, ServeS, GrantS};
  boolean ex;
  integer xNull, xWaitS, xWaitE, xShared, xExclusive;
  initial: state=Idle && !ex && xNull>=1 && xShared=0 && xExclusive=0 &&
             xWaitE=0 && xWaitS=0;
  restrict: xNull>=0 && xWaitS>=0 && xWaitE>=0 && xShared>=0 &&
xExclusive>=0;

  module Request()
    reqS:    state=Idle && xNull>=1 &&
               state'=ServeS && xNull'=xNull-1 && xWaitS'=xWaitS+1;
    reqE1:   state=Idle && xNull>=1 &&
               state'=ServeE && xNull'=xNull-1 && xWaitE'=xWaitE+1;
    reqE2:   state=Idle && xShared>=1 &&
               state'=ServeE && xShared'=xShared-1 && xWaitE'=xWaitE+1;
    Request: reqS | reqE1 | reqE2;
  endmodule
                    
  module Serve()
    inv:     state=ServeS && ex && xExclusive>=1 &&
               state'=GrantS && xExclusive'=xExclusive-1 && xNull'=xNull+1
&& !ex;
    nonex:   state=ServeS && !ex &&
               state'=GrantS;
    invS:    state=ServeE &&
               state'=InvE && xNull'=xNull+xShared && xShared'=0;
    invE:    state=InvE && ex && xExclusive >=1 &&
               state'=GrantE && xNull'=xNull+1 && xExclusive'=xExclusive-1
&& !ex;
    nonexE:  state=InvE && !ex &&
               state'=GrantE;
    Serve :  inv | nonex | invS | invE | nonexE;
  endmodule
                                      
  module Grant()
    grantS:   state=GrantS && xWaitS>=1 &&
                state'= Idle && xWaitS'=xWaitS-1 && xShared'=xShared+1;
    grantE:   state=GrantE && xWaitE>=1 &&
                state'=Idle && xWaitE'=xWaitE-1 &&
xExclusive'=xExclusive+1 && ex';
    Grant :   grantS | grantE;
  endmodule

  main: Request() | Serve() | Grant();
  spec: AG([!((xShared>=1 && xExclusive>=1) || xExclusive>=2)])
endmodule
You can verify automatically the property by running the spec using Action Language verifier.
The specification of the refined protocol (with invalidate-loop) using the Omega-based constraint solver of Tevfik is as follows:
machine refined_protocol 
number_of_nodes 10
number_of_dynamic_variables 5
number_of_transitions 14
initial_node 0
static_variables
variable_ranges
{[xN,xWS,xWE,xS,xE] : xN,xWS,xWE,xS,xE >= 0} 
initial_condition
{[xN,xWS,xWE,xS,xE] : xN>=1 && xS=0 && xE=0 && xWE=0 && xWS=0}
transition_list


##### Request

# transition reqS  (ex=ex'=false)
        source_node 0
        destination_node  2
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS',xWE,xS,xE] : 
 xN>=1 && xN'=xN-1 && xWS'=xWS+1
}

# transition reqS  (ex=ex'=true)
        source_node 1
        destination_node  3
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS',xWE,xS,xE] : 
 xN>=1 && xN'=xN-1 && xWS'=xWS+1
}


# transition reqE1  (ex=ex'=false)
        source_node 0
        destination_node 4 
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS,xWE',xS,xE] : 
 xN>=1 && xN'=xN-1 && xWE'=xWE+1
}

# transition reqE1  (ex=ex'=true)
        source_node 1
        destination_node 5
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS,xWE',xS,xE] :
 xN>=1 && xN'=xN-1 && xWE'=xWE+1
}

# transition reqE2  (ex=ex'=false)
        source_node 0
        destination_node 4
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE',xS',xE] : 
 xS>=1 && xS'=xS-1 && xWE'=xWE+1
}

# transition reqE2  (ex=ex'=true)
        source_node 1
        destination_node 5
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE',xS',xE] : 
 xS>=1 && xS'=xS-1 && xWE'=xWE+1
}


##### Serve 

# transition inv1  (ex=ex'=true)
        source_node 3
        destination_node 3
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS,xWE,xS',xE] :
  xS>=1 && xS'=xS-1 && xN'=xN+1
}

# transition inv1  (ex=ex'=false)
        source_node 2
        destination_node 2
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS,xWE,xS',xE] :
  xS>=1 && xS'=xS-1 && xN'=xN+1
}

# transition inv1  (ex=true & ex'=false)
        source_node 3
        destination_node 2 
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN',xWS,xWE,xS,xE'] : 
  xE>=1 && xE'=xE-1 && xN'=xN+1
}


# transition nonex  (ex=ex'=false)
        source_node 2
        destination_node 6
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE,xS,xE] }


# transition inv2  (ex=ex'=false)
        source_node 4
        destination_node 8
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE,xS,xE] : 
  xS=0 && xE=0 
}

# transition inv2  (ex=ex'=true)
        source_node 5
        destination_node 9
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE,xS,xE] : 
  xS=0 && xE=0
}


##### Grant

# transition grantS  (ex=ex'=false)
        source_node 6
        destination_node 0
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS',xWE,xS',xE] : 
  xWS>=1 && xWS'=xWS-1 && xS'=xS+1
}

# transition grantS  (ex=ex'=true)
        source_node 7
        destination_node 1
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS',xWE,xS',xE] : 
  xWS>=1 && xWS'=xWS-1 && xS'=xS+1
}

# transition grantE  (ex=false & ex'=true)
        source_node 8
        destination_node 1
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE',xS,xE'] : 
  xWE>=1 && xWE'=xWE-1 && xE'=xE+1 
}

# transition grantE  (ex=ex'=true)
        source_node 9
        destination_node 1
        transformation_relation
{[xN,xWS,xWE,xS,xE] -> [xN,xWS,xWE',xS,xE'] : 
  xWE>=1 && xWE'=xWE-1 && xE'=xE+1 
}


formula_list

AG( {[xN,xWS,xWE,xS,xE] : !(xS>=1 && xE>=1 || xE>=2) } )

# AG( {[xN,xWS,xWE,xS,xE] : xE >= 0} )

# AG({[xN,xWS,xWE,xS,xE] : xWS>=1} => EF({[xN,xWS,xWE,xS,xE] : xS>=1}))

# AG({[xN,xWS,xWE,xS,xE] : xWS>=1} => AF({[xN,xWS,xWE,xS,xE] : xS>=1}))

# AG({[xN,xWS,xWE,xS,xE] : xWS>=2} => EF({[xN,xWS,xWE,xS,xE] : xS>=2}))

# AG({[xN,xWS,xWE,xS,xE] : xWS>=2} => AF({[xN,xWS,xWE,xS,xE] : xS>=2}))

# AG({[xN,xWS,xWE,xS,xE] : xWE>=1} => EF({[xN,xWS,xWE,xS,xE] : xE>=1}))

# AG({[xN,xWS,xWE,xS,xE] : xWE>=1} => AF({[xN,xWS,xWE,xS,xE] : xE>=1}))

end