Facility to generate a blocktree prune list using ledger tool (#5041)
automerge
This commit is contained in:
@ -2,8 +2,11 @@ use clap::{crate_description, crate_name, crate_version, value_t, App, Arg, SubC
|
||||
use solana::blocktree::Blocktree;
|
||||
use solana::blocktree_processor::process_blocktree;
|
||||
use solana_sdk::genesis_block::GenesisBlock;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::{stdout, Write};
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum LedgerOutputMethod {
|
||||
@ -58,6 +61,7 @@ fn output_ledger(blocktree: Blocktree, starting_slot: u64, method: LedgerOutputM
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const DEFAULT_ROOT_COUNT: &str = "1";
|
||||
solana_logger::setup();
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
@ -82,6 +86,36 @@ fn main() {
|
||||
.subcommand(SubCommand::with_name("print").about("Print the ledger"))
|
||||
.subcommand(SubCommand::with_name("json").about("Print the ledger in JSON format"))
|
||||
.subcommand(SubCommand::with_name("verify").about("Verify the ledger's PoH"))
|
||||
.subcommand(SubCommand::with_name("prune").about("Prune the ledger at the block height").arg(
|
||||
Arg::with_name("slot_list")
|
||||
.long("slot-list")
|
||||
.value_name("FILENAME")
|
||||
.takes_value(true)
|
||||
.help("The location of the YAML file with a list of rollback slot heights and hashes"),
|
||||
))
|
||||
.subcommand(SubCommand::with_name("list-roots").about("Output upto last <num-roots> root hashes and their heights starting at the given block height").arg(
|
||||
Arg::with_name("max_height")
|
||||
.long("max-height")
|
||||
.value_name("NUM")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Maximum block height"),
|
||||
).arg(
|
||||
Arg::with_name("slot_list")
|
||||
.long("slot-list")
|
||||
.value_name("FILENAME")
|
||||
.required(false)
|
||||
.takes_value(true)
|
||||
.help("The location of the output YAML file. A list of rollback slot heights and hashes will be written to the file."),
|
||||
).arg(
|
||||
Arg::with_name("num_roots")
|
||||
.long("num-roots")
|
||||
.value_name("NUM")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_ROOT_COUNT)
|
||||
.required(false)
|
||||
.help("Number of roots in the output"),
|
||||
))
|
||||
.get_matches();
|
||||
|
||||
let ledger_path = matches.value_of("ledger").unwrap();
|
||||
@ -113,7 +147,7 @@ fn main() {
|
||||
}
|
||||
("verify", _) => {
|
||||
println!("Verifying ledger...");
|
||||
match process_blocktree(&genesis_block, &blocktree, None) {
|
||||
match process_blocktree(&genesis_block, &blocktree, None, true) {
|
||||
Ok((_bank_forks, bank_forks_info, _)) => {
|
||||
println!("{:?}", bank_forks_info);
|
||||
}
|
||||
@ -123,6 +157,97 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
("prune", Some(args_matches)) => {
|
||||
if let Some(prune_file_path) = args_matches.value_of("slot_list") {
|
||||
let prune_file = File::open(prune_file_path.to_string()).unwrap();
|
||||
let slot_hashes: BTreeMap<u64, String> =
|
||||
serde_yaml::from_reader(prune_file).unwrap();
|
||||
|
||||
let iter = blocktree
|
||||
.rooted_slot_iterator(0)
|
||||
.expect("Failed to get rooted slot");
|
||||
|
||||
let potential_hashes: Vec<_> = iter
|
||||
.filter_map(|(slot, meta)| {
|
||||
let blockhash = blocktree
|
||||
.get_slot_entries(slot, meta.last_index, Some(1))
|
||||
.unwrap()
|
||||
.first()
|
||||
.unwrap()
|
||||
.hash
|
||||
.to_string();
|
||||
|
||||
slot_hashes.get(&slot).and_then(|hash| {
|
||||
if *hash == blockhash {
|
||||
Some((slot, blockhash))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (target_slot, target_hash) = potential_hashes
|
||||
.last()
|
||||
.expect("Failed to find a valid slot");
|
||||
println!("Prune at slot {:?} hash {:?}", target_slot, target_hash);
|
||||
// ToDo: Do the actual pruning of the database
|
||||
}
|
||||
}
|
||||
("list-roots", Some(args_matches)) => {
|
||||
let max_height = if let Some(height) = args_matches.value_of("max_height") {
|
||||
usize::from_str(height).expect("Maximum height must be a number")
|
||||
} else {
|
||||
panic!("Maximum height must be provided");
|
||||
};
|
||||
let num_roots = if let Some(roots) = args_matches.value_of("num_roots") {
|
||||
usize::from_str(roots).expect("Number of roots must be a number")
|
||||
} else {
|
||||
usize::from_str(DEFAULT_ROOT_COUNT).unwrap()
|
||||
};
|
||||
|
||||
let iter = blocktree
|
||||
.rooted_slot_iterator(0)
|
||||
.expect("Failed to get rooted slot");
|
||||
|
||||
let slot_hash: Vec<_> = iter
|
||||
.filter_map(|(slot, meta)| {
|
||||
if slot <= max_height as u64 {
|
||||
let blockhash = blocktree
|
||||
.get_slot_entries(slot, meta.last_index, Some(1))
|
||||
.unwrap()
|
||||
.first()
|
||||
.unwrap()
|
||||
.hash;
|
||||
Some((slot, blockhash))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut output_file: Box<Write> = if let Some(path) = args_matches.value_of("slot_list")
|
||||
{
|
||||
match File::create(path) {
|
||||
Ok(file) => Box::new(file),
|
||||
_ => Box::new(stdout()),
|
||||
}
|
||||
} else {
|
||||
Box::new(stdout())
|
||||
};
|
||||
|
||||
slot_hash
|
||||
.into_iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.for_each(|(i, (slot, hash))| {
|
||||
if i < num_roots {
|
||||
output_file
|
||||
.write_all(format!("{:?}: {:?}\n", slot, hash).as_bytes())
|
||||
.expect("failed to write");
|
||||
}
|
||||
});
|
||||
}
|
||||
("", _) => {
|
||||
eprintln!("{}", matches.usage());
|
||||
exit(1);
|
||||
|
Reference in New Issue
Block a user