Function Annotations
Function specifications are used to specify the behaviour of a single function.
Successful termination
Scribble currently has a single type of function specification, called if_succeeds
. Using if_succeeds, annotations you can assert properties that you expect to hold if (and only if) the annotated function successfully terminates.
These annotations have the following structure & are placed right above a function declaration:
if_succeeds {:msg "<Description of your property>"} <boolean scribble expression>;
The following is a simple example property, where we check that the function always returns 0.
contract ExampleContract {
/// #if_succeeds {:msg "result is always zero"} result == 0;
function example() public returns (uint result) {
...
}
}
Note that as of Scribble 0.4.0 you can also specify an if_succeeds
annotation at the contract level. See the section below.
Encoding Pre- and Post- Conditions
Pre- and postconditions are frequently used concepts in verification languages.
A precondition is what should be true before a function is executed.
A postcondition is what should be true after the function, if the precondition was true.
In it's simplest form if_succeeds
allows you to write postconditions (i.e. properties over the state after the function is done).
However, if_succeeds is quite versatile and you can use it to check preconditions as well. You'd do so by using the old() function.
We expect that a selfdestruct function only succeeds if the transaction sender is the owner of the contract.
/// #if_succeeds {:msg ""} old(msg.sender == owner);
function selfdestruct() public onlyOwner {}
Encoding conditional "behaviors"
Functions can have multiple behaviors depending on the inputs and contract state. Take the following example of a simple calculator function implementing multiplication and addition.
contract Calculator {
enum Op {
PLUS,
TIMES
}
function calc(Op operator, uint left, uint right) public returns (uint result) {
if (operator == Op.TIMES) {
return left * right;
}
if (operator == Op.PLUS) {
return left + right;
}
assert (false);
}
}
In this case, there are two distinct behaviors: multiplication and addition. Writing two separate post-conditions wouldn't work, since scribble
checks the conjunction of all function annotations.
/// #if_succeeds "result is product of left and right" result == left * right;
/// #if_succeeds "result is sum of left and right" result == left + right;
function calc(Op operator, uint left, uint right) public returns (uint result) { ... }
Luckily, you can use the implication operator here to describe the conditions under which each behavior holds:
/// #if_succeeds "result of times operator is product of left and right" operator == Op.TIMES ==> result == left * right;
/// #if_succeeds "result of plus operator is sum of left and right" operator == Op.PLUS ==> result == left + right;
function calc(Op operator, uint left, uint right) public returns (uint result) { ... }
Contract-level if_succeeds annotations
As of Scribble 0.4.0 you can also specify an if_succeeds
annotation at the contract level. A contract-level if_succeeds
is automatically applied to all public and external functions in the annotated contract, and all inheriting contracts. For example, in the below sample the annotation x > 0
will be applied to the functions A.pubFun
, A.externFun
and B.pubFunB
. Note that pure
and view
functions are skipped. So in the below example A.pubViewFun
will not be annotated.
/// #if_succeeds x > 0;
contract A {
uint x;
function pubFun() public { ... }
function externFun() external { ... }
function internalFun() internal { ... }
function pubViewFun() external view { ... }
}
contract B is A {
function pubFunB() public { ... }
function privFunB() private { ... }
}
Note that since the same expression needs to hold for an unknown set of functions, when writing a contract-level if_succeeds
function you can only talk about the contract's state vars, and other global transaction state. You cannot refer to function arguments/returns (even if all functions in the contract have the same arguments or returns).
Last updated
Was this helpful?