reth_cli_commands/db/
mod.rs1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
2use clap::{Parser, Subcommand};
3use reth_chainspec::{EthChainSpec, EthereumHardforks};
4use reth_cli::chainspec::ChainSpecParser;
5use reth_db::version::{get_db_version, DatabaseVersionError, DB_VERSION};
6use reth_db_common::DbTool;
7use std::{
8 io::{self, Write},
9 sync::Arc,
10};
11mod checksum;
12mod clear;
13mod diff;
14mod get;
15mod list;
16mod repair_trie;
17mod stats;
18mod tui;
20
21#[derive(Debug, Parser)]
23pub struct Command<C: ChainSpecParser> {
24 #[command(flatten)]
25 env: EnvironmentArgs<C>,
26
27 #[command(subcommand)]
28 command: Subcommands,
29}
30
31#[derive(Subcommand, Debug)]
32pub enum Subcommands {
34 Stats(stats::Command),
36 List(list::Command),
38 Checksum(checksum::Command),
40 Diff(diff::Command),
42 Get(get::Command),
44 Drop {
46 #[arg(short, long)]
48 force: bool,
49 },
50 Clear(clear::Command),
52 RepairTrie(repair_trie::Command),
54 Version,
56 Path,
58}
59
60macro_rules! db_ro_exec {
62 ($env:expr, $tool:ident, $N:ident, $command:block) => {
63 let Environment { provider_factory, .. } = $env.init::<$N>(AccessRights::RO)?;
64
65 let $tool = DbTool::new(provider_factory)?;
66 $command;
67 };
68}
69
70impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
71 pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(self) -> eyre::Result<()> {
73 let data_dir = self.env.datadir.clone().resolve_datadir(self.env.chain.chain());
74 let db_path = data_dir.db();
75 let static_files_path = data_dir.static_files();
76 let exex_wal_path = data_dir.exex_wal();
77
78 eyre::ensure!(
80 data_dir.data_dir().is_dir(),
81 "Datadir does not exist: {:?}",
82 data_dir.data_dir()
83 );
84
85 eyre::ensure!(db_path.is_dir(), "Database does not exist: {:?}", db_path);
87
88 match self.command {
89 Subcommands::Stats(command) => {
91 db_ro_exec!(self.env, tool, N, {
92 command.execute(data_dir, &tool)?;
93 });
94 }
95 Subcommands::List(command) => {
96 db_ro_exec!(self.env, tool, N, {
97 command.execute(&tool)?;
98 });
99 }
100 Subcommands::Checksum(command) => {
101 db_ro_exec!(self.env, tool, N, {
102 command.execute(&tool)?;
103 });
104 }
105 Subcommands::Diff(command) => {
106 db_ro_exec!(self.env, tool, N, {
107 command.execute(&tool)?;
108 });
109 }
110 Subcommands::Get(command) => {
111 db_ro_exec!(self.env, tool, N, {
112 command.execute(&tool)?;
113 });
114 }
115 Subcommands::Drop { force } => {
116 if !force {
117 print!(
119 "Are you sure you want to drop the database at {data_dir}? This cannot be undone. (y/N): "
120 );
121 io::stdout().flush().unwrap();
123
124 let mut input = String::new();
125 io::stdin().read_line(&mut input).expect("Failed to read line");
126
127 if !input.trim().eq_ignore_ascii_case("y") {
128 println!("Database drop aborted!");
129 return Ok(())
130 }
131 }
132
133 let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
134 let tool = DbTool::new(provider_factory)?;
135 tool.drop(db_path, static_files_path, exex_wal_path)?;
136 }
137 Subcommands::Clear(command) => {
138 let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
139 command.execute(provider_factory)?;
140 }
141 Subcommands::RepairTrie(command) => {
142 let access_rights =
143 if command.dry_run { AccessRights::RO } else { AccessRights::RW };
144 let Environment { provider_factory, .. } = self.env.init::<N>(access_rights)?;
145 command.execute(provider_factory)?;
146 }
147 Subcommands::Version => {
148 let local_db_version = match get_db_version(&db_path) {
149 Ok(version) => Some(version),
150 Err(DatabaseVersionError::MissingFile) => None,
151 Err(err) => return Err(err.into()),
152 };
153
154 println!("Current database version: {DB_VERSION}");
155
156 if let Some(version) = local_db_version {
157 println!("Local database version: {version}");
158 } else {
159 println!("Local database is uninitialized");
160 }
161 }
162 Subcommands::Path => {
163 println!("{}", db_path.display());
164 }
165 }
166
167 Ok(())
168 }
169}
170
171impl<C: ChainSpecParser> Command<C> {
172 pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
174 Some(&self.env.chain)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS};
182 use std::path::Path;
183
184 #[test]
185 fn parse_stats_globals() {
186 let path = format!("../{}", SUPPORTED_CHAINS[0]);
187 let cmd = Command::<EthereumChainSpecParser>::try_parse_from([
188 "reth",
189 "--datadir",
190 &path,
191 "stats",
192 ])
193 .unwrap();
194 assert_eq!(cmd.env.datadir.resolve_datadir(cmd.env.chain.chain).as_ref(), Path::new(&path));
195 }
196}