Add Echidna Tests#16
Conversation
|
Thanks, can you add some additional docs to the readme with general setup for this? |
|
I've added the fuzz docs as well as GitHub Actions Fuzz CI and badge 🙂 |
src/fuzz/DssVestEchidnaTest.sol
Outdated
| } | ||
|
|
||
| function test_init_params(uint256 _amt, uint256 _bgn, uint256 _tau, uint256 _clf, uint256 _pmt, address _mgr) public { | ||
| _amt = 1 * WAD + _amt % uint128(-1); |
There was a problem hiding this comment.
this can exceed uint128(-1), which I assume will produce a violation since init will revert
There was a problem hiding this comment.
I just set _amt and _pmt min seed value to zero
src/fuzz/DssVestEchidnaTest.sol
Outdated
| } | ||
|
|
||
| function test_vest(uint256 _amt, uint256 _bgn, uint256 _tau, uint256 _clf, uint256 _pmt, address _mgr, uint256 _tick) public { | ||
| _amt = 1 * WAD + _amt % uint128(-1); |
src/fuzz/DssVestEchidnaTest.sol
Outdated
| _amt = 1 * WAD + _amt % uint128(-1); | ||
| _bgn = block.timestamp + _bgn % vest.MAX_VEST_PERIOD(); | ||
| _tau = 1 + _tau % vest.MAX_VEST_PERIOD(); | ||
| _clf = 1 + _clf % _tau; |
There was a problem hiding this comment.
Why not allow _clf ==0? Seems like a valid case.
src/fuzz/DssVestEchidnaTest.sol
Outdated
| _amt = 1 * WAD + _amt % uint128(-1); | ||
| _bgn = block.timestamp + _bgn % vest.MAX_VEST_PERIOD(); | ||
| _tau = 1 + _tau % vest.MAX_VEST_PERIOD(); | ||
| _clf = 1 + _clf % _tau; |
src/fuzz/DssVestEchidnaTest.sol
Outdated
| _tau = 1 + _tau % vest.MAX_VEST_PERIOD(); | ||
| _clf = 1 + _clf % _tau; | ||
| _pmt = 1 * WAD + _pmt % _amt; | ||
| _tick = block.timestamp + _tick % uint128(-1); |
There was a problem hiding this comment.
Okay I need some education on echidna...how does the value of _tick affect the behavior of the the vest call? I guess what I'm expecting is some sort of special instruction that creates a relation between _tick and block.timestamp before the call to vest, similar to hevm.warp, but I'm not seeing it.
There was a problem hiding this comment.
I've added _tick as a seed to simulate time for time dependant scenarios.
There was a problem hiding this comment.
I've just removed _tick as echidna should increase block.timestamp when fuzzing.
Also fixed the check in test_vest with block.timestamp >= fin.
Let me know if it's ok.
src/fuzz/DssVestEchidnaTest.sol
Outdated
| } | ||
|
|
||
| function test_init_ids(uint256 _amt, uint256 _bgn, uint256 _tau, uint256 _clf, uint256 _pmt, address _mgr) public { | ||
| _amt = 0 + _amt % uint128(-1); |
There was a problem hiding this comment.
I kind of think _amt = 0 is an uninteresting case--I'd recommend enforcing a reasonable minimum like you were before, just in a way that won't exceed the uint128(-1) limit. For example, here's one option:
_amt = _amt % uint128(-1);
if (_amt < WAD) _amt *= WAD;
There was a problem hiding this comment.
That will never cause a uint128 overflow since WAD * (WAD - 1) < 2^128 - 1.
There was a problem hiding this comment.
Oh yeah...we want to avoid _amt == 0...so we need something like:
_amt = _amt % uint128(-1);
if (_amt < WAD) _amt = (1 + _amt) * WAD;
src/fuzz/DssVestEchidnaTest.sol
Outdated
| _pmt = _pmt % _amt; | ||
| uint256 prevId = vest.ids(); | ||
| uint256 id = vest.init(address(this), _amt, _bgn, _tau, _clf, _pmt, _mgr); | ||
| assert(vest.ids() == add(prevId, id)); |
There was a problem hiding this comment.
This won't hold in general. Suggest replacing with:
assert(vest.ids() == add(prevId, 1));
assert(vest.ids() == id);
src/fuzz/DssVestEchidnaTest.sol
Outdated
| require((z = x - y) <= x); | ||
| } | ||
|
|
||
| function test_init_ids(uint256 _amt, uint256 _bgn, uint256 _tau, uint256 _clf, uint256 _pmt, address _mgr) public { |
There was a problem hiding this comment.
If we're bothering to pass _mgr as a fuzz parameter, we should at least verify that it's being set correctly in the award (same for the other values).
There was a problem hiding this comment.
this should be done in test_init_params
There was a problem hiding this comment.
Actually I'd suggest to combine test_init_ids and test_init_params into a single test named test_init. And it's probably unnecessary to pass _mgr in test_vest; alternatively, test_vest could be modified to randomly call vest on a previously-initialized award.
There was a problem hiding this comment.
Removed the _mgr seed and replace with echidna_mgr
| assert(z == x); | ||
| } | ||
|
|
||
| function test_init(uint256 _tot, uint256 _bgn, uint256 _tau, uint256 _clf, uint256 _end) public { |
There was a problem hiding this comment.
might also be worthwhile to have a test that hevm warps to some random point during or after init and check that payouts and accrued and unpaid values all line up.
There was a problem hiding this comment.
yeah, I think @kmbarry1 will make some dapp fuzz tests as well leveraging on hevm.warp to fuzz time-dependant logic and compare the two approaches.
There was a problem hiding this comment.
yeah I was kind of waiting for all the churn to settle down in the code itself
| } | ||
|
|
||
| function test_vest(uint256 id) internal { | ||
| vest.vest(id); |
There was a problem hiding this comment.
So, the issue here is that the award will almost never exist if id is a random uint256. You'll need to mod it into the appropriate range:
id = 1 + id % vest.ids();
to effectively test already-created vests.
There was a problem hiding this comment.
test_vest and test_yank are called inside test_init with an existing valid id.
I can fuzz them independently leveraging on the preserved state option eventually
There was a problem hiding this comment.
I think we shouldn't fuzz the ids cause they're checked in all the functions on dss-vest, if the award exists, so declaring both test_vest and test_yank internal allows to check for asserts on test_init seeds.
There was a problem hiding this comment.
I think this approach is good enough. The only downside is every award gets yanked before any more are created, so you're never testing with multiple awards in existence. However, given how the contract is written, there's very little chance of bugs that only surface when multiple awards exist.
| } | ||
|
|
||
| function test_yank(uint256 id, uint256 end) internal { | ||
| ( , , , uint48 _fin, , , ) = vest.awards(id); |
There was a problem hiding this comment.
You'll have a similar issue here as above.
There was a problem hiding this comment.
In this case test_yank, that is called inside test_init, will use an existing valid id plus an _end seed from test_init.
Description:
https://app.clubhouse.io/scdomaincommunityteam/story/11462/add-echidna-tests-for-dss-vest
COB Team Name or Author(s): @naszam
Contribution Checklist