transaction_fuzzer/account_universe/
universe.rs1use proptest::{
7 collection::{SizeRange, vec},
8 prelude::*,
9};
10use proptest_derive::Arbitrary;
11
12use crate::{
13 account_universe::{
14 account::{AccountCurrent, AccountData},
15 default_num_accounts, default_num_transactions,
16 helpers::{Index, pick_slice_idxs},
17 },
18 executor::Executor,
19};
20
21const PICK_SIZE: usize = 3;
22
23#[derive(Debug)]
25pub struct AccountUniverseGen {
26 accounts: Vec<AccountData>,
27 pick_style: AccountPickStyle,
28}
29
30#[derive(Clone, Debug)]
33pub struct AccountUniverse {
34 accounts: Vec<AccountCurrent>,
35 picker: AccountPicker,
36 ignore_new_accounts: bool,
39}
40
41#[derive(Arbitrary, Clone, Debug)]
43pub struct AccountPairGen {
44 indices: [Index; PICK_SIZE],
45 reverse: bool,
48}
49
50#[derive(Clone, Debug)]
52pub enum AccountPickStyle {
53 Unlimited,
55 Limited(usize),
57}
58
59#[derive(Clone, Debug)]
60enum AccountPicker {
61 Unlimited(usize),
62 Limited(Vec<(usize, usize)>),
64}
65
66impl AccountUniverseGen {
67 pub fn strategy(
70 num_accounts: impl Into<SizeRange>,
71 balance_strategy: impl Strategy<Value = u64>,
72 ) -> impl Strategy<Value = Self> {
73 vec(AccountData::strategy(balance_strategy), num_accounts).prop_map(|accounts| Self {
79 accounts,
80 pick_style: AccountPickStyle::Unlimited,
81 })
82 }
83
84 pub fn success_strategy(min_accounts: usize) -> impl Strategy<Value = Self> {
88 let min_balance = (100_000 * (default_num_transactions()) * 5) as u64;
91 let max_balance = min_balance * 10;
92 Self::strategy(
93 min_accounts..default_num_accounts(),
94 min_balance..max_balance,
95 )
96 }
97
98 pub fn set_pick_style(&mut self, pick_style: AccountPickStyle) -> &mut Self {
100 self.pick_style = pick_style;
101 self
102 }
103
104 pub fn num_accounts(&self) -> usize {
106 self.accounts.len()
107 }
108
109 pub fn setup(self, executor: &mut Executor) -> AccountUniverse {
112 for account_data in &self.accounts {
113 executor.add_objects(&account_data.coins);
114 }
115
116 AccountUniverse::new(self.accounts, self.pick_style, false)
117 }
118}
119
120impl AccountUniverse {
121 fn new(
122 accounts: Vec<AccountData>,
123 pick_style: AccountPickStyle,
124 ignore_new_accounts: bool,
125 ) -> Self {
126 let accounts: Vec<_> = accounts.into_iter().map(AccountCurrent::new).collect();
127 let picker = AccountPicker::new(pick_style, accounts.len());
128
129 Self {
130 accounts,
131 picker,
132 ignore_new_accounts,
133 }
134 }
135
136 pub fn num_accounts(&self) -> usize {
141 self.accounts.len()
142 }
143
144 pub fn accounts(&self) -> &[AccountCurrent] {
149 &self.accounts
150 }
151
152 pub fn add_account(&mut self, account_data: AccountData) {
158 if !self.ignore_new_accounts {
159 self.accounts.push(AccountCurrent::new(account_data));
160 }
161 }
162
163 pub fn pick(&mut self, index: Index) -> (usize, &mut AccountCurrent) {
165 let idx = self.picker.pick(index);
166 (idx, &mut self.accounts[idx])
167 }
168}
169
170impl AccountPicker {
171 fn new(pick_style: AccountPickStyle, num_accounts: usize) -> Self {
172 match pick_style {
173 AccountPickStyle::Unlimited => AccountPicker::Unlimited(num_accounts),
174 AccountPickStyle::Limited(limit) => {
175 let remaining = (0..num_accounts).map(|idx| (idx, limit)).collect();
176 AccountPicker::Limited(remaining)
177 }
178 }
179 }
180
181 fn pick(&mut self, index: Index) -> usize {
182 match self {
183 AccountPicker::Unlimited(num_accounts) => index.index(*num_accounts),
184 AccountPicker::Limited(remaining) => {
185 let remaining_idx = index.index(remaining.len());
186 Self::pick_limited(remaining, remaining_idx)
187 }
188 }
189 }
190
191 fn pick_account_indices(&mut self, indexes: &[Index; PICK_SIZE]) -> [usize; PICK_SIZE] {
192 match self {
193 AccountPicker::Unlimited(num_accounts) => {
194 Self::pick_account_indices_impl(*num_accounts, indexes)
195 }
196 AccountPicker::Limited(remaining) => {
197 Self::pick_account_indices_impl(remaining.len(), indexes).map(|idx| {
198 let (account_idx, _) = remaining[idx];
199 account_idx
200 })
201 }
202 }
203 }
204
205 fn pick_account_indices_impl(max: usize, indexes: &[Index; PICK_SIZE]) -> [usize; PICK_SIZE] {
206 let idxs = pick_slice_idxs(max, indexes);
207 assert_eq!(idxs.len(), PICK_SIZE);
208 let idxs: [usize; PICK_SIZE] = idxs[0..PICK_SIZE].try_into().unwrap();
209 assert!(
210 idxs[0] < idxs[1],
211 "pick_slice_idxs should return sorted order"
212 );
213 idxs
214 }
215
216 fn pick_limited(remaining: &mut Vec<(usize, usize)>, remaining_idx: usize) -> usize {
217 let (account_idx, times_remaining) = {
218 let (account_idx, times_remaining) = &mut remaining[remaining_idx];
219 *times_remaining -= 1;
220 (*account_idx, *times_remaining)
221 };
222
223 if times_remaining == 0 {
224 remaining.remove(remaining_idx);
226 }
227
228 account_idx
229 }
230}
231
232impl AccountPairGen {
233 pub fn pick<'a>(&self, universe: &'a mut AccountUniverse) -> AccountTriple<'a> {
236 let [low_idx, mid_idx, high_idx] = universe.picker.pick_account_indices(&self.indices);
237 let (head, tail) = universe.accounts.split_at_mut(low_idx + 1);
240 let (mid, tail) = tail.split_at_mut(mid_idx - low_idx);
241 let (low_account, mid_account, high_account) = (
242 head.last_mut().unwrap(),
243 mid.last_mut().unwrap(),
244 tail.last_mut().unwrap(),
245 );
246
247 if self.reverse {
248 AccountTriple {
249 idx_1: high_idx,
250 idx_2: mid_idx,
251 idx_3: low_idx,
252 account_1: high_account,
253 account_2: mid_account,
254 account_3: low_account,
255 }
256 } else {
257 AccountTriple {
258 idx_1: low_idx,
259 idx_2: mid_idx,
260 idx_3: high_idx,
261 account_1: low_account,
262 account_2: mid_account,
263 account_3: high_account,
264 }
265 }
266 }
267}
268
269pub struct AccountTriple<'a> {
272 pub idx_1: usize,
274 pub idx_2: usize,
276 pub idx_3: usize,
278 pub account_1: &'a mut AccountCurrent,
280 pub account_2: &'a mut AccountCurrent,
282 pub account_3: &'a mut AccountCurrent,
284}