|
1 | 1 | use super::state::EvmFuzzState;
|
2 | 2 | use alloy_dyn_abi::{DynSolType, DynSolValue};
|
3 | 3 | use alloy_primitives::{Address, B256, I256, U256};
|
4 |
| -use proptest::prelude::*; |
| 4 | +use proptest::{prelude::*, test_runner::TestRunner}; |
5 | 5 | use rand::{SeedableRng, rngs::StdRng};
|
6 | 6 |
|
7 | 7 | /// The max length of arrays we fuzz for is 256.
|
@@ -104,6 +104,65 @@ fn fuzz_param_inner(
|
104 | 104 | }
|
105 | 105 | }
|
106 | 106 |
|
| 107 | +/// Mutates the current value of a given a parameter type and value. |
| 108 | +/// TODO: add more mutations, see https://github.com/fuzzland/ityfuzz/blob/master/src/mutation_utils.rs#L449-L452 |
| 109 | +/// for static args, |
| 110 | +pub fn mutate_param_value( |
| 111 | + param: &DynSolType, |
| 112 | + value: DynSolValue, |
| 113 | + test_runner: &mut TestRunner, |
| 114 | + state: &EvmFuzzState, |
| 115 | +) -> DynSolValue { |
| 116 | + let new_value = |param: &DynSolType, test_runner: &mut TestRunner| { |
| 117 | + fuzz_param_from_state(param, state) |
| 118 | + .new_tree(test_runner) |
| 119 | + .expect("Could not generate case") |
| 120 | + .current() |
| 121 | + }; |
| 122 | + |
| 123 | + match value { |
| 124 | + // flip boolean value |
| 125 | + DynSolValue::Bool(val) => DynSolValue::Bool(!val), |
| 126 | + // Uint: increment, decrement or generate new value from state. |
| 127 | + DynSolValue::Uint(val, size) => match test_runner.rng().random_range(0..=2) { |
| 128 | + 0 => DynSolValue::Uint(val.saturating_add(U256::ONE), size), |
| 129 | + 1 => DynSolValue::Uint(val.saturating_sub(U256::ONE), size), |
| 130 | + _ => new_value(param, test_runner), |
| 131 | + }, |
| 132 | + // Int: increment, decrement or generate new value from state. |
| 133 | + DynSolValue::Int(val, size) => match test_runner.rng().random_range(0..=2) { |
| 134 | + 0 => DynSolValue::Int(val.saturating_add(I256::ONE), size), |
| 135 | + 1 => DynSolValue::Int(val.saturating_sub(I256::ONE), size), |
| 136 | + _ => new_value(param, test_runner), |
| 137 | + }, |
| 138 | + DynSolValue::Array(mut values) => { |
| 139 | + if values.is_empty() { |
| 140 | + return new_value(param, test_runner); |
| 141 | + } |
| 142 | + |
| 143 | + let DynSolType::Array(param_type) = param else { return new_value(param, test_runner) }; |
| 144 | + |
| 145 | + match test_runner.rng().random_range(0..=2) { |
| 146 | + // Decrease array size by removing a random element. |
| 147 | + 0 => { |
| 148 | + values.remove(test_runner.rng().random_range(0..values.len())); |
| 149 | + } |
| 150 | + // Increase array size. |
| 151 | + 1 => values.push(new_value(param_type, test_runner)), |
| 152 | + // Mutate array element. |
| 153 | + _ => { |
| 154 | + let id_to_mutate = test_runner.rng().random_range(0..values.len()); |
| 155 | + let val = values.get(id_to_mutate).unwrap(); |
| 156 | + let new_value = mutate_param_value(param_type, val.clone(), test_runner, state); |
| 157 | + values[id_to_mutate] = new_value; |
| 158 | + } |
| 159 | + }; |
| 160 | + DynSolValue::Array(values) |
| 161 | + } |
| 162 | + _ => new_value(param, test_runner), |
| 163 | + } |
| 164 | +} |
| 165 | + |
107 | 166 | /// Given a parameter type, returns a strategy for generating values for that type, given some EVM
|
108 | 167 | /// fuzz state.
|
109 | 168 | ///
|
|
0 commit comments