Skip to content

Commit

Permalink
Refactors dump-db to accept an optional block height
Browse files Browse the repository at this point in the history
  • Loading branch information
grarco committed Feb 28, 2023
1 parent 6e18fad commit 6465f58
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 65 deletions.
50 changes: 27 additions & 23 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,10 @@ pub mod cmds {

fn def() -> App {
App::new(Self::CMD)
.about("Dump Namada ledger node's DB from a block into a file.")
.about(
"Dump Namada ledger node's DB from a block into a file. \
By default only dump the \"subspace\" subkeys.",
)
.add_args::<args::LedgerDumpDb>()
}
}
Expand Down Expand Up @@ -1572,7 +1575,7 @@ pub mod args {
use namada::types::governance::ProposalVote;
use namada::types::key::*;
use namada::types::masp::MaspValue;
use namada::types::storage::{self, Epoch};
use namada::types::storage::{self, BlockHeight, Epoch};
use namada::types::time::DateTimeUtc;
use namada::types::token;
use namada::types::transaction::GasLimit;
Expand Down Expand Up @@ -1601,7 +1604,7 @@ pub mod args {
Err(_) => config::DEFAULT_BASE_DIR.into(),
}),
);
// const BLOCK_HEIGHT_OPT: ArgOpt<BlockHeight> = arg_opt("height");
const BLOCK_HEIGHT_OPT: ArgOpt<BlockHeight> = arg_opt("height");
const BROADCAST_ONLY: ArgFlag = flag("broadcast-only");
const CHAIN_ID: Arg<ChainId> = arg("chain-id");
const CHAIN_ID_OPT: ArgOpt<ChainId> = CHAIN_ID.opt();
Expand Down Expand Up @@ -1631,7 +1634,6 @@ pub mod args {
arg_default_from_ctx("gas-token", DefaultFn(|| "NAM".into()));
const GENESIS_PATH: Arg<PathBuf> = arg("genesis-path");
const GENESIS_VALIDATOR: ArgOpt<String> = arg("genesis-validator").opt();
const HISTORIC: ArgFlag = flag("historic");
const LEDGER_ADDRESS_ABOUT: &str =
"Address of a ledger node as \"{scheme}://{host}:{port}\". If the \
scheme is not supplied, it is assumed to be TCP.";
Expand Down Expand Up @@ -1678,6 +1680,7 @@ pub mod args {
const SOURCE_OPT: ArgOpt<WalletAddress> = SOURCE.opt();
const STORAGE_KEY: Arg<storage::Key> = arg("storage-key");
const SUB_PREFIX: ArgOpt<String> = arg_opt("sub-prefix");
const EXTENDED_DUMP: ArgFlag = flag("extended");
const TIMEOUT_HEIGHT: ArgOpt<u64> = arg_opt("timeout-height");
const TIMEOUT_SEC_OFFSET: ArgOpt<u64> = arg_opt("timeout-sec-offset");
const TOKEN_OPT: ArgOpt<WalletAddress> = TOKEN.opt();
Expand Down Expand Up @@ -1767,40 +1770,41 @@ pub mod args {

#[derive(Clone, Debug)]
pub struct LedgerDumpDb {
// TODO: allow to specify height
// pub block_height: Option<BlockHeight>,
pub block_height: Option<BlockHeight>,
pub extended_dump: bool,
pub out_file_path: PathBuf,
pub historic: bool,
}

impl Args for LedgerDumpDb {
fn parse(matches: &ArgMatches) -> Self {
// let block_height = BLOCK_HEIGHT_OPT.parse(matches);
let block_height = BLOCK_HEIGHT_OPT.parse(matches);
let extended_dump = EXTENDED_DUMP.parse(matches);
let out_file_path = OUT_FILE_PATH_OPT
.parse(matches)
.unwrap_or_else(|| PathBuf::from("db_dump".to_string()));
let historic = HISTORIC.parse(matches);

Self {
// block_height,
block_height,
extended_dump,
out_file_path,
historic,
}
}

fn def(app: App) -> App {
app
// .arg(BLOCK_HEIGHT_OPT.def().about(
// "The block height to dump. Defaults to latest committed
// block.", ))
.arg(OUT_FILE_PATH_OPT.def().about(
"Path for the output file (omitting file extension). \
Defaults to \"db_dump.{block_height}.toml\" in the \
current working directory.",
))
.arg(HISTORIC.def().about(
"If provided, dump also the diff of the last height",
))
app.arg(BLOCK_HEIGHT_OPT.def().about(
"The block height to dump. Defaults to latest committed
block.",
))
.arg(
EXTENDED_DUMP
.def()
.about("Dump also the \"non-subspace\" subkeys."),
)
.arg(OUT_FILE_PATH_OPT.def().about(
"Path for the output file (omitting file extension). Defaults \
to \"db_dump.{block_height}.toml\" in the current working \
directory.",
))
}
}

Expand Down
6 changes: 3 additions & 3 deletions apps/src/lib/node/ledger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ pub fn reset(config: config::Ledger) -> Result<(), shell::Error> {
pub fn dump_db(
config: config::Ledger,
args::LedgerDumpDb {
// block_height,
block_height,
extended_dump,
out_file_path,
historic,
}: args::LedgerDumpDb,
) {
use namada::ledger::storage::DB;
Expand All @@ -215,7 +215,7 @@ pub fn dump_db(
let db_path = config.shell.db_dir(&chain_id);

let db = storage::PersistentDB::open(db_path, None);
db.dump_last_block(out_file_path, historic);
db.dump_block(block_height, extended_dump, out_file_path);
}

/// Runs and monitors a few concurrent tasks.
Expand Down
89 changes: 50 additions & 39 deletions apps/src/lib/node/ledger/storage/rocksdb.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! The persistent storage in RocksDB.
//!
//! The current storage tree is:
//! - `chain_id`
//! - `height`: the last committed block height
//! - `tx_queue`: txs to be decrypted in the next block
//! - `pred`: predecessor values of the top-level keys of the same name
Expand All @@ -15,6 +14,7 @@
//! - `next_epoch_min_start_time`
//! - `subspace`: accounts sub-spaces
//! - `{address}/{dyn}`: any byte data associated with accounts
//! - `results`: block results
//! - `h`: for each block at height `h`:
//! - `tree`: merkle tree
//! - `root`: root hash
Expand Down Expand Up @@ -241,22 +241,26 @@ impl RocksDB {
.map_err(|e| Error::DBError(e.into_string()))
}

/// Dump last known block
pub fn dump_last_block(
/// Dump the block at the provided height, or the last block if no height is
/// specified
pub fn dump_block(
&self,
block_height: Option<BlockHeight>,
extended_dump: bool,
out_file_path: std::path::PathBuf,
historic: bool,
) {
use std::io::Write;

// Find the last block height
let height: BlockHeight = types::decode(
self.0
.get("height")
.expect("Unable to read DB")
.expect("No block height found"),
)
.expect("Unable to decode block height");
// Find the last block height if one was not provided
let height = block_height.unwrap_or_else(|| {
types::decode(
self.0
.get("height")
.expect("Unable to read DB")
.expect("No block height found"),
)
.expect("Unable to decode block height")
});

let full_path = out_file_path
.with_file_name(format!(
Expand All @@ -277,37 +281,44 @@ impl RocksDB {
println!("Will write to {} ...", full_path.to_string_lossy());

let mut dump_it = |prefix: String| {
for next in self.0.iterator(IteratorMode::From(
prefix.as_bytes(),
Direction::Forward,
)) {
match next {
Err(e) => {
eprintln!(
"Something failed in a \"{prefix}\" iterator: {e}"
)
}
Ok((raw_key, raw_val)) => {
let key = std::str::from_utf8(&raw_key)
.expect("All keys should be valid UTF-8 strings");
let val = HEXLOWER.encode(&raw_val);
let bytes = format!("\"{key}\" = \"{val}\"\n");
file.write_all(bytes.as_bytes())
.expect("Unable to write to output file");
}
};
let mut read_opts = ReadOptions::default();
read_opts.set_total_order_seek(true);

let mut upper_prefix = prefix.clone().into_bytes();
if let Some(last) = upper_prefix.pop() {
upper_prefix.push(last + 1);
}
};
read_opts.set_iterate_upper_bound(upper_prefix);

let prefix = if historic {
// Dump subspace and diffs from last block height
height.raw()
} else {
// Dump only accounts subspace
"subspace".to_string()
let iter = self.0.iterator_opt(
IteratorMode::From(prefix.as_bytes(), Direction::Forward),
read_opts,
);

for (key, raw_val, _gas) in PersistentPrefixIterator(
PrefixIterator::new(iter, String::default()), /* Empty string to prevent prefix stripping, the prefix is already in the enclosed iterator */
) {
let val = HEXLOWER.encode(&raw_val);
let bytes = format!("\"{key}\" = \"{val}\"\n");
file.write_all(bytes.as_bytes())
.expect("Unable to write to output file");
}
};

dump_it(prefix);
dump_it("subspace".to_string());

if extended_dump {
// Dump the keys prepended with the selected block height (includes
// subspace diff keys)
dump_it(height.raw());
// Dump all the non-height prepended keys
dump_it("height".to_string());
dump_it("next_epoch_min_start_height".to_string());
dump_it("next_epoch_min_start_time".to_string());
dump_it("pred".to_string());
dump_it(format!("results/{}", height.raw()));
dump_it("tx_queue".to_string());
}

println!("Done writing to {}", full_path.to_string_lossy());
}
Expand Down

0 comments on commit 6465f58

Please sign in to comment.