Simple cap by including sysvars and native programs (#13884)

* Simpler cap by including sysvars and native programs

* Fix tests

* Add comment

* revert some unrelated code

* Update test_bank_update_sysvar_account for cap.

* Test cap. for add_native_program using new helper

* Improve the cap adjustment with new tests

* Fix typo...

* Adjust test for improved code coverage

* Rename simpler_capitalization => simple_capitalization

* More rename and bonus commenting
This commit is contained in:
Ryo Onodera
2020-12-14 21:03:24 +09:00
committed by GitHub
parent 4bcc8afdc0
commit de9ac43ebf
8 changed files with 439 additions and 63 deletions

View File

@@ -1272,7 +1272,12 @@ impl Bank {
{
let old_account = self.get_sysvar_account(pubkey);
let new_account = updater(&old_account);
self.store_account(pubkey, &new_account);
if !self.simple_capitalization_enabled() {
self.store_account(pubkey, &new_account);
} else {
self.store_account_and_update_capitalization(pubkey, &new_account);
}
}
fn inherit_specially_retained_account_balance(&self, old_account: &Option<Account>) -> u64 {
@@ -2020,7 +2025,6 @@ impl Bank {
// Bootstrap validator collects fees until `new_from_parent` is called.
self.fee_rate_governor = genesis_config.fee_rate_governor.clone();
self.fee_calculator = self.fee_rate_governor.create_fee_calculator();
self.update_fees();
for (pubkey, account) in genesis_config.accounts.iter() {
if self.get_account(&pubkey).is_some() {
@@ -2029,6 +2033,9 @@ impl Bank {
self.store_account(pubkey, account);
self.capitalization.fetch_add(account.lamports, Relaxed);
}
// updating sysvars (the fees sysvar in this case) now depends on feature activations in
// genesis_config.accounts above
self.update_fees();
for (pubkey, account) in genesis_config.rewards_pools.iter() {
if self.get_account(&pubkey).is_some() {
@@ -2148,7 +2155,11 @@ impl Bank {
name,
self.inherit_specially_retained_account_balance(&existing_genuine_program),
);
self.store_account(&program_id, &account);
if !self.simple_capitalization_enabled() {
self.store_account(&program_id, &account);
} else {
self.store_account_and_update_capitalization(&program_id, &account);
}
debug!("Added native program {} under {:?}", name, program_id);
}
@@ -3765,7 +3776,6 @@ impl Bank {
}
}
#[cfg(test)]
fn store_account_and_update_capitalization(&self, pubkey: &Pubkey, new_account: &Account) {
if let Some(old_account) = self.get_account(&pubkey) {
match new_account.lamports.cmp(&old_account.lamports) {
@@ -4071,6 +4081,7 @@ impl Bank {
self.slot(),
&self.ancestors,
self.capitalization(),
self.simple_capitalization_enabled(),
)
}
@@ -4101,7 +4112,9 @@ impl Bank {
}
pub fn calculate_capitalization(&self) -> u64 {
self.rc.accounts.calculate_capitalization(&self.ancestors)
self.rc
.accounts
.calculate_capitalization(&self.ancestors, self.simple_capitalization_enabled())
}
pub fn calculate_and_verify_capitalization(&self) -> bool {
@@ -4132,11 +4145,11 @@ impl Bank {
}
pub fn update_accounts_hash(&self) -> Hash {
let (hash, total_lamports) = self
.rc
.accounts
.accounts_db
.update_accounts_hash(self.slot(), &self.ancestors);
let (hash, total_lamports) = self.rc.accounts.accounts_db.update_accounts_hash(
self.slot(),
&self.ancestors,
self.simple_capitalization_enabled(),
);
assert_eq!(total_lamports, self.capitalization());
hash
}
@@ -4445,6 +4458,28 @@ impl Bank {
.is_active(&feature_set::stake_program_v2::id())
}
pub fn simple_capitalization_enabled(&self) -> bool {
self.simple_capitalization_enabled_at_genesis()
|| self
.feature_set
.is_active(&feature_set::simple_capitalization::id())
}
fn simple_capitalization_enabled_at_genesis(&self) -> bool {
// genesis builtin initialization codepath is called even before the initial
// feature activation, so we need to peek this flag at very early bank
// initialization phase for the development genesis case
if let Some(account) = self.get_account(&feature_set::simple_capitalization::id()) {
if let Some(feature) = feature::from_account(&account) {
if feature.activated_at == Some(0) {
return true;
}
}
}
false
}
// This is called from snapshot restore AND for each epoch boundary
// The entire code path herein must be idempotent
fn apply_feature_activations(&mut self, init_finish_or_warp: bool) {
@@ -4476,6 +4511,10 @@ impl Bank {
self.rewrite_stakes();
}
if new_feature_activations.contains(&feature_set::simple_capitalization::id()) {
self.adjust_capitalization_for_existing_specially_retained_accounts();
}
self.ensure_feature_builtins(init_finish_or_warp, &new_feature_activations);
self.reconfigure_token2_native_mint();
self.ensure_no_storage_rewards_pool();
@@ -4560,6 +4599,39 @@ impl Bank {
}
}
fn adjust_capitalization_for_existing_specially_retained_accounts(&self) {
use solana_sdk::{bpf_loader, bpf_loader_deprecated, secp256k1_program};
let mut existing_sysvar_account_count = 8;
let mut existing_native_program_account_count = 4;
if self.get_account(&sysvar::rewards::id()).is_some() {
existing_sysvar_account_count += 1;
}
if self.get_account(&bpf_loader::id()).is_some() {
existing_native_program_account_count += 1;
}
if self.get_account(&bpf_loader_deprecated::id()).is_some() {
existing_native_program_account_count += 1;
}
if self.get_account(&secp256k1_program::id()).is_some() {
existing_native_program_account_count += 1;
}
info!(
"Adjusted capitalization for existing {} sysvars and {} native programs from {}",
existing_sysvar_account_count,
existing_native_program_account_count,
self.capitalization()
);
self.capitalization.fetch_add(
existing_sysvar_account_count + existing_native_program_account_count,
Relaxed,
);
}
fn reconfigure_token2_native_mint(&mut self) {
let reconfigure_token2_native_mint = match self.cluster_type() {
ClusterType::Development => true,
@@ -5223,6 +5295,19 @@ pub(crate) mod tests {
assert_eq!(bank.capitalization(), bank.calculate_capitalization());
}
fn assert_capitalization_diff_with_new_bank(
bank: &Bank,
updater: impl Fn() -> Bank,
asserter: impl Fn(u64, u64),
) -> Bank {
let old = bank.capitalization();
let bank = updater();
let new = bank.capitalization();
asserter(old, new);
assert_eq!(bank.capitalization(), bank.calculate_capitalization());
bank
}
#[test]
fn test_store_account_and_update_capitalization_missing() {
let (genesis_config, _mint_keypair) = create_genesis_config(0);
@@ -5425,6 +5510,7 @@ pub(crate) mod tests {
burn_percent: 10,
};
genesis_config.disable_cap_altering_features_for_preciseness();
let mut bank = Bank::new(&genesis_config);
// Enable rent collection
bank.rent_collector.epoch = 5;
@@ -6416,6 +6502,11 @@ pub(crate) mod tests {
.map(|(slot, _)| *slot)
.collect::<Vec<Slot>>()
}
fn first_slot_in_next_epoch(&self) -> Slot {
self.epoch_schedule()
.get_first_slot_in_epoch(self.epoch() + 1)
}
}
#[test]
@@ -7121,6 +7212,7 @@ pub(crate) mod tests {
let (expected_fee_collected, expected_fee_burned) =
genesis_config.fee_rate_governor.burn(expected_fee_paid);
genesis_config.disable_cap_altering_features_for_preciseness();
let mut bank = Bank::new(&genesis_config);
let capitalization = bank.capitalization();
@@ -7919,18 +8011,22 @@ pub(crate) mod tests {
assert_eq!(None, bank3.get_account_modified_since_parent(&pubkey));
}
#[test]
fn test_bank_update_sysvar_account() {
fn do_test_bank_update_sysvar_account(simple_capitalization_enabled: bool) {
use sysvar::clock::Clock;
let dummy_clock_id = solana_sdk::pubkey::new_rand();
let (genesis_config, _mint_keypair) = create_genesis_config(500);
let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
let expected_previous_slot = 3;
let expected_next_slot = expected_previous_slot + 1;
// First, initialize the clock sysvar
if simple_capitalization_enabled {
activate_all_features(&mut genesis_config);
}
let bank1 = Arc::new(Bank::new(&genesis_config));
assert_eq!(bank1.calculate_capitalization(), bank1.capitalization());
assert_capitalization_diff(
&bank1,
|| {
@@ -7952,7 +8048,12 @@ pub(crate) mod tests {
);
},
|old, new| {
assert_eq!(old, new);
// only if simple_capitalization_enabled, cap should increment
if simple_capitalization_enabled {
assert_eq!(old + 1, new);
} else {
assert_eq!(old, new);
}
},
);
@@ -8040,6 +8141,16 @@ pub(crate) mod tests {
);
}
#[test]
fn test_bank_update_sysvar_account_with_simple_capitalization_disabled() {
do_test_bank_update_sysvar_account(false)
}
#[test]
fn test_bank_update_sysvar_account_with_simple_capitalization_enabled() {
do_test_bank_update_sysvar_account(true);
}
#[test]
fn test_bank_epoch_vote_accounts() {
let leader_pubkey = solana_sdk::pubkey::new_rand();
@@ -10101,9 +10212,11 @@ pub(crate) mod tests {
assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot);
}
#[test]
fn test_add_native_program_no_overwrite() {
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
fn do_test_add_native_program(simple_capitalization_enabled: bool) {
let (mut genesis_config, _mint_keypair) = create_genesis_config(100_000);
if simple_capitalization_enabled {
activate_all_features(&mut genesis_config);
}
let slot = 123;
let program_id = solana_sdk::pubkey::new_rand();
@@ -10119,7 +10232,11 @@ pub(crate) mod tests {
&bank,
|| bank.add_native_program("mock_program", &program_id, false),
|old, new| {
assert_eq!(old, new);
if simple_capitalization_enabled {
assert_eq!(old + 1, new);
} else {
assert_eq!(old, new);
}
},
);
@@ -10162,6 +10279,16 @@ pub(crate) mod tests {
);
}
#[test]
fn test_add_native_program_with_simple_capitalization_disabled() {
do_test_add_native_program(false);
}
#[test]
fn test_add_native_program_with_simple_capitalization_enabled() {
do_test_add_native_program(true);
}
#[test]
fn test_add_native_program_inherited_cap_while_replacing() {
let (genesis_config, mint_keypair) = create_genesis_config(100_000);
@@ -10323,6 +10450,7 @@ pub(crate) mod tests {
reward_pubkey,
Account::new(u64::MAX, 0, &solana_sdk::pubkey::new_rand()),
);
genesis_config.disable_cap_altering_features_for_preciseness();
let bank0 = Bank::new(&genesis_config);
// because capitalization has been reset with bogus capitalization calculation allowing overflows,
// deliberately substract 1 lamport to simulate it
@@ -10743,6 +10871,113 @@ pub(crate) mod tests {
);
}
#[test]
fn test_simple_capitalization_adjustment_minimum_genesis_set() {
solana_logger::setup();
let (mut genesis_config, _mint_keypair) = create_genesis_config(0);
let feature_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
// inhibit deprecated rewards sysvar creation altogether
genesis_config.accounts.insert(
feature_set::deprecate_rewards_sysvar::id(),
feature::create_account(
&Feature {
activated_at: Some(0),
},
feature_balance,
),
);
let bank0 = Bank::new(&genesis_config);
let bank1 = Arc::new(new_from_parent(&Arc::new(bank0)));
// schedule activation of simple capitalization
bank1.store_account_and_update_capitalization(
&feature_set::simple_capitalization::id(),
&feature::create_account(&Feature { activated_at: None }, feature_balance),
);
// 12 is minimum adjusted cap increase in adjust_capitalization_for_existing_specially_retained_accounts
assert_capitalization_diff_with_new_bank(
&bank1,
|| Bank::new_from_parent(&bank1, &Pubkey::default(), bank1.first_slot_in_next_epoch()),
|old, new| assert_eq!(old + 12, new),
);
}
#[test]
fn test_simple_capitalization_adjustment_full_set() {
solana_logger::setup();
let (mut genesis_config, _mint_keypair) = create_genesis_config(0);
let feature_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
// activate all features but simple capitalization
activate_all_features(&mut genesis_config);
genesis_config
.accounts
.remove(&feature_set::simple_capitalization::id());
// intentionally create deprecated rewards sysvar creation
genesis_config
.accounts
.remove(&feature_set::deprecate_rewards_sysvar::id());
// intentionally create bogus native programs
fn mock_process_instruction(
_program_id: &Pubkey,
_keyed_accounts: &[KeyedAccount],
_data: &[u8],
_invoke_context: &mut dyn InvokeContext,
) -> std::result::Result<(), solana_sdk::instruction::InstructionError> {
Ok(())
}
let builtins = Builtins {
genesis_builtins: vec![
Builtin::new(
"mock bpf",
solana_sdk::bpf_loader::id(),
mock_process_instruction,
),
Builtin::new(
"mock bpf",
solana_sdk::bpf_loader_deprecated::id(),
mock_process_instruction,
),
],
feature_builtins: (vec![]),
};
let bank0 = Arc::new(Bank::new_with_paths(
&genesis_config,
Vec::new(),
&[],
None,
Some(&builtins),
));
// move to next epoch to create now deprecated rewards sysvar intentionally
let bank1 = Arc::new(Bank::new_from_parent(
&bank0,
&Pubkey::default(),
bank0.first_slot_in_next_epoch(),
));
// schedule activation of simple capitalization
bank1.store_account_and_update_capitalization(
&feature_set::simple_capitalization::id(),
&feature::create_account(&Feature { activated_at: None }, feature_balance),
);
// 16 is maximum adjusted cap increase in adjust_capitalization_for_existing_specially_retained_accounts
assert_capitalization_diff_with_new_bank(
&bank1,
|| Bank::new_from_parent(&bank1, &Pubkey::default(), bank1.first_slot_in_next_epoch()),
|old, new| assert_eq!(old + 16, new),
);
}
#[test]
fn test_timestamp_bounding_feature() {
let leader_pubkey = solana_sdk::pubkey::new_rand();