diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index 5c95235f2a3a..e92c2d4642ca 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -17,6 +17,7 @@ pub struct IntValueTree { /// If true cannot be simplified or complexified fixed: bool, } + impl IntValueTree { /// Create a new tree /// # Arguments @@ -37,6 +38,7 @@ impl IntValueTree { true } } + fn magnitude_greater(lhs: I256, rhs: I256) -> bool { if lhs.is_zero() { return false @@ -44,11 +46,14 @@ impl IntValueTree { (lhs > rhs) ^ (lhs.is_negative()) } } + impl ValueTree for IntValueTree { type Value = I256; + fn current(&self) -> Self::Value { self.curr } + fn simplify(&mut self) -> bool { if self.fixed || !IntValueTree::magnitude_greater(self.hi, self.lo) { return false @@ -56,6 +61,7 @@ impl ValueTree for IntValueTree { self.hi = self.curr; self.reposition() } + fn complicate(&mut self) -> bool { if self.fixed || !IntValueTree::magnitude_greater(self.hi, self.lo) { return false @@ -66,6 +72,7 @@ impl ValueTree for IntValueTree { self.reposition() } } + /// Value tree for signed ints (up to int256). /// The strategy combines 3 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` @@ -85,6 +92,7 @@ pub struct IntStrategy { /// The weight for purely random values random_weight: usize, } + impl IntStrategy { /// Create a new strategy. /// #Arguments @@ -99,6 +107,7 @@ impl IntStrategy { random_weight: 50usize, } } + fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); @@ -117,6 +126,7 @@ impl IntStrategy { }; Ok(IntValueTree::new(start, false)) } + fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree { // generate edge cases if there's no fixtures if self.fixtures.is_empty() { @@ -125,8 +135,10 @@ impl IntStrategy { let idx = runner.rng().gen_range(0..self.fixtures.len()); Ok(IntValueTree::new(self.fixtures[idx], false)) } + fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); + // generate random number of bits uniformly let bits = rng.gen_range(0..=self.bits); @@ -137,6 +149,7 @@ impl IntStrategy { // init 2 128-bit randoms let mut higher: u128 = rng.gen_range(0..=u128::MAX); let mut lower: u128 = rng.gen_range(0..=u128::MAX); + // cut 2 randoms according to bits size match bits - 1 { x if x < 128 => { @@ -154,17 +167,20 @@ impl IntStrategy { inner[1] = (lower >> 64) as u64; inner[2] = (higher & mask64) as u64; inner[3] = (higher >> 64) as u64; - let sign = if rng.gen_bool(0.5) { Sign::Positive } else { Sign::Negative }; + // we have a small bias here, i.e. intN::min will never be generated // but it's ok since it's generated in `fn generate_edge_tree(...)` + let sign = if rng.gen_bool(0.5) { Sign::Positive } else { Sign::Negative }; let (start, _) = I256::overflowing_from_sign_and_abs(sign, U256::from_limbs(inner)); Ok(IntValueTree::new(start, false)) } } + impl Strategy for IntStrategy { type Tree = IntValueTree; type Value = I256; + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let total_weight = self.random_weight + self.fixtures_weight + self.edge_weight; let bias = runner.rng().gen_range(0..total_weight); diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index 5f53d838754a..e1d74552612e 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -17,6 +17,7 @@ pub struct UintValueTree { /// If true cannot be simplified or complexified fixed: bool, } + impl UintValueTree { /// Create a new tree /// # Arguments @@ -38,11 +39,14 @@ impl UintValueTree { } } } + impl ValueTree for UintValueTree { type Value = U256; + fn current(&self) -> Self::Value { self.curr } + fn simplify(&mut self) -> bool { if self.fixed || (self.hi <= self.lo) { return false @@ -50,6 +54,7 @@ impl ValueTree for UintValueTree { self.hi = self.curr; self.reposition() } + fn complicate(&mut self) -> bool { if self.fixed || (self.hi <= self.lo) { return false @@ -59,6 +64,7 @@ impl ValueTree for UintValueTree { self.reposition() } } + /// Value tree for unsigned ints (up to uint256). /// The strategy combines 3 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` @@ -78,6 +84,7 @@ pub struct UintStrategy { /// The weight for purely random values random_weight: usize, } + impl UintStrategy { /// Create a new strategy. /// #Arguments @@ -92,6 +99,7 @@ impl UintStrategy { random_weight: 50usize, } } + fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); // Choose if we want values around 0 or max @@ -102,6 +110,7 @@ impl UintStrategy { let start = if is_min { offset } else { max.saturating_sub(offset) }; Ok(UintValueTree::new(start, false)) } + fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree { // generate edge cases if there's no fixtures if self.fixtures.is_empty() { @@ -110,13 +119,17 @@ impl UintStrategy { let idx = runner.rng().gen_range(0..self.fixtures.len()); Ok(UintValueTree::new(self.fixtures[idx], false)) } + fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); + // generate random number of bits uniformly let bits = rng.gen_range(0..=self.bits); + // init 2 128-bit randoms let mut higher: u128 = rng.gen_range(0..=u128::MAX); let mut lower: u128 = rng.gen_range(0..=u128::MAX); + // cut 2 randoms according to bits size match bits { x if x < 128 => { @@ -126,6 +139,7 @@ impl UintStrategy { x if (128..256).contains(&x) => higher &= (1u128 << (x - 128)) - 1, _ => {} }; + // init U256 from 2 randoms let mut inner: [u64; 4] = [0; 4]; let mask64 = (1 << 65) - 1; @@ -138,6 +152,7 @@ impl UintStrategy { Ok(UintValueTree::new(start, false)) } } + impl Strategy for UintStrategy { type Tree = UintValueTree; type Value = U256;