Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom height db dump #1468

Merged
merged 3 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Adds the possibility to dump the state of the db at a custom height.
([\#1468](https://github.com/anoma/namada/pull/1468))
36 changes: 18 additions & 18 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2341,7 +2341,7 @@ pub mod args {
}),
);
pub const BLOCK_HEIGHT: Arg<BlockHeight> = arg("block-height");
// pub const BLOCK_HEIGHT_OPT: ArgOpt<BlockHeight> = arg_opt("height");
pub const BLOCK_HEIGHT_OPT: ArgOpt<BlockHeight> = arg_opt("height");
pub const BROADCAST_ONLY: ArgFlag = flag("broadcast-only");
pub const CHAIN_ID: Arg<ChainId> = arg("chain-id");
pub const CHAIN_ID_OPT: ArgOpt<ChainId> = CHAIN_ID.opt();
Expand Down Expand Up @@ -2608,41 +2608,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 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 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,
out_file_path,
historic,
}
}

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

Expand Down
4 changes: 2 additions & 2 deletions apps/src/lib/node/ledger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ pub fn reset(config: config::Ledger) -> Result<(), shell::Error> {
pub fn dump_db(
config: config::Ledger,
args::LedgerDumpDb {
// block_height,
block_height,
out_file_path,
historic,
}: args::LedgerDumpDb,
Expand All @@ -226,7 +226,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(out_file_path, historic, block_height);
}

/// Roll Namada state back to the previous height
Expand Down
62 changes: 52 additions & 10 deletions apps/src/lib/node/ledger/storage/rocksdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@
//! - `header`: block's header

use std::fs::File;
use std::io::BufWriter;
use std::path::Path;
use std::str::FromStr;
use std::sync::Mutex;

use ark_serialize::Write;
use borsh::{BorshDeserialize, BorshSerialize};
use data_encoding::HEXLOWER;
use namada::core::types::ethereum_structs;
Expand Down Expand Up @@ -252,23 +254,27 @@ impl RocksDB {
}

/// Dump last known block
pub fn dump_last_block(
pub fn dump_block(
&self,
out_file_path: std::path::PathBuf,
historic: bool,
height: Option<BlockHeight>,
) {
// Find the last block height
let state_cf = self
.get_column_family(STATE_CF)
.expect("State column family should exist");
let height: BlockHeight = types::decode(

let last_height: BlockHeight = types::decode(
self.0
.get_cf(state_cf, "height")
.expect("Unable to read DB")
.expect("No block height found"),
)
.expect("Unable to decode block height");

let height = height.unwrap_or(last_height);

let full_path = out_file_path
.with_file_name(format!(
"{}_{height}",
Expand Down Expand Up @@ -306,10 +312,46 @@ impl RocksDB {
}

// subspace
let cf = self
.get_column_family(SUBSPACE_CF)
.expect("Subspace column family should exist");
self.dump_it(cf, None, &mut file);
if height != last_height {
// Restoring subspace at specified height
let restored_subspace = self
.iter_prefix(None)
.par_bridge()
.fold(
|| "".to_string(),
|mut cur, (key, _value, _gas)| match self
.read_subspace_val_with_height(
&Key::from(key.to_db_key()),
height,
last_height,
)
.expect("Unable to find subspace key")
{
Some(value) => {
let val = HEXLOWER.encode(&value);
let new_line = format!("\"{key}\" = \"{val}\"\n");
cur.push_str(new_line.as_str());
cur
}
None => cur,
},
)
.reduce(
|| "".to_string(),
|mut a: String, b: String| {
a.push_str(&b);
a
},
);
file.write_all(restored_subspace.as_bytes())
.expect("Unable to write to output file");
} else {
// Just dump the current subspace
let cf = self
.get_column_family(SUBSPACE_CF)
.expect("Subspace column family should exist");
self.dump_it(cf, None, &mut file);
}

println!("Done writing to {}", full_path.to_string_lossy());
}
Expand All @@ -321,8 +363,6 @@ impl RocksDB {
prefix: Option<String>,
file: &mut File,
) {
use std::io::Write;

let read_opts = make_iter_read_opts(prefix.clone());
let iter = if let Some(prefix) = prefix {
self.0.iterator_cf_opt(
Expand All @@ -334,16 +374,18 @@ impl RocksDB {
self.0.iterator_cf_opt(cf, read_opts, IteratorMode::Start)
};

let mut buf = BufWriter::new(file);
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");
buf.write_all(bytes.as_bytes())
.expect("Unable to write to buffer");
}
buf.flush().expect("Unable to write to output file");
}

/// Rollback to previous block. Given the inner working of tendermint
Expand Down