Cleanup activated rent_for_sysvars feature (#22454)

This commit is contained in:
Justin Starry 2022-01-14 20:34:09 +08:00 committed by GitHub
parent ae6c511f13
commit cddab635ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 521 deletions

View File

@ -253,7 +253,6 @@ impl Accounts {
let mut accounts = Vec::with_capacity(message.account_keys_len());
let mut account_deps = Vec::with_capacity(message.account_keys_len());
let mut rent_debits = RentDebits::default();
let rent_for_sysvars = feature_set.is_active(&feature_set::rent_for_sysvars::id());
for (i, key) in message.account_keys_iter().enumerate() {
let account = if !message.is_non_loader_key(i) {
// Fill in an empty account for the program slots.
@ -279,7 +278,6 @@ impl Accounts {
.collect_from_existing_account(
key,
&mut account,
rent_for_sysvars,
self.accounts_db.filler_account_suffix.as_ref(),
)
.rent_amount;
@ -1105,7 +1103,6 @@ impl Accounts {
rent_collector: &RentCollector,
blockhash: &Hash,
lamports_per_signature: u64,
rent_for_sysvars: bool,
leave_nonce_on_success: bool,
) {
let accounts_to_store = self.collect_accounts_to_store(
@ -1115,7 +1112,6 @@ impl Accounts {
rent_collector,
blockhash,
lamports_per_signature,
rent_for_sysvars,
leave_nonce_on_success,
);
self.accounts_db.store_cached(slot, &accounts_to_store);
@ -1142,7 +1138,6 @@ impl Accounts {
rent_collector: &RentCollector,
blockhash: &Hash,
lamports_per_signature: u64,
rent_for_sysvars: bool,
leave_nonce_on_success: bool,
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
let mut accounts = Vec::with_capacity(load_results.len());
@ -1202,7 +1197,7 @@ impl Accounts {
if execution_status.is_ok() || is_nonce_account || is_fee_payer {
if account.rent_epoch() == INITIAL_RENT_EPOCH {
let rent = rent_collector
.collect_from_created_account(address, account, rent_for_sysvars)
.collect_from_created_account(address, account)
.rent_amount;
loaded_transaction.rent += rent;
loaded_transaction.rent_debits.insert(
@ -2917,7 +2912,6 @@ mod tests {
&rent_collector,
&Hash::default(),
0,
true,
true, // leave_nonce_on_success
);
assert_eq!(collected_accounts.len(), 2);
@ -3346,7 +3340,6 @@ mod tests {
&rent_collector,
&next_blockhash,
0,
true,
true, // leave_nonce_on_success
);
assert_eq!(collected_accounts.len(), 2);
@ -3456,7 +3449,6 @@ mod tests {
&rent_collector,
&next_blockhash,
0,
true,
true, // leave_nonce_on_success
);
assert_eq!(collected_accounts.len(), 1);

View File

@ -6708,7 +6708,7 @@ impl AccountsDb {
accounts_data_len += stored_account.data().len() as u64;
}
if !rent_collector.should_collect_rent(&pubkey, &stored_account, false)
if !rent_collector.should_collect_rent(&pubkey, &stored_account)
|| rent_collector.get_rent_due(&stored_account).is_exempt()
{
num_accounts_rent_exempt += 1;

View File

@ -1925,34 +1925,15 @@ impl Bank {
where
F: Fn(&Option<AccountSharedData>) -> AccountSharedData,
{
let old_account = if !self.rent_for_sysvars() {
// This old behavior is being retired for simpler reasoning for the benefits of all.
// Specifically, get_sysvar_account_with_fixed_root() doesn't work nicely with eager
// rent collection, which becomes significant for sysvars after rent_for_sysvars
// activation. That's because get_sysvar_account_with_fixed_root() invocations by both
// update_slot_history() and update_recent_blockhashes() ignores any updates
// by eager rent collection in this slot.
// Also, it turned out that get_sysvar_account_with_fixed_root()'s special
// behavior (idempotent) isn't needed to begin with, because we're fairly certain that
// we don't call new_from_parent() with same child slot multiple times in the
// production code (except after proper handling of duplicate slot dumping)...
self.get_sysvar_account_with_fixed_root(pubkey)
} else {
self.get_account_with_fixed_root(pubkey)
};
let old_account = self.get_account_with_fixed_root(pubkey);
let mut new_account = updater(&old_account);
if self.rent_for_sysvars() {
// When new sysvar comes into existence (with RENT_UNADJUSTED_INITIAL_BALANCE lamports),
// this code ensures that the sysvar's balance is adjusted to be rent-exempt.
// Note that all of existing sysvar balances must be adjusted immediately (i.e. reset) upon
// the `rent_for_sysvars` feature activation (ref: reset_all_sysvar_balances).
//
// More generally, this code always re-calculates for possible sysvar data size change,
// although there is no such sysvars currently.
self.adjust_sysvar_balance_for_rent(&mut new_account);
}
// When new sysvar comes into existence (with RENT_UNADJUSTED_INITIAL_BALANCE lamports),
// this code ensures that the sysvar's balance is adjusted to be rent-exempt.
//
// More generally, this code always re-calculates for possible sysvar data size change,
// although there is no such sysvars currently.
self.adjust_sysvar_balance_for_rent(&mut new_account);
self.store_account_and_update_capitalization(pubkey, &new_account);
}
@ -1967,16 +1948,10 @@ impl Bank {
.as_ref()
.map(|a| a.lamports())
.unwrap_or(RENT_UNADJUSTED_INITIAL_BALANCE),
if !self.rent_for_sysvars() {
INITIAL_RENT_EPOCH
} else {
// start to inherit rent_epoch updated by rent collection to be consistent with
// other normal accounts
old_account
.as_ref()
.map(|a| a.rent_epoch())
.unwrap_or(INITIAL_RENT_EPOCH)
},
old_account
.as_ref()
.map(|a| a.rent_epoch())
.unwrap_or(INITIAL_RENT_EPOCH),
)
}
@ -4155,7 +4130,6 @@ impl Bank {
&self.rent_collector,
&blockhash,
lamports_per_signature,
self.rent_for_sysvars(),
self.leave_nonce_on_success(),
);
let rent_debits = self.collect_rent(&execution_results, loaded_txs);
@ -4437,14 +4411,12 @@ impl Bank {
let account_count = accounts.len();
// parallelize?
let rent_for_sysvars = self.rent_for_sysvars();
let mut rent_debits = RentDebits::default();
let mut total_collected = CollectedInfo::default();
for (pubkey, mut account) in accounts {
let collected = self.rent_collector.collect_from_existing_account(
&pubkey,
&mut account,
rent_for_sysvars,
self.rc.accounts.accounts_db.filler_account_suffix.as_ref(),
);
total_collected += collected;
@ -5312,24 +5284,6 @@ impl Bank {
self.rc.accounts.load_with_fixed_root(ancestors, pubkey)
}
// Exclude self to really fetch the parent Bank's account hash and data.
//
// Being idempotent is needed to make the lazy initialization possible,
// especially for update_slot_hashes at the moment, which can be called
// multiple times with the same parent_slot in the case of forking.
//
// Generally, all of sysvar update granularity should be slot boundaries.
//
// This behavior is deprecated... See comment in update_sysvar_account() for details
fn get_sysvar_account_with_fixed_root(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
let mut ancestors = self.ancestors.clone();
ancestors.remove(&self.slot());
self.rc
.accounts
.load_with_fixed_root(&ancestors, pubkey)
.map(|(acc, _slot)| acc)
}
pub fn get_program_accounts(
&self,
program_id: &Pubkey,
@ -6173,14 +6127,6 @@ impl Bank {
if new_feature_activations.contains(&feature_set::spl_token_v3_3_0_release::id()) {
self.apply_spl_token_v3_3_0_release();
}
if new_feature_activations.contains(&feature_set::rent_for_sysvars::id()) {
// when this feature is activated, immediately all of existing sysvars are susceptible
// to rent collection and account data removal due to insufficient balance due to only
// having 1 lamport.
// so before any is accessed, reset the balance to be rent-exempt here at the same
// timing when perpetual balance adjustment is started in update_sysvar_account().
self.reset_all_sysvar_balances();
}
if !debug_do_not_add_builtins {
self.ensure_feature_builtins(init_finish_or_warp, &new_feature_activations);
@ -6189,36 +6135,6 @@ impl Bank {
self.ensure_no_storage_rewards_pool();
}
fn reset_all_sysvar_balances(&self) {
for sysvar_id in &[
sysvar::clock::id(),
sysvar::epoch_schedule::id(),
#[allow(deprecated)]
sysvar::fees::id(),
#[allow(deprecated)]
sysvar::recent_blockhashes::id(),
sysvar::rent::id(),
sysvar::rewards::id(),
sysvar::slot_hashes::id(),
sysvar::slot_history::id(),
sysvar::stake_history::id(),
] {
if let Some(mut account) = self.get_account(sysvar_id) {
let (old_data_len, old_lamports) = (account.data().len(), account.lamports());
self.adjust_sysvar_balance_for_rent(&mut account);
info!(
"reset_all_sysvar_balances (slot: {}): {} ({} bytes) is reset from {} to {}",
self.slot(),
sysvar_id,
old_data_len,
old_lamports,
account.lamports()
);
self.store_account_and_update_capitalization(sysvar_id, &account);
}
}
}
fn adjust_sysvar_balance_for_rent(&self, account: &mut AccountSharedData) {
account.set_lamports(
self.get_minimum_balance_for_rent_exemption(account.data().len())
@ -6405,11 +6321,6 @@ impl Bank {
}
}
fn rent_for_sysvars(&self) -> bool {
self.feature_set
.is_active(&feature_set::rent_for_sysvars::id())
}
/// Get all the accounts for this bank and calculate stats
pub fn get_total_accounts_stats(&self) -> ScanResult<TotalAccountsStats> {
let accounts = self.get_all_accounts_with_modified_slots()?;
@ -6437,7 +6348,7 @@ impl Bank {
total_accounts_stats.executable_data_len += data_len;
}
if !rent_collector.should_collect_rent(pubkey, account, false)
if !rent_collector.should_collect_rent(pubkey, account)
|| rent_collector.get_rent_due(account).is_exempt()
{
total_accounts_stats.num_rent_exempt_accounts += 1;
@ -6542,7 +6453,8 @@ pub(crate) mod tests {
genesis_utils::{
activate_all_features, bootstrap_validator_stake_lamports,
create_genesis_config_with_leader, create_genesis_config_with_vote_accounts,
GenesisConfigInfo, ValidatorVoteKeypairs,
genesis_sysvar_and_builtin_program_lamports, GenesisConfigInfo,
ValidatorVoteKeypairs,
},
stake_delegations::StakeDelegations,
status_cache::MAX_CACHE_ENTRIES,
@ -6873,6 +6785,21 @@ pub(crate) mod tests {
);
}
fn bank0_sysvar_delta() -> u64 {
const SLOT_HISTORY_SYSVAR_MIN_BALANCE: u64 = 913_326_000;
SLOT_HISTORY_SYSVAR_MIN_BALANCE
}
fn bank1_sysvar_delta() -> u64 {
const SLOT_HASHES_SYSVAR_MIN_BALANCE: u64 = 143_487_360;
SLOT_HASHES_SYSVAR_MIN_BALANCE
}
fn new_epoch_sysvar_delta() -> u64 {
const REWARDS_SYSVAR_MIN_BALANCE: u64 = 1_002_240;
REWARDS_SYSVAR_MIN_BALANCE
}
#[test]
fn test_bank_capitalization() {
let bank0 = Arc::new(Bank::new_for_tests(&GenesisConfig {
@ -6887,16 +6814,26 @@ pub(crate) mod tests {
cluster_type: ClusterType::MainnetBeta,
..GenesisConfig::default()
}));
let sysvar_and_builtin_program_delta0 = 11;
assert_eq!(
bank0.capitalization(),
42 * 42 + sysvar_and_builtin_program_delta0
42 * 42 + genesis_sysvar_and_builtin_program_lamports(),
);
bank0.freeze();
assert_eq!(
bank0.capitalization(),
42 * 42 + genesis_sysvar_and_builtin_program_lamports() + bank0_sysvar_delta(),
);
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
let sysvar_and_builtin_program_delta1 = 2;
assert_eq!(
bank1.capitalization(),
42 * 42 + sysvar_and_builtin_program_delta0 + sysvar_and_builtin_program_delta1,
42 * 42
+ genesis_sysvar_and_builtin_program_lamports()
+ bank0_sysvar_delta()
+ bank1_sysvar_delta(),
);
}
@ -7131,19 +7068,6 @@ pub(crate) mod tests {
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
}
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(true));
bank
}
#[test]
fn test_store_account_and_update_capitalization_missing() {
let (genesis_config, _mint_keypair) = create_genesis_config(0);
@ -8430,18 +8354,13 @@ 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]
fn test_rent_eager_collect_rent_in_partition() {
solana_logger::setup();
let (mut genesis_config, _mint_keypair) = create_genesis_config(1);
let (mut genesis_config, _mint_keypair) = create_genesis_config(1_000_000);
activate_all_features(&mut genesis_config);
let zero_lamport_pubkey = solana_sdk::pubkey::new_rand();
@ -8494,8 +8413,7 @@ pub(crate) mod tests {
bank.collect_rent_in_partition((0, 0, 1)); // all range
// unrelated 1-lamport accounts exists
assert_eq!(bank.collected_rent.load(Relaxed), rent_collected + 2);
assert_eq!(bank.collected_rent.load(Relaxed), rent_collected);
assert_eq!(
bank.get_account(&rent_due_pubkey).unwrap().lamports(),
little_lamports - rent_collected
@ -8616,15 +8534,14 @@ pub(crate) mod tests {
// not being eagerly-collected for exact rewards calculation
bank0.restore_old_behavior_for_fragile_tests();
let sysvar_and_builtin_program_delta0 = 11;
assert_eq!(
bank0.capitalization(),
42 * 1_000_000_000 + sysvar_and_builtin_program_delta0
42 * 1_000_000_000 + genesis_sysvar_and_builtin_program_lamports(),
);
assert!(bank0.rewards.read().unwrap().is_empty());
let ((vote_id, mut vote_account), (stake_id, stake_account)) =
crate::stakes::tests::create_staked_node_accounts(1_0000);
crate::stakes::tests::create_staked_node_accounts(10_000);
let starting_vote_and_stake_balance = 10_000 + 1;
// set up accounts
bank0.store_account_and_update_capitalization(&stake_id, &stake_account);
@ -8646,6 +8563,16 @@ pub(crate) mod tests {
};
}
bank0.store_account_and_update_capitalization(&vote_id, &vote_account);
bank0.freeze();
assert_eq!(
bank0.capitalization(),
42 * 1_000_000_000
+ genesis_sysvar_and_builtin_program_lamports()
+ starting_vote_and_stake_balance
+ bank0_sysvar_delta(),
);
assert!(bank0.rewards.read().unwrap().is_empty());
let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
let validator_points: u128 = bank0
@ -8682,9 +8609,10 @@ pub(crate) mod tests {
assert_ne!(bank1.capitalization(), bank0.capitalization());
// verify the inflation is represented in validator_points *
let sysvar_and_builtin_program_delta1 = 2;
let paid_rewards =
bank1.capitalization() - bank0.capitalization() - sysvar_and_builtin_program_delta1;
let paid_rewards = bank1.capitalization()
- bank0.capitalization()
- bank1_sysvar_delta()
- new_epoch_sysvar_delta();
let rewards = bank1
.get_account(&sysvar::rewards::id())
@ -8751,12 +8679,10 @@ pub(crate) mod tests {
// not being eagerly-collected for exact rewards calculation
bank.restore_old_behavior_for_fragile_tests();
let sysvar_and_builtin_program_delta = 11;
assert_eq!(
bank.capitalization(),
42 * 1_000_000_000 + sysvar_and_builtin_program_delta
42 * 1_000_000_000 + genesis_sysvar_and_builtin_program_lamports()
);
assert!(bank.rewards.read().unwrap().is_empty());
let vote_id = solana_sdk::pubkey::new_rand();
let mut vote_account =
@ -9059,17 +8985,17 @@ pub(crate) mod tests {
let normal_pubkey = solana_sdk::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);
assert_eq!(bank.get_balance(&sysvar_pubkey), 1_169_280);
bank.transfer(500, &mint_keypair, &normal_pubkey).unwrap();
bank.transfer(500, &mint_keypair, &sysvar_pubkey)
.unwrap_err();
assert_eq!(bank.get_balance(&normal_pubkey), 500);
assert_eq!(bank.get_balance(&sysvar_pubkey), 1);
assert_eq!(bank.get_balance(&sysvar_pubkey), 1_169_280);
let bank = Arc::new(new_from_parent(&bank));
assert_eq!(bank.get_balance(&normal_pubkey), 500);
assert_eq!(bank.get_balance(&sysvar_pubkey), 1);
assert_eq!(bank.get_balance(&sysvar_pubkey), 1_169_280);
}
#[test]
@ -10112,8 +10038,6 @@ pub(crate) mod tests {
expected_next_slot,
from_account::<Clock, _>(&current_account).unwrap().slot
);
// inherit_specially_retained_account_fields() now starts to inherit rent_epoch too
// with rent_for_sysvars
assert_eq!(dummy_rent_epoch, current_account.rent_epoch());
},
|old, new| {
@ -12256,25 +12180,25 @@ pub(crate) mod tests {
if bank.slot == 0 {
assert_eq!(
bank.hash().to_string(),
"DqaWg7EVKzb5Fpe92zNBtXAWqLwcedgHDicYrCBnf3QK"
"2VLeMNvmpPEDy7sWw48gUVzjMa3WmgoegjavyhLTCnq7"
);
}
if bank.slot == 32 {
assert_eq!(
bank.hash().to_string(),
"AYdhzhKrM74r9XuZBDGcHeFzg2DEtp1boggnEnzDjZSq"
"AYnKo6pV8WJy5yXiSkYk79LWnCUCG714fQG7Xq2EizLv"
);
}
if bank.slot == 64 {
assert_eq!(
bank.hash().to_string(),
"EsbPVYzo1qz5reEUH5okKW4ExB6WbcidkVdW5mzpFn7C"
"37iX2uYecqAzxwyeWL8FCFeWg31B8u9hQhCRF76atrWy"
);
}
if bank.slot == 128 {
assert_eq!(
bank.hash().to_string(),
"H3DWrQ6FqbLkFNDxbWQ62UKRbw2dbuxf3oVF2VpBk6Ga"
"4TDaCAvTJJg1YZ6aDhoM33Hk2cDzaraaxtGMJCmXG4wf"
);
break;
}
@ -13388,15 +13312,11 @@ pub(crate) mod tests {
.sum()
}
fn expected_cap_delta_after_sysvar_reset(bank: &Bank, sysvar_ids: &[Pubkey]) -> u64 {
min_rent_excempt_balance_for_sysvars(bank, sysvar_ids) - sysvar_ids.len() as u64
}
#[test]
fn test_adjust_sysvar_balance_for_rent() {
let (genesis_config, _mint_keypair) = create_genesis_config(0);
let bank = Bank::new_for_tests(&genesis_config);
let mut smaller_sample_sysvar = bank.get_account(&sysvar::clock::id()).unwrap();
let mut smaller_sample_sysvar = AccountSharedData::new(1, 0, &Pubkey::default());
assert_eq!(smaller_sample_sysvar.lamports(), 1);
bank.adjust_sysvar_balance_for_rent(&mut smaller_sample_sysvar);
assert_eq!(
@ -13419,245 +13339,6 @@ pub(crate) mod tests {
assert_eq!(smaller_sample_sysvar.lamports(), excess_lamports);
}
// this test can be removed after rent_for_sysvars activation on mainnet-beta
#[test]
fn test_no_deletion_due_to_rent_upon_rent_for_sysvar_activation() {
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 rent_for_sysvars
activate_all_features(&mut genesis_config);
genesis_config
.accounts
.remove(&feature_set::rent_for_sysvars::id());
let bank0 = Bank::new_for_tests(&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::rent_for_sysvars::id(),
&feature::create_account(&Feature { activated_at: None }, feature_balance),
);
let bank2 =
Bank::new_from_parent(&bank1, &Pubkey::default(), bank1.first_slot_in_next_epoch());
assert_eq!(
bank2
.get_program_accounts(&sysvar::id(), &ScanConfig::default(),)
.unwrap()
.len(),
8
);
// force rent collection for sysvars
bank2.collect_rent_in_partition((0, 0, 1)); // all range
// no sysvar should be deleted due to rent
assert_eq!(
bank2
.get_program_accounts(&sysvar::id(), &ScanConfig::default(),)
.unwrap()
.len(),
8
);
}
// this test can be removed after rent_for_sysvars activation on mainnet-beta
#[test]
fn test_rent_for_sysvars_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(),
Account::from(feature::create_account(
&Feature {
activated_at: Some(0),
},
feature_balance,
)),
);
let bank0 = Bank::new_for_tests(&genesis_config);
let bank1 = Arc::new(new_from_parent(&Arc::new(bank0)));
// schedule activation of rent_for_sysvars
bank1.store_account_and_update_capitalization(
&feature_set::rent_for_sysvars::id(),
&feature::create_account(&Feature { activated_at: None }, feature_balance),
);
{
let sysvars = bank1
.get_program_accounts(&sysvar::id(), &ScanConfig::default())
.unwrap();
assert_eq!(sysvars.len(), 8);
assert!(sysvars
.iter()
.map(|(_pubkey, account)| account.lamports())
.all(|lamports| lamports == 1));
}
// 8 sysvars should be reset by reset_all_sysvar_balances()
let bank2 = 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 + expected_cap_delta_after_sysvar_reset(
&bank1,
&[
sysvar::clock::id(),
sysvar::epoch_schedule::id(),
#[allow(deprecated)]
sysvar::fees::id(),
#[allow(deprecated)]
sysvar::recent_blockhashes::id(),
sysvar::rent::id(),
sysvar::slot_hashes::id(),
sysvar::slot_history::id(),
sysvar::stake_history::id(),
]
),
new
)
},
);
{
let sysvars = bank2
.get_program_accounts(&sysvar::id(), &ScanConfig::default())
.unwrap();
assert_eq!(sysvars.len(), 8);
assert!(sysvars
.iter()
.map(|(_pubkey, account)| account.lamports())
.all(|lamports| lamports > 1));
}
}
// this test can be removed after rent_for_sysvars activation on mainnet-beta
#[test]
fn test_rent_for_sysvars_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 rent_for_sysvars
activate_all_features(&mut genesis_config);
genesis_config
.accounts
.remove(&feature_set::rent_for_sysvars::id());
// intentionally create deprecated rewards sysvar creation
genesis_config
.accounts
.remove(&feature_set::deprecate_rewards_sysvar::id());
// intentionally create bogus builtin programs
#[allow(clippy::unnecessary_wraps)]
fn mock_process_instruction(
_first_instruction_account: usize,
_data: &[u8],
_invoke_context: &mut 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_for_tests(
&genesis_config,
Vec::new(),
None,
Some(&builtins),
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
));
// 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::rent_for_sysvars::id(),
&feature::create_account(&Feature { activated_at: None }, feature_balance),
);
{
let sysvars = bank1
.get_program_accounts(&sysvar::id(), &ScanConfig::default())
.unwrap();
assert_eq!(sysvars.len(), 9);
assert!(sysvars
.iter()
.map(|(_pubkey, account)| account.lamports())
.all(|lamports| lamports == 1));
}
// 9 sysvars should be reset by reset_all_sysvar_balances()
let bank2 = 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 + expected_cap_delta_after_sysvar_reset(
&bank1,
&[
sysvar::clock::id(),
sysvar::epoch_schedule::id(),
#[allow(deprecated)]
sysvar::fees::id(),
#[allow(deprecated)]
sysvar::recent_blockhashes::id(),
sysvar::rent::id(),
sysvar::rewards::id(),
sysvar::slot_hashes::id(),
sysvar::slot_history::id(),
sysvar::stake_history::id(),
]
),
new
)
},
);
{
let sysvars = bank2
.get_program_accounts(&sysvar::id(), &ScanConfig::default())
.unwrap();
assert_eq!(sysvars.len(), 9);
assert!(sysvars
.iter()
.map(|(_pubkey, account)| account.lamports())
.all(|lamports| lamports > 1));
}
}
#[test]
fn test_update_clock_timestamp() {
let leader_pubkey = solana_sdk::pubkey::new_rand();

View File

@ -24,6 +24,25 @@ pub fn bootstrap_validator_stake_lamports() -> u64 {
StakeState::get_rent_exempt_reserve(&Rent::default())
}
// Number of lamports automatically used for genesis accounts
pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
const NUM_BUILTIN_PROGRAMS: u64 = 5;
const FEES_SYSVAR_MIN_BALANCE: u64 = 946_560;
const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
FEES_SYSVAR_MIN_BALANCE
+ STAKE_HISTORY_MIN_BALANCE
+ CLOCK_SYSVAR_MIN_BALANCE
+ RENT_SYSVAR_MIN_BALANCE
+ EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE
+ RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE
+ NUM_BUILTIN_PROGRAMS
}
pub struct ValidatorVoteKeypairs {
pub node_keypair: Keypair,
pub vote_keypair: Keypair,

View File

@ -219,6 +219,7 @@ solana_sdk::pubkeys!(
mod tests {
use {
super::*,
crate::genesis_utils::genesis_sysvar_and_builtin_program_lamports,
solana_sdk::{
account::{Account, AccountSharedData},
epoch_schedule::EpochSchedule,
@ -278,11 +279,10 @@ mod tests {
..GenesisConfig::default()
};
let mut bank = Arc::new(Bank::new_for_tests(&genesis_config));
let sysvar_and_native_program_delta = 11;
assert_eq!(
bank.capitalization(),
(num_genesis_accounts + num_non_circulating_accounts + num_stake_accounts) * balance
+ sysvar_and_native_program_delta,
+ genesis_sysvar_and_builtin_program_lamports(),
);
let non_circulating_supply = calculate_non_circulating_supply(&bank).unwrap();

View File

@ -7,7 +7,6 @@ use solana_sdk::{
incinerator,
pubkey::Pubkey,
rent::{Rent, RentDue},
sysvar,
};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, AbiExample)]
@ -53,14 +52,8 @@ impl RentCollector {
}
/// true if it is easy to determine this account should consider having rent collected from it
pub fn should_collect_rent(
&self,
address: &Pubkey,
account: &impl ReadableAccount,
rent_for_sysvars: bool,
) -> bool {
pub fn should_collect_rent(&self, address: &Pubkey, account: &impl ReadableAccount) -> bool {
!(account.executable() // executable accounts must be rent-exempt balance
|| (!rent_for_sysvars && sysvar::check_id(account.owner()))
|| *address == incinerator::id())
}
@ -90,11 +83,9 @@ impl RentCollector {
&self,
address: &Pubkey,
account: &mut AccountSharedData,
rent_for_sysvars: bool,
filler_account_suffix: Option<&Pubkey>,
) -> CollectedInfo {
if self.can_skip_rent_collection(address, account, rent_for_sysvars, filler_account_suffix)
{
if self.can_skip_rent_collection(address, account, filler_account_suffix) {
return CollectedInfo::default();
}
@ -134,11 +125,10 @@ impl RentCollector {
&self,
address: &Pubkey,
account: &mut AccountSharedData,
rent_for_sysvars: bool,
) -> CollectedInfo {
// initialize rent_epoch as created at this epoch
account.set_rent_epoch(self.epoch);
self.collect_from_existing_account(address, account, rent_for_sysvars, None)
self.collect_from_existing_account(address, account, None)
}
/// Performs easy checks to see if rent collection can be skipped
@ -146,10 +136,9 @@ impl RentCollector {
&self,
address: &Pubkey,
account: &mut AccountSharedData,
rent_for_sysvars: bool,
filler_account_suffix: Option<&Pubkey>,
) -> bool {
!self.should_collect_rent(address, account, rent_for_sysvars)
!self.should_collect_rent(address, account)
|| account.rent_epoch() > self.epoch
|| crate::accounts_db::AccountsDb::is_filler_account_helper(
address,
@ -186,7 +175,10 @@ impl std::ops::AddAssign for CollectedInfo {
#[cfg(test)]
mod tests {
use {super::*, solana_sdk::account::Account};
use {
super::*,
solana_sdk::{account::Account, sysvar},
};
#[test]
fn test_collect_from_account_created_and_existing() {
@ -207,11 +199,8 @@ mod tests {
let rent_collector = RentCollector::default().clone_with_epoch(new_epoch);
// collect rent on a newly-created account
let collected = rent_collector.collect_from_created_account(
&solana_sdk::pubkey::new_rand(),
&mut created_account,
true,
);
let collected = rent_collector
.collect_from_created_account(&solana_sdk::pubkey::new_rand(), &mut created_account);
assert!(created_account.lamports() < old_lamports);
assert_eq!(
created_account.lamports() + collected.rent_amount,
@ -224,7 +213,6 @@ mod tests {
let collected = rent_collector.collect_from_existing_account(
&solana_sdk::pubkey::new_rand(),
&mut existing_account,
true,
None,
);
assert!(existing_account.lamports() < old_lamports);
@ -255,8 +243,7 @@ mod tests {
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
// first mark account as being collected while being rent-exempt
let collected =
rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
let collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, None);
assert_eq!(account.lamports(), huge_lamports);
assert_eq!(collected, CollectedInfo::default());
@ -264,8 +251,7 @@ mod tests {
account.set_lamports(tiny_lamports);
// ... and trigger another rent collection on the same epoch and check that rent is working
let collected =
rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
let collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, None);
assert_eq!(account.lamports(), tiny_lamports - collected.rent_amount);
assert_ne!(collected, CollectedInfo::default());
}
@ -284,15 +270,7 @@ mod tests {
let epoch = 3;
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
// old behavior: sysvars are special-cased
let collected =
rent_collector.collect_from_existing_account(&pubkey, &mut account, false, None);
assert_eq!(account.lamports(), tiny_lamports);
assert_eq!(collected, CollectedInfo::default());
// new behavior: sysvars are NOT special-cased
let collected =
rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
let collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, None);
assert_eq!(account.lamports(), 0);
assert_eq!(collected.rent_amount, 1);
}
@ -312,12 +290,8 @@ mod tests {
});
let rent_collector = RentCollector::default().clone_with_epoch(account_rent_epoch + 2);
let collected = rent_collector.collect_from_existing_account(
&Pubkey::new_unique(),
&mut account,
true,
None,
);
let collected =
rent_collector.collect_from_existing_account(&Pubkey::new_unique(), &mut account, None);
assert_eq!(collected.rent_amount, account_lamports);
assert_eq!(

View File

@ -15,7 +15,7 @@ use {
NonceError, SystemError, SystemInstruction, MAX_PERMITTED_DATA_LENGTH,
},
system_program,
sysvar::{self, rent::Rent},
sysvar::rent::Rent,
},
std::collections::HashSet,
};
@ -124,19 +124,6 @@ fn assign(
return Err(InstructionError::MissingRequiredSignature);
}
// bpf programs are allowed to do this; so this is inconsistent...
// Thus, we're starting to remove this restriction from system instruction
// processor for consistency and fewer special casing by piggybacking onto
// the related feature gate..
let rent_for_sysvars = invoke_context
.feature_set
.is_active(&feature_set::rent_for_sysvars::id());
if !rent_for_sysvars && sysvar::check_id(owner) {
// guard against sysvars being made
ic_msg!(invoke_context, "Assign: cannot assign to sysvar, {}", owner);
return Err(SystemError::InvalidProgramId.into());
}
account.set_owner(*owner);
Ok(())
}
@ -492,7 +479,6 @@ mod tests {
use solana_sdk::{
account::{self, Account, AccountSharedData},
client::SyncClient,
feature_set::FeatureSet,
genesis_config::create_genesis_config,
hash::{hash, Hash},
instruction::{AccountMeta, Instruction, InstructionError},
@ -980,41 +966,6 @@ mod tests {
assert_eq!(result, Ok(()));
}
#[test]
fn test_create_sysvar_invalid_id_without_feature() {
let mut feature_set = FeatureSet::all_enabled();
feature_set
.active
.remove(&feature_set::rent_for_sysvars::id());
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
// Attempt to create system account in account already owned by another program
let from = Pubkey::new_unique();
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
let to = Pubkey::new_unique();
let to_account = RefCell::new(AccountSharedData::new(0, 0, &system_program::id()));
let signers = [from, to].iter().cloned().collect::<HashSet<_>>();
let to_address = to.into();
let result = create_account(
&KeyedAccount::new(&from, true, &from_account),
&KeyedAccount::new(&to, false, &to_account),
&to_address,
50,
2,
&sysvar::id(),
&signers,
&invoke_context,
);
assert_eq!(result, Err(SystemError::InvalidProgramId.into()));
}
#[test]
fn test_create_data_populated() {
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
@ -1151,34 +1102,6 @@ mod tests {
);
}
#[test]
fn test_assign_to_sysvar_without_feature() {
let mut feature_set = FeatureSet::all_enabled();
feature_set
.active
.remove(&feature_set::rent_for_sysvars::id());
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
let new_owner = sysvar::id();
let from = Pubkey::new_unique();
let mut from_account = AccountSharedData::new(100, 0, &system_program::id());
assert_eq!(
assign(
&mut from_account,
&from.into(),
&new_owner,
&[from].iter().cloned().collect::<HashSet<_>>(),
&invoke_context,
),
Err(SystemError::InvalidProgramId.into())
);
}
#[test]
fn test_process_bogus_instruction() {
// Attempt to assign with no accounts