1use crate::cli::config::RethTransactionPoolConfig;
4use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE};
5use alloy_primitives::Address;
6use clap::Args;
7use reth_cli_util::parse_duration_from_secs_or_ms;
8use reth_transaction_pool::{
9 blobstore::disk::DEFAULT_MAX_CACHED_BLOBS,
10 maintain::MAX_QUEUED_TRANSACTION_LIFETIME,
11 pool::{NEW_TX_LISTENER_BUFFER_SIZE, PENDING_TX_LISTENER_BUFFER_SIZE},
12 validate::DEFAULT_MAX_TX_INPUT_BYTES,
13 LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP,
14 DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS, MAX_NEW_PENDING_TXS_NOTIFICATIONS,
15 REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
16 TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
17};
18use std::time::Duration;
19
20#[derive(Debug, Clone, Args, PartialEq, Eq)]
22#[command(next_help_heading = "TxPool")]
23pub struct TxPoolArgs {
24 #[arg(long = "txpool.pending-max-count", alias = "txpool.pending_max_count", default_value_t = TXPOOL_SUBPOOL_MAX_TXS_DEFAULT)]
26 pub pending_max_count: usize,
27 #[arg(long = "txpool.pending-max-size", alias = "txpool.pending_max_size", default_value_t = TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT)]
29 pub pending_max_size: usize,
30
31 #[arg(long = "txpool.basefee-max-count", alias = "txpool.basefee_max_count", default_value_t = TXPOOL_SUBPOOL_MAX_TXS_DEFAULT)]
33 pub basefee_max_count: usize,
34 #[arg(long = "txpool.basefee-max-size", alias = "txpool.basefee_max_size", default_value_t = TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT)]
36 pub basefee_max_size: usize,
37
38 #[arg(long = "txpool.queued-max-count", alias = "txpool.queued_max_count", default_value_t = TXPOOL_SUBPOOL_MAX_TXS_DEFAULT)]
40 pub queued_max_count: usize,
41 #[arg(long = "txpool.queued-max-size", alias = "txpool.queued_max_size", default_value_t = TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT)]
43 pub queued_max_size: usize,
44
45 #[arg(long = "txpool.blobpool-max-count", alias = "txpool.blobpool_max_count", default_value_t = TXPOOL_SUBPOOL_MAX_TXS_DEFAULT)]
47 pub blobpool_max_count: usize,
48 #[arg(long = "txpool.blobpool-max-size", alias = "txpool.blobpool_max_size", default_value_t = TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT)]
50 pub blobpool_max_size: usize,
51
52 #[arg(long = "txpool.blob-cache-size", alias = "txpool.blob_cache_size")]
54 pub blob_cache_size: Option<u32>,
55
56 #[arg(long = "txpool.max-account-slots", alias = "txpool.max_account_slots", default_value_t = TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER)]
58 pub max_account_slots: usize,
59
60 #[arg(long = "txpool.pricebump", default_value_t = DEFAULT_PRICE_BUMP)]
62 pub price_bump: u128,
63
64 #[arg(long = "txpool.minimal-protocol-fee", default_value_t = MIN_PROTOCOL_BASE_FEE)]
66 pub minimal_protocol_basefee: u64,
67
68 #[arg(long = "txpool.minimum-priority-fee")]
71 pub minimum_priority_fee: Option<u128>,
72
73 #[arg(long = "txpool.gas-limit", default_value_t = ETHEREUM_BLOCK_GAS_LIMIT_30M)]
75 pub enforced_gas_limit: u64,
76
77 #[arg(long = "txpool.max-tx-gas")]
80 pub max_tx_gas_limit: Option<u64>,
81
82 #[arg(long = "blobpool.pricebump", default_value_t = REPLACE_BLOB_PRICE_BUMP)]
84 pub blob_transaction_price_bump: u128,
85
86 #[arg(long = "txpool.max-tx-input-bytes", alias = "txpool.max_tx_input_bytes", default_value_t = DEFAULT_MAX_TX_INPUT_BYTES)]
88 pub max_tx_input_bytes: usize,
89
90 #[arg(long = "txpool.max-cached-entries", alias = "txpool.max_cached_entries", default_value_t = DEFAULT_MAX_CACHED_BLOBS)]
92 pub max_cached_entries: u32,
93
94 #[arg(long = "txpool.nolocals")]
96 pub no_locals: bool,
97 #[arg(long = "txpool.locals")]
99 pub locals: Vec<Address>,
100 #[arg(long = "txpool.no-local-transactions-propagation")]
102 pub no_local_transactions_propagation: bool,
103 #[arg(long = "txpool.additional-validation-tasks", alias = "txpool.additional_validation_tasks", default_value_t = DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS)]
105 pub additional_validation_tasks: usize,
106
107 #[arg(long = "txpool.max-pending-txns", alias = "txpool.max_pending_txns", default_value_t = PENDING_TX_LISTENER_BUFFER_SIZE)]
109 pub pending_tx_listener_buffer_size: usize,
110
111 #[arg(long = "txpool.max-new-txns", alias = "txpool.max_new_txns", default_value_t = NEW_TX_LISTENER_BUFFER_SIZE)]
113 pub new_tx_listener_buffer_size: usize,
114
115 #[arg(long = "txpool.max-new-pending-txs-notifications", alias = "txpool.max-new-pending-txs-notifications", default_value_t = MAX_NEW_PENDING_TXS_NOTIFICATIONS)]
118 pub max_new_pending_txs_notifications: usize,
119
120 #[arg(long = "txpool.lifetime", value_parser = parse_duration_from_secs_or_ms, default_value = "10800", value_name = "DURATION")]
122 pub max_queued_lifetime: Duration,
123
124 #[arg(long = "txpool.transactions-backup", alias = "txpool.journal", value_name = "PATH")]
126 pub transactions_backup_path: Option<std::path::PathBuf>,
127
128 #[arg(
130 long = "txpool.disable-transactions-backup",
131 alias = "txpool.disable-journal",
132 conflicts_with = "transactions_backup_path"
133 )]
134 pub disable_transactions_backup: bool,
135
136 #[arg(long = "txpool.max-batch-size", default_value_t = 1)]
138 pub max_batch_size: usize,
139}
140
141impl TxPoolArgs {
142 pub const fn with_disabled_protocol_base_fee(self) -> Self {
146 self.with_protocol_base_fee(0)
147 }
148
149 pub const fn with_protocol_base_fee(mut self, protocol_base_fee: u64) -> Self {
154 self.minimal_protocol_basefee = protocol_base_fee;
155 self
156 }
157}
158
159impl Default for TxPoolArgs {
160 fn default() -> Self {
161 Self {
162 pending_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
163 pending_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
164 basefee_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
165 basefee_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
166 queued_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
167 queued_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
168 blobpool_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
169 blobpool_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
170 blob_cache_size: None,
171 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
172 price_bump: DEFAULT_PRICE_BUMP,
173 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
174 minimum_priority_fee: None,
175 enforced_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
176 max_tx_gas_limit: None,
177 blob_transaction_price_bump: REPLACE_BLOB_PRICE_BUMP,
178 max_tx_input_bytes: DEFAULT_MAX_TX_INPUT_BYTES,
179 max_cached_entries: DEFAULT_MAX_CACHED_BLOBS,
180 no_locals: false,
181 locals: Default::default(),
182 no_local_transactions_propagation: false,
183 additional_validation_tasks: DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS,
184 pending_tx_listener_buffer_size: PENDING_TX_LISTENER_BUFFER_SIZE,
185 new_tx_listener_buffer_size: NEW_TX_LISTENER_BUFFER_SIZE,
186 max_new_pending_txs_notifications: MAX_NEW_PENDING_TXS_NOTIFICATIONS,
187 max_queued_lifetime: MAX_QUEUED_TRANSACTION_LIFETIME,
188 transactions_backup_path: None,
189 disable_transactions_backup: false,
190 max_batch_size: 1,
191 }
192 }
193}
194
195impl RethTransactionPoolConfig for TxPoolArgs {
196 fn pool_config(&self) -> PoolConfig {
198 PoolConfig {
199 local_transactions_config: LocalTransactionConfig {
200 no_exemptions: self.no_locals,
201 local_addresses: self.locals.clone().into_iter().collect(),
202 propagate_local_transactions: !self.no_local_transactions_propagation,
203 },
204 pending_limit: SubPoolLimit {
205 max_txs: self.pending_max_count,
206 max_size: self.pending_max_size.saturating_mul(1024 * 1024),
207 },
208 basefee_limit: SubPoolLimit {
209 max_txs: self.basefee_max_count,
210 max_size: self.basefee_max_size.saturating_mul(1024 * 1024),
211 },
212 queued_limit: SubPoolLimit {
213 max_txs: self.queued_max_count,
214 max_size: self.queued_max_size.saturating_mul(1024 * 1024),
215 },
216 blob_limit: SubPoolLimit {
217 max_txs: self.blobpool_max_count,
218 max_size: self.blobpool_max_size.saturating_mul(1024 * 1024),
219 },
220 blob_cache_size: self.blob_cache_size,
221 max_account_slots: self.max_account_slots,
222 price_bumps: PriceBumpConfig {
223 default_price_bump: self.price_bump,
224 replace_blob_tx_price_bump: self.blob_transaction_price_bump,
225 },
226 minimal_protocol_basefee: self.minimal_protocol_basefee,
227 minimum_priority_fee: self.minimum_priority_fee,
228 gas_limit: self.enforced_gas_limit,
229 pending_tx_listener_buffer_size: self.pending_tx_listener_buffer_size,
230 new_tx_listener_buffer_size: self.new_tx_listener_buffer_size,
231 max_new_pending_txs_notifications: self.max_new_pending_txs_notifications,
232 max_queued_lifetime: self.max_queued_lifetime,
233 }
234 }
235
236 fn max_batch_size(&self) -> usize {
238 self.max_batch_size
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use clap::Parser;
246
247 #[derive(Parser)]
249 struct CommandParser<T: Args> {
250 #[command(flatten)]
251 args: T,
252 }
253
254 #[test]
255 fn txpool_args_default_sanity_test() {
256 let default_args = TxPoolArgs::default();
257 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
258 assert_eq!(args, default_args);
259 }
260
261 #[test]
262 fn txpool_parse_locals() {
263 let args = CommandParser::<TxPoolArgs>::parse_from([
264 "reth",
265 "--txpool.locals",
266 "0x0000000000000000000000000000000000000000",
267 ])
268 .args;
269 assert_eq!(args.locals, vec![Address::ZERO]);
270 }
271
272 #[test]
273 fn txpool_parse_max_tx_lifetime() {
274 let args =
276 CommandParser::<TxPoolArgs>::parse_from(["reth", "--txpool.lifetime", "300"]).args;
277 assert_eq!(args.max_queued_lifetime, Duration::from_secs(300));
278
279 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
281 assert_eq!(args.max_queued_lifetime, Duration::from_secs(3 * 60 * 60)); }
283
284 #[test]
285 fn txpool_parse_max_tx_lifetime_invalid() {
286 let result =
287 CommandParser::<TxPoolArgs>::try_parse_from(["reth", "--txpool.lifetime", "invalid"]);
288
289 assert!(result.is_err(), "Expected an error for invalid duration");
290 }
291}