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

* 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

(cherry picked from commit de9ac43ebf)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
This commit is contained in:
mergify[bot]
2020-12-14 13:29:09 +00:00
committed by GitHub
parent 7e3bf25991
commit 87a578a1f9
8 changed files with 439 additions and 63 deletions

View File

@ -98,7 +98,10 @@ fn main() {
} else {
let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash");
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors).0;
let hash = accounts
.accounts_db
.update_accounts_hash(0, &ancestors, true)
.0;
time.stop();
println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0);

View File

@ -1217,6 +1217,13 @@ fn main() {
.help("Enable stake program v2 (several inflation-related staking \
bugs are feature-gated behind this)"),
)
.arg(
Arg::with_name("enable_simple_capitalization")
.required(false)
.long("enable-simple-capitalization")
.takes_value(false)
.help("Enable simple capitalization to test hardcoded cap adjustments"),
)
.arg(
Arg::with_name("recalculate_capitalization")
.required(false)
@ -2115,11 +2122,47 @@ fn main() {
.lazy_rent_collection
.store(true, std::sync::atomic::Ordering::Relaxed);
let feature_account_balance = std::cmp::max(
genesis_config.rent.minimum_balance(Feature::size_of()),
1,
);
if arg_matches.is_present("enable_simple_capitalization") {
if base_bank
.get_account(&feature_set::simple_capitalization::id())
.is_none()
{
base_bank.store_account(
&feature_set::simple_capitalization::id(),
&feature::create_account(
&Feature { activated_at: None },
feature_account_balance,
),
);
if base_bank
.get_account(&feature_set::cumulative_rent_related_fixes::id())
.is_some()
{
// steal some lamports from the pretty old feature not to affect
// capitalizaion, which doesn't affect inflation behavior!
base_bank.store_account(
&feature_set::cumulative_rent_related_fixes::id(),
&Account::default(),
);
} else {
let old_cap = base_bank.set_capitalization();
let new_cap = base_bank.capitalization();
warn!(
"Skewing capitalization a bit to enable simple capitalization as \
requested: increasing {} from {} to {}",
feature_account_balance, old_cap, new_cap,
);
assert_eq!(old_cap + feature_account_balance, new_cap);
}
} else {
warn!("Already simple_capitalization is activated (or scheduled)");
}
}
if arg_matches.is_present("enable_stake_program_v2") {
let feature_account_balance = std::cmp::max(
genesis_config.rent.minimum_balance(Feature::size_of()),
1,
);
let mut force_enabled_count = 0;
if base_bank
.get_account(&feature_set::stake_program_v2::id())

View File

@ -93,8 +93,12 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
let slot = 0;
create_test_accounts(&accounts, &mut pubkeys, num_accounts, slot);
let ancestors = vec![(0, 0)].into_iter().collect();
let (_, total_lamports) = accounts.accounts_db.update_accounts_hash(0, &ancestors);
bencher.iter(|| assert!(accounts.verify_bank_hash_and_lamports(0, &ancestors, total_lamports)));
let (_, total_lamports) = accounts
.accounts_db
.update_accounts_hash(0, &ancestors, true);
bencher.iter(|| {
assert!(accounts.verify_bank_hash_and_lamports(0, &ancestors, total_lamports, true))
});
}
#[bench]
@ -108,7 +112,9 @@ fn test_update_accounts_hash(bencher: &mut Bencher) {
create_test_accounts(&accounts, &mut pubkeys, 50_000, 0);
let ancestors = vec![(0, 0)].into_iter().collect();
bencher.iter(|| {
accounts.accounts_db.update_accounts_hash(0, &ancestors);
accounts
.accounts_db
.update_accounts_hash(0, &ancestors, true);
});
}

View File

@ -460,7 +460,11 @@ impl Accounts {
accounts_balances
}
pub fn calculate_capitalization(&self, ancestors: &Ancestors) -> u64 {
pub fn calculate_capitalization(
&self,
ancestors: &Ancestors,
simple_capitalization_enabled: bool,
) -> u64 {
let balances =
self.load_all_unchecked(ancestors)
.into_iter()
@ -469,6 +473,7 @@ impl Accounts {
account.lamports,
&account.owner,
account.executable,
simple_capitalization_enabled,
)
});
@ -481,11 +486,14 @@ impl Accounts {
slot: Slot,
ancestors: &Ancestors,
total_lamports: u64,
simple_capitalization_enabled: bool,
) -> bool {
if let Err(err) =
self.accounts_db
.verify_bank_hash_and_lamports(slot, ancestors, total_lamports)
{
if let Err(err) = self.accounts_db.verify_bank_hash_and_lamports(
slot,
ancestors,
total_lamports,
simple_capitalization_enabled,
) {
warn!("verify_bank_hash failed: {:?}", err);
false
} else {

View File

@ -1959,7 +1959,12 @@ impl AccountsDB {
lamports: u64,
owner: &Pubkey,
executable: bool,
simple_capitalization_enabled: bool,
) -> u64 {
if simple_capitalization_enabled {
return lamports;
}
let is_specially_retained = (solana_sdk::native_loader::check_id(owner) && executable)
|| solana_sdk::sysvar::check_id(owner);
@ -1978,6 +1983,7 @@ impl AccountsDB {
slot: Slot,
ancestors: &Ancestors,
check_hash: bool,
simple_capitalization_enabled: bool,
) -> Result<(Hash, u64), BankHashVerificationError> {
use BankHashVerificationError::*;
let mut scan = Measure::start("scan");
@ -2008,6 +2014,7 @@ impl AccountsDB {
account_info.lamports,
&account.account_meta.owner,
account.account_meta.executable,
simple_capitalization_enabled,
);
if check_hash {
@ -2066,9 +2073,14 @@ impl AccountsDB {
bank_hash_info.snapshot_hash
}
pub fn update_accounts_hash(&self, slot: Slot, ancestors: &Ancestors) -> (Hash, u64) {
pub fn update_accounts_hash(
&self,
slot: Slot,
ancestors: &Ancestors,
simple_capitalization_enabled: bool,
) -> (Hash, u64) {
let (hash, total_lamports) = self
.calculate_accounts_hash(slot, ancestors, false)
.calculate_accounts_hash(slot, ancestors, false, simple_capitalization_enabled)
.unwrap();
let mut bank_hashes = self.bank_hashes.write().unwrap();
let mut bank_hash_info = bank_hashes.get_mut(&slot).unwrap();
@ -2081,11 +2093,12 @@ impl AccountsDB {
slot: Slot,
ancestors: &Ancestors,
total_lamports: u64,
simple_capitalization_enabled: bool,
) -> Result<(), BankHashVerificationError> {
use BankHashVerificationError::*;
let (calculated_hash, calculated_lamports) =
self.calculate_accounts_hash(slot, ancestors, true)?;
self.calculate_accounts_hash(slot, ancestors, true, simple_capitalization_enabled)?;
if calculated_lamports != total_lamports {
warn!(
@ -3608,8 +3621,8 @@ pub mod tests {
let ancestors = linear_ancestors(latest_slot);
assert_eq!(
daccounts.update_accounts_hash(latest_slot, &ancestors),
accounts.update_accounts_hash(latest_slot, &ancestors)
daccounts.update_accounts_hash(latest_slot, &ancestors, true),
accounts.update_accounts_hash(latest_slot, &ancestors, true)
);
}
@ -3762,12 +3775,12 @@ pub mod tests {
let ancestors = linear_ancestors(current_slot);
info!("ancestors: {:?}", ancestors);
let hash = accounts.update_accounts_hash(current_slot, &ancestors);
let hash = accounts.update_accounts_hash(current_slot, &ancestors, true);
accounts.clean_accounts(None);
assert_eq!(
accounts.update_accounts_hash(current_slot, &ancestors),
accounts.update_accounts_hash(current_slot, &ancestors, true),
hash
);
@ -3884,7 +3897,7 @@ pub mod tests {
accounts.add_root(current_slot);
accounts.print_accounts_stats("pre_f");
accounts.update_accounts_hash(4, &HashMap::default());
accounts.update_accounts_hash(4, &HashMap::default(), true);
let accounts = f(accounts, current_slot);
@ -3896,7 +3909,7 @@ pub mod tests {
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
accounts
.verify_bank_hash_and_lamports(4, &HashMap::default(), 1222)
.verify_bank_hash_and_lamports(4, &HashMap::default(), 1222, true)
.unwrap();
}
@ -4294,15 +4307,15 @@ pub mod tests {
db.store(some_slot, &[(&key, &account)]);
db.add_root(some_slot);
db.update_accounts_hash(some_slot, &ancestors);
db.update_accounts_hash(some_slot, &ancestors, true);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
Ok(_)
);
db.bank_hashes.write().unwrap().remove(&some_slot).unwrap();
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
Err(MissingBankHash)
);
@ -4317,7 +4330,7 @@ pub mod tests {
.unwrap()
.insert(some_slot, bank_hash_info);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
Err(MismatchedBankHash)
);
}
@ -4336,9 +4349,9 @@ pub mod tests {
db.store(some_slot, &[(&key, &account)]);
db.add_root(some_slot);
db.update_accounts_hash(some_slot, &ancestors);
db.update_accounts_hash(some_slot, &ancestors, true);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
Ok(_)
);
@ -4350,15 +4363,19 @@ pub mod tests {
&solana_sdk::native_loader::create_loadable_account("foo", 1),
)],
);
db.update_accounts_hash(some_slot, &ancestors);
db.update_accounts_hash(some_slot, &ancestors, true);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, false),
Ok(_)
);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 2, true),
Ok(_)
);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10),
Err(MismatchedTotalLamports(expected, actual)) if expected == 1 && actual == 10
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10, true),
Err(MismatchedTotalLamports(expected, actual)) if expected == 2 && actual == 10
);
}
@ -4375,9 +4392,9 @@ pub mod tests {
.unwrap()
.insert(some_slot, BankHashInfo::default());
db.add_root(some_slot);
db.update_accounts_hash(some_slot, &ancestors);
db.update_accounts_hash(some_slot, &ancestors, true);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 0),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 0, true),
Ok(_)
);
}
@ -4402,7 +4419,7 @@ pub mod tests {
db.store_accounts_default(some_slot, accounts, &[some_hash]);
db.add_root(some_slot);
assert_matches!(
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
Err(MismatchedAccountHash)
);
}
@ -4841,14 +4858,14 @@ pub mod tests {
);
let no_ancestors = HashMap::default();
accounts.update_accounts_hash(current_slot, &no_ancestors);
accounts.update_accounts_hash(current_slot, &no_ancestors, true);
accounts
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300)
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300, true)
.unwrap();
let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot);
accounts
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300)
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300, true)
.unwrap();
// repeating should be no-op
@ -5045,7 +5062,7 @@ pub mod tests {
fn test_account_balance_for_capitalization_normal() {
// system accounts
assert_eq!(
AccountsDB::account_balance_for_capitalization(10, &Pubkey::default(), false),
AccountsDB::account_balance_for_capitalization(10, &Pubkey::default(), false, true),
10
);
// any random program data accounts
@ -5053,7 +5070,17 @@ pub mod tests {
AccountsDB::account_balance_for_capitalization(
10,
&solana_sdk::pubkey::new_rand(),
false
false,
true,
),
10
);
assert_eq!(
AccountsDB::account_balance_for_capitalization(
10,
&solana_sdk::pubkey::new_rand(),
false,
false,
),
10
);
@ -5069,16 +5096,40 @@ pub mod tests {
AccountsDB::account_balance_for_capitalization(
normal_sysvar.lamports,
&normal_sysvar.owner,
normal_sysvar.executable
normal_sysvar.executable,
false,
),
0
);
assert_eq!(
AccountsDB::account_balance_for_capitalization(
normal_sysvar.lamports,
&normal_sysvar.owner,
normal_sysvar.executable,
true,
),
1
);
// currently transactions can send any lamports to sysvars although this is not sensible.
assert_eq!(
AccountsDB::account_balance_for_capitalization(10, &solana_sdk::sysvar::id(), false),
AccountsDB::account_balance_for_capitalization(
10,
&solana_sdk::sysvar::id(),
false,
false
),
9
);
assert_eq!(
AccountsDB::account_balance_for_capitalization(
10,
&solana_sdk::sysvar::id(),
false,
true
),
10
);
}
#[test]
@ -5088,20 +5139,40 @@ pub mod tests {
AccountsDB::account_balance_for_capitalization(
normal_native_program.lamports,
&normal_native_program.owner,
normal_native_program.executable
normal_native_program.executable,
false,
),
0
);
assert_eq!(
AccountsDB::account_balance_for_capitalization(
normal_native_program.lamports,
&normal_native_program.owner,
normal_native_program.executable,
true,
),
1
);
// test maliciously assigned bogus native loader account
assert_eq!(
AccountsDB::account_balance_for_capitalization(
1,
&solana_sdk::native_loader::id(),
false
false,
false,
),
1
)
);
assert_eq!(
AccountsDB::account_balance_for_capitalization(
1,
&solana_sdk::native_loader::id(),
false,
true,
),
1
);
}
#[test]

