Assert Annotations
Checking properties in the middle of a function
Most properties discussed so far can only reference the contract's state variables (invariant
and if_updated
) or the contract's state variables and the arguments/returns of a specific function (if_succeeds
). While this is already powerful, sometimes you really want to check a property in the middle of an existing function (e.g. inside a loop?) and wish to talk about the values of local variables in the function. For this purpose we have added the #assert
annotation.
Motivating Example
Consider the following code sample:
One property we might want to express is that "the zero account is never made an admin". We can already express this using the universal quantifier forall
in the following way:
Or even with an if_updated
annotation like so:
However in both those cases, the instrumentation will have to re-write admins
state variable into a custom type that tracks the set of keys. This adds multiple arrays and maps, and also adds extra loops in the instrumentation code (implementing the actual forall
checks). This instrumentation can sometimes cause trouble for backend tools (such as fuzzers and symbolic execution engines) because of all of the extra paths its adding to the code.
An alternative, more light-weight annotation you might want to add is just a check in the loop in the body of setAdmins
that makes sure that each assigned address is not 0. You can express this like so:
The above annotation works exactly as a normal assert()
would inserted in this case.
Syntactic Rules about `#assert` annotations
There are several things to remember about #assert
:
1. #assert
can only be inserted directly before another statement, and in the instrumented code they would also be inserted before this statement.
You cannot insert an #assert
annotation at the end of a code block! If you do, it will be silently ignored! This limitation is due to the way the solidity compiler reports docstrings on statements to us. We are working on resolving this. Until then, if you need to insert an annotation at the end of a code block, insert a dummy no-op statement. For example:
...
/// #assert false;
0; // Dummy no-op statement inserted
}
2. #assert can talk about any of the local variables that are in scope at the point where it is defined.
3. You cannot use old()
inside of an assert()
- it can only talk about the state of the program at the point of the #assert
. If you want to talk about changes in state across some statements, check out the articles on the new experimental let feature(TODO).
Last updated