diff --git a/bin/reth/src/db/get.rs b/bin/reth/src/db/get.rs new file mode 100644 index 000000000000..bc90e4c410a6 --- /dev/null +++ b/bin/reth/src/db/get.rs @@ -0,0 +1,151 @@ +use crate::utils::DbTool; +use clap::Parser; +use eyre::WrapErr; +use reth_db::{database::Database, table::Table, tables}; +use serde::Deserialize; +use tracing::error; + +/// The arguments for the `reth db get` command +#[derive(Parser, Debug)] +pub struct Command { + /// The table name + /// + /// NOTE: The dupsort tables are not supported now. + #[arg()] + pub table: String, // TODO: Convert to enum + + /// The key to get content for + #[arg(value_parser = maybe_json_value_parser)] + pub key: String, +} + +impl Command { + /// Execute `db get` command + pub fn execute(self, mut tool: DbTool<'_, DB>) -> eyre::Result<()> { + macro_rules! table_get { + ([$($table:ident),*]) => { + match self.table.as_str() { + $(stringify!($table) => { + let table_key = self.table_key::().wrap_err("Could not parse the given table key.")?; + + match tool.get::(table_key)? { + Some(content) => { + println!("{}", serde_json::to_string_pretty(&content)?); + } + None => { + error!(target: "reth::cli", "No content for the given table key."); + }, + }; + return Ok(()); + },)* + _ => { + error!(target: "reth::cli", "Unknown or unsupported table."); + return Ok(()); + } + } + } + } + + table_get!([ + CanonicalHeaders, + HeaderTD, + HeaderNumbers, + Headers, + BlockBodyIndices, + BlockOmmers, + BlockWithdrawals, + TransactionBlock, + Transactions, + TxHashNumber, + Receipts, + PlainAccountState, + Bytecodes, + AccountHistory, + StorageHistory, + HashedAccount, + AccountsTrie, + TxSenders, + SyncStage, + SyncStageProgress + ]); + } + + /// Get an instance of key for given table + fn table_key(&self) -> Result + where + for<'a> T::Key: Deserialize<'a>, + { + assert_eq!(T::NAME, self.table); + + serde_json::from_str::(&self.key).map_err(|e| eyre::eyre!(e)) + } +} + +/// Map the user input value to json +fn maybe_json_value_parser(value: &str) -> Result { + if serde_json::from_str::(value).is_ok() { + Ok(value.to_string()) + } else { + serde_json::to_string(&value).map_err(|e| eyre::eyre!(e)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Args, Parser}; + use reth_db::{ + models::storage_sharded_key::StorageShardedKey, HashedAccount, Headers, StorageHistory, + SyncStage, + }; + use reth_primitives::{H160, H256}; + use std::str::FromStr; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn parse_numeric_key_args() { + let args = CommandParser::::parse_from(["reth", "Headers", "123"]).args; + assert_eq!(args.table_key::().unwrap(), 123); + + let args = CommandParser::::parse_from([ + "reth", + "HashedAccount", + "0x0ac361fe774b78f8fc4e86c1916930d150865c3fc2e21dca2e58833557608bac", + ]) + .args; + assert_eq!( + args.table_key::().unwrap(), + H256::from_str("0x0ac361fe774b78f8fc4e86c1916930d150865c3fc2e21dca2e58833557608bac") + .unwrap() + ); + } + + #[test] + fn parse_string_key_args() { + let args = + CommandParser::::parse_from(["reth", "SyncStage", "MerkleExecution"]).args; + assert_eq!(args.table_key::().unwrap(), "MerkleExecution"); + } + + #[test] + fn parse_json_key_args() { + let args = CommandParser::::parse_from(["reth", "StorageHistory", r#"{ "address": "0x01957911244e546ce519fbac6f798958fafadb41", "sharded_key": { "key": "0x0000000000000000000000000000000000000000000000000000000000000003", "highest_block_number": 18446744073709551615 } }"#]).args; + assert_eq!( + args.table_key::().unwrap(), + StorageShardedKey::new( + H160::from_str("0x01957911244e546ce519fbac6f798958fafadb41").unwrap(), + H256::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000003" + ) + .unwrap(), + 18446744073709551615 + ) + ); + } +} diff --git a/bin/reth/src/db/mod.rs b/bin/reth/src/db/mod.rs index 04c933a5de66..c9c2865d8559 100644 --- a/bin/reth/src/db/mod.rs +++ b/bin/reth/src/db/mod.rs @@ -13,6 +13,7 @@ use reth_staged_sync::utils::chainspec::genesis_value_parser; use std::sync::Arc; use tracing::error; +mod get; /// DB List TUI mod tui; @@ -60,6 +61,8 @@ pub enum Subcommands { Stats, /// Lists the contents of a table List(ListArgs), + /// Gets the content of a table for the given key + Get(get::Command), /// Deletes all database entries Drop, } @@ -96,7 +99,7 @@ impl Command { let mut tool = DbTool::new(&db)?; - match &self.command { + match self.command { // TODO: We'll need to add this on the DB trait. Subcommands::Stats { .. } => { let mut stats_table = ComfyTable::new(); @@ -211,6 +214,9 @@ impl Command { SyncStageProgress ]); } + Subcommands::Get(command) => { + command.execute(tool)?; + } Subcommands::Drop => { tool.drop(db_path)?; } diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index e36cb2f8aec2..92a49f8d28ac 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -85,6 +85,11 @@ impl<'a, DB: Database> DbTool<'a, DB> { .map_err(|e| eyre::eyre!(e)) } + /// Grabs the content of the table for the given key + pub fn get(&mut self, key: T::Key) -> Result> { + self.db.view(|tx| tx.get::(key))?.map_err(|e| eyre::eyre!(e)) + } + /// Drops the database at the given path. pub fn drop(&mut self, path: impl AsRef) -> Result<()> { let path = path.as_ref(); diff --git a/crates/storage/db/src/tables/models/sharded_key.rs b/crates/storage/db/src/tables/models/sharded_key.rs index b66f761b4301..1127ab86d225 100644 --- a/crates/storage/db/src/tables/models/sharded_key.rs +++ b/crates/storage/db/src/tables/models/sharded_key.rs @@ -5,7 +5,7 @@ use crate::{ DatabaseError, }; use reth_primitives::BlockNumber; -use serde::Serialize; +use serde::{Deserialize, Serialize}; /// Number of indices in one shard. pub const NUM_OF_INDICES_IN_SHARD: usize = 100; @@ -16,7 +16,7 @@ pub const NUM_OF_INDICES_IN_SHARD: usize = 100; /// `Address | 200` -> data is from block 0 to 200. /// /// `Address | 300` -> data is from block 201 to 300. -#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)] +#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct ShardedKey { /// The key for this type. pub key: T, diff --git a/crates/storage/db/src/tables/models/storage_sharded_key.rs b/crates/storage/db/src/tables/models/storage_sharded_key.rs index fe2adbb35801..0791c8ad1204 100644 --- a/crates/storage/db/src/tables/models/storage_sharded_key.rs +++ b/crates/storage/db/src/tables/models/storage_sharded_key.rs @@ -6,7 +6,7 @@ use crate::{ }; use reth_primitives::{BlockNumber, H160, H256}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use super::ShardedKey; @@ -19,7 +19,7 @@ pub const NUM_OF_INDICES_IN_SHARD: usize = 100; /// `Address | Storagekey | 200` -> data is from transition 0 to 200. /// /// `Address | StorageKey | 300` -> data is from transition 201 to 300. -#[derive(Debug, Default, Clone, Eq, Ord, PartialOrd, PartialEq, Serialize)] +#[derive(Debug, Default, Clone, Eq, Ord, PartialOrd, PartialEq, Serialize, Deserialize)] pub struct StorageShardedKey { /// Storage account address. pub address: H160,