reth_node_core/args/
txpool.rs

1//! Transaction pool arguments
2
3use 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/// Parameters for debugging purposes
21#[derive(Debug, Clone, Args, PartialEq, Eq)]
22#[command(next_help_heading = "TxPool")]
23pub struct TxPoolArgs {
24    /// Max number of transaction in the pending sub-pool.
25    #[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    /// Max size of the pending sub-pool in megabytes.
28    #[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    /// Max number of transaction in the basefee sub-pool
32    #[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    /// Max size of the basefee sub-pool in megabytes.
35    #[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    /// Max number of transaction in the queued sub-pool
39    #[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    /// Max size of the queued sub-pool in megabytes.
42    #[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    /// Max number of transaction in the blobpool
46    #[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    /// Max size of the blobpool in megabytes.
49    #[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    /// Max number of entries for the in memory cache of the blob store.
53    #[arg(long = "txpool.blob-cache-size", alias = "txpool.blob_cache_size")]
54    pub blob_cache_size: Option<u32>,
55
56    /// Disable EIP-4844 blob transaction support
57    #[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    /// Max number of executable transaction slots guaranteed per account
61    #[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    /// Price bump (in %) for the transaction pool underpriced check.
65    #[arg(long = "txpool.pricebump", default_value_t = DEFAULT_PRICE_BUMP)]
66    pub price_bump: u128,
67
68    /// Minimum base fee required by the protocol.
69    #[arg(long = "txpool.minimal-protocol-fee", default_value_t = MIN_PROTOCOL_BASE_FEE)]
70    pub minimal_protocol_basefee: u64,
71
72    /// Minimum priority fee required for transaction acceptance into the pool.
73    /// Transactions with priority fee below this value will be rejected.
74    #[arg(long = "txpool.minimum-priority-fee")]
75    pub minimum_priority_fee: Option<u128>,
76
77    /// The default enforced gas limit for transactions entering the pool
78    #[arg(long = "txpool.gas-limit", default_value_t = ETHEREUM_BLOCK_GAS_LIMIT_30M)]
79    pub enforced_gas_limit: u64,
80
81    /// Maximum gas limit for individual transactions. Transactions exceeding this limit will be
82    /// rejected by the transaction pool
83    #[arg(long = "txpool.max-tx-gas")]
84    pub max_tx_gas_limit: Option<u64>,
85
86    /// Price bump percentage to replace an already existing blob transaction
87    #[arg(long = "blobpool.pricebump", default_value_t = REPLACE_BLOB_PRICE_BUMP)]
88    pub blob_transaction_price_bump: u128,
89
90    /// Max size in bytes of a single transaction allowed to enter the pool
91    #[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    /// The maximum number of blobs to keep in the in memory blob cache.
95    #[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    /// Flag to disable local transaction exemptions.
99    #[arg(long = "txpool.nolocals")]
100    pub no_locals: bool,
101    /// Flag to allow certain addresses as local.
102    #[arg(long = "txpool.locals")]
103    pub locals: Vec<Address>,
104    /// Flag to toggle local transaction propagation.
105    #[arg(long = "txpool.no-local-transactions-propagation")]
106    pub no_local_transactions_propagation: bool,
107    /// Number of additional transaction validation tasks to spawn.
108    #[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    /// Maximum number of pending transactions from the network to buffer
112    #[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    /// Maximum number of new transactions to buffer
116    #[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    /// How many new pending transactions to buffer and send to in progress pending transaction
120    /// iterators.
121    #[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    /// Maximum amount of time non-executable transaction are queued.
125    #[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    /// Path to store the local transaction backup at, to survive node restarts.
129    #[arg(long = "txpool.transactions-backup", alias = "txpool.journal", value_name = "PATH")]
130    pub transactions_backup_path: Option<std::path::PathBuf>,
131
132    /// Disables transaction backup to disk on node shutdown.
133    #[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    /// Max batch size for transaction pool insertions
141    #[arg(long = "txpool.max-batch-size", default_value_t = 1)]
142    pub max_batch_size: usize,
143}
144
145impl TxPoolArgs {
146    /// Sets the minimal protocol base fee to 0, effectively disabling checks that enforce that a
147    /// transaction's fee must be higher than the [`MIN_PROTOCOL_BASE_FEE`] which is the lowest
148    /// value the ethereum EIP-1559 base fee can reach.
149    pub const fn with_disabled_protocol_base_fee(self) -> Self {
150        self.with_protocol_base_fee(0)
151    }
152
153    /// Configures the minimal protocol base fee that should be enforced.
154    ///
155    /// Ethereum's EIP-1559 base fee can't drop below [`MIN_PROTOCOL_BASE_FEE`] hence this is
156    /// enforced by default in the pool.
157    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    /// Returns transaction pool configuration.
202    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    /// Returns max batch size for transaction batch insertion.
243    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    /// A helper type to parse Args more easily
254    #[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        // Test with a custom duration
281        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        // Test with the default value
286        let args = CommandParser::<TxPoolArgs>::parse_from(["reth"]).args;
287        assert_eq!(args.max_queued_lifetime, Duration::from_secs(3 * 60 * 60)); // Default is 3h
288    }
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}