foundry_evm_fuzz/strategies/
uint.rs
1use alloy_dyn_abi::{DynSolType, DynSolValue};
2use alloy_primitives::U256;
3use proptest::{
4 strategy::{NewTree, Strategy, ValueTree},
5 test_runner::TestRunner,
6};
7use rand::Rng;
8
9pub struct UintValueTree {
11 lo: U256,
13 curr: U256,
15 hi: U256,
17 fixed: bool,
19}
20
21impl UintValueTree {
22 fn new(start: U256, fixed: bool) -> Self {
27 Self { lo: U256::ZERO, curr: start, hi: start, fixed }
28 }
29
30 fn reposition(&mut self) -> bool {
31 let interval = self.hi - self.lo;
32 let new_mid = self.lo + interval / U256::from(2);
33
34 if new_mid == self.curr {
35 false
36 } else {
37 self.curr = new_mid;
38 true
39 }
40 }
41}
42
43impl ValueTree for UintValueTree {
44 type Value = U256;
45
46 fn current(&self) -> Self::Value {
47 self.curr
48 }
49
50 fn simplify(&mut self) -> bool {
51 if self.fixed || (self.hi <= self.lo) {
52 return false
53 }
54 self.hi = self.curr;
55 self.reposition()
56 }
57
58 fn complicate(&mut self) -> bool {
59 if self.fixed || (self.hi <= self.lo) {
60 return false
61 }
62
63 self.lo = self.curr + U256::from(1);
64 self.reposition()
65 }
66}
67
68#[derive(Debug)]
83pub struct UintStrategy {
84 bits: usize,
86 fixtures: Vec<DynSolValue>,
88 edge_weight: usize,
90 fixtures_weight: usize,
92 random_weight: usize,
94}
95
96impl UintStrategy {
97 pub fn new(bits: usize, fixtures: Option<&[DynSolValue]>) -> Self {
102 Self {
103 bits,
104 fixtures: Vec::from(fixtures.unwrap_or_default()),
105 edge_weight: 10usize,
106 fixtures_weight: 40usize,
107 random_weight: 50usize,
108 }
109 }
110
111 fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
112 let rng = runner.rng();
113 let is_min = rng.gen_bool(0.5);
115 let offset = U256::from(rng.gen_range(0..4));
116 let start = if is_min { offset } else { self.type_max().saturating_sub(offset) };
117 Ok(UintValueTree::new(start, false))
118 }
119
120 fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
121 if self.fixtures.is_empty() {
123 return self.generate_random_tree(runner)
124 }
125
126 let fixture = &self.fixtures[runner.rng().gen_range(0..self.fixtures.len())];
128 if let Some(uint_fixture) = fixture.as_uint() {
129 if uint_fixture.1 == self.bits {
130 return Ok(UintValueTree::new(uint_fixture.0, false));
131 }
132 }
133
134 error!("{:?} is not a valid {} fixture", fixture, DynSolType::Uint(self.bits));
136 self.generate_random_tree(runner)
137 }
138
139 fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
140 let rng = runner.rng();
141
142 let bits = rng.gen_range(0..=self.bits);
144
145 let mut higher: u128 = rng.gen_range(0..=u128::MAX);
147 let mut lower: u128 = rng.gen_range(0..=u128::MAX);
148
149 match bits {
151 x if x < 128 => {
152 lower &= (1u128 << x) - 1;
153 higher = 0;
154 }
155 x if (128..256).contains(&x) => higher &= (1u128 << (x - 128)) - 1,
156 _ => {}
157 };
158
159 let mut inner: [u64; 4] = [0; 4];
161 let mask64 = (1 << 65) - 1;
162 inner[0] = (lower & mask64) as u64;
163 inner[1] = (lower >> 64) as u64;
164 inner[2] = (higher & mask64) as u64;
165 inner[3] = (higher >> 64) as u64;
166 let start: U256 = U256::from_limbs(inner);
167
168 Ok(UintValueTree::new(start, false))
169 }
170
171 fn type_max(&self) -> U256 {
172 if self.bits < 256 {
173 (U256::from(1) << self.bits) - U256::from(1)
174 } else {
175 U256::MAX
176 }
177 }
178}
179
180impl Strategy for UintStrategy {
181 type Tree = UintValueTree;
182 type Value = U256;
183 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
184 let total_weight = self.random_weight + self.fixtures_weight + self.edge_weight;
185 let bias = runner.rng().gen_range(0..total_weight);
186 match bias {
188 x if x < self.edge_weight => self.generate_edge_tree(runner),
189 x if x < self.edge_weight + self.fixtures_weight => self.generate_fixtures_tree(runner),
190 _ => self.generate_random_tree(runner),
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use crate::strategies::uint::UintValueTree;
198 use alloy_primitives::U256;
199 use proptest::strategy::ValueTree;
200
201 #[test]
202 fn test_uint_tree_complicate_max() {
203 let mut uint_tree = UintValueTree::new(U256::MAX, false);
204 assert_eq!(uint_tree.hi, U256::MAX);
205 assert_eq!(uint_tree.curr, U256::MAX);
206 uint_tree.complicate();
207 assert_eq!(uint_tree.lo, U256::MIN);
208 }
209}