mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add db get subcommand (#2772)
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
151
bin/reth/src/db/get.rs
Normal file
151
bin/reth/src/db/get.rs
Normal file
@ -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<DB: Database>(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::<tables::$table>().wrap_err("Could not parse the given table key.")?;
|
||||
|
||||
match tool.get::<tables::$table>(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<T: Table>(&self) -> Result<T::Key, eyre::Error>
|
||||
where
|
||||
for<'a> T::Key: Deserialize<'a>,
|
||||
{
|
||||
assert_eq!(T::NAME, self.table);
|
||||
|
||||
serde_json::from_str::<T::Key>(&self.key).map_err(|e| eyre::eyre!(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Map the user input value to json
|
||||
fn maybe_json_value_parser(value: &str) -> Result<String, eyre::Error> {
|
||||
if serde_json::from_str::<serde::de::IgnoredAny>(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<T: Args> {
|
||||
#[clap(flatten)]
|
||||
args: T,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_numeric_key_args() {
|
||||
let args = CommandParser::<Command>::parse_from(["reth", "Headers", "123"]).args;
|
||||
assert_eq!(args.table_key::<Headers>().unwrap(), 123);
|
||||
|
||||
let args = CommandParser::<Command>::parse_from([
|
||||
"reth",
|
||||
"HashedAccount",
|
||||
"0x0ac361fe774b78f8fc4e86c1916930d150865c3fc2e21dca2e58833557608bac",
|
||||
])
|
||||
.args;
|
||||
assert_eq!(
|
||||
args.table_key::<HashedAccount>().unwrap(),
|
||||
H256::from_str("0x0ac361fe774b78f8fc4e86c1916930d150865c3fc2e21dca2e58833557608bac")
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_string_key_args() {
|
||||
let args =
|
||||
CommandParser::<Command>::parse_from(["reth", "SyncStage", "MerkleExecution"]).args;
|
||||
assert_eq!(args.table_key::<SyncStage>().unwrap(), "MerkleExecution");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_json_key_args() {
|
||||
let args = CommandParser::<Command>::parse_from(["reth", "StorageHistory", r#"{ "address": "0x01957911244e546ce519fbac6f798958fafadb41", "sharded_key": { "key": "0x0000000000000000000000000000000000000000000000000000000000000003", "highest_block_number": 18446744073709551615 } }"#]).args;
|
||||
assert_eq!(
|
||||
args.table_key::<StorageHistory>().unwrap(),
|
||||
StorageShardedKey::new(
|
||||
H160::from_str("0x01957911244e546ce519fbac6f798958fafadb41").unwrap(),
|
||||
H256::from_str(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003"
|
||||
)
|
||||
.unwrap(),
|
||||
18446744073709551615
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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)?;
|
||||
}
|
||||
|
||||
@ -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<T: Table>(&mut self, key: T::Key) -> Result<Option<T::Value>> {
|
||||
self.db.view(|tx| tx.get::<T>(key))?.map_err(|e| eyre::eyre!(e))
|
||||
}
|
||||
|
||||
/// Drops the database at the given path.
|
||||
pub fn drop(&mut self, path: impl AsRef<Path>) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
|
||||
Reference in New Issue
Block a user