diff --git a/core/src/validator.rs b/core/src/validator.rs index c2f84bc871d07b..66b6855c378d07 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -19,7 +19,6 @@ use crate::{ tvu::{Sockets, Tvu}, }; use crossbeam_channel::unbounded; -use solana_ledger::shred::Shred; use solana_ledger::{ bank_forks::{BankForks, SnapshotConfig}, bank_forks_utils, @@ -28,13 +27,14 @@ use solana_ledger::{ create_new_tmp_ledger, leader_schedule::FixedSchedule, leader_schedule_cache::LeaderScheduleCache, + shred_version::compute_shred_version, }; use solana_metrics::datapoint_info; -use solana_runtime::{bank::Bank, hard_forks::HardForks}; +use solana_runtime::bank::Bank; use solana_sdk::{ clock::{Slot, DEFAULT_SLOTS_PER_TURN}, genesis_config::GenesisConfig, - hash::{extend_and_hash, Hash}, + hash::Hash, poh_config::PohConfig, pubkey::Pubkey, signature::{Keypair, KeypairUtil}, @@ -193,7 +193,7 @@ impl Validator { node.info.wallclock = timestamp(); node.info.shred_version = - compute_shred_version(&genesis_hash, &bank.hard_forks().read().unwrap()); + compute_shred_version(&genesis_hash, Some(&bank.hard_forks().read().unwrap())); Self::print_node_info(&node); if let Some(expected_shred_version) = config.expected_shred_version { @@ -471,20 +471,6 @@ impl Validator { } } -fn compute_shred_version(genesis_hash: &Hash, hard_forks: &HardForks) -> u16 { - use byteorder::{ByteOrder, LittleEndian}; - - let mut hash = *genesis_hash; - for (slot, count) in hard_forks.iter() { - let mut buf = [0u8; 16]; - LittleEndian::write_u64(&mut buf[..8], *slot); - LittleEndian::write_u64(&mut buf[8..], *count as u64); - hash = extend_and_hash(&hash, &buf); - } - - Shred::version_from_hash(&hash) -} - fn new_banks_from_blockstore( expected_genesis_hash: Option, blockstore_path: &Path, @@ -683,16 +669,6 @@ mod tests { use crate::genesis_utils::create_genesis_config_with_leader; use std::fs::remove_dir_all; - #[test] - fn test_compute_shred_version() { - let mut hard_forks = HardForks::default(); - assert_eq!(compute_shred_version(&Hash::default(), &hard_forks), 1); - hard_forks.register(1); - assert_eq!(compute_shred_version(&Hash::default(), &hard_forks), 55551); - hard_forks.register(1); - assert_eq!(compute_shred_version(&Hash::default(), &hard_forks), 46353); - } - #[test] fn validator_exit() { solana_logger::setup(); diff --git a/genesis/src/main.rs b/genesis/src/main.rs index bd036a58acc6c2..ac2b94854b1201 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -7,7 +7,10 @@ use solana_clap_utils::{ input_validators::{is_rfc3339_datetime, is_valid_percentage}, }; use solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account}; -use solana_ledger::{blockstore::create_new_ledger, poh::compute_hashes_per_tick}; +use solana_ledger::{ + blockstore::create_new_ledger, poh::compute_hashes_per_tick, + shred_version::compute_shred_version, +}; use solana_sdk::{ account::Account, clock, @@ -521,10 +524,19 @@ fn main() -> Result<(), Box> { create_new_ledger(&ledger_path, &genesis_config)?; println!( - "Genesis hash: {}\nCreation time: {}\nOperating mode: {:?}\nHashes per tick: {:?}\nSlots per epoch: {}\nCapitalization: {} SOL in {} accounts", - genesis_config.hash(), + "\ + Creation time: {}\n\ + Operating mode: {:?}\n\ + Genesis hash: {}\n\ + Shred version: {}\n\ + Hashes per tick: {:?}\n\ + Slots per epoch: {}\n\ + Capitalization: {} SOL in {} accounts\ + ", Utc.timestamp(genesis_config.creation_time, 0).to_rfc3339(), operating_mode, + genesis_config.hash(), + compute_shred_version(&genesis_config.hash(), None), genesis_config.poh_config.hashes_per_tick, slots_per_epoch, lamports_to_sol( @@ -537,7 +549,8 @@ fn main() -> Result<(), Box> { } account.lamports }) - .sum::()), + .sum::() + ), genesis_config.accounts.len() ); diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 60285fff3c5bde..d769eadc35b77a 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -11,6 +11,7 @@ use solana_ledger::{ blockstore_db::{self, Column, Database}, blockstore_processor::{BankForksInfo, BlockstoreProcessorResult, ProcessOptions}, rooted_slot_iterator::RootedSlotIterator, + shred_version::compute_shred_version, snapshot_utils, }; use solana_sdk::{ @@ -526,6 +527,7 @@ fn hardforks_of(matches: &ArgMatches<'_>, name: &str) -> Option> { fn load_bank_forks( arg_matches: &ArgMatches, ledger_path: &PathBuf, + genesis_config: &GenesisConfig, process_options: ProcessOptions, ) -> BlockstoreProcessorResult { let snapshot_config = if arg_matches.is_present("no_snapshot") { @@ -544,7 +546,7 @@ fn load_bank_forks( }; bank_forks_utils::load( - &open_genesis_config(&ledger_path), + &genesis_config, &open_blockstore(&ledger_path), account_paths, snapshot_config.as_ref(), @@ -615,9 +617,14 @@ fn main() { ) ) .subcommand( - SubCommand::with_name("print-genesis-hash") + SubCommand::with_name("genesis-hash") .about("Prints the ledger's genesis hash") ) + .subcommand( + SubCommand::with_name("shred-version") + .about("Prints the ledger's shred hash") + .arg(&hard_forks_arg) + ) .subcommand( SubCommand::with_name("bounds") .about("Print lowest and highest non-empty slots. Note that there may be empty slots within the bounds") @@ -741,19 +748,49 @@ fn main() { }); match matches.subcommand() { - ("print", Some(args_matches)) => { - let starting_slot = value_t_or_exit!(args_matches, "starting_slot", Slot); + ("print", Some(arg_matches)) => { + let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot); output_ledger( open_blockstore(&ledger_path), starting_slot, LedgerOutputMethod::Print, ); } - ("print-genesis-hash", Some(_args_matches)) => { + ("genesis-hash", Some(_arg_matches)) => { println!("{}", open_genesis_config(&ledger_path).hash()); } - ("print-slot", Some(args_matches)) => { - let slots = values_t_or_exit!(args_matches, "slots", Slot); + ("shred-version", Some(arg_matches)) => { + let genesis_config = open_genesis_config(&ledger_path); + println!("{}", genesis_config.hash()); + + let process_options = ProcessOptions { + dev_halt_at_slot: Some(0), + new_hard_forks: hardforks_of(arg_matches, "hard_forks"), + poh_verify: false, + ..ProcessOptions::default() + }; + let genesis_config = open_genesis_config(&ledger_path); + match load_bank_forks(arg_matches, &ledger_path, &genesis_config, process_options) { + Ok((bank_forks, bank_forks_info, _leader_schedule_cache)) => { + let bank_info = &bank_forks_info[0]; + let bank = bank_forks[bank_info.bank_slot].clone(); + + println!( + "{}", + compute_shred_version( + &genesis_config.hash(), + Some(&bank.hard_forks().read().unwrap()) + ) + ); + } + Err(err) => { + eprintln!("Failed to load ledger: {:?}", err); + exit(1); + } + } + } + ("print-slot", Some(arg_matches)) => { + let slots = values_t_or_exit!(arg_matches, "slots", Slot); for slot in slots { println!("Slot {}", slot); output_slot( @@ -763,8 +800,8 @@ fn main() { ); } } - ("json", Some(args_matches)) => { - let starting_slot = value_t_or_exit!(args_matches, "starting_slot", Slot); + ("json", Some(arg_matches)) => { + let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot); output_ledger( open_blockstore(&ledger_path), starting_slot, @@ -778,8 +815,15 @@ fn main() { poh_verify: !arg_matches.is_present("skip_poh_verify"), ..ProcessOptions::default() }; + println!("{}", open_genesis_config(&ledger_path).hash()); - load_bank_forks(arg_matches, &ledger_path, process_options).unwrap_or_else(|err| { + load_bank_forks( + arg_matches, + &ledger_path, + &open_genesis_config(&ledger_path), + process_options, + ) + .unwrap_or_else(|err| { eprintln!("Ledger verification failed: {:?}", err); exit(1); }); @@ -795,7 +839,12 @@ fn main() { ..ProcessOptions::default() }; - match load_bank_forks(arg_matches, &ledger_path, process_options) { + match load_bank_forks( + arg_matches, + &ledger_path, + &open_genesis_config(&ledger_path), + process_options, + ) { Ok((bank_forks, bank_forks_info, _leader_schedule_cache)) => { let dot = graph_forks( &bank_forks, @@ -834,7 +883,8 @@ fn main() { poh_verify: false, ..ProcessOptions::default() }; - match load_bank_forks(arg_matches, &ledger_path, process_options) { + let genesis_config = open_genesis_config(&ledger_path); + match load_bank_forks(arg_matches, &ledger_path, &genesis_config, process_options) { Ok((bank_forks, _bank_forks_info, _leader_schedule_cache)) => { let bank = bank_forks.get(snapshot_slot).unwrap_or_else(|| { eprintln!("Error: Slot {} is not available", snapshot_slot); @@ -865,6 +915,13 @@ fn main() { "Successfully created snapshot for slot {}: {:?}", snapshot_slot, package.tar_output_file ); + println!( + "Shred version: {}", + compute_shred_version( + &genesis_config.hash(), + Some(&bank.hard_forks().read().unwrap()) + ) + ); ok }) }) @@ -879,8 +936,8 @@ fn main() { } } } - ("prune", Some(args_matches)) => { - if let Some(prune_file_path) = args_matches.value_of("slot_list") { + ("prune", Some(arg_matches)) => { + if let Some(prune_file_path) = arg_matches.value_of("slot_list") { let blockstore = open_blockstore(&ledger_path); let prune_file = File::open(prune_file_path.to_string()).unwrap(); let slot_hashes: BTreeMap = @@ -916,14 +973,14 @@ fn main() { blockstore.prune(*target_slot); } } - ("list-roots", Some(args_matches)) => { + ("list-roots", Some(arg_matches)) => { let blockstore = open_blockstore(&ledger_path); - let max_height = if let Some(height) = args_matches.value_of("max_height") { + let max_height = if let Some(height) = arg_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") { + let num_roots = if let Some(roots) = arg_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() @@ -948,7 +1005,7 @@ fn main() { .collect(); let mut output_file: Box = - if let Some(path) = args_matches.value_of("slot_list") { + if let Some(path) = arg_matches.value_of("slot_list") { match File::create(path) { Ok(file) => Box::new(file), _ => Box::new(stdout()), @@ -969,10 +1026,10 @@ fn main() { } }); } - ("bounds", Some(args_matches)) => { + ("bounds", Some(arg_matches)) => { match open_blockstore(&ledger_path).slot_meta_iterator(0) { Ok(metas) => { - let all = args_matches.is_present("all"); + let all = arg_matches.is_present("all"); println!("Collecting Ledger information..."); let slots: Vec<_> = metas.map(|(slot, _)| slot).collect(); diff --git a/ledger/src/lib.rs b/ledger/src/lib.rs index dde02ecdb8158d..8350381a7e096d 100644 --- a/ledger/src/lib.rs +++ b/ledger/src/lib.rs @@ -15,6 +15,7 @@ pub mod leader_schedule_utils; pub mod poh; pub mod rooted_slot_iterator; pub mod shred; +pub mod shred_version; pub mod sigverify_shreds; pub mod snapshot_package; pub mod snapshot_utils; diff --git a/ledger/src/shred_version.rs b/ledger/src/shred_version.rs new file mode 100644 index 00000000000000..2149637085bed8 --- /dev/null +++ b/ledger/src/shred_version.rs @@ -0,0 +1,44 @@ +use crate::shred::Shred; +use solana_runtime::hard_forks::HardForks; +use solana_sdk::hash::{extend_and_hash, Hash}; + +pub fn compute_shred_version(genesis_hash: &Hash, hard_forks: Option<&HardForks>) -> u16 { + use byteorder::{ByteOrder, LittleEndian}; + + let mut hash = *genesis_hash; + if let Some(hard_forks) = hard_forks { + for (slot, count) in hard_forks.iter() { + let mut buf = [0u8; 16]; + LittleEndian::write_u64(&mut buf[..8], *slot); + LittleEndian::write_u64(&mut buf[8..], *count as u64); + hash = extend_and_hash(&hash, &buf); + } + } + + Shred::version_from_hash(&hash) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_compute_shred_version() { + assert_eq!(compute_shred_version(&Hash::default(), None), 1); + let mut hard_forks = HardForks::default(); + assert_eq!( + compute_shred_version(&Hash::default(), Some(&hard_forks)), + 1 + ); + hard_forks.register(1); + assert_eq!( + compute_shred_version(&Hash::default(), Some(&hard_forks)), + 55551 + ); + hard_forks.register(1); + assert_eq!( + compute_shred_version(&Hash::default(), Some(&hard_forks)), + 46353 + ); + } +}