reth_transaction_pool/test_utils/
pool.rs
1#![allow(dead_code)]
4
5use crate::{
6 pool::{txpool::TxPool, AddedTransaction},
7 test_utils::{MockOrdering, MockTransactionDistribution, MockTransactionFactory},
8 TransactionOrdering,
9};
10use alloy_primitives::{Address, U256};
11use rand::Rng;
12use serde::{Deserialize, Serialize};
13use std::{
14 collections::HashMap,
15 ops::{Deref, DerefMut},
16};
17
18pub(crate) struct MockPool<T: TransactionOrdering = MockOrdering> {
20 pool: TxPool<T>,
22}
23
24impl MockPool {
25 fn total_subpool_size(&self) -> usize {
27 self.pool.pending().len() + self.pool.base_fee().len() + self.pool.queued().len()
28 }
29
30 fn enforce_invariants(&self) {
32 assert_eq!(
33 self.pool.len(),
34 self.total_subpool_size(),
35 "Tx in AllTransactions and sum(subpools) must match"
36 );
37 }
38}
39
40impl Default for MockPool {
41 fn default() -> Self {
42 Self { pool: TxPool::new(MockOrdering::default(), Default::default()) }
43 }
44}
45
46impl<T: TransactionOrdering> Deref for MockPool<T> {
47 type Target = TxPool<T>;
48
49 fn deref(&self) -> &Self::Target {
50 &self.pool
51 }
52}
53
54impl<T: TransactionOrdering> DerefMut for MockPool<T> {
55 fn deref_mut(&mut self) -> &mut Self::Target {
56 &mut self.pool
57 }
58}
59
60pub(crate) struct MockTransactionSimulator<R: Rng> {
62 base_fee: u128,
64 tx_generator: MockTransactionDistribution,
66 balances: HashMap<Address, U256>,
68 nonces: HashMap<Address, u64>,
70 senders: Vec<Address>,
72 scenarios: Vec<ScenarioType>,
74 executed: HashMap<Address, ExecutedScenarios>,
76 validator: MockTransactionFactory,
78 rng: R,
80}
81
82impl<R: Rng> MockTransactionSimulator<R> {
83 pub(crate) fn new(mut rng: R, config: MockSimulatorConfig) -> Self {
85 let senders = config.addresses(&mut rng);
86 Self {
87 base_fee: config.base_fee,
88 balances: senders.iter().copied().map(|a| (a, rng.gen())).collect(),
89 nonces: senders.iter().copied().map(|a| (a, 0)).collect(),
90 senders,
91 scenarios: config.scenarios,
92 tx_generator: config.tx_generator,
93 executed: Default::default(),
94 validator: Default::default(),
95 rng,
96 }
97 }
98
99 fn rng_address(&mut self) -> Address {
101 let idx = self.rng.gen_range(0..self.senders.len());
102 self.senders[idx]
103 }
104
105 fn rng_scenario(&mut self) -> ScenarioType {
107 let idx = self.rng.gen_range(0..self.scenarios.len());
108 self.scenarios[idx].clone()
109 }
110
111 pub(crate) fn next(&mut self, pool: &mut MockPool) {
113 let sender = self.rng_address();
114 let scenario = self.rng_scenario();
115 let on_chain_nonce = self.nonces[&sender];
116 let on_chain_balance = self.balances[&sender];
117
118 match scenario {
119 ScenarioType::OnchainNonce => {
120 let tx = self
121 .tx_generator
122 .tx(on_chain_nonce, &mut self.rng)
123 .with_gas_price(self.base_fee);
124 let valid_tx = self.validator.validated(tx);
125
126 let res = pool.add_transaction(valid_tx, on_chain_balance, on_chain_nonce).unwrap();
127
128 match res {
132 AddedTransaction::Pending(_) => {}
133 AddedTransaction::Parked { .. } => {
134 panic!("expected pending")
135 }
136 }
137
138 }
140 ScenarioType::HigherNonce { .. } => {
141 unimplemented!()
142 }
143 }
144
145 pool.enforce_invariants()
147 }
148}
149
150pub(crate) struct MockSimulatorConfig {
152 pub(crate) num_senders: usize,
154 pub(crate) scenarios: Vec<ScenarioType>,
156 pub(crate) base_fee: u128,
158 pub(crate) tx_generator: MockTransactionDistribution,
160}
161
162impl MockSimulatorConfig {
163 pub(crate) fn addresses(&self, rng: &mut impl rand::Rng) -> Vec<Address> {
165 std::iter::repeat_with(|| Address::random_with(rng)).take(self.num_senders).collect()
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub(crate) enum ScenarioType {
172 OnchainNonce,
173 HigherNonce { skip: u64 },
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
182pub(crate) enum Scenario {
183 OnchainNonce { nonce: u64 },
185 HigherNonce { onchain: u64, nonce: u64 },
187 Multi {
188 scenario: Vec<Scenario>,
190 },
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub(crate) struct ExecutedScenario {
196 balance: U256,
198 nonce: u64,
200 scenario: Scenario,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206pub(crate) struct ExecutedScenarios {
207 sender: Address,
208 scenarios: Vec<ExecutedScenario>,
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::test_utils::{MockFeeRange, MockTransactionRatio};
215
216 #[test]
217 fn test_on_chain_nonce_scenario() {
218 let transaction_ratio = MockTransactionRatio {
219 legacy_pct: 30,
220 dynamic_fee_pct: 70,
221 access_list_pct: 0,
222 blob_pct: 0,
223 };
224
225 let fee_ranges = MockFeeRange {
226 gas_price: (10u128..100).into(),
227 priority_fee: (10u128..100).into(),
228 max_fee: (100u128..110).into(),
229 max_fee_blob: (1u128..100).into(),
230 };
231
232 let config = MockSimulatorConfig {
233 num_senders: 10,
234 scenarios: vec![ScenarioType::OnchainNonce],
235 base_fee: 10,
236 tx_generator: MockTransactionDistribution::new(
237 transaction_ratio,
238 fee_ranges,
239 10..100,
240 10..100,
241 ),
242 };
243 let mut simulator = MockTransactionSimulator::new(rand::thread_rng(), config);
244 let mut pool = MockPool::default();
245
246 simulator.next(&mut pool);
247 }
248}