From 14baa511f004c18311fb4c806cfaa28d47048fd4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 12:00:56 +0000 Subject: [PATCH] Add --warp-epoch and --enable-inflation to ledger-tool cap. (bp #11107) (#11132) * Add --warp-epoch and --force-inflation to ledger-tool cap. (#11107) * Add --warp-epoch and --force-inflation to ledger-tool cap. * Add more arguments * Address review comments * Fix message * Fix various typos... (cherry picked from commit 3db246f596ab7e5f220adfe9a6961cf31e59c71c) # Conflicts: # ledger-tool/src/main.rs * Fix conflicts.. Co-authored-by: Ryo Onodera --- ledger-tool/src/main.rs | 170 ++++++++++++++++++++++++++++++---------- runtime/src/accounts.rs | 19 +++-- runtime/src/bank.rs | 34 +++++++- sdk/src/native_token.rs | 26 ++++++ 4 files changed, 201 insertions(+), 48 deletions(-) diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 4ffae20122a42e..d2c757c3a17bf9 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -20,13 +20,17 @@ use solana_ledger::{ }; use solana_runtime::bank::Bank; use solana_sdk::{ - clock::Slot, genesis_config::GenesisConfig, native_token::lamports_to_sol, pubkey::Pubkey, + clock::{Epoch, Slot}, + genesis_config::GenesisConfig, + inflation::Inflation, + native_token::{lamports_to_sol, Sol}, + pubkey::Pubkey, shred_version::compute_shred_version, }; use solana_vote_program::vote_state::VoteState; use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - convert::TryInto, + convert::{TryFrom, TryInto}, ffi::OsStr, fs::{self, File}, io::{self, stdout, BufRead, BufReader, Write}, @@ -614,6 +618,19 @@ fn open_genesis_config_by(ledger_path: &Path, matches: &ArgMatches<'_>) -> Genes open_genesis_config(ledger_path, max_genesis_archive_unpacked_size) } +fn assert_capitalization(bank: &Bank) { + let calculated_capitalization = bank.calculate_capitalization(); + assert_eq!( + bank.capitalization(), + calculated_capitalization, + "Capitalization mismatch!?: +/-{}", + Sol(u64::try_from( + (i128::from(calculated_capitalization) - i128::from(bank.capitalization())).abs() + ) + .unwrap()), + ); +} + #[allow(clippy::cognitive_complexity)] fn main() { // Ignore SIGUSR1 to prevent long-running calls being killed by logrotate @@ -857,12 +874,36 @@ fn main() { .arg(&max_genesis_archive_unpacked_size_arg) ).subcommand( SubCommand::with_name("capitalization") - .about("Print capitalization (aka, total suppy)") + .about("Print capitalization (aka, total suppy) while checksumming it") .arg(&no_snapshot_arg) .arg(&account_paths_arg) .arg(&halt_at_slot_arg) .arg(&hard_forks_arg) .arg(&max_genesis_archive_unpacked_size_arg) + .arg( + Arg::with_name("warp_epoch") + .required(false) + .long("warp-epoch") + .takes_value(true) + .value_name("WARP_EPOCH") + .help("After loading the snapshot warp the ledger to WARP_EPOCH, \ + which could be an epoch in a galaxy far far away"), + ) + .arg( + Arg::with_name("enable_inflation") + .required(false) + .long("enable-inflation") + .takes_value(false) + .help("Always enable inflation when warping even if it's disabled"), + ) + .arg( + Arg::with_name("recalculate_capitalization") + .required(false) + .long("recalculate-capitalization") + .takes_value(false) + .help("Recalculate capitalization before warping; circumvents \ + bank's out-of-sync capitalization"), + ) ).subcommand( SubCommand::with_name("purge") .about("Delete a range of slots from the ledger.") @@ -1325,52 +1366,97 @@ fn main() { exit(1); }); - use solana_sdk::native_token::LAMPORTS_PER_SOL; - use std::fmt::{Display, Formatter, Result}; - pub struct Sol(u64); - - impl Display for Sol { - fn fmt(&self, f: &mut Formatter) -> Result { - write!( - f, - "{}.{:09} SOL", - self.0 / LAMPORTS_PER_SOL, - self.0 % LAMPORTS_PER_SOL - ) + if arg_matches.is_present("recalculate_capitalization") { + println!("Recalculating capitalization"); + let old_capitalization = bank.set_capitalization(); + if old_capitalization == bank.capitalization() { + eprintln!("Capitalization was identical: {}", Sol(old_capitalization)); } } - let computed_capitalization: u64 = bank - .get_program_accounts(None) - .into_iter() - .filter_map(|(_pubkey, account)| { - if account.lamports == u64::max_value() { - return None; - } + if arg_matches.is_present("warp_epoch") { + let base_bank = bank; - let is_specially_retained = - solana_sdk::native_loader::check_id(&account.owner) - || solana_sdk::sysvar::check_id(&account.owner); - - if is_specially_retained { - // specially retained accounts are ensured to exist by - // alwaysing having a balance of 1 lamports, which is - // outside the capitalization calculation. - Some(account.lamports - 1) - } else { - Some(account.lamports) - } - }) - .sum(); + let raw_warp_epoch = value_t!(arg_matches, "warp_epoch", String).unwrap(); + let warp_epoch = if raw_warp_epoch.starts_with('+') { + base_bank.epoch() + value_t!(arg_matches, "warp_epoch", Epoch).unwrap() + } else { + value_t!(arg_matches, "warp_epoch", Epoch).unwrap() + }; + if warp_epoch < base_bank.epoch() { + eprintln!( + "Error: can't warp epoch backwards: {} => {}", + base_bank.epoch(), + warp_epoch + ); + exit(1); + } - if bank.capitalization() != computed_capitalization { - panic!( - "Capitalization mismatch!?: {} != {}", - bank.capitalization(), - computed_capitalization + if arg_matches.is_present("enable_inflation") { + let inflation = Inflation::default(); + println!( + "Forcing to: {:?} (was: {:?})", + inflation, + base_bank.inflation() + ); + base_bank.set_inflation(inflation); + } + + let next_epoch = base_bank + .epoch_schedule() + .get_first_slot_in_epoch(warp_epoch); + let warped_bank = + Bank::new_from_parent(&base_bank, base_bank.collector_id(), next_epoch); + + println!("Slot: {} => {}", base_bank.slot(), warped_bank.slot()); + println!("Epoch: {} => {}", base_bank.epoch(), warped_bank.epoch()); + assert_capitalization(&base_bank); + assert_capitalization(&warped_bank); + println!( + "Capitalization: {} => {} (+{} {}%)", + Sol(base_bank.capitalization()), + Sol(warped_bank.capitalization()), + Sol(warped_bank.capitalization() - base_bank.capitalization()), + ((warped_bank.capitalization() as f64) + / (base_bank.capitalization() as f64) + * 100_f64), ); + + let mut overall_delta = 0; + for (pubkey, warped_account) in + warped_bank.get_all_accounts_modified_since_parent() + { + if let Some(base_account) = base_bank.get_account(&pubkey) { + if base_account.lamports != warped_account.lamports { + let delta = warped_account.lamports - base_account.lamports; + println!( + "{}({}): {} => {} (+{})", + pubkey, + base_account.owner, + Sol(base_account.lamports), + Sol(warped_account.lamports), + Sol(delta), + ); + overall_delta += delta; + } + } + } + if overall_delta > 0 { + println!("Sum of lamports changes: {}", Sol(overall_delta)); + } + } else { + if arg_matches.is_present("recalculate_capitalization") { + eprintln!("Capitalization isn't verified because it's recalculated"); + } + if arg_matches.is_present("enable_inflation") { + eprintln!( + "Forcing inflation isn't meaningful because bank isn't warping" + ); + } + + assert_capitalization(&bank); + println!("Capitalization: {}", Sol(bank.capitalization())); } - println!("Capitalization: {}", Sol(bank.capitalization())); } Err(err) => { eprintln!("Failed to load ledger: {:?}", err); diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index e65b67e4e936b8..c4aa04047d41b4 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -393,9 +393,18 @@ impl Accounts { .collect() } - pub fn load_by_program_slot(&self, slot: Slot, program_id: &Pubkey) -> Vec<(Pubkey, Account)> { + pub fn load_by_program_slot( + &self, + slot: Slot, + program_id: Option<&Pubkey>, + ) -> Vec<(Pubkey, Account)> { self.scan_slot(slot, |stored_account| { - if stored_account.account_meta.owner == *program_id { + let hit = match program_id { + None => true, + Some(program_id) => stored_account.account_meta.owner == *program_id, + }; + + if hit { Some((stored_account.meta.pubkey, stored_account.clone_account())) } else { None @@ -1387,11 +1396,11 @@ mod tests { let account2 = Account::new(1, 0, &Pubkey::new(&[3; 32])); accounts.store_slow(0, &pubkey2, &account2); - let loaded = accounts.load_by_program_slot(0, &Pubkey::new(&[2; 32])); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[2; 32]))); assert_eq!(loaded.len(), 2); - let loaded = accounts.load_by_program_slot(0, &Pubkey::new(&[3; 32])); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[3; 32]))); assert_eq!(loaded, vec![(pubkey2, account2)]); - let loaded = accounts.load_by_program_slot(0, &Pubkey::new(&[4; 32])); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[4; 32]))); assert_eq!(loaded, vec![]); } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index e17d0b274bf2b5..5785fbe2acba88 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2320,7 +2320,11 @@ impl Bank { ) -> Vec<(Pubkey, Account)> { self.rc .accounts - .load_by_program_slot(self.slot(), program_id) + .load_by_program_slot(self.slot(), Some(program_id)) + } + + pub fn get_all_accounts_modified_since_parent(&self) -> Vec<(Pubkey, Account)> { + self.rc.accounts.load_by_program_slot(self.slot(), None) } pub fn get_account_modified_since_parent(&self, pubkey: &Pubkey) -> Option<(Account, Slot)> { @@ -2465,6 +2469,34 @@ impl Bank { } } + pub fn calculate_capitalization(&self) -> u64 { + self.get_program_accounts(None) + .into_iter() + .map(|(_pubkey, account)| { + let is_specially_retained = solana_sdk::native_loader::check_id(&account.owner) + || solana_sdk::sysvar::check_id(&account.owner); + + if is_specially_retained { + // specially retained accounts are ensured to exist by + // always having a balance of 1 lamports, which is + // outside the capitalization calculation. + account.lamports - 1 + } else { + account.lamports + } + }) + .sum() + } + + /// Forcibly overwrites current capitalization by actually recalculating accounts' balances. + /// This should only be used for developing purposes. + pub fn set_capitalization(&self) -> u64 { + let old = self.capitalization(); + self.capitalization + .store(self.calculate_capitalization(), Ordering::Relaxed); + old + } + pub fn get_accounts_hash(&self) -> Hash { self.rc.accounts.accounts_db.get_accounts_hash(self.slot) } diff --git a/sdk/src/native_token.rs b/sdk/src/native_token.rs index 816efc161c5525..1275b591f05c06 100644 --- a/sdk/src/native_token.rs +++ b/sdk/src/native_token.rs @@ -10,3 +10,29 @@ pub fn lamports_to_sol(lamports: u64) -> f64 { pub fn sol_to_lamports(sol: f64) -> u64 { (sol * LAMPORTS_PER_SOL as f64) as u64 } + +use std::fmt::{Debug, Display, Formatter, Result}; +pub struct Sol(pub u64); + +impl Sol { + fn write_in_sol(&self, f: &mut Formatter) -> Result { + write!( + f, + "◎{}.{:09}", + self.0 / LAMPORTS_PER_SOL, + self.0 % LAMPORTS_PER_SOL + ) + } +} + +impl Display for Sol { + fn fmt(&self, f: &mut Formatter) -> Result { + self.write_in_sol(f) + } +} + +impl Debug for Sol { + fn fmt(&self, f: &mut Formatter) -> Result { + self.write_in_sol(f) + } +}