Skip to content

Commit f2b88ab

Browse files
authored
Merge branch 'crytic:master' into master
2 parents 6ec3bed + 9b770b1 commit f2b88ab

File tree

8 files changed

+179
-5
lines changed

8 files changed

+179
-5
lines changed

.github/workflows/echidna.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ jobs:
117117
contract: TestDepositWithPermit
118118
outcome: success
119119
expected: 'testERC20PermitDeposit(uint256):\s*passed'
120+
- name: MultiABI
121+
workdir: program-analysis/echidna/example/
122+
files: multiabi.sol
123+
solc-version: 0.8.0
124+
config: multiabi.yaml
125+
contract: EchidnaTest
126+
outcome: failure
127+
expected: 'test_flag_is_false():\s*failed'
120128

121129
steps:
122130
- name: Checkout repository

CODEOWNERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
* @montyly
2+
/development-guidelines/ @0xicingdeath
3+
/program-analysis/echidna/ @ggrieco-tob
4+
/not-so-smart-contracts/algorand/ @S3v3ru5
5+
/not-so-smart-contracts/cairo/ @technovision99
6+
/not-so-smart-contracts/cosmos/ @GrosQuildu
7+
/not-so-smart-contracts/substrate/ @0xicingdeath
8+
/learn_evm/ @bohendo

program-analysis/echidna/Exercise-6.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ We recommend to first try without reading the following hints. The hints are in
4646

4747
## Solution
4848

