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