Skip to content

Commit

Permalink
feat: add db get subcommand (#2772)
Browse files Browse the repository at this point in the history
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
  • Loading branch information
3 people authored May 25, 2023
1 parent f00dc09 commit d05ba97
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 5 deletions.
151 changes: 151 additions & 0 deletions bin/reth/src/db/get.rs
Original file line number Diff line number Diff line change
@@ -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
)
);
}
}
8 changes: 7 additions & 1 deletion bin/reth/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -211,6 +214,9 @@ impl Command {
SyncStageProgress
]);
}
Subcommands::Get(command) => {
command.execute(tool)?;
}
Subcommands::Drop => {
tool.drop(db_path)?;
}
Expand Down
5 changes: 5 additions & 0 deletions bin/reth/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions crates/storage/db/src/tables/models/sharded_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<T> {
/// The key for this type.
pub key: T,
Expand Down
4 changes: 2 additions & 2 deletions crates/storage/db/src/tables/models/storage_sharded_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
};

use reth_primitives::{BlockNumber, H160, H256};
use serde::Serialize;
use serde::{Deserialize, Serialize};

use super::ShardedKey;

Expand All @@ -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,
Expand Down

0 comments on commit d05ba97

Please sign in to comment.