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