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.disable-blobs-support", alias = "txpool.disable_blobs_support", default_value_t = false, conflicts_with_all = ["blobpool_max_count", "blobpool_max_size", "blob_cache_size", "blob_transaction_price_bump"])]
58 pub disable_blobs_support: bool,
59
60 #[arg(long = "txpool.max-account-slots", alias = "txpool.max_account_slots", default_value_t = TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER)]
62 pub max_account_slots: usize,
63
64 #[arg(long = "txpool.pricebump", default_value_t = DEFAULT_PRICE_BUMP)]
66 pub price_bump: u128,
67
68 #[arg(long = "txpool.minimal-protocol-fee", default_value_t = MIN_PROTOCOL_BASE_FEE)]
70 pub minimal_protocol_basefee: u64,
71
72 #[arg(long = "txpool.minimum-priority-fee")]
75 pub minimum_priority_fee: Option<u128>,
76
77 #[arg(long = "txpool.gas-limit", default_value_t = ETHEREUM_BLOCK_GAS_LIMIT_30M)]
79 pub enforced_gas_limit: u64,
80
81 #[arg(long = "txpool.max-tx-gas")]
84 pub max_tx_gas_limit: Option<u64>,
85
86 #[arg(long = "blobpool.pricebump", default_value_t = REPLACE_BLOB_PRICE_BUMP)]
88 pub blob_transaction_price_bump: u128,
89
90 #[arg(long = "txpool.max-tx-input-bytes", alias = "txpool.max_tx_input_bytes", default_value_t = DEFAULT_MAX_TX_INPUT_BYTES)]
92 pub max_tx_input_bytes: usize,
93
94 #[arg(long = "txpool.max-cached-entries", alias = "txpool.max_cached_entries", default_value_t = DEFAULT_MAX_CACHED_BLOBS)]
96 pub max_cached_entries: u32,
97
98 #[arg(long = "txpool.nolocals")]
100 pub no_locals: bool,
101 #[arg(long = "txpool.locals")]
103 pub locals: Vec<Address>,
104 #[arg(long = "txpool.no-local-transactions-propagation")]
106 pub no_local_transactions_propagation: bool,
107 #[arg(long = "txpool.additional-validation-tasks", alias = "txpool.additional_validation_tasks", default_value_t = DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS)]
109 pub additional_validation_tasks: usize,
110
111 #[arg(long = "txpool.max-pending-txns", alias = "txpool.max_pending_txns", default_value_t = PENDING_TX_LISTENER_BUFFER_SIZE)]
113 pub pending_tx_listener_buffer_size: usize,
114
115 #[arg(long = "txpool.max-new-txns", alias = "txpool.max_new_txns", default_value_t = NEW_TX_LISTENER_BUFFER_SIZE)]
117 pub new_tx_listener_buffer_size: usize,
118
119 #[arg(long = "txpool.max-new-pending-txs-notifications", alias = "txpool.max-new-pending-txs-notifications", default_value_t = MAX_NEW_PENDING_TXS_NOTIFICATIONS)]
122 pub max_new_pending_txs_notifications: usize,
123
124 #[arg(long = "txpool.lifetime", value_parser = parse_duration_from_secs_or_ms, default_value = "10800", value_name = "DURATION")]
126 pub max_queued_lifetime: Duration,
127
128 #[arg(long = "txpool.transactions-backup", alias = "txpool.journal", value_name = "PATH")]
130 pub transactions_backup_path: Option<std::path::PathBuf>,
131
132 #[arg(
134 long = "txpool.disable-transactions-backup",
135 alias = "txpool.disable-journal",
136 conflicts_with = "transactions_backup_path"
137 )]
138 pub disable_transactions_backup: bool,
139
140 #[arg(long = "txpool.max-batch-size", default_value_t = 1)]
142 pub max_batch_size: usize,
143}
144
145impl TxPoolArgs {
146 pub const fn with_disabled_protocol_base_fee(self) -> Self {
150 self.with_protocol_base_fee(0)
151 }
152
153 pub const fn with_protocol_base_fee(mut self, protocol_base_fee: u64) -> Self {
158 self.minimal_protocol_basefee = protocol_base_fee;
159 self
160 }
161}
162
163impl Default for TxPoolArgs {
164 fn default() -> Self {
165 Self {
166 pending_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
167 pending_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
168 basefee_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
169 basefee_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
170 queued_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
171 queued_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
172 blobpool_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
173 blobpool_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
174 blob_cache_size: None,
175 disable_blobs_support: false,
176 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
177 price_bump: DEFAULT_PRICE_BUMP,
178 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
179 minimum_priority_fee: None,
180 enforced_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
181 max_tx_gas_limit: None,
182 blob_transaction_price_bump: REPLACE_BLOB_PRICE_BUMP,
183 max_tx_input_bytes: DEFAULT_MAX_TX_INPUT_BYTES,
184 max_cached_entries: DEFAULT_MAX_CACHED_BLOBS,
185 no_locals: false,
186 locals: Default::default(),
187 no_local_transactions_propagation: false,
188 additional_validation_tasks: DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS,
189 pending_tx_listener_buffer_size: PENDING_TX_LISTENER_BUFFER_SIZE,
190 new_tx_listener_buffer_size: NEW_TX_LISTENER_BUFFER_SIZE,
191 max_new_pending_txs_notifications: MAX_NEW_PENDING_TXS_NOTIFICATIONS,
192 max_queued_lifetime: MAX_QUEUED_TRANSACTION_LIFETIME,
193 transactions_backup_path: None,
194 disable_transactions_backup: false,
195 max_batch_size: 1,
196 }
197 }
198}
199
200impl RethTransactionPoolConfig for TxPoolArgs {
201 fn pool_config(&self) -> PoolConfig {
203 PoolConfig {
204 local_transactions_config: LocalTransactionConfig {
205 no_exemptions: self.no_locals,
206 local_addresses: self.locals.clone().into_iter().collect(),
207 propagate_local_transactions: !self.no_local_transactions_propagation,
208 },
209 pending_limit: SubPoolLimit {
210 max_txs: self.pending_max_count,
211 max_size: self.pending_max_size.saturating_mul(1024 * 1024),
212 },
213 basefee_limit: SubPoolLimit {
214 max_txs: self.basefee_max_count,
215 max_size: self.basefee_max_size.saturating_mul(1024 * 1024),
216 },
217 queued_limit: SubPoolLimit {
218 max_txs: self.queued_max_count,
219 max_size: self.queued_max_size.saturating_mul(1024 * 1024),
220 },
221 blob_limit: SubPoolLimit {
222 max_txs: self.blobpool_max_count,
223 max_size: self.blobpool_max_size.saturating_mul(1024 * 1024),
224 },
225 blob_cache_size: self.blob_cache_size,
226 max_account_slots: self.max_account_slots,
227 price_bumps: PriceBumpConfig {
228 default_price_bump: self.price_bump,
229 replace_blob_tx_price_bump: self.blob_transaction_price_bump,
230 },
231 minimal_protocol_basefee: self.minimal_protocol_basefee,
232 minimum_priority_fee: self.minimum_priority_fee,
233 gas_limit: self.enforced_gas_limit,
234 pending_tx_listener_buffer_size: self.pending_tx_listener_buffer_size,
235 new_tx_listener_buffer_size: self.new_tx_listener_buffer_size,
236 max_new_pending_txs_notifications: self.max_new_pending_txs_notifications,
237 max_queued_lifetime: self.max_queued_lifetime,
238 ..Default::default()
239 }
240 }
241
242 fn max_batch_size(&self) -> usize {
244 self.max_batch_size
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use clap::Parser;
252
253 #[derive(Parser)]
255 struct CommandParser<T: Args> {
256 #[command(flatten)]
257 args: T,
258 }
259
260 #[test]
261 fn txpool_args_default_sanity_test() {
262 let default_args = TxPoolArgs::default();
263 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
264 assert_eq!(args, default_args);
265 }
266
267 #[test]
268 fn txpool_parse_locals() {
269 let args = CommandParser::<TxPoolArgs>::parse_from([
270 "reth",
271 "--txpool.locals",
272 "0x0000000000000000000000000000000000000000",
273 ])
274 .args;
275 assert_eq!(args.locals, vec![Address::ZERO]);
276 }
277
278 #[test]
279 fn txpool_parse_max_tx_lifetime() {
280 let args =
282 CommandParser::<TxPoolArgs>::parse_from(["reth", "--txpool.lifetime", "300"]).args;
283 assert_eq!(args.max_queued_lifetime, Duration::from_secs(300));
284
285 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
287 assert_eq!(args.max_queued_lifetime, Duration::from_secs(3 * 60 * 60)); }
289
290 #[test]
291 fn txpool_parse_max_tx_lifetime_invalid() {
292 let result =
293 CommandParser::<TxPoolArgs>::try_parse_from(["reth", "--txpool.lifetime", "invalid"]);
294
295 assert!(result.is_err(), "Expected an error for invalid duration");
296 }
297}