Skip to content

Commit 1a7d9e7

Browse files
authored
Merge pull request #254 from crytic/fix-use-allContracts
Replace multi-abi by allContracts and add a note regarding this change
2 parents dc963c6 + 1e4bccf commit 1a7d9e7

File tree

12 files changed

+134
-132
lines changed

12 files changed

+134
-132
lines changed

.github/workflows/echidna.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ jobs:
126126
expected: 'testERC20PermitDeposit(uint256):\s*passing'
127127
- name: MultiABI
128128
workdir: program-analysis/echidna/example/
129-
files: multiabi.sol
129+
files: allContracts.sol
130130
solc-version: 0.8.0
131-
config: multiabi.yaml
131+
config: allContracts.yaml
132132
contract: EchidnaTest
133133
outcome: failure
134134
expected: 'test_flag_is_false():\s*failed'

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@
8383
- [How to test bytecode-only contracts](./program-analysis/echidna/advanced/testing-bytecode.md)
8484
- [How to use hevm cheats to test permit](./program-analysis/echidna/advanced/hevm-cheats-to-test-permit.md)
8585
- [How to seed Echidna with unit tests](./program-analysis/echidna/advanced/end-to-end-testing.md)
86+
- [Understanding and using `allContracts`](./program-analysis/echidna/advanced/using-all-contracts.md)
8687
- [How to fuzz contracts with external libraries](./program-analysis/echidna/advanced/working-with-libraries.md)
87-
- [Understanding and using `multi-abi`](./program-analysis/echidna/advanced/using-multi-abi.md)
8888
- [Interacting with off-chain data via FFI cheatcode](./program-analysis/echidna/advanced/interacting-with-offchain-data-via-ffi.md)
8989
- [Fuzzing tips](./program-analysis/echidna/fuzzing_tips.md)
9090
- [Frequently Asked Questions](./program-analysis/echidna/frequently_asked_questions.md)

