Specifications

The application specifications the CEC tool takes as input are formatted in a way similar to a Boogie specification.

Examples of application specifications can be found in the specifications/applications directory present in the Git Hub repository.

Example

System Description

Consider as an example a distributed banking application.

The application has the following interface:

The withdraw operation withdraws money from a client's bank account. The deposit operation deposits money in a client's bank account. The applyInterest operation multiplies the amount that all clients have in their bank accounts by a constant.

The invariant of the system states that no bank account may have a credit below zero.

System Specification

@import Map1K;

@init
type Client;
var INTEREST :int where INTEREST > 0;

@variables
var balances : [Client]int;

@equals [Client]int @as map1KEquals(@this, @other);

@invariant
forall c: Client :: balances[c] >= 0;


@operations

procedure deposit(c1: Client, a1: int)
    modifies balances;
    requires a1 >= 0;
    ensures map1KSet(c1, old(balances)[c1] + a1, balances, old(balances));

procedure withdraw(c1: Client, a1: int)
    modifies balances;
    requires balances[c1] - a1 >= 0;
    ensures map1KSet(c1, old(balances)[c1] - a1, balances, old(balances));

procedure applyInterest()
    modifies balances;
    ensures forall c : Client :: balances[c] == old(balances[c]) * INTEREST;

The Specification Format

The specifications are composed by six separate sections: imports, initializer, variables, equals expressions, invariant and operations. All sections have to be defined in order. Additionally all sections, besides the operations section, are optional.

Step by Step System Specification

Lets start by looking at the initializer section of this specification.

@init
type Client;
var INTEREST :int where INTEREST > 0;

The second line declares a new type, the type Client. The third line declares that there is a constant that is bigger then zero, named INTEREST. This is the multiplication factor that is applied in the operation applyInterest.

Now we define the variables that represents the state of the system.

@variables
var balances : [Client]int;

In this case we only use one variable, balances, that is defined as a map of Client to the amount the client has in its bank account.

Next, we define the equality expression for the variable we defined. We could define it using by writing an expression from scratch but the unit named Map1K, specified in specifications/libraries/map1k_utils.spec, already provides a default equality function for a 1 key 1 value Boogie map. To use it, we first need to import it.

@import Map1K;

Having imported the unit we can now use its functions. In this case we are using the map1KEquals function.

@equals [Client]int @as map1KEquals(@this, @other);

Now we can define the invariant.

@invariant
forall c: Client :: balances[c] >= 0;

Finally, we specify each of the three operations.

@operations

procedure deposit(c1: Client, a1: int)
    modifies balances;
    requires a1 >= 0;
    ensures map1KSet(c1, old(balances)[c1] + a1, balances, old(balances));

procedure withdraw(c1: Client, a1: int)
    modifies balances;
    requires balances[c1] - a1 >= 0;
    ensures map1KSet(c1, old(balances)[c1] - a1, balances, old(balances));

procedure applyInterest()
    modifies balances;
    ensures forall c : Client :: balances[c] == old(balances[c]) * INTEREST;

This specification can be found can be found in specifications/applications/basic_bank.json in the Git Hub repository.

Notes