reth_transaction_pool/test_utils/
pool.rs1#![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 std::{
13 collections::HashMap,
14 ops::{Deref, DerefMut},
15};
16
17pub(crate) struct MockPool<T: TransactionOrdering = MockOrdering> {
19 pool: TxPool<T>,
21}
22
23impl MockPool {
24 fn total_subpool_size(&self) -> usize {
26 self.pool.pending().len() + self.pool.base_fee().len() + self.pool.queued().len()
27 }
28
29 fn enforce_invariants(&self) {
31 assert_eq!(
32 self.pool.len(),
33 self.total_subpool_size(),
34 "Tx in AllTransactions and sum(subpools) must match"
35 );
36 }
37}
38
39impl Default for MockPool {
40 fn default() -> Self {
41 Self { pool: TxPool::new(MockOrdering::default(), Default::default()) }
42 }
43}
44
45impl<T: TransactionOrdering> Deref for MockPool<T> {
46 type Target = TxPool<T>;
47
48 fn deref(&self) -> &Self::Target {
49 &self.pool
50 }
51}
52
53impl<T: TransactionOrdering> DerefMut for MockPool<T> {
54 fn deref_mut(&mut self) -> &mut Self::Target {
55 &mut self.pool
56 }
57}
58
59pub(crate) struct MockTransactionSimulator<R: Rng> {
61 base_fee: u128,
63 tx_generator: MockTransactionDistribution,
65 balances: HashMap<Address, U256>,
67 nonces: HashMap<Address, u64>,
69 senders: Vec<Address>,
71 scenarios: Vec<ScenarioType>,
73 executed: HashMap<Address, ExecutedScenarios>,
75 validator: MockTransactionFactory,
77 rng: R,
79}
80
81impl<R: Rng> MockTransactionSimulator<R> {
82 pub(crate) fn new(mut rng: R, config: MockSimulatorConfig) -> Self {
84 let senders = config.addresses(&mut rng);
85 Self {
86 base_fee: config.base_fee,
87 balances: senders.iter().copied().map(|a| (a, rng.random())).collect(),
88 nonces: senders.iter().copied().map(|a| (a, 0)).collect(),
89 senders,
90 scenarios: config.scenarios,
91 tx_generator: config.tx_generator,
92 executed: Default::default(),
93 validator: Default::default(),
94 rng,
95 }
96 }
97
98 fn rng_address(&mut self) -> Address {
100 let idx = self.rng.random_range(0..self.senders.len());
101 self.senders[idx]
102 }
103
104 fn rng_scenario(&mut self) -> ScenarioType {
106 let idx = self.rng.random_range(0..self.scenarios.len());
107 self.scenarios[idx].clone()
108 }
109
110 pub(crate) fn next(&mut self, pool: &mut MockPool) {
112 let sender = self.rng_address();
113 let scenario = self.rng_scenario();
114 let on_chain_nonce = self.nonces[&sender];
115 let on_chain_balance = self.balances[&sender];
116
117 match scenario {
118 ScenarioType::OnchainNonce => {
119 let tx = self
120 .tx_generator
121 .tx(on_chain_nonce, &mut self.rng)
122 .with_gas_price(self.base_fee);
123 let valid_tx = self.validator.validated(tx);
124
125 let res =
126 pool.add_transaction(valid_tx, on_chain_balance, on_chain_nonce, None).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)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub(crate) enum ScenarioType {
173 OnchainNonce,
174 HigherNonce { skip: u64 },
175}
176
177#[derive(Debug, Clone)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184pub(crate) enum Scenario {
185 OnchainNonce { nonce: u64 },
187 HigherNonce { onchain: u64, nonce: u64 },
189 Multi {
190 scenario: Vec<Scenario>,
192 },
193}
194
195#[derive(Debug, Clone)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198pub(crate) struct ExecutedScenario {
199 balance: U256,
201 nonce: u64,
203 scenario: Scenario,
205}
206
207#[derive(Debug, Clone)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210pub(crate) struct ExecutedScenarios {
211 sender: Address,
212 scenarios: Vec<ExecutedScenario>,
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::test_utils::{MockFeeRange, MockTransactionRatio};
219
220 #[test]
221 fn test_on_chain_nonce_scenario() {
222 let transaction_ratio = MockTransactionRatio {
223 legacy_pct: 30,
224 dynamic_fee_pct: 70,
225 access_list_pct: 0,
226 blob_pct: 0,
227 };
228
229 let fee_ranges = MockFeeRange {
230 gas_price: (10u128..100).try_into().unwrap(),
231 priority_fee: (10u128..100).try_into().unwrap(),
232 max_fee: (100u128..110).try_into().unwrap(),
233 max_fee_blob: (1u128..100).try_into().unwrap(),
234 };
235
236 let config = MockSimulatorConfig {
237 num_senders: 10,
238 scenarios: vec![ScenarioType::OnchainNonce],
239 base_fee: 10,
240 tx_generator: MockTransactionDistribution::new(
241 transaction_ratio,
242 fee_ranges,
243 10..100,
244 10..100,
245 ),
246 };
247 let mut simulator = MockTransactionSimulator::new(rand::rng(), config);
248 let mut pool = MockPool::default();
249
250 simulator.next(&mut pool);
251 }
252}