reth_cli_commands/stage/
drop.rs

1//! Database debugging tool
2use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
3use clap::Parser;
4use itertools::Itertools;
5use reth_chainspec::EthChainSpec;
6use reth_cli::chainspec::ChainSpecParser;
7use reth_db::{mdbx::tx::Tx, static_file::iter_static_files, tables, DatabaseError};
8use reth_db_api::transaction::{DbTx, DbTxMut};
9use reth_db_common::{
10    init::{insert_genesis_header, insert_genesis_history, insert_genesis_state},
11    DbTool,
12};
13use reth_node_core::args::StageEnum;
14use reth_provider::{
15    writer::UnifiedStorageWriter, DatabaseProviderFactory, StaticFileProviderFactory,
16};
17use reth_prune::PruneSegment;
18use reth_stages::StageId;
19use reth_static_file_types::StaticFileSegment;
20
21/// `reth drop-stage` command
22#[derive(Debug, Parser)]
23pub struct Command<C: ChainSpecParser> {
24    #[command(flatten)]
25    env: EnvironmentArgs<C>,
26
27    stage: StageEnum,
28}
29
30impl<C: ChainSpecParser> Command<C> {
31    /// Execute `db` command
32    pub async fn execute<N: CliNodeTypes>(self) -> eyre::Result<()>
33    where
34        C: ChainSpecParser<ChainSpec = N::ChainSpec>,
35    {
36        let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
37
38        let tool = DbTool::new(provider_factory)?;
39
40        let static_file_segment = match self.stage {
41            StageEnum::Headers => Some(StaticFileSegment::Headers),
42            StageEnum::Bodies => Some(StaticFileSegment::Transactions),
43            StageEnum::Execution => Some(StaticFileSegment::Receipts),
44            _ => None,
45        };
46
47        // Delete static file segment data before inserting the genesis header below
48        if let Some(static_file_segment) = static_file_segment {
49            let static_file_provider = tool.provider_factory.static_file_provider();
50            let static_files = iter_static_files(static_file_provider.directory())?;
51            if let Some(segment_static_files) = static_files.get(&static_file_segment) {
52                // Delete static files from the highest to the lowest block range
53                for (block_range, _) in segment_static_files
54                    .iter()
55                    .sorted_by_key(|(block_range, _)| block_range.start())
56                    .rev()
57                {
58                    static_file_provider.delete_jar(static_file_segment, block_range.start())?;
59                }
60            }
61        }
62
63        let provider_rw = tool.provider_factory.database_provider_rw()?;
64        let tx = provider_rw.tx_ref();
65
66        match self.stage {
67            StageEnum::Headers => {
68                tx.clear::<tables::CanonicalHeaders>()?;
69                tx.clear::<tables::Headers>()?;
70                tx.clear::<tables::HeaderTerminalDifficulties>()?;
71                tx.clear::<tables::HeaderNumbers>()?;
72                reset_stage_checkpoint(tx, StageId::Headers)?;
73
74                insert_genesis_header(&provider_rw, &self.env.chain)?;
75            }
76            StageEnum::Bodies => {
77                tx.clear::<tables::BlockBodyIndices>()?;
78                tx.clear::<tables::Transactions>()?;
79                reset_prune_checkpoint(tx, PruneSegment::Transactions)?;
80
81                tx.clear::<tables::TransactionBlocks>()?;
82                tx.clear::<tables::BlockOmmers>()?;
83                tx.clear::<tables::BlockWithdrawals>()?;
84                reset_stage_checkpoint(tx, StageId::Bodies)?;
85
86                insert_genesis_header(&provider_rw, &self.env.chain)?;
87            }
88            StageEnum::Senders => {
89                tx.clear::<tables::TransactionSenders>()?;
90                // Reset pruned numbers to not count them in the next rerun's stage progress
91                reset_prune_checkpoint(tx, PruneSegment::SenderRecovery)?;
92                reset_stage_checkpoint(tx, StageId::SenderRecovery)?;
93            }
94            StageEnum::Execution => {
95                tx.clear::<tables::PlainAccountState>()?;
96                tx.clear::<tables::PlainStorageState>()?;
97                tx.clear::<tables::AccountChangeSets>()?;
98                tx.clear::<tables::StorageChangeSets>()?;
99                tx.clear::<tables::Bytecodes>()?;
100                tx.clear::<tables::Receipts>()?;
101
102                reset_prune_checkpoint(tx, PruneSegment::Receipts)?;
103                reset_prune_checkpoint(tx, PruneSegment::ContractLogs)?;
104                reset_stage_checkpoint(tx, StageId::Execution)?;
105
106                let alloc = &self.env.chain.genesis().alloc;
107                insert_genesis_state(&provider_rw, alloc.iter())?;
108            }
109            StageEnum::AccountHashing => {
110                tx.clear::<tables::HashedAccounts>()?;
111                reset_stage_checkpoint(tx, StageId::AccountHashing)?;
112            }
113            StageEnum::StorageHashing => {
114                tx.clear::<tables::HashedStorages>()?;
115                reset_stage_checkpoint(tx, StageId::StorageHashing)?;
116            }
117            StageEnum::Hashing => {
118                // Clear hashed accounts
119                tx.clear::<tables::HashedAccounts>()?;
120                reset_stage_checkpoint(tx, StageId::AccountHashing)?;
121
122                // Clear hashed storages
123                tx.clear::<tables::HashedStorages>()?;
124                reset_stage_checkpoint(tx, StageId::StorageHashing)?;
125            }
126            StageEnum::Merkle => {
127                tx.clear::<tables::AccountsTrie>()?;
128                tx.clear::<tables::StoragesTrie>()?;
129
130                reset_stage_checkpoint(tx, StageId::MerkleExecute)?;
131                reset_stage_checkpoint(tx, StageId::MerkleUnwind)?;
132
133                tx.delete::<tables::StageCheckpointProgresses>(
134                    StageId::MerkleExecute.to_string(),
135                    None,
136                )?;
137            }
138            StageEnum::AccountHistory | StageEnum::StorageHistory => {
139                tx.clear::<tables::AccountsHistory>()?;
140                tx.clear::<tables::StoragesHistory>()?;
141
142                reset_stage_checkpoint(tx, StageId::IndexAccountHistory)?;
143                reset_stage_checkpoint(tx, StageId::IndexStorageHistory)?;
144
145                insert_genesis_history(&provider_rw, self.env.chain.genesis().alloc.iter())?;
146            }
147            StageEnum::TxLookup => {
148                tx.clear::<tables::TransactionHashNumbers>()?;
149                reset_prune_checkpoint(tx, PruneSegment::TransactionLookup)?;
150
151                reset_stage_checkpoint(tx, StageId::TransactionLookup)?;
152                insert_genesis_header(&provider_rw, &self.env.chain)?;
153            }
154        }
155
156        tx.put::<tables::StageCheckpoints>(StageId::Finish.to_string(), Default::default())?;
157
158        UnifiedStorageWriter::commit_unwind(provider_rw)?;
159
160        Ok(())
161    }
162}
163
164fn reset_prune_checkpoint(
165    tx: &Tx<reth_db::mdbx::RW>,
166    prune_segment: PruneSegment,
167) -> Result<(), DatabaseError> {
168    if let Some(mut prune_checkpoint) = tx.get::<tables::PruneCheckpoints>(prune_segment)? {
169        prune_checkpoint.block_number = None;
170        prune_checkpoint.tx_number = None;
171        tx.put::<tables::PruneCheckpoints>(prune_segment, prune_checkpoint)?;
172    }
173
174    Ok(())
175}
176
177fn reset_stage_checkpoint(
178    tx: &Tx<reth_db::mdbx::RW>,
179    stage_id: StageId,
180) -> Result<(), DatabaseError> {
181    tx.put::<tables::StageCheckpoints>(stage_id.to_string(), Default::default())?;
182
183    Ok(())
184}