reth_node_core/args/
pruning.rs

1//! Pruning and full node arguments
2
3use std::ops::Not;
4
5use crate::primitives::EthereumHardfork;
6use alloy_primitives::BlockNumber;
7use clap::{builder::RangedU64ValueParser, Args};
8use reth_chainspec::EthereumHardforks;
9use reth_config::config::PruneConfig;
10use reth_prune_types::{PruneMode, PruneModes, MINIMUM_PRUNING_DISTANCE};
11
12/// Parameters for pruning and full node
13#[derive(Debug, Clone, Args, PartialEq, Eq, Default)]
14#[command(next_help_heading = "Pruning")]
15pub struct PruningArgs {
16    /// Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored.
17    #[arg(long, default_value_t = false)]
18    pub full: bool,
19
20    /// Minimum pruning interval measured in blocks.
21    #[arg(long = "prune.block-interval", alias = "block-interval", value_parser = RangedU64ValueParser::<u64>::new().range(1..))]
22    pub block_interval: Option<u64>,
23
24    // Sender Recovery
25    /// Prunes all sender recovery data.
26    #[arg(long = "prune.sender-recovery.full", alias = "prune.senderrecovery.full", conflicts_with_all = &["sender_recovery_distance", "sender_recovery_before"])]
27    pub sender_recovery_full: bool,
28    /// Prune sender recovery data before the `head-N` block number. In other words, keep last N +
29    /// 1 blocks.
30    #[arg(long = "prune.sender-recovery.distance", alias = "prune.senderrecovery.distance", value_name = "BLOCKS", conflicts_with_all = &["sender_recovery_full", "sender_recovery_before"])]
31    pub sender_recovery_distance: Option<u64>,
32    /// Prune sender recovery data before the specified block number. The specified block number is
33    /// not pruned.
34    #[arg(long = "prune.sender-recovery.before", alias = "prune.senderrecovery.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["sender_recovery_full", "sender_recovery_distance"])]
35    pub sender_recovery_before: Option<BlockNumber>,
36
37    // Transaction Lookup
38    /// Prunes all transaction lookup data.
39    #[arg(long = "prune.transaction-lookup.full", alias = "prune.transactionlookup.full", conflicts_with_all = &["transaction_lookup_distance", "transaction_lookup_before"])]
40    pub transaction_lookup_full: bool,
41    /// Prune transaction lookup data before the `head-N` block number. In other words, keep last N
42    /// + 1 blocks.
43    #[arg(long = "prune.transaction-lookup.distance", alias = "prune.transactionlookup.distance", value_name = "BLOCKS", conflicts_with_all = &["transaction_lookup_full", "transaction_lookup_before"])]
44    pub transaction_lookup_distance: Option<u64>,
45    /// Prune transaction lookup data before the specified block number. The specified block number
46    /// is not pruned.
47    #[arg(long = "prune.transaction-lookup.before", alias = "prune.transactionlookup.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["transaction_lookup_full", "transaction_lookup_distance"])]
48    pub transaction_lookup_before: Option<BlockNumber>,
49
50    // Receipts
51    /// Prunes all receipt data.
52    #[arg(long = "prune.receipts.full", conflicts_with_all = &["receipts_pre_merge", "receipts_distance", "receipts_before"])]
53    pub receipts_full: bool,
54    /// Prune receipts before the merge block.
55    #[arg(long = "prune.receipts.pre-merge", conflicts_with_all = &["receipts_full", "receipts_distance", "receipts_before"])]
56    pub receipts_pre_merge: bool,
57    /// Prune receipts before the `head-N` block number. In other words, keep last N + 1 blocks.
58    #[arg(long = "prune.receipts.distance", value_name = "BLOCKS", conflicts_with_all = &["receipts_full", "receipts_pre_merge", "receipts_before"])]
59    pub receipts_distance: Option<u64>,
60    /// Prune receipts before the specified block number. The specified block number is not pruned.
61    #[arg(long = "prune.receipts.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["receipts_full", "receipts_pre_merge", "receipts_distance"])]
62    pub receipts_before: Option<BlockNumber>,
63    /// Receipts Log Filter
64    #[arg(
65        long = "prune.receipts-log-filter",
66        alias = "prune.receiptslogfilter",
67        value_name = "FILTER_CONFIG",
68        hide = true
69    )]
70    #[deprecated]
71    pub receipts_log_filter: Option<String>,
72
73    // Account History
74    /// Prunes all account history.
75    #[arg(long = "prune.account-history.full", alias = "prune.accounthistory.full", conflicts_with_all = &["account_history_distance", "account_history_before"])]
76    pub account_history_full: bool,
77    /// Prune account before the `head-N` block number. In other words, keep last N + 1 blocks.
78    #[arg(long = "prune.account-history.distance", alias = "prune.accounthistory.distance", value_name = "BLOCKS", conflicts_with_all = &["account_history_full", "account_history_before"])]
79    pub account_history_distance: Option<u64>,
80    /// Prune account history before the specified block number. The specified block number is not
81    /// pruned.
82    #[arg(long = "prune.account-history.before", alias = "prune.accounthistory.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["account_history_full", "account_history_distance"])]
83    pub account_history_before: Option<BlockNumber>,
84
85    // Storage History
86    /// Prunes all storage history data.
87    #[arg(long = "prune.storage-history.full", alias = "prune.storagehistory.full", conflicts_with_all = &["storage_history_distance", "storage_history_before"])]
88    pub storage_history_full: bool,
89    /// Prune storage history before the `head-N` block number. In other words, keep last N + 1
90    /// blocks.
91    #[arg(long = "prune.storage-history.distance", alias = "prune.storagehistory.distance", value_name = "BLOCKS", conflicts_with_all = &["storage_history_full", "storage_history_before"])]
92    pub storage_history_distance: Option<u64>,
93    /// Prune storage history before the specified block number. The specified block number is not
94    /// pruned.
95    #[arg(long = "prune.storage-history.before", alias = "prune.storagehistory.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["storage_history_full", "storage_history_distance"])]
96    pub storage_history_before: Option<BlockNumber>,
97
98    // Bodies
99    /// Prune bodies before the merge block.
100    #[arg(long = "prune.bodies.pre-merge", value_name = "BLOCKS", conflicts_with_all = &["bodies_distance", "bodies_before"])]
101    pub bodies_pre_merge: bool,
102    /// Prune bodies before the `head-N` block number. In other words, keep last N + 1
103    /// blocks.
104    #[arg(long = "prune.bodies.distance", value_name = "BLOCKS", conflicts_with_all = &["bodies_pre_merge", "bodies_before"])]
105    pub bodies_distance: Option<u64>,
106    /// Prune storage history before the specified block number. The specified block number is not
107    /// pruned.
108    #[arg(long = "prune.bodies.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["bodies_distance", "bodies_pre_merge"])]
109    pub bodies_before: Option<BlockNumber>,
110}
111
112impl PruningArgs {
113    /// Returns pruning configuration.
114    ///
115    /// Returns [`None`] if no parameters are specified and default pruning configuration should be
116    /// used.
117    pub fn prune_config<ChainSpec>(&self, chain_spec: &ChainSpec) -> Option<PruneConfig>
118    where
119        ChainSpec: EthereumHardforks,
120    {
121        // Initialize with a default prune configuration.
122        let mut config = PruneConfig::default();
123
124        // If --full is set, use full node defaults.
125        if self.full {
126            config = PruneConfig {
127                block_interval: config.block_interval,
128                segments: PruneModes {
129                    sender_recovery: Some(PruneMode::Full),
130                    transaction_lookup: None,
131                    receipts: Some(PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)),
132                    account_history: Some(PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)),
133                    storage_history: Some(PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)),
134                    bodies_history: chain_spec
135                        .ethereum_fork_activation(EthereumHardfork::Paris)
136                        .block_number()
137                        .map(PruneMode::Before),
138                    merkle_changesets: PruneMode::Distance(MINIMUM_PRUNING_DISTANCE),
139                    #[expect(deprecated)]
140                    receipts_log_filter: (),
141                },
142            }
143        }
144
145        // Override with any explicitly set prune.* flags.
146        if let Some(block_interval) = self.block_interval {
147            config.block_interval = block_interval as usize;
148        }
149        if let Some(mode) = self.sender_recovery_prune_mode() {
150            config.segments.sender_recovery = Some(mode);
151        }
152        if let Some(mode) = self.transaction_lookup_prune_mode() {
153            config.segments.transaction_lookup = Some(mode);
154        }
155        if let Some(mode) = self.receipts_prune_mode(chain_spec) {
156            config.segments.receipts = Some(mode);
157        }
158        if let Some(mode) = self.account_history_prune_mode() {
159            config.segments.account_history = Some(mode);
160        }
161        if let Some(mode) = self.bodies_prune_mode(chain_spec) {
162            config.segments.bodies_history = Some(mode);
163        }
164        if let Some(mode) = self.storage_history_prune_mode() {
165            config.segments.storage_history = Some(mode);
166        }
167
168        // Log warning if receipts_log_filter is set (deprecated feature)
169        #[expect(deprecated)]
170        if self.receipts_log_filter.is_some() {
171            tracing::warn!(
172                target: "reth::cli",
173                "The --prune.receiptslogfilter flag is deprecated and has no effect. It will be removed in a future release."
174            );
175        }
176
177        config.is_default().not().then_some(config)
178    }
179
180    fn bodies_prune_mode<ChainSpec>(&self, chain_spec: &ChainSpec) -> Option<PruneMode>
181    where
182        ChainSpec: EthereumHardforks,
183    {
184        if self.bodies_pre_merge {
185            chain_spec
186                .ethereum_fork_activation(EthereumHardfork::Paris)
187                .block_number()
188                .map(PruneMode::Before)
189        } else if let Some(distance) = self.bodies_distance {
190            Some(PruneMode::Distance(distance))
191        } else {
192            self.bodies_before.map(PruneMode::Before)
193        }
194    }
195
196    const fn sender_recovery_prune_mode(&self) -> Option<PruneMode> {
197        if self.sender_recovery_full {
198            Some(PruneMode::Full)
199        } else if let Some(distance) = self.sender_recovery_distance {
200            Some(PruneMode::Distance(distance))
201        } else if let Some(block_number) = self.sender_recovery_before {
202            Some(PruneMode::Before(block_number))
203        } else {
204            None
205        }
206    }
207
208    const fn transaction_lookup_prune_mode(&self) -> Option<PruneMode> {
209        if self.transaction_lookup_full {
210            Some(PruneMode::Full)
211        } else if let Some(distance) = self.transaction_lookup_distance {
212            Some(PruneMode::Distance(distance))
213        } else if let Some(block_number) = self.transaction_lookup_before {
214            Some(PruneMode::Before(block_number))
215        } else {
216            None
217        }
218    }
219
220    fn receipts_prune_mode<ChainSpec>(&self, chain_spec: &ChainSpec) -> Option<PruneMode>
221    where
222        ChainSpec: EthereumHardforks,
223    {
224        if self.receipts_pre_merge {
225            chain_spec
226                .ethereum_fork_activation(EthereumHardfork::Paris)
227                .block_number()
228                .map(PruneMode::Before)
229        } else if self.receipts_full {
230            Some(PruneMode::Full)
231        } else if let Some(distance) = self.receipts_distance {
232            Some(PruneMode::Distance(distance))
233        } else {
234            self.receipts_before.map(PruneMode::Before)
235        }
236    }
237
238    const fn account_history_prune_mode(&self) -> Option<PruneMode> {
239        if self.account_history_full {
240            Some(PruneMode::Full)
241        } else if let Some(distance) = self.account_history_distance {
242            Some(PruneMode::Distance(distance))
243        } else if let Some(block_number) = self.account_history_before {
244            Some(PruneMode::Before(block_number))
245        } else {
246            None
247        }
248    }
249
250    const fn storage_history_prune_mode(&self) -> Option<PruneMode> {
251        if self.storage_history_full {
252            Some(PruneMode::Full)
253        } else if let Some(distance) = self.storage_history_distance {
254            Some(PruneMode::Distance(distance))
255        } else if let Some(block_number) = self.storage_history_before {
256            Some(PruneMode::Before(block_number))
257        } else {
258            None
259        }
260    }
261}