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.max-account-slots", alias = "txpool.max_account_slots", default_value_t = TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER)]
54 pub max_account_slots: usize,
55
56 #[arg(long = "txpool.pricebump", default_value_t = DEFAULT_PRICE_BUMP)]
58 pub price_bump: u128,
59
60 #[arg(long = "txpool.minimal-protocol-fee", default_value_t = MIN_PROTOCOL_BASE_FEE)]
62 pub minimal_protocol_basefee: u64,
63
64 #[arg(long = "txpool.gas-limit", default_value_t = ETHEREUM_BLOCK_GAS_LIMIT_30M)]
66 pub enforced_gas_limit: u64,
67
68 #[arg(long = "blobpool.pricebump", default_value_t = REPLACE_BLOB_PRICE_BUMP)]
70 pub blob_transaction_price_bump: u128,
71
72 #[arg(long = "txpool.max-tx-input-bytes", alias = "txpool.max_tx_input_bytes", default_value_t = DEFAULT_MAX_TX_INPUT_BYTES)]
74 pub max_tx_input_bytes: usize,
75
76 #[arg(long = "txpool.max-cached-entries", alias = "txpool.max_cached_entries", default_value_t = DEFAULT_MAX_CACHED_BLOBS)]
78 pub max_cached_entries: u32,
79
80 #[arg(long = "txpool.nolocals")]
82 pub no_locals: bool,
83 #[arg(long = "txpool.locals")]
85 pub locals: Vec<Address>,
86 #[arg(long = "txpool.no-local-transactions-propagation")]
88 pub no_local_transactions_propagation: bool,
89 #[arg(long = "txpool.additional-validation-tasks", alias = "txpool.additional_validation_tasks", default_value_t = DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS)]
91 pub additional_validation_tasks: usize,
92
93 #[arg(long = "txpool.max-pending-txns", alias = "txpool.max_pending_txns", default_value_t = PENDING_TX_LISTENER_BUFFER_SIZE)]
95 pub pending_tx_listener_buffer_size: usize,
96
97 #[arg(long = "txpool.max-new-txns", alias = "txpool.max_new_txns", default_value_t = NEW_TX_LISTENER_BUFFER_SIZE)]
99 pub new_tx_listener_buffer_size: usize,
100
101 #[arg(long = "txpool.max-new-pending-txs-notifications", alias = "txpool.max-new-pending-txs-notifications", default_value_t = MAX_NEW_PENDING_TXS_NOTIFICATIONS)]
104 pub max_new_pending_txs_notifications: usize,
105
106 #[arg(long = "txpool.lifetime", value_parser = parse_duration_from_secs_or_ms, default_value = "10800", value_name = "DURATION")]
108 pub max_queued_lifetime: Duration,
109}
110
111impl Default for TxPoolArgs {
112 fn default() -> Self {
113 Self {
114 pending_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
115 pending_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
116 basefee_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
117 basefee_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
118 queued_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
119 queued_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
120 blobpool_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
121 blobpool_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT,
122 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
123 price_bump: DEFAULT_PRICE_BUMP,
124 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
125 enforced_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
126 blob_transaction_price_bump: REPLACE_BLOB_PRICE_BUMP,
127 max_tx_input_bytes: DEFAULT_MAX_TX_INPUT_BYTES,
128 max_cached_entries: DEFAULT_MAX_CACHED_BLOBS,
129 no_locals: false,
130 locals: Default::default(),
131 no_local_transactions_propagation: false,
132 additional_validation_tasks: DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS,
133 pending_tx_listener_buffer_size: PENDING_TX_LISTENER_BUFFER_SIZE,
134 new_tx_listener_buffer_size: NEW_TX_LISTENER_BUFFER_SIZE,
135 max_new_pending_txs_notifications: MAX_NEW_PENDING_TXS_NOTIFICATIONS,
136 max_queued_lifetime: MAX_QUEUED_TRANSACTION_LIFETIME,
137 }
138 }
139}
140
141impl RethTransactionPoolConfig for TxPoolArgs {
142 fn pool_config(&self) -> PoolConfig {
144 PoolConfig {
145 local_transactions_config: LocalTransactionConfig {
146 no_exemptions: self.no_locals,
147 local_addresses: self.locals.clone().into_iter().collect(),
148 propagate_local_transactions: !self.no_local_transactions_propagation,
149 },
150 pending_limit: SubPoolLimit {
151 max_txs: self.pending_max_count,
152 max_size: self.pending_max_size.saturating_mul(1024 * 1024),
153 },
154 basefee_limit: SubPoolLimit {
155 max_txs: self.basefee_max_count,
156 max_size: self.basefee_max_size.saturating_mul(1024 * 1024),
157 },
158 queued_limit: SubPoolLimit {
159 max_txs: self.queued_max_count,
160 max_size: self.queued_max_size.saturating_mul(1024 * 1024),
161 },
162 blob_limit: SubPoolLimit {
163 max_txs: self.blobpool_max_count,
164 max_size: self.blobpool_max_size.saturating_mul(1024 * 1024),
165 },
166 max_account_slots: self.max_account_slots,
167 price_bumps: PriceBumpConfig {
168 default_price_bump: self.price_bump,
169 replace_blob_tx_price_bump: self.blob_transaction_price_bump,
170 },
171 minimal_protocol_basefee: self.minimal_protocol_basefee,
172 gas_limit: self.enforced_gas_limit,
173 pending_tx_listener_buffer_size: self.pending_tx_listener_buffer_size,
174 new_tx_listener_buffer_size: self.new_tx_listener_buffer_size,
175 max_new_pending_txs_notifications: self.max_new_pending_txs_notifications,
176 max_queued_lifetime: self.max_queued_lifetime,
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use clap::Parser;
185
186 #[derive(Parser)]
188 struct CommandParser<T: Args> {
189 #[command(flatten)]
190 args: T,
191 }
192
193 #[test]
194 fn txpool_args_default_sanity_test() {
195 let default_args = TxPoolArgs::default();
196 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
197 assert_eq!(args, default_args);
198 }
199
200 #[test]
201 fn txpool_parse_locals() {
202 let args = CommandParser::<TxPoolArgs>::parse_from([
203 "reth",
204 "--txpool.locals",
205 "0x0000000000000000000000000000000000000000",
206 ])
207 .args;
208 assert_eq!(args.locals, vec![Address::ZERO]);
209 }
210
211 #[test]
212 fn txpool_parse_max_tx_lifetime() {
213 let args =
215 CommandParser::<TxPoolArgs>::parse_from(["reth", "--txpool.lifetime", "300"]).args;
216 assert_eq!(args.max_queued_lifetime, Duration::from_secs(300));
217
218 let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
220 assert_eq!(args.max_queued_lifetime, Duration::from_secs(3 * 60 * 60)); }
222
223 #[test]
224 fn txpool_parse_max_tx_lifetime_invalid() {
225 let result =
226 CommandParser::<TxPoolArgs>::try_parse_from(["reth", "--txpool.lifetime", "invalid"]);
227
228 assert!(result.is_err(), "Expected an error for invalid duration");
229 }
230}