Skip to content

Commit 52a49d7

Browse files
authored
Merge pull request #253 from crytic/dev-working-with-ether
A tutorial on the use of ether in echidna
2 parents 339acd7 + 40bf0bc commit 52a49d7

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
- [How to filter functions](./program-analysis/echidna/basic/filtering-functions.md)
7373
- [How to test assertions](./program-analysis/echidna/basic/assertion-checking.md)
7474
- [How to write good properties step by step](./program-analysis/echidna/basic/property-creation.md)
75+
- [How to write properties that use ether](./program-analysis/echidna/basic/working-with-eth.md)
7576
- [Advanced](./program-analysis/echidna/advanced/README.md)
7677
- [How to collect a corpus](./program-analysis/echidna/advanced/collecting-a-corpus.md)
7778
- [How to use optimization mode](./program-analysis/echidna/advanced/optimization_mode.md)

program-analysis/echidna/basic/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
- [How to Determine the Best Testing Approach](./common-testing-approaches.md): Deciding on the Optimal Testing Method
55
- [How to Filter Functions](./filtering-functions.md): Filtering the Functions to be Fuzzed
66
- [How to Test Assertions Effectively](./assertion-checking.md): Efficiently Testing Assertions with Echidna
7-
- [Step by Step Guide to Writing Quality Properties](./property-creation.md): Improving Property Testing through Iteration
7+
- [How to write properties that use ether](./working-with-eth.md): Fuzzing ether during fuzzing campaigns
8+
- [How to Write Good Properties Step by Step](./property-creation.md): Improving Property Testing through Iteration
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Using ether during a fuzzing campaign
2+
3+
**Table of contents:**
4+
5+
- [Using ether during a fuzzing campaign](#using-ether-during-a-fuzzing-campaign)
6+
- [Introduction](#introduction)
7+
- [Controlling the amount of ether in payable functions](#controlling-the-amount-of-ether-in-payable-functions)
8+
- [Controlling the amount of ether in contracts](#controlling-the-amount-of-ether-in-contracts)
9+
- [Summary: Working with ether](#summary-working-with-ether)
10+
11+
## Introduction
12+
13+
We will see how to use ether during a fuzzing campaign. The following smart contract will be used as example:
14+
15+
```solidity
16+
contract C {
17+
function pay() public payable {
18+
require(msg.value == 12000);
19+
}
20+
21+
function echidna_has_some_value() public returns (bool) {
22+
return (address(this).balance != 12000);
23+
}
24+
}
25+
```
26+
27+
This code forces Echidna to send a particular amount of ether as value in the `pay` function.
28+
Echidna will do this for each payable function in the target function (or any contract if `allContracts` is enabled):
29+
30+
```
31+
$ echidna balanceSender.sol
32+
...
33+
echidna_has_some_value: failed!💥
34+
Call sequence:
35+
pay() Value: 0x2ee0
36+
```
37+
38+
Echidna will show the value amount in hexadecimal.
39+
40+
## Controlling the amount of ether in payable functions
41+
42+
The amount of ether to send in each payable function will be randomly selected, but with a maximum value determined by the `maxValue` value
43+
with a default of 100 ether per transaction:
44+
45+
```yaml
46+
maxValue: 100000000000000000000
47+
```
48+
49+
This means that each transaction will contain, at most, 100 ether in value. However, there is no maximum that will be used in total.
50+
The maximum amount to receive will be determined by the number of transactions. If you are using 100 transactions (`--seq-len 100`),
51+
then the total amount of ether used for all the transactions will be between 0 and 100 \* 100 ethers.
52+
53+
Keep in mind that the balance of the senders (e.g. `msg.sender.balance`) is a fixed value that will NOT change between transactions.
54+
This value is determined by the following config option:
55+
56+
```yaml
57+
balanceAddr: 0xffffffff
58+
```
59+
60+
## Controlling the amount of ether in contracts
61+
62+
Another approach to handle ether will be allow the testing contract to receive certain amount and then use it to send it.
63+
64+
```solidity
65+
contract A {
66+
C internal c;
67+
68+
constructor() public payable {
69+
require(msg.value == 12000);
70+
c = new C();
71+
}
72+
73+
function payToContract(uint256 toPay) public {
74+
toPay = toPay % (address(this).balance + 1);
75+
c.pay{ value: toPay }();
76+
}
77+
78+
function echidna_C_has_some_value() public returns (bool) {
79+
return (address(c).balance != 12000);
80+
}
81+
}
82+
83+
contract C {
84+
function pay() public payable {
85+
require(msg.value == 12000);
86+
}
87+
}
88+
```
89+
90+
However, if we run this directly with echidna, it will fail:
91+
92+
```
93+
$ echidna balanceContract.sol
94+
...
95+
echidna: Deploying the contract 0x00a329c0648769A73afAc7F9381E08FB43dBEA72 failed (revert, out-of-gas, sending ether to an non-payable constructor, etc.):
96+
```
97+
98+
We need to define the amount to send during the contract creation:
99+
100+
```yaml
101+
balanceContract: 12000
102+
```
103+
104+
We can re-run echidna, using that config file, to obtain the expected result:
105+
106+
```
107+
$ echidna balanceContract.sol --config balanceContract.yaml
108+
...
109+
echidna_C_has_some_value: failed!💥
110+
Call sequence:
111+
payToContract(12000)
112+
```
113+
114+
## Summary: Working with ether
115+
116+
Echidna has two options for using ether during a fuzzing campaign.
117+
118+
- `maxValue` to set the max amount of ether per transaction
119+
- `contractBalance` to set the initial amount of ether that the testing contract receives in the constructor.

0 commit comments

Comments
 (0)