1use 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#[derive(Debug, Clone, Args, PartialEq, Eq, Default)]
14#[command(next_help_heading = "Pruning")]
15pub struct PruningArgs {
16 #[arg(long, default_value_t = false)]
18 pub full: bool,
19
20 #[arg(long = "prune.block-interval", alias = "block-interval", value_parser = RangedU64ValueParser::<u64>::new().range(1..))]
22 pub block_interval: Option<u64>,
23
24 #[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 #[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 #[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 #[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 #[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 #[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 #[arg(long = "prune.receipts.full", conflicts_with_all = &["receipts_pre_merge", "receipts_distance", "receipts_before"])]
53 pub receipts_full: bool,
54 #[arg(long = "prune.receipts.pre-merge", conflicts_with_all = &["receipts_full", "receipts_distance", "receipts_before"])]
56 pub receipts_pre_merge: bool,
57 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[arg(long = "prune.bodies.pre-merge", value_name = "BLOCKS", conflicts_with_all = &["bodies_distance", "bodies_before"])]
101 pub bodies_pre_merge: bool,
102 #[arg(long = "prune.bodies.distance", value_name = "BLOCKS", conflicts_with_all = &["bodies_pre_merge", "bodies_before"])]
105 pub bodies_distance: Option<u64>,
106 #[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 pub fn prune_config<ChainSpec>(&self, chain_spec: &ChainSpec) -> Option<PruneConfig>
118 where
119 ChainSpec: EthereumHardforks,
120 {
121 let mut config = PruneConfig::default();
123
124 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 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 #[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}