reth_prune/segments/user/
merkle_change_sets.rs1use crate::{
2 db_ext::DbTxPruneExt,
3 segments::{PruneInput, Segment},
4 PrunerError,
5};
6use alloy_primitives::B256;
7use reth_db_api::{models::BlockNumberHashedAddress, table::Value, tables, transaction::DbTxMut};
8use reth_primitives_traits::NodePrimitives;
9use reth_provider::{
10 errors::provider::ProviderResult, BlockReader, ChainStateBlockReader, DBProvider,
11 NodePrimitivesProvider, PruneCheckpointWriter, TransactionsProvider,
12};
13use reth_prune_types::{
14 PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment, SegmentOutput, SegmentOutputCheckpoint,
15};
16use tracing::{instrument, trace};
17
18#[derive(Debug)]
19pub struct MerkleChangeSets {
20 mode: PruneMode,
21}
22
23impl MerkleChangeSets {
24 pub const fn new(mode: PruneMode) -> Self {
25 Self { mode }
26 }
27}
28
29impl<Provider> Segment<Provider> for MerkleChangeSets
30where
31 Provider: DBProvider<Tx: DbTxMut>
32 + PruneCheckpointWriter
33 + TransactionsProvider
34 + BlockReader
35 + ChainStateBlockReader
36 + NodePrimitivesProvider<Primitives: NodePrimitives<Receipt: Value>>,
37{
38 fn segment(&self) -> PruneSegment {
39 PruneSegment::MerkleChangeSets
40 }
41
42 fn mode(&self) -> Option<PruneMode> {
43 Some(self.mode)
44 }
45
46 fn purpose(&self) -> PrunePurpose {
47 PrunePurpose::User
48 }
49
50 #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)]
51 fn prune(&self, provider: &Provider, input: PruneInput) -> Result<SegmentOutput, PrunerError> {
52 let Some(block_range) = input.get_next_block_range() else {
53 trace!(target: "pruner", "No change sets to prune");
54 return Ok(SegmentOutput::done())
55 };
56
57 let block_range_end = *block_range.end();
58 let mut limiter = input.limiter;
59
60 let storage_range_start: BlockNumberHashedAddress =
62 (*block_range.start(), B256::ZERO).into();
63 let storage_range_end: BlockNumberHashedAddress =
64 (*block_range.end() + 1, B256::ZERO).into();
65 let storage_range = storage_range_start..storage_range_end;
66
67 let mut last_storages_pruned_block = None;
68 let (storages_pruned, done) =
69 provider.tx_ref().prune_table_with_range::<tables::StoragesTrieChangeSets>(
70 storage_range,
71 &mut limiter,
72 |_| false,
73 |(BlockNumberHashedAddress((block_number, _)), _)| {
74 last_storages_pruned_block = Some(block_number);
75 },
76 )?;
77
78 trace!(target: "pruner", %storages_pruned, %done, "Pruned storages change sets");
79
80 let mut last_accounts_pruned_block = block_range_end;
81 let last_storages_pruned_block = last_storages_pruned_block
82 .map(|block_number| if done { block_number } else { block_number.saturating_sub(1) })
85 .unwrap_or(block_range_end);
86
87 let (accounts_pruned, done) =
88 provider.tx_ref().prune_table_with_range::<tables::AccountsTrieChangeSets>(
89 block_range,
90 &mut limiter,
91 |_| false,
92 |row| last_accounts_pruned_block = row.0,
93 )?;
94
95 trace!(target: "pruner", %accounts_pruned, %done, "Pruned accounts change sets");
96
97 let progress = limiter.progress(done);
98
99 Ok(SegmentOutput {
100 progress,
101 pruned: accounts_pruned + storages_pruned,
102 checkpoint: Some(SegmentOutputCheckpoint {
103 block_number: Some(last_storages_pruned_block.min(last_accounts_pruned_block)),
104 tx_number: None,
105 }),
106 })
107 }
108
109 fn save_checkpoint(
110 &self,
111 provider: &Provider,
112 checkpoint: PruneCheckpoint,
113 ) -> ProviderResult<()> {
114 provider.save_prune_checkpoint(PruneSegment::MerkleChangeSets, checkpoint)
115 }
116}