program-analysis/echidna/advanced/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
- [How to Test Bytecode-only Contracts](./testing-bytecode.md): Learn how to fuzz contracts without source code or perform differential fuzzing between Solidity and Vyper.
99
- [How to Use Hevm Cheats to Test Permit](./hevm-cheats-to-test-permit.md): Find out how to test code that relies on ecrecover signatures by using hevm cheat codes.
1010
- [How to Seed Echidna with Unit Tests](./end-to-end-testing.md): Discover how to use existing unit tests to seed Echidna.
11-
- [Understanding and Using `multi-abi`](./using-multi-abi.md): Learn what `multi-abi` testing is and how to utilize it effectively.
11+
- [Understanding and Using `allContracts`](./using-all-contracts.md): Learn what `allContracts` testing is and how to utilize it effectively.
1212
- [How to do on-chain fuzzing with state forking](./state-network-forking.md): How Echidna can use the state of blockchain during a fuzzing campaign
1313
- [Interacting with off-chain data via FFI cheatcode](./interacting-with-offchain-data-via-ffi.md): Using the `ffi` cheatcode as a way of communicating with the operating system

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ This simple property checks if the stored data remains constant. To run it, you
148148
```yaml
149149
prefix: crytic_
150150
initialize: init.json
151-
multi-abi: true
151+
allContracts: true
152152
cryticArgs: ["--truffle-build-directory", "app/src/contracts/"] # needed by Drizzle
153153
```
154154
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Understanding and using `allContracts` in Echidna
2+
3+
**Table of contents:**
4+
5+
- [Understanding and using `allContracts` in Echidna](#understanding-and-using-allContracts-in-echidna)
6+
- [Introduction](#introduction)
7+
- [What is `allContracts` testing?](#what-is-allContracts-testing)
8+
- [When and how to use `allContracts`](#when-and-how-to-use-allContracts)
9+
- [Run Echidna](#run-echidna)
10+
- [Example run with `allContracts` set to `false`](#example-run-with-allContracts-set-to-false)
11+
- [Example run with `allContracts` set to `true`](#example-run-with-allContracts-set-to-true)
12+
- [Use cases and conclusions](#use-cases-and-conclusions)
13+
14+
## Introduction
15+
16+
This tutorial is written as a hands-on guide to using `allContracts` testing in Echidna. You will learn what `allContracts` testing is, how to use it in your tests, and what to expect from its usage.
17+
18+
> This feature used to be called `multi-abi` but it was later renamed to `allContracts` in Echidna 2.1.0. As expected, this version or later is required for this tutorial.
19+
20+
## What is `allContracts` testing?
21+
22+
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.
23+
24+
## When and how to use `allContracts`
25+
26+
By default, Echidna calls functions from the contract to be analyzed, sending the transactions randomly from addresses `0x10000`, `0x20000` and `0x30000`.
27+
28+
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.
29+
30+
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.
31+
32+
This is where `allContracts` 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.
33+
34+
## Run Echidna
35+
36+
We will use a simple example to show how `allContracts` works. We will be using two contracts, `Flag` and `EchidnaTest`, both available in [allContracts.sol](../example/allContracts.sol).
37+
38+
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.
39+
40+
```solidity
41+
contract Flag {
42+
bool flag = false;
43+
44+
function flip() public {
45+
flag = !flag;
46+
}
47+
48+
function get() public returns (bool) {
49+
return flag;
50+
}
51+
52+
function test_fail() public {
53+
assert(false);
54+
}
55+
}
56+
```
57+
58+
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.
59+
60+
```solidity
61+
contract EchidnaTest {
62+
Flag f;
63+
64+
constructor() {
65+
f = new Flag();
66+
}
67+
68+
function test_flag_is_false() public {
69+
assert(f.get() == false);
70+
}
71+
}
72+
```
73+
74+
In a non `allContracts` 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 `allContracts` testing, the invariant is broken. You can access [allContracts.yaml here](../example/allContracts.yaml).
75+
76+
```yaml
77+
testMode: assertion
78+
testLimit: 50000
79+
allContracts: true
80+
```
81+
82+
To run the Echidna tests, run `echidna allContracts.sol --contract EchidnaTest --config allContracts.yaml` from the `example` directory. Alternatively, you can specify `--all-contracts` in the command line instead of using a configuration file.
83+
84+
### Example run with `allContracts` set to `false`
85+
86+
```
87+
echidna allContracts.sol --contract EchidnaTest --config allContracts.yaml
88+
Analyzing contract: building-secure-contracts/program-analysis/echidna/example/allContracts.sol:EchidnaTest
89+
test_flag_is_false(): passed! 🎉
90+
AssertionFailed(..): passed! 🎉
91+
92+
Unique instructions: 282
93+
Unique codehashes: 2
94+
Corpus size: 2
95+
Seed: -8252538430849362039
96+
```
97+
98+
### Example run with `allContracts` set to `true`
99+
100+
```
101+
echidna allContracts.sol --contract EchidnaTest --config allContracts.yaml
102+
Analyzing contract: building-secure-contracts/program-analysis/echidna/example/allContracts.sol:EchidnaTest
103+
test_flag_is_false(): failed!💥
104+
Call sequence:
105+
flip()
106+
flip()
107+
flip()
108+
test_flag_is_false()
109+
110+
Event sequence: Panic(1)
111+
AssertionFailed(..): passed! 🎉
112+
113+
Unique instructions: 368
114+
Unique codehashes: 2
115+
Corpus size: 6
116+
Seed: -6168343983565830424
117+
```
118+
119+
## Use cases and conclusions
120+
121+
Testing with `allContracts` 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.
122+
123+
A side-effect of using `allContracts` 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.
124+
125+
A final remark is that `allContracts` 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.

program-analysis/echidna/advanced/using-multi-abi.md

Lines changed: 0 additions & 123 deletions
This file was deleted.

program-analysis/echidna/basic/common-testing-approaches.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ contract ExternalTest {
7777
}
7878
```
7979

80-
**Multi ABI**: Echidna can perform direct calls to every contract if the `multi-abi` mode is enabled. This means that using it does not require wrapped calls. However, since every deployed contract can be called, unintended effects may occur. For example, if we have a property to ensure that the amount of tokens is limited:
80+
**allContracts**: Echidna can perform direct calls to every contract if the `allContracts` mode is enabled. This means that using it does not require wrapped calls. However, since every deployed contract can be called, unintended effects may occur. For example, if we have a property to ensure that the amount of tokens is limited:
8181

8282
```solidity
8383
contract ExternalTest {

program-analysis/echidna/exercises/Exercise-5.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ It is recommended to first attempt without reading the hints. The hints can be f
3939

4040
- Remember that you might need to supply the test contract with Ether. Read more in [the Echidna wiki](https://github.com/crytic/echidna/wiki/Config) and check [the default config setup](https://github.com/crytic/echidna/blob/master/tests/solidity/basic/default.yaml).
4141
- The invariant to look for is that "the balance of the receiver contract cannot decrease."
42-
- Learn about the [multi abi option](../basic/common-testing-approaches.md#external-testing).
42+
- Learn about the [allContracts optio](../basic/common-testing-approaches.md#external-testing).
4343
- A template is provided in [contracts/naive-receiver/NaiveReceiverEchidna.sol](https://github.com/crytic/damn-vulnerable-defi-echidna/blob/hints/contracts/naive-receiver/NaiveReceiverEchidna.sol).
4444
- A config file is provided in [naivereceiver.yaml](https://github.com/crytic/damn-vulnerable-defi-echidna/blob/hints/naivereceiver.yaml).
4545

0 commit comments

Comments
 (0)