1use crate::{
2 maintain::MAX_QUEUED_TRANSACTION_LIFETIME,
3 pool::{NEW_TX_LISTENER_BUFFER_SIZE, PENDING_TX_LISTENER_BUFFER_SIZE},
4 PoolSize, TransactionOrigin,
5};
6use alloy_consensus::constants::EIP4844_TX_TYPE_ID;
7use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE};
8use alloy_primitives::Address;
9use std::{collections::HashSet, ops::Mul, time::Duration};
10
11pub const TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER: usize = 16;
13
14pub const TXPOOL_SUBPOOL_MAX_TXS_DEFAULT: usize = 10_000;
16
17pub const TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT: usize = 20;
19
20pub const DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS: usize = 1;
22
23pub const DEFAULT_PRICE_BUMP: u128 = 10;
25
26pub const REPLACE_BLOB_PRICE_BUMP: u128 = 100;
30
31pub const MAX_NEW_PENDING_TXS_NOTIFICATIONS: usize = 200;
33
34#[derive(Debug, Clone)]
36pub struct PoolConfig {
37 pub pending_limit: SubPoolLimit,
39 pub basefee_limit: SubPoolLimit,
41 pub queued_limit: SubPoolLimit,
43 pub blob_limit: SubPoolLimit,
45 pub blob_cache_size: Option<u32>,
47 pub max_account_slots: usize,
49 pub price_bumps: PriceBumpConfig,
51 pub minimal_protocol_basefee: u64,
53 pub minimum_priority_fee: Option<u128>,
55 pub gas_limit: u64,
57 pub local_transactions_config: LocalTransactionConfig,
60 pub pending_tx_listener_buffer_size: usize,
62 pub new_tx_listener_buffer_size: usize,
64 pub max_new_pending_txs_notifications: usize,
66 pub max_queued_lifetime: Duration,
68}
69
70impl PoolConfig {
71 pub const fn with_disabled_protocol_base_fee(self) -> Self {
75 self.with_protocol_base_fee(0)
76 }
77
78 pub const fn with_protocol_base_fee(mut self, protocol_base_fee: u64) -> Self {
83 self.minimal_protocol_basefee = protocol_base_fee;
84 self
85 }
86
87 #[inline]
89 pub const fn is_exceeded(&self, pool_size: PoolSize) -> bool {
90 self.blob_limit.is_exceeded(pool_size.blob, pool_size.blob_size) ||
91 self.pending_limit.is_exceeded(pool_size.pending, pool_size.pending_size) ||
92 self.basefee_limit.is_exceeded(pool_size.basefee, pool_size.basefee_size) ||
93 self.queued_limit.is_exceeded(pool_size.queued, pool_size.queued_size)
94 }
95}
96
97impl Default for PoolConfig {
98 fn default() -> Self {
99 Self {
100 pending_limit: Default::default(),
101 basefee_limit: Default::default(),
102 queued_limit: Default::default(),
103 blob_limit: Default::default(),
104 blob_cache_size: None,
105 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
106 price_bumps: Default::default(),
107 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
108 minimum_priority_fee: None,
109 gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
110 local_transactions_config: Default::default(),
111 pending_tx_listener_buffer_size: PENDING_TX_LISTENER_BUFFER_SIZE,
112 new_tx_listener_buffer_size: NEW_TX_LISTENER_BUFFER_SIZE,
113 max_new_pending_txs_notifications: MAX_NEW_PENDING_TXS_NOTIFICATIONS,
114 max_queued_lifetime: MAX_QUEUED_TRANSACTION_LIFETIME,
115 }
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub struct SubPoolLimit {
122 pub max_txs: usize,
124 pub max_size: usize,
126}
127
128impl SubPoolLimit {
129 pub const fn new(max_txs: usize, max_size: usize) -> Self {
131 Self { max_txs, max_size }
132 }
133
134 pub const fn max() -> Self {
136 Self::new(usize::MAX, usize::MAX)
137 }
138
139 #[inline]
141 pub const fn is_exceeded(&self, txs: usize, size: usize) -> bool {
142 self.max_txs < txs || self.max_size < size
143 }
144}
145
146impl Mul<usize> for SubPoolLimit {
147 type Output = Self;
148
149 fn mul(self, rhs: usize) -> Self::Output {
150 let Self { max_txs, max_size } = self;
151 Self { max_txs: max_txs * rhs, max_size: max_size * rhs }
152 }
153}
154
155impl Default for SubPoolLimit {
156 fn default() -> Self {
157 Self {
159 max_txs: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
160 max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT * 1024 * 1024,
161 }
162 }
163}
164
165#[derive(Debug, Clone, Copy, Eq, PartialEq)]
167pub struct PriceBumpConfig {
168 pub default_price_bump: u128,
170 pub replace_blob_tx_price_bump: u128,
172}
173
174impl PriceBumpConfig {
175 #[inline]
177 pub const fn price_bump(&self, tx_type: u8) -> u128 {
178 if tx_type == EIP4844_TX_TYPE_ID {
179 return self.replace_blob_tx_price_bump
180 }
181 self.default_price_bump
182 }
183}
184
185impl Default for PriceBumpConfig {
186 fn default() -> Self {
187 Self {
188 default_price_bump: DEFAULT_PRICE_BUMP,
189 replace_blob_tx_price_bump: REPLACE_BLOB_PRICE_BUMP,
190 }
191 }
192}
193
194#[derive(Debug, Clone, Eq, PartialEq)]
197pub struct LocalTransactionConfig {
198 pub no_exemptions: bool,
205 pub local_addresses: HashSet<Address>,
207 pub propagate_local_transactions: bool,
209}
210
211impl Default for LocalTransactionConfig {
212 fn default() -> Self {
213 Self {
214 no_exemptions: false,
215 local_addresses: HashSet::default(),
216 propagate_local_transactions: true,
217 }
218 }
219}
220
221impl LocalTransactionConfig {
222 #[inline]
224 pub const fn no_local_exemptions(&self) -> bool {
225 self.no_exemptions
226 }
227
228 #[inline]
230 pub fn contains_local_address(&self, address: &Address) -> bool {
231 self.local_addresses.contains(address)
232 }
233
234 #[inline]
238 pub fn is_local(&self, origin: TransactionOrigin, sender: &Address) -> bool {
239 if self.no_local_exemptions() {
240 return false
241 }
242 origin.is_local() || self.contains_local_address(sender)
243 }
244
245 pub const fn set_propagate_local_transactions(mut self, propagate_local_txs: bool) -> Self {
252 self.propagate_local_transactions = propagate_local_txs;
253 self
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_pool_size_sanity() {
263 let pool_size = PoolSize {
264 pending: 0,
265 pending_size: 0,
266 basefee: 0,
267 basefee_size: 0,
268 queued: 0,
269 queued_size: 0,
270 blob: 0,
271 blob_size: 0,
272 ..Default::default()
273 };
274
275 let config = PoolConfig::default();
277 assert!(!config.is_exceeded(pool_size));
278
279 let pool_size = PoolSize {
281 pending: config.pending_limit.max_txs + 1,
282 pending_size: config.pending_limit.max_size + 1,
283 basefee: config.basefee_limit.max_txs + 1,
284 basefee_size: config.basefee_limit.max_size + 1,
285 queued: config.queued_limit.max_txs + 1,
286 queued_size: config.queued_limit.max_size + 1,
287 blob: config.blob_limit.max_txs + 1,
288 blob_size: config.blob_limit.max_size + 1,
289 ..Default::default()
290 };
291
292 assert!(config.is_exceeded(pool_size));
294 }
295
296 #[test]
297 fn test_default_config() {
298 let config = LocalTransactionConfig::default();
299
300 assert!(!config.no_exemptions);
301 assert!(config.local_addresses.is_empty());
302 assert!(config.propagate_local_transactions);
303 }
304
305 #[test]
306 fn test_no_local_exemptions() {
307 let config = LocalTransactionConfig { no_exemptions: true, ..Default::default() };
308 assert!(config.no_local_exemptions());
309 }
310
311 #[test]
312 fn test_contains_local_address() {
313 let address = Address::new([1; 20]);
314 let mut local_addresses = HashSet::default();
315 local_addresses.insert(address);
316
317 let config = LocalTransactionConfig { local_addresses, ..Default::default() };
318
319 assert!(config.contains_local_address(&address));
321
322 assert!(!config.contains_local_address(&Address::new([2; 20])));
324 }
325
326 #[test]
327 fn test_is_local_with_no_exemptions() {
328 let address = Address::new([1; 20]);
329 let config = LocalTransactionConfig {
330 no_exemptions: true,
331 local_addresses: HashSet::default(),
332 ..Default::default()
333 };
334
335 assert!(!config.is_local(TransactionOrigin::Local, &address));
337 }
338
339 #[test]
340 fn test_is_local_without_no_exemptions() {
341 let address = Address::new([1; 20]);
342 let mut local_addresses = HashSet::default();
343 local_addresses.insert(address);
344
345 let config =
346 LocalTransactionConfig { no_exemptions: false, local_addresses, ..Default::default() };
347
348 assert!(config.is_local(TransactionOrigin::Local, &Address::new([2; 20])));
350 assert!(config.is_local(TransactionOrigin::Local, &address));
351
352 assert!(config.is_local(TransactionOrigin::External, &address));
354 assert!(!config.is_local(TransactionOrigin::External, &Address::new([2; 20])));
356 }
357
358 #[test]
359 fn test_set_propagate_local_transactions() {
360 let config = LocalTransactionConfig::default();
361 assert!(config.propagate_local_transactions);
362
363 let new_config = config.set_propagate_local_transactions(false);
364 assert!(!new_config.propagate_local_transactions);
365 }
366
367 #[test]
368 fn scale_pool_limit() {
369 let limit = SubPoolLimit::default();
370 let double = limit * 2;
371 assert_eq!(
372 double,
373 SubPoolLimit { max_txs: limit.max_txs * 2, max_size: limit.max_size * 2 }
374 )
375 }
376}