foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.25k stars 1.73k forks source link

Ability to get sorted arrays when fuzzing #4097

Open PaulRBerg opened 1 year ago

PaulRBerg commented 1 year ago

Component

Forge

Describe the feature you would like

As discussed in https://github.com/foundry-rs/foundry/discussions/3948, my protocol has some business logic that is dependent upon the end user sending a sorted array as a function argument.

I've been bashing my head against the wall trying to fuzz the input arrays in a way that is scalable (tests don't take ages to run), to no fruition.

I pulled together an implementation of the quicksort algorithm in Solidity based on the sources I found on the Internet. The issue is that sorting a fuzzed arrays slows down test run times.

It would be nice to be able to instruct Foundry to sort the array on our behalf. I'm thinking of something like this (echo-ing the proposal I left in https://github.com/foundry-rs/foundry/issues/4085):

/// @custom:sort-array
function test_Something(uint256[] memory arr) external {
    // ...
}

That is, whenever the custom NatSpec tag above is specified, Foundry would provide the user with a sorted array arr, every time. It would be cheaper to let Foundry do this in Rust rather than sorting the array in memory in Solidity.

As an alternative to the above, we could consider using a specific prefix for the array variable names:

function test_Something(uint256[] memory sortedArr) external {
    // ...
}

The idea being, every time Foundry sees a function param that is an array type and its name starts with sorted, it would automatically sort the array.

Sorted arrays should be a common enough use case for a feature like this to be worth considering and implementing.

0xMelkor commented 1 year ago

This sounds interesting.

Technically this is feasible

We can use proptest::prop_map to generate a sorted array out of a proptest strategy. We could also extend the input of the fuzz_param function to accept some kind of metadata associated to param. Such metadata would let us generate more sophisticated compound strategies (i.e. for sorted arrays).

https://github.com/foundry-rs/foundry/blob/8307d6dc09dbd99d64239b901413869dc33cfa3e/evm/src/fuzz/strategies/param.rs#L14

The config part

imho the NatSpec approach is probably more flexible than the param name. Given though we should define some meta language to convey strategy configs to the fuzzer i.e.

/// forge-config:fuzz:strategy:sort(arr, desc)
function test_Something(uint256[] memory arr) external {
    // ...
}