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.

crash

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

ComponentDescription
1729082451630Timestamp representing when the crash was recorded.
id:000000Unique identifier for the crash.
sig:06Signal number that triggered the crash.
src:000008Source test case number.
time:627512Execution time since the start of the testing process.
execs:3066742Cumulative number of executions performed until the crash.
op:havoc,rep:2Type of fuzzing operation (havoc) and its repetition number.

By running this, you should have an output similar to the screenshot below:

crash

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.

Coverage Report Part 1

Figure 1: Coverage Report of one specific file.

coverage_2

Figure 2: List of fuzzed Rust files from the ink! smart-contract.