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