Writing Properties for ink! Contracts
Adding Properties
Inside your Cargo.toml
First, you need to add the phink
feature to your Cargo.toml, such as:
[features]
phink = []
Inside your file.rs
Then, you can use the following example to create invariants. You need to create another impl
if your contract, and to
put
it under the feature of phink
. Use assert!
or panic!
for your properties.
#![allow(unused)] fn main() { #[cfg(feature = "phink")] #[ink(impl)] impl DomainNameService { // This invariant ensures that nobody registed the forbidden number #[ink(message)] #[cfg(feature = "phink")] pub fn phink_assert_dangerous_number(&self) { let forbidden_number = 42; assert_ne!(self.dangerous_number, forbidden_number); } } }
You can find more informations in the page dedicated to invariants.
Running Phink
1. Instrument the Contract
Run the following command to instrument your ink! smart contract, enabling it for fuzzing:
phink instrument my_contract/
This step modifies the contract to include necessary hooks for Phink’s fuzzing process. It creates a fork of the contract, so you don’t have to make a copy before.
2. Run the fuzzer
After instrumenting your contract and writing properties and configuring your phink.toml
, execute the
fuzzing process:
phink fuzz
After executing this command, your fuzzing tests will begin based on the configurations specified in your phink.toml
file. You should see a user interface appear.
If you’re utilizing the advanced UI, you’ll receive real-time updates on the fuzzed messages at the bottom of the screen. For more detailed log information, you can use the following command:
watch -c -t -n 0.1 "clear && cat output/phink/logs/last_seed.phink" # `output` is the default, but it depends of your `phink.toml`
This will provide you with clearer logs by continuously updating them every 0.1 seconds.
Analyzing Results
Crashes
In case of crashes, you should see something like the following.
To analyze the crash, you can run phink execute <your_crash>
, for instance
phink execute output/phink/crashes/1729082451630/id:000000,sig:06,src:000008,time:627512,execs:3066742,op:havoc,rep:2
Component | Description |
---|---|
1729082451630 | Timestamp representing when the crash was recorded. |
id:000000 | Unique identifier for the crash. |
sig:06 | Signal number that triggered the crash. |
src:000008 | Source test case number. |
time:627512 | Execution time since the start of the testing process. |
execs:3066742 | Cumulative number of executions performed until the crash. |
op:havoc,rep:2 | Type of fuzzing operation (havoc) and its repetition number. |
By running this, you should have an output similar to the screenshot below:
Coverage (this feature is in alpha and unstable)
Generating a coverage report
First, you need to create a traces.cov
file. For this, execute the command below.
phink run
Once done, generate coverage reports to analyze which parts of the contract were tested:
phink coverage my_contract/
Some HTML files should then be generated in the path you’ve configured inside your phink.toml
. The coverage report
provides a visual representation of tested code areas. Basically, the more green lines, the better. You can find below
an example of the coverage report.
Coverage Report Example
Green Lines: Code that has been tested.
Figure 1: Coverage Report of one specific file.
Figure 2: List of fuzzed Rust files from the ink! smart-contract.