From 00e45ec9358ef42141fae849da6865dd4552b77c Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Sat, 9 May 2020 02:42:32 +0900 Subject: [PATCH] Maintain sysvar balances for consistent market cap. (#9936) * Maintain sysvar balances for consistent market cap. * Unindent --- ledger-tool/src/main.rs | 78 ++++++++++++++++++++++++++++++++++++++++ runtime/src/bank.rs | 79 ++++++++++++++++++++++++++++++++--------- 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index ccc7d372ec..f1f2a1b2ec 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -778,6 +778,14 @@ fn main() { .help("Include sysvars too"), ) .arg(&max_genesis_archive_unpacked_size_arg) + ).subcommand( + SubCommand::with_name("capitalization") + .about("Print capitalization (aka, total suppy)") + .arg(&no_snapshot_arg) + .arg(&account_paths_arg) + .arg(&halt_at_slot_arg) + .arg(&hard_forks_arg) + .arg(&max_genesis_archive_unpacked_size_arg) ).subcommand( SubCommand::with_name("prune") .about("Prune the ledger from a yaml file containing a list of slots to prune.") @@ -1112,6 +1120,76 @@ fn main() { } } } + ("capitalization", Some(arg_matches)) => { + let dev_halt_at_slot = value_t!(arg_matches, "halt_at_slot", Slot).ok(); + let process_options = ProcessOptions { + dev_halt_at_slot, + new_hard_forks: hardforks_of(arg_matches, "hard_forks"), + poh_verify: false, + ..ProcessOptions::default() + }; + let genesis_config = open_genesis_config_by(&ledger_path, arg_matches); + match load_bank_forks(arg_matches, &ledger_path, &genesis_config, process_options) { + Ok((bank_forks, _leader_schedule_cache, _snapshot_hash)) => { + let slot = bank_forks.working_bank().slot(); + let bank = bank_forks.get(slot).unwrap_or_else(|| { + eprintln!("Error: Slot {} is not available", slot); + 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 + ) + } + } + + let computed_capitalization: u64 = bank + .get_program_accounts(None) + .into_iter() + .filter_map(|(_pubkey, account)| { + if account.lamports == u64::max_value() { + return None; + } + + 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(); + + if bank.capitalization() != computed_capitalization { + panic!( + "Capitalization mismatch!?: {} != {}", + bank.capitalization(), + computed_capitalization + ); + } + println!("Capitalization: {}", Sol(bank.capitalization())); + } + Err(err) => { + eprintln!("Failed to load ledger: {:?}", err); + exit(1); + } + } + } ("purge", Some(arg_matches)) => { let start_slot = value_t_or_exit!(arg_matches, "start_slot", Slot); let end_slot = value_t!(arg_matches, "end_slot", Slot); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index addd71145a..6213089aaa 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -563,8 +563,12 @@ impl Bank { self.store_account(pubkey, &new_account); } + fn inherit_sysvar_account_balance(&self, old_account: &Option) -> u64 { + old_account.as_ref().map(|a| a.lamports).unwrap_or(1) + } + fn update_clock(&self) { - self.update_sysvar_account(&sysvar::clock::id(), |_| { + self.update_sysvar_account(&sysvar::clock::id(), |account| { sysvar::clock::Clock { slot: self.slot, segment: get_segment_from_slot(self.slot, self.slots_per_segment), @@ -572,7 +576,7 @@ impl Bank { leader_schedule_epoch: self.epoch_schedule.get_leader_schedule_epoch(self.slot), unix_timestamp: self.unix_timestamp(), } - .create_account(1) + .create_account(self.inherit_sysvar_account_balance(account)) }); } @@ -583,7 +587,7 @@ impl Bank { .map(|account| SlotHistory::from_account(&account).unwrap()) .unwrap_or_default(); slot_history.add(self.slot()); - slot_history.create_account(1) + slot_history.create_account(self.inherit_sysvar_account_balance(account)) }); } @@ -594,7 +598,7 @@ impl Bank { .map(|account| SlotHashes::from_account(&account).unwrap()) .unwrap_or_default(); slot_hashes.add(self.parent_slot, self.parent_hash); - slot_hashes.create_account(1) + slot_hashes.create_account(self.inherit_sysvar_account_balance(account)) }); } @@ -629,20 +633,29 @@ impl Bank { } fn update_fees(&self) { - self.update_sysvar_account(&sysvar::fees::id(), |_| { - sysvar::fees::create_account(1, &self.fee_calculator) + self.update_sysvar_account(&sysvar::fees::id(), |account| { + sysvar::fees::create_account( + self.inherit_sysvar_account_balance(account), + &self.fee_calculator, + ) }); } fn update_rent(&self) { - self.update_sysvar_account(&sysvar::rent::id(), |_| { - sysvar::rent::create_account(1, &self.rent_collector.rent) + self.update_sysvar_account(&sysvar::rent::id(), |account| { + sysvar::rent::create_account( + self.inherit_sysvar_account_balance(account), + &self.rent_collector.rent, + ) }); } fn update_epoch_schedule(&self) { - self.update_sysvar_account(&sysvar::epoch_schedule::id(), |_| { - sysvar::epoch_schedule::create_account(1, &self.epoch_schedule) + self.update_sysvar_account(&sysvar::epoch_schedule::id(), |account| { + sysvar::epoch_schedule::create_account( + self.inherit_sysvar_account_balance(account), + &self.epoch_schedule, + ) }); } @@ -651,8 +664,11 @@ impl Bank { return; } // if I'm the first Bank in an epoch, ensure stake_history is updated - self.update_sysvar_account(&sysvar::stake_history::id(), |_| { - sysvar::stake_history::create_account(1, self.stakes.read().unwrap().history()) + self.update_sysvar_account(&sysvar::stake_history::id(), |account| { + sysvar::stake_history::create_account( + self.inherit_sysvar_account_balance(account), + self.stakes.read().unwrap().history(), + ) }); } @@ -687,8 +703,12 @@ impl Bank { validator_rewards / validator_points as f64, storage_rewards / storage_points as f64, ); - self.update_sysvar_account(&sysvar::rewards::id(), |_| { - sysvar::rewards::create_account(1, validator_point_value, storage_point_value) + self.update_sysvar_account(&sysvar::rewards::id(), |account| { + sysvar::rewards::create_account( + self.inherit_sysvar_account_balance(account), + validator_point_value, + storage_point_value, + ) }); let validator_rewards = self.pay_validator_rewards(validator_point_value); @@ -754,10 +774,13 @@ impl Bank { } pub fn update_recent_blockhashes(&self) { - self.update_sysvar_account(&sysvar::recent_blockhashes::id(), |_| { + self.update_sysvar_account(&sysvar::recent_blockhashes::id(), |account| { let blockhash_queue = self.blockhash_queue.read().unwrap(); let recent_blockhash_iter = blockhash_queue.get_recent_blockhashes(); - sysvar::recent_blockhashes::create_account_with_data(1, recent_blockhash_iter) + sysvar::recent_blockhashes::create_account_with_data( + self.inherit_sysvar_account_balance(account), + recent_blockhash_iter, + ) }); } @@ -1945,12 +1968,13 @@ impl Bank { } info!( - "bank frozen: {} hash: {} accounts_delta: {} signature_count: {} last_blockhash: {}", + "bank frozen: {} hash: {} accounts_delta: {} signature_count: {} last_blockhash: {} capitalization: {}", self.slot(), hash, accounts_delta_hash.hash, self.signature_count(), self.last_blockhash(), + self.capitalization(), ); info!( @@ -3558,6 +3582,27 @@ mod tests { assert_eq!(bank.get_balance(&pubkey), 500); } + #[test] + fn test_transfer_to_sysvar() { + solana_logger::setup(); + let (genesis_config, mint_keypair) = create_genesis_config(10_000); + let bank = Arc::new(Bank::new(&genesis_config)); + + let normal_pubkey = Pubkey::new_rand(); + let sysvar_pubkey = sysvar::clock::id(); + assert_eq!(bank.get_balance(&normal_pubkey), 0); + assert_eq!(bank.get_balance(&sysvar_pubkey), 1); + + bank.transfer(500, &mint_keypair, &normal_pubkey).unwrap(); + bank.transfer(500, &mint_keypair, &sysvar_pubkey).unwrap(); + assert_eq!(bank.get_balance(&normal_pubkey), 500); + assert_eq!(bank.get_balance(&sysvar_pubkey), 501); + + let bank = Arc::new(new_from_parent(&bank)); + assert_eq!(bank.get_balance(&normal_pubkey), 500); + assert_eq!(bank.get_balance(&sysvar_pubkey), 501); + } + #[test] fn test_bank_deposit() { let (genesis_config, _mint_keypair) = create_genesis_config(100);