View File

@ -1241,7 +1241,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 {
@ -1989,7 +1994,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() {
@ -1998,6 +2002,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() {
@ -2117,7 +2124,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);
}
@ -3734,7 +3745,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) {
@ -4040,6 +4050,7 @@ impl Bank {
self.slot(),
&self.ancestors,
self.capitalization(),
self.simple_capitalization_enabled(),
)
}
@ -4070,7 +4081,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 {
@ -4101,11 +4114,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
}
@ -4414,6 +4427,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) {
@ -4445,6 +4480,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();
@ -4529,6 +4568,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,
@ -5185,6 +5257,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);
@ -5387,6 +5472,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;
@ -6378,6 +6464,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]
@ -7083,6 +7174,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();
@ -7881,18 +7973,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,
|| {
@ -7914,7 +8010,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);
}
},
);
@ -8002,6 +8103,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();
@ -10061,9 +10172,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();
@ -10079,7 +10192,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);
}
},
);
@ -10122,6 +10239,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);
@ -10283,6 +10410,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
@ -10702,6 +10830,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();

View File

@ -98,6 +98,10 @@ pub mod filter_stake_delegation_accounts {
solana_sdk::declare_id!("GE7fRxmW46K6EmCD9AMZSbnaJ2e3LfqCZzdHi9hmYAgi");
}
pub mod simple_capitalization {
solana_sdk::declare_id!("9r69RnnxABmpcPFfj1yhg4n9YFR2MNaLdKJCC6v3Speb");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -124,6 +128,7 @@ lazy_static! {
(stake_program_v2::id(), "solana_stake_program v2"),
(rewrite_stake::id(), "rewrite stake"),
(filter_stake_delegation_accounts::id(), "filter stake_delegation_accounts #14062"),
(simple_capitalization::id(), "simple capitalization"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -149,6 +149,11 @@ impl GenesisConfig {
hash(&serialized)
}
pub fn disable_cap_altering_features_for_preciseness(&mut self) {
self.accounts
.remove(&crate::feature_set::simple_capitalization::id());
}
fn genesis_filename(ledger_path: &Path) -> PathBuf {
Path::new(ledger_path).join("genesis.bin")
}