reth_prune/segments/user/
merkle_change_sets.rs

1use 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        // Create range for StoragesTrieChangeSets which uses BlockNumberHashedAddress as key
61        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            // If there's more storage changesets to prune, set the checkpoint block number to
83            // previous, so we could finish pruning its storage changesets on the next run.
84            .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}