diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 805a22fc3db19a..ddda35884cf330 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -140,7 +140,7 @@ use { packet::PACKET_DATA_SIZE, precompiles::get_precompiles, pubkey::Pubkey, - rent_collector::{CollectedInfo, RentCollector}, + rent_collector::{CollectedInfo, RentCollector, RENT_EXEMPT_RENT_EPOCH}, rent_debits::RentDebits, reserved_account_keys::ReservedAccountKeys, reward_info::RewardInfo, @@ -1940,6 +1940,15 @@ impl Bank { // 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, this code ensures that the + // sysvar's rent_epoch is set to RENT_EXEMPT_EPOCH (i.e. U64::MAX). This + // is because sysvars are never deleted, and are always rent exempt. + // Currently, it relies on rent collection code to update rent_epoch to + // RENT_EXEMPT_EPOCH. In the future, we will remove rent collection + // code. Therefore, we should set rent_epoch here, and don't depend on + // rent collection code to update rent_epoch.. + new_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH); self.store_account_and_update_capitalization(pubkey, &new_account); } diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 70e730affa745d..226e3fa21f75c7 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -3951,7 +3951,6 @@ fn test_bank_update_sysvar_account() { // (since rent collection will update the rent epoch, thus causing the // subsequent checks to fail spuriously). let dummy_clock_id = Pubkey::from_str_const("64jsX5hwtsjsKR7eNcNU4yhgwjuXoU9KR2MpnV47iXXz"); - let dummy_rent_epoch = 44; let (mut genesis_config, _mint_keypair) = create_genesis_config(500); let expected_previous_slot = 3; @@ -3974,22 +3973,20 @@ fn test_bank_update_sysvar_account() { bank1.update_sysvar_account(&dummy_clock_id, |optional_account| { assert!(optional_account.is_none()); - let mut account = create_account( + create_account( &Clock { slot: expected_previous_slot, ..Clock::default() }, bank1.inherit_specially_retained_account_fields(optional_account), - ); - account.set_rent_epoch(dummy_rent_epoch); - account + ) }); let current_account = bank1.get_account(&dummy_clock_id).unwrap(); assert_eq!( expected_previous_slot, from_account::(¤t_account).unwrap().slot ); - assert_eq!(dummy_rent_epoch, current_account.rent_epoch()); + assert_eq!(RENT_EXEMPT_RENT_EPOCH, current_account.rent_epoch()); }, |old, new| { assert_eq!( @@ -4053,7 +4050,7 @@ fn test_bank_update_sysvar_account() { expected_next_slot, from_account::(¤t_account).unwrap().slot ); - assert_eq!(dummy_rent_epoch, current_account.rent_epoch()); + assert_eq!(RENT_EXEMPT_RENT_EPOCH, current_account.rent_epoch()); }, |old, new| { // if existing, capitalization shouldn't change @@ -6472,30 +6469,29 @@ fn test_bank_hash_consistency() { assert_eq!(bank.get_slots_in_epoch(0), 32); loop { goto_end_of_slot(bank.clone()); - if bank.slot == 0 { assert_eq!( bank.hash().to_string(), - "Hn2FoJuoFWXVFVnwcQ6peuT24mUPmhDtXHXVjKD7M4yP", + "5twxs7hhHtqsMWjW3CVQdxp5f9FVTYtFs4obs5aPAPeY", ); } if bank.slot == 32 { assert_eq!( bank.hash().to_string(), - "7FPfwBut4b7bXtKPsobQS1cuFgF47SZHDb4teQcJRomv" + "7bLTgyPMtNYd853kzSgiFjipQFcCGxNCqCEbtymmjRBS" ); } if bank.slot == 64 { assert_eq!( bank.hash().to_string(), - "28CWiEuA3izdt5xe4LyS4Q1DTALmYgrVctSTazFiPVcW" + "G7yjmpzektRBH6KcQs7UXpueb1U5Xw8eL9dm7qiqjHLz" ); } if bank.slot == 128 { assert_eq!( bank.hash().to_string(), - "AdCmEvRXWKpvXb9fG6AFQhzGgB5ciAXnDajvaNK7YUg8" + "F5Z5MfY4pgpHKu6HrSc2bMqf3MXyNdHKaWYsponpSzVT" ); break; } @@ -12376,6 +12372,22 @@ where let bank = new_from_parent_next_epoch(bank, &bank_forks, 2); bank.freeze(); // trigger rent collection + // Move to the next slot to create a new bank, bypassing the bank at the + // epoch boundary. An epoch boundary bank *might* not be completely dead + // even if all user accounts are overwritten in later slots. This is because + // an epoch rewards sysvar is written in the first slot of the epoch. + // Before, we used to have sysvar's rent epoch initialized to zero and set + // to u64:max later at rent collection time. This way, the epoch rewards + // sysvar was rewritten and became dead in the first slot of the epoch. And the + // first bank at the epoch boundary was cleanable. However, now we have + // sysvar's rent epoch initialized to u64:max at creation time, so the epoch + // rewards sysvar is *not* rewritten later in rent collection and is alive. + // Therefore, the first slot at the epoch boundary is not completely dead + // and uncleanable. To avoid interaction with the epoch rewards sysvar in + // clean, we need to advance to the next slot. + let slot = bank.slot() + 1; + let bank = new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank, &collector, slot); + // create zero-lamports account to be cleaned let account = AccountSharedData::new(0, len1, &program); let slot = bank.slot() + 1; @@ -12423,9 +12435,9 @@ fn test_create_zero_lamport_with_clean() { bank.squash(); bank.force_flush_accounts_cache(); // do clean and assert that it actually did its job - assert_eq!(5, bank.get_snapshot_storages(None).len()); + assert_eq!(6, bank.get_snapshot_storages(None).len()); bank.clean_accounts(); - assert_eq!(4, bank.get_snapshot_storages(None).len()); + assert_eq!(5, bank.get_snapshot_storages(None).len()); }); }