reth_transaction_pool/test_utils/
pool.rs#![allow(dead_code)]
use crate::{
pool::{txpool::TxPool, AddedTransaction},
test_utils::{MockOrdering, MockTransactionDistribution, MockTransactionFactory},
TransactionOrdering,
};
use alloy_primitives::{Address, U256};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
pub(crate) struct MockPool<T: TransactionOrdering = MockOrdering> {
pool: TxPool<T>,
}
impl MockPool {
fn total_subpool_size(&self) -> usize {
self.pool.pending().len() + self.pool.base_fee().len() + self.pool.queued().len()
}
fn enforce_invariants(&self) {
assert_eq!(
self.pool.len(),
self.total_subpool_size(),
"Tx in AllTransactions and sum(subpools) must match"
);
}
}
impl Default for MockPool {
fn default() -> Self {
Self { pool: TxPool::new(MockOrdering::default(), Default::default()) }
}
}
impl<T: TransactionOrdering> Deref for MockPool<T> {
type Target = TxPool<T>;
fn deref(&self) -> &Self::Target {
&self.pool
}
}
impl<T: TransactionOrdering> DerefMut for MockPool<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.pool
}
}
pub(crate) struct MockTransactionSimulator<R: Rng> {
base_fee: u128,
tx_generator: MockTransactionDistribution,
balances: HashMap<Address, U256>,
nonces: HashMap<Address, u64>,
senders: Vec<Address>,
scenarios: Vec<ScenarioType>,
executed: HashMap<Address, ExecutedScenarios>,
validator: MockTransactionFactory,
rng: R,
}
impl<R: Rng> MockTransactionSimulator<R> {
pub(crate) fn new(mut rng: R, config: MockSimulatorConfig) -> Self {
let senders = config.addresses(&mut rng);
Self {
base_fee: config.base_fee,
balances: senders.iter().copied().map(|a| (a, rng.gen())).collect(),
nonces: senders.iter().copied().map(|a| (a, 0)).collect(),
senders,
scenarios: config.scenarios,
tx_generator: config.tx_generator,
executed: Default::default(),
validator: Default::default(),
rng,
}
}
fn rng_address(&mut self) -> Address {
let idx = self.rng.gen_range(0..self.senders.len());
self.senders[idx]
}
fn rng_scenario(&mut self) -> ScenarioType {
let idx = self.rng.gen_range(0..self.scenarios.len());
self.scenarios[idx].clone()
}
pub(crate) fn next(&mut self, pool: &mut MockPool) {
let sender = self.rng_address();
let scenario = self.rng_scenario();
let on_chain_nonce = self.nonces[&sender];
let on_chain_balance = self.balances[&sender];
match scenario {
ScenarioType::OnchainNonce => {
let tx = self
.tx_generator
.tx(on_chain_nonce, &mut self.rng)
.with_gas_price(self.base_fee);
let valid_tx = self.validator.validated(tx);
let res = pool.add_transaction(valid_tx, on_chain_balance, on_chain_nonce).unwrap();
match res {
AddedTransaction::Pending(_) => {}
AddedTransaction::Parked { .. } => {
panic!("expected pending")
}
}
}
ScenarioType::HigherNonce { .. } => {
unimplemented!()
}
}
pool.enforce_invariants()
}
}
pub(crate) struct MockSimulatorConfig {
pub(crate) num_senders: usize,
pub(crate) scenarios: Vec<ScenarioType>,
pub(crate) base_fee: u128,
pub(crate) tx_generator: MockTransactionDistribution,
}
impl MockSimulatorConfig {
pub(crate) fn addresses(&self, rng: &mut impl rand::Rng) -> Vec<Address> {
std::iter::repeat_with(|| Address::random_with(rng)).take(self.num_senders).collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum ScenarioType {
OnchainNonce,
HigherNonce { skip: u64 },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum Scenario {
OnchainNonce { nonce: u64 },
HigherNonce { onchain: u64, nonce: u64 },
Multi {
scenario: Vec<Scenario>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ExecutedScenario {
balance: U256,
nonce: u64,
scenario: Scenario,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ExecutedScenarios {
sender: Address,
scenarios: Vec<ExecutedScenario>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{MockFeeRange, MockTransactionRatio};
#[test]
fn test_on_chain_nonce_scenario() {
let transaction_ratio = MockTransactionRatio {
legacy_pct: 30,
dynamic_fee_pct: 70,
access_list_pct: 0,
blob_pct: 0,
};
let fee_ranges = MockFeeRange {
gas_price: (10u128..100).into(),
priority_fee: (10u128..100).into(),
max_fee: (100u128..110).into(),
max_fee_blob: (1u128..100).into(),
};
let config = MockSimulatorConfig {
num_senders: 10,
scenarios: vec![ScenarioType::OnchainNonce],
base_fee: 10,
tx_generator: MockTransactionDistribution::new(
transaction_ratio,
fee_ranges,
10..100,
10..100,
),
};
let mut simulator = MockTransactionSimulator::new(rand::thread_rng(), config);
let mut pool = MockPool::default();
simulator.next(&mut pool);
}
}