49-
This solution can be found in the [`solutions` branch](https://github.com/crytic/damn-vulnerable-defi-echidna/blob/hints/contracts/unstoppable/UnstoppableEchidna.sol).
49+
This solution can be found in the [`solutions` branch](https://github.com/crytic/damn-vulnerable-defi-echidna/blob/solutions/contracts/unstoppable/UnstoppableEchidna.sol).
5050

5151
[ctf]: https://www.damnvulnerabledefi.xyz/
5252

program-analysis/echidna/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ Watch our [Fuzzing workshop](https://www.youtube.com/watch?v=QofNQxW_K08&list=PL
2525
- [How to test bytecode-only contracts](./testing-bytecode.md): How to fuzz a contract without bytecode or to perform differential fuzzing between Solidity and Vyper
2626
- [How to use hevm cheats to test permit](./hevm-cheats-to-test-permit.md): How to test code that depends on ecrecover signatures using hevm cheat codes
2727
- [How to seed Echidna with unit tests](./end-to-end-testing.md): How to use existing unit tests to seed Echidna
28+
- [Understanding and using `multi-abi`](./using-multi-abi.md): What is `multi-abi` testing, and how can it be used
2829
- [Fuzzing tips](./fuzzing_tips.md): General fuzzing tips
2930
- Exercises
3031
- [Exercise 1](./Exercise-1.md): Testing token balances
3132
- [Exercise 2](./Exercise-2.md): Testing access control
3233
- [Exercise 3](./Exercise-3.md): Testing with custom initialization
3334
- [Exercise 4](./Exercise-4.md): Testing with `assert`
34-
- [Exercise 5](./Exercise-5.md): Solving Damn Vulnerable DeFi - Unstoppable
35-
- [Exercise 6](./Exercise-6.md): Solving Damn Vulnerable DeFi - Naive Receiver
35+
- [Exercise 5](./Exercise-5.md): Solving Damn Vulnerable DeFi - Naive Receiver
36+
- [Exercise 6](./Exercise-6.md): Solving Damn Vulnerable DeFi - Unstoppable
3637
- [Exercise 7](./Exercise-7.md): Solving Damn Vulnerable DeFi - Side Entrance
3738
- [Exercise 8](./Exercise-8.md): Solving Damn Vulnerable DeFi - The Rewarder
3839

program-analysis/echidna/end-to-end-testing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ $ npm i truffle
2727
If `ganache` is not installed, add it manually. In our example, we will run:
2828

2929
```
30-
$ npm i ganache
30+
$ npm -g i ganache
3131
```
3232

3333
Other projects using yarn will require:
3434

3535
```
36-
$ yarn add ganache
36+
$ yarn global add ganache
3737
```
3838

3939
Ensure that `$ ganache --version` outputs `ganache v7.3.2` or greater.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
pragma solidity ^0.8.0;
2+
3+
contract Flag {
4+
5+
bool flag = false;
6+
7+
function flip() public {
8+
flag = !flag;
9+
}
10+
11+
function get() public returns (bool) {
12+
return flag;
13+
}
14+
15+
function test_fail() public {
16+
assert(false);
17+
}
18+
}
19+
20+
21+
contract EchidnaTest {
22+
Flag f;
23+
24+
constructor() {
25+
f = new Flag();
26+
}
27+
28+
function test_flag_is_false() public {
29+
assert(f.get() == false);
30+
}
31+
32+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
testMode: assertion
2+
testLimit: 50000
3+
multi-abi: true
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Understanding and using `multi-abi` in Echidna
2+
3+
**Table of contents:**
4+
5+
- [Introduction](#introduction)
6+
- [What is `multi-abi` testing?](#what-is-multi-abi-testing)
7+
- [When and how to use `multi-abi`](#when-and-how-to-use-multi-abi)
8+
- [Run Echidna](#run-echidna)
9+
- [Use cases and conclusions](#use-cases-and-conclusions)
10+
11+
## Introduction
12+
13+
This tutorial is written as a hands-on guide to using `multi-abi` testing in Echidna. You will learn what `multi-abi` testing is, how to use it in your tests, and what to expect from its usage.
14+
15+
## What is `multi-abi` testing?
16+
17+
It is a testing mode that allows Echidna to call functions from any contract not directly under test. The ABI for the contract must be known, and it must have been deployed by the contract under test.
18+
19+
## When and how to use `multi-abi`
20+
21+
By default, Echidna calls functions from the contract to be analyzed, sending the transactions randomly from addresses `0x10000`, `0x20000` and `0x30000`.
22+
23+
In some systems, the user has to interact with other contracts prior to calling a function on the fuzzed contract. A common example is when you want to provide liquidity to a DeFi protocol, you will first need to approve the protocol for spending your tokens. This transaction has to be initiated from your account before actually interacting with the protocol contract.
24+
25+
A fuzzing campaign meant to test this example protocol contract won't be able to modify users allowances, therefore most of the interactions with the protocol won't be tested correctly.
26+
27+
This is where `multi-abi` testing is useful: It allows Echidna to call functions from other contracts (not just from the contract under test), sending the transactions from the same accounts that will interact with the target contract.
28+
29+
## Run Echidna
30+
31+
We will use a simple example to show how `multi-abi` works. We will be using two contracts, `Flag` and `EchidnaTest`, both available in [`multiabi.sol`](example/multiabi.sol).
32+
33+
The `Flag` contract contains a boolean flag that is only set if `flip()` is called, and a getter function that returns the value of the flag. For now, ignore `test_fail()`, we will talk about this function later.
34+
35+
```solidity
36+
contract Flag {
37+
38+
bool flag = false;
39+
40+
function flip() public {
41+
flag = !flag;
42+
}
43+
44+
function get() public returns (bool) {
45+
return flag;
46+
}
47+
48+
function test_fail() public {
49+
assert(false);
50+
}
51+
}
52+
```
53+
54+
The test harness will instantiate a new `Flag`, and the invariant under test will be that `f.get()` (that is, the boolean value of the flag) is always false.
55+
56+
```solidity
57+
contract EchidnaTest {
58+
Flag f;
59+
60+
constructor() {
61+
f = new Flag();
62+
}
63+
64+
function test_flag_is_false() public {
65+
assert(f.get() == false);
66+
}
67+
68+
}
69+
```
70+
71+
In a non `multi-abi` fuzzing campaign, Echidna is not able to break the invariant, because it only interacts with `EchidnaTest` functions. However, if we use the following configuration file, enabling `multi-abi` testing, the invariant is broken. You can access [`multiabi.yaml` here](example/multiabi.yaml).
72+
73+
```yaml
74+
testMode: assertion
75+
testLimit: 50000
76+
multi-abi: true
77+
```
78+
79+
To run the Echidna tests, run `echidna-test multiabi.sol --contract EchidnaTest --config multiabi.yaml` from the `example` directory. Alternatively, you can specify `--multi-abi` in the command line instead of using a configuration file.
80+
81+
### Example run with `multi-abi` set to `false`
82+
83+
```
84+
$ echidna-test multiabi.sol --contract EchidnaTest --config multiabi.yaml
85+
Analyzing contract: building-secure-contracts/program-analysis/echidna/example/multiabi.sol:EchidnaTest
86+
test_flag_is_false(): passed! 🎉
87+
AssertionFailed(..): passed! 🎉
88+
89+
Unique instructions: 282
90+
Unique codehashes: 2
91+
Corpus size: 2
92+
Seed: -8252538430849362039
93+
```
94+
95+
### Example run with `multi-abi` set to `true`
96+
97+
```
98+
$ echidna-test multiabi.sol --contract EchidnaTest --config multiabi.yaml
99+
Analyzing contract: building-secure-contracts/program-analysis/echidna/example/multiabi.sol:EchidnaTest
100+
test_flag_is_false(): failed!💥
101+
Call sequence:
102+
flip()
103+
flip()
104+
flip()
105+
test_flag_is_false()
106+
107+
Event sequence: Panic(1)
108+
AssertionFailed(..): passed! 🎉
109+
110+
Unique instructions: 368
111+
Unique codehashes: 2
112+
Corpus size: 6
113+
Seed: -6168343983565830424
114+
```
115+
116+
## Use cases and conclusions
117+
118+
Testing with `multi-abi` is a useful tool for complex systems that require the user to interact with more than one contract, as we mentioned earlier. Another use case is for deployed contracts that require interactions to be initiated by specific addresses: for those, specifying the `sender` configuration setting allows to send the transactions from the correct account.
119+
120+
A side-effect of using `multi-abi` is that the search space grows with the number of functions that can be called. This, combined with high values of sequence lengths, can make the fuzzing test not so thorough, because the dimension of the search space is simply too big to reasonably explore. Finally, adding more functions as fuzzing candidates makes the campaigns to take up more execution time.
121+
122+
A final remark is that `multi-abi` testing in assertion mode ignores all assert failures from the contracts not under test. This is shown in `Flag.test_fail()` function: even though it explicitly asserts false, the Echidna test ignores it.

0 commit comments

Comments
 (0)