Introduction
Last updated
Was this helpful?
Last updated
Was this helpful?
All scribble properties are expressed as specially formatted docstrings. For example:
In the above sample if_succeeds {:msg "P0"} y == x + 1;
is the property. Note the semicolon at the end - it is required for all properties.
There are several kinds of Scribble annotations.
Property annotations directly correspond to specific properties the user wants to check. There are 4 types of properties supported currently:
if_succeeds
properties are usually specified before functions. They specify conditions that must hold if the function returns successfully. (if_succeeds
can also be specified on contracts. For more details on their semantics see section).
invariant
properties are specified only before contracts. They specify conditions that must hold whenever we are NOT executing code in the contract (more info in section).
if_updated
properties are specified only before state variables. They specify conditions that must be checked whenever we are updating the value of a state variable (or some of part of the state variable in the case of complex data structures such as arrays, structs and maps). More info in section.
assert
properties are specified before a statement in the body of the function. They specify a condition that must be checked inline before the target statement, and can refer to any local variable in scope for the target statement. For more details see section.
All properties allow adding a human-readable message using {:msg "message"}
. This is helpful when mapping the properties to their description in other documents, or just as a reminder about the intent of a complex property.
Utility annotations are helpers that make writing property annotations easier. We currently support:
To give you a quick intro to scribble annotations lets look at two small examples uinsg the #if_succeeds
annotation.
In the below contract the function inc
increments the value of the state variable x
by 1. The property x == old(x) + 1
captures exactly this behavior.
Note that we can also write that property as x == old(x + 1)
since the value of the constant 1
doesn't change before and after the function.
For the contract from the previous example we can also add a contract invariant stating that x >= 1
as shown below:
Note that this specification is incomplete - it doesn't say what happens when y == 0
. We can make it complete by adding another property:
Multiple if_succeeds
annotations for the same functions are conjoined together. The same holds for multiple invariant
annotations for a contract.
The expressions that are allowed in custom properties are mostly a subset of the pure Solidity expressions, with some additional syntactic sugar. For more details see section.
- helpful for defining commonly used predicates and reducing code duplication
allow automatically annotating a contract with a whole set of predefined annotations (from a macro definition)
(#try
and #require
) - annotations providing guidance to backend tools
In verification people often think of function pre- and post- conditions (these come from ). One can encode a precondition P
and post-condition Q
as: P ==> Q
. So for example for the sillyDiv
function below, we can express a pre-condition (y != 0
) and post-condition (z == x/y
) as y!=0 ==> z == x/y
.