diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index 7af70af031..604e2d5051 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -7612,7 +7612,10 @@ impl AccountsDb { /// 1. pubkey, hash pairs for the slot /// 2. us spent scanning /// 3. Measure started when we began accumulating - fn get_pubkey_hash_for_slot(&self, slot: Slot) -> (Vec<(Pubkey, Hash)>, u64, Measure) { + pub(crate) fn get_pubkey_hash_for_slot( + &self, + slot: Slot, + ) -> (Vec<(Pubkey, Hash)>, u64, Measure) { let mut scan = Measure::start("scan"); let scan_result: ScanStorageResult<(Pubkey, Hash), DashMapVersionHash> = self diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 60a98b222f..6a5540f2b7 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -5342,6 +5342,15 @@ impl Bank { } } + /// true if rent collection does NOT rewrite accounts whose pubkey indicates + /// it is time for rent collection, but the account is rent exempt. + /// false if rent collection DOES rewrite accounts if the account is rent exempt + /// This is the default behavior historically. + fn bank_hash_skips_rent_rewrites(&self) -> bool { + self.feature_set + .is_active(&feature_set::skip_rent_rewrites::id()) + } + /// Collect rent from `accounts` /// /// This fn is called inside a parallel loop from `collect_rent_in_partition()`. Avoid adding @@ -5363,7 +5372,7 @@ impl Bank { Vec::<(&Pubkey, &AccountSharedData)>::with_capacity(accounts.len()); let mut time_collecting_rent_us = 0; let mut time_storing_accounts_us = 0; - let can_skip_rewrites = false; // this will be goverened by a feature soon + let can_skip_rewrites = self.bank_hash_skips_rent_rewrites(); let set_exempt_rent_epoch_max: bool = self .feature_set .is_active(&solana_sdk::feature_set::set_exempt_rent_epoch_max::id()); @@ -10046,28 +10055,48 @@ pub(crate) mod tests { fn test_collect_rent_from_accounts() { solana_logger::setup(); - let zero_lamport_pubkey = Pubkey::new(&[0; 32]); + for skip_rewrites in [false, true] { + let zero_lamport_pubkey = Pubkey::new(&[0; 32]); - let genesis_bank = create_simple_test_arc_bank(100000); - let first_bank = Arc::new(new_from_parent(&genesis_bank)); - let first_slot = 1; - assert_eq!(first_slot, first_bank.slot()); - let epoch_delta = 4; - let later_bank = Arc::new(new_from_parent_next_epoch(&first_bank, epoch_delta)); // a bank a few epochs in the future - let later_slot = later_bank.slot(); - assert!(later_bank.epoch() == genesis_bank.epoch() + epoch_delta); + let genesis_bank = create_simple_test_arc_bank(100000); + let mut first_bank = new_from_parent(&genesis_bank); + if skip_rewrites { + first_bank.activate_feature(&feature_set::skip_rent_rewrites::id()); + } + let first_bank = Arc::new(first_bank); - let data_size = 0; // make sure we're rent exempt - let lamports = later_bank.get_minimum_balance_for_rent_exemption(data_size); // cannot be 0 or we zero out rent_epoch in rent collection and we need to be rent exempt - let mut account = AccountSharedData::new(lamports, data_size, &Pubkey::default()); - account.set_rent_epoch(later_bank.epoch() - 1); // non-zero, but less than later_bank's epoch + let first_slot = 1; + assert_eq!(first_slot, first_bank.slot()); + let epoch_delta = 4; + let later_bank = Arc::new(new_from_parent_next_epoch(&first_bank, epoch_delta)); // a bank a few epochs in the future + let later_slot = later_bank.slot(); + assert!(later_bank.epoch() == genesis_bank.epoch() + epoch_delta); - // loaded from previous slot, so we skip rent collection on it - let _result = later_bank.collect_rent_from_accounts( - vec![(zero_lamport_pubkey, account, later_slot - 1)], - None, - PartitionIndex::default(), - ); + let data_size = 0; // make sure we're rent exempt + let lamports = later_bank.get_minimum_balance_for_rent_exemption(data_size); // cannot be 0 or we zero out rent_epoch in rent collection and we need to be rent exempt + let mut account = AccountSharedData::new(lamports, data_size, &Pubkey::default()); + account.set_rent_epoch(later_bank.epoch() - 1); // non-zero, but less than later_bank's epoch + + // loaded from previous slot, so we skip rent collection on it + let _result = later_bank.collect_rent_from_accounts( + vec![(zero_lamport_pubkey, account, later_slot - 1)], + None, + PartitionIndex::default(), + ); + + let deltas = later_bank + .rc + .accounts + .accounts_db + .get_pubkey_hash_for_slot(later_slot) + .0; + assert_eq!( + !deltas + .iter() + .any(|(pubkey, _)| pubkey == &zero_lamport_pubkey), + skip_rewrites + ); + } } #[test] diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 12541a44d7..3eaeae0241 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -462,6 +462,10 @@ pub mod enable_early_verification_of_account_modifications { solana_sdk::declare_id!("7Vced912WrRnfjaiKRiNBcbuFw7RrnLv3E3z95Y4GTNc"); } +pub mod skip_rent_rewrites { + solana_sdk::declare_id!("CGB2jM8pwZkeeiXQ66kBMyBR6Np61mggL7XUsmLjVcrw"); +} + pub mod prevent_crediting_accounts_that_end_rent_paying { solana_sdk::declare_id!("812kqX67odAp5NFwM8D2N24cku7WTm9CHUTFUXaDkWPn"); } @@ -679,6 +683,7 @@ lazy_static! { (stake_redelegate_instruction::id(), "enable the redelegate stake instruction #26294"), (preserve_rent_epoch_for_rent_exempt_accounts::id(), "preserve rent epoch for rent exempt accounts #26479"), (enable_bpf_loader_extend_program_ix::id(), "enable bpf upgradeable loader ExtendProgram instruction #25234"), + (skip_rent_rewrites::id(), "skip rewriting rent exempt accounts during rent collection #26491"), (enable_early_verification_of_account_modifications::id(), "enable early verification of account modifications #25899"), (disable_rehash_for_rent_epoch::id(), "on accounts hash calculation, do not try to rehash accounts #28934"), (account_hash_ignore_slot::id(), "ignore slot when calculating an account hash #28420"),