From 0e0725c31e7ccd6c5da5893cfd2d5b715fbfd83f Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Fri, 8 May 2020 16:28:29 +0900 Subject: [PATCH 1/4] Maintain sysvar balances for consistent market cap. --- ledger-tool/src/main.rs | 78 ++++++++++++++++++++++++++++++++++++++++ runtime/src/bank/mod.rs | 79 ++++++++++++++++++++++++++++++++--------- 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 759f95994de37e..4308ae9ed5e413 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -724,6 +724,14 @@ fn main() { .takes_value(false) .help("Include sysvars too"), ) + ).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 at the block height") @@ -1047,6 +1055,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/mod.rs b/runtime/src/bank/mod.rs index 3d4c2f84aa21e6..47f69a29d642d1 100644 --- a/runtime/src/bank/mod.rs +++ b/runtime/src/bank/mod.rs @@ -556,8 +556,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), @@ -565,7 +569,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)) }); } @@ -576,7 +580,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)) }); } @@ -587,7 +591,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)) }); } @@ -622,20 +626,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, + ) }); } @@ -644,8 +657,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(), + ) }); } @@ -680,8 +696,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); @@ -747,10 +767,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, + ) }); } @@ -1896,12 +1919,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!( @@ -3484,6 +3508,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); From 0fcf631451a43c11c017db1abf12bb3d16676938 Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Fri, 8 May 2020 20:18:32 +0900 Subject: [PATCH 2/4] Back-port fun and gating adjustments --- ledger-tool/src/main.rs | 14 ++++++++++---- runtime/src/accounts_db.rs | 2 +- runtime/src/bank/mod.rs | 6 +++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 4308ae9ed5e413..c6c486741ca1b3 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -731,7 +731,6 @@ fn main() { .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 at the block height") @@ -1063,10 +1062,17 @@ fn main() { poh_verify: false, ..ProcessOptions::default() }; - let genesis_config = open_genesis_config_by(&ledger_path, arg_matches); + let genesis_config = open_genesis_config(&ledger_path); 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(); + Ok((bank_forks, bank_forks_info, _leader_schedule_cache, _snapshot_hash)) => { + let slot = dev_halt_at_slot.unwrap_or_else(|| { + if bank_forks_info.len() > 1 { + eprintln!("Error: multiple forks present"); + exit(1); + } + bank_forks_info[0].bank_slot + }); + let bank = bank_forks.get(slot).unwrap_or_else(|| { eprintln!("Error: Slot {} is not available", slot); exit(1); diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index 6fd64ee040835e..7b5d9d934c1087 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -1071,7 +1071,7 @@ impl AccountsDB { pub fn include_owner_in_hash(slot: Slot) -> bool { // Account hashing updated to include owner activates at this slot on the mainnet-beta - slot >= 11_000_000 + slot >= 12_500_000 } pub fn hash_account_data( diff --git a/runtime/src/bank/mod.rs b/runtime/src/bank/mod.rs index 47f69a29d642d1..8cd09ef72ba736 100644 --- a/runtime/src/bank/mod.rs +++ b/runtime/src/bank/mod.rs @@ -557,7 +557,11 @@ impl Bank { } fn inherit_sysvar_account_balance(&self, old_account: &Option) -> u64 { - old_account.as_ref().map(|a| a.lamports).unwrap_or(1) + if self.epoch() >= 24 { + old_account.as_ref().map(|a| a.lamports).unwrap_or(1) + } else { + 1 + } } fn update_clock(&self) { From 3d93f08df229654c48699d1f5dab14ef8926ff90 Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Fri, 8 May 2020 20:52:58 +0900 Subject: [PATCH 3/4] Add comment --- runtime/src/bank/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/src/bank/mod.rs b/runtime/src/bank/mod.rs index 8cd09ef72ba736..72435b7c6116d2 100644 --- a/runtime/src/bank/mod.rs +++ b/runtime/src/bank/mod.rs @@ -557,7 +557,8 @@ impl Bank { } fn inherit_sysvar_account_balance(&self, old_account: &Option) -> u64 { - if self.epoch() >= 24 { + // Corrent sysvar account balance maintenance activates at this epoch on the mainnet-beta + if self.epoch() >= 25 { old_account.as_ref().map(|a| a.lamports).unwrap_or(1) } else { 1 From 73e404ee3da4314e25555eae9670afee67212267 Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Fri, 8 May 2020 23:20:55 +0900 Subject: [PATCH 4/4] Adjust test --- runtime/src/bank/mod.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/runtime/src/bank/mod.rs b/runtime/src/bank/mod.rs index 72435b7c6116d2..fa6a0386860e4c 100644 --- a/runtime/src/bank/mod.rs +++ b/runtime/src/bank/mod.rs @@ -72,6 +72,8 @@ pub const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; // 32 GiB pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; +const SYSVAR_BALANCE_ACTIVATION_EPOCH: Epoch = 25; + type BankStatusCache = StatusCache>; pub type BankSlotDelta = SlotDelta>; type TransactionAccountRefCells = Vec>>; @@ -558,7 +560,7 @@ impl Bank { fn inherit_sysvar_account_balance(&self, old_account: &Option) -> u64 { // Corrent sysvar account balance maintenance activates at this epoch on the mainnet-beta - if self.epoch() >= 25 { + if self.epoch() >= SYSVAR_BALANCE_ACTIVATION_EPOCH { old_account.as_ref().map(|a| a.lamports).unwrap_or(1) } else { 1 @@ -3516,7 +3518,7 @@ mod tests { #[test] fn test_transfer_to_sysvar() { solana_logger::setup(); - let (genesis_config, mint_keypair) = create_genesis_config(10_000); + let (genesis_config, mint_keypair) = create_genesis_config(10_000_000); let bank = Arc::new(Bank::new(&genesis_config)); let normal_pubkey = Pubkey::new_rand(); @@ -3531,7 +3533,25 @@ mod tests { let bank = Arc::new(new_from_parent(&bank)); assert_eq!(bank.get_balance(&normal_pubkey), 500); - assert_eq!(bank.get_balance(&sysvar_pubkey), 501); + assert_eq!(bank.get_balance(&sysvar_pubkey), 1); + + let bank = Arc::new(Bank::new_from_parent( + &bank, + &Pubkey::default(), + genesis_config + .epoch_schedule + .get_first_slot_in_epoch(SYSVAR_BALANCE_ACTIVATION_EPOCH), + )); + + let normal_pubkey = Pubkey::new_rand(); + bank.transfer(6000, &mint_keypair, &normal_pubkey).unwrap(); + bank.transfer(6000, &mint_keypair, &sysvar_pubkey).unwrap(); + assert_eq!(bank.get_balance(&normal_pubkey), 3561); + assert_eq!(bank.get_balance(&sysvar_pubkey), 6001); + + let bank = Arc::new(new_from_parent(&bank)); + assert_eq!(bank.get_balance(&normal_pubkey), 3561); + assert_eq!(bank.get_balance(&sysvar_pubkey), 6001); } #[test]