Prevent Rent-reward recipients from ending up RentPaying (#30130)
This commit is contained in:
parent
01f0dcdad4
commit
a14473eb54
|
@ -39,6 +39,7 @@ pub use solana_sdk::reward_type::RewardType;
|
|||
use {
|
||||
crate::{
|
||||
account_overrides::AccountOverrides,
|
||||
account_rent_state::RentState,
|
||||
accounts::{
|
||||
AccountAddressFilter, Accounts, LoadedTransaction, PubkeyAccountSlot,
|
||||
TransactionLoadResult,
|
||||
|
@ -5032,8 +5033,27 @@ impl Bank {
|
|||
let mut account = self
|
||||
.get_account_with_fixed_root(&pubkey)
|
||||
.unwrap_or_default();
|
||||
if account.checked_add_lamports(rent_to_be_paid).is_err() {
|
||||
// overflow adding lamports
|
||||
let rent = self.rent_collector().rent;
|
||||
let recipient_pre_rent_state = RentState::from_account(&account, &rent);
|
||||
let distribution = account.checked_add_lamports(rent_to_be_paid);
|
||||
let recipient_post_rent_state = RentState::from_account(&account, &rent);
|
||||
let rent_state_transition_allowed = recipient_post_rent_state
|
||||
.transition_allowed_from(&recipient_pre_rent_state);
|
||||
if !rent_state_transition_allowed {
|
||||
warn!(
|
||||
"Rent distribution of {rent_to_be_paid} to {pubkey} results in \
|
||||
invalid RentState: {recipient_post_rent_state:?}"
|
||||
);
|
||||
inc_new_counter_warn!(
|
||||
"rent-distribution-rent-paying",
|
||||
rent_to_be_paid as usize
|
||||
);
|
||||
}
|
||||
if distribution.is_err()
|
||||
|| (self.prevent_rent_paying_rent_recipients()
|
||||
&& !rent_state_transition_allowed)
|
||||
{
|
||||
// overflow adding lamports or resulting account is not rent-exempt
|
||||
self.capitalization.fetch_sub(rent_to_be_paid, Relaxed);
|
||||
error!(
|
||||
"Burned {} rent lamports instead of sending to {}",
|
||||
|
@ -7363,6 +7383,11 @@ impl Bank {
|
|||
.is_active(&feature_set::no_overflow_rent_distribution::id())
|
||||
}
|
||||
|
||||
pub fn prevent_rent_paying_rent_recipients(&self) -> bool {
|
||||
self.feature_set
|
||||
.is_active(&feature_set::prevent_rent_paying_rent_recipients::id())
|
||||
}
|
||||
|
||||
pub fn versioned_tx_message_enabled(&self) -> bool {
|
||||
self.feature_set
|
||||
.is_active(&feature_set::versioned_tx_message_enabled::id())
|
||||
|
|
|
@ -1115,6 +1115,189 @@ fn test_distribute_rent_to_validators_overflow() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_distribute_rent_to_validators_rent_paying() {
|
||||
solana_logger::setup();
|
||||
|
||||
const RENT_PER_VALIDATOR: u64 = 55;
|
||||
const TOTAL_RENT: u64 = RENT_PER_VALIDATOR * 4;
|
||||
|
||||
let empty_validator = ValidatorVoteKeypairs::new_rand();
|
||||
let rent_paying_validator = ValidatorVoteKeypairs::new_rand();
|
||||
let becomes_rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
|
||||
let rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
|
||||
let keypairs = vec![
|
||||
&empty_validator,
|
||||
&rent_paying_validator,
|
||||
&becomes_rent_exempt_validator,
|
||||
&rent_exempt_validator,
|
||||
];
|
||||
let genesis_config_info = create_genesis_config_with_vote_accounts(
|
||||
sol_to_lamports(1000.),
|
||||
&keypairs,
|
||||
vec![sol_to_lamports(1000.); 4],
|
||||
);
|
||||
let mut genesis_config = genesis_config_info.genesis_config;
|
||||
genesis_config.rent = Rent::default(); // Ensure rent is non-zero, as genesis_utils sets Rent::free by default
|
||||
|
||||
for deactivate_feature in [false, true] {
|
||||
if deactivate_feature {
|
||||
genesis_config
|
||||
.accounts
|
||||
.remove(&feature_set::prevent_rent_paying_rent_recipients::id())
|
||||
.unwrap();
|
||||
}
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let rent = bank.rent_collector().rent;
|
||||
let rent_exempt_minimum = rent.minimum_balance(0);
|
||||
|
||||
// Make one validator have an empty identity account
|
||||
let mut empty_validator_account = bank
|
||||
.get_account_with_fixed_root(&empty_validator.node_keypair.pubkey())
|
||||
.unwrap();
|
||||
empty_validator_account.set_lamports(0);
|
||||
bank.store_account(
|
||||
&empty_validator.node_keypair.pubkey(),
|
||||
&empty_validator_account,
|
||||
);
|
||||
|
||||
// Make one validator almost rent-exempt, less RENT_PER_VALIDATOR
|
||||
let mut becomes_rent_exempt_validator_account = bank
|
||||
.get_account_with_fixed_root(&becomes_rent_exempt_validator.node_keypair.pubkey())
|
||||
.unwrap();
|
||||
becomes_rent_exempt_validator_account
|
||||
.set_lamports(rent_exempt_minimum - RENT_PER_VALIDATOR);
|
||||
bank.store_account(
|
||||
&becomes_rent_exempt_validator.node_keypair.pubkey(),
|
||||
&becomes_rent_exempt_validator_account,
|
||||
);
|
||||
|
||||
// Make one validator rent-exempt
|
||||
let mut rent_exempt_validator_account = bank
|
||||
.get_account_with_fixed_root(&rent_exempt_validator.node_keypair.pubkey())
|
||||
.unwrap();
|
||||
rent_exempt_validator_account.set_lamports(rent_exempt_minimum);
|
||||
bank.store_account(
|
||||
&rent_exempt_validator.node_keypair.pubkey(),
|
||||
&rent_exempt_validator_account,
|
||||
);
|
||||
|
||||
let get_rent_state = |bank: &Bank, address: &Pubkey| -> RentState {
|
||||
let account = bank
|
||||
.get_account_with_fixed_root(address)
|
||||
.unwrap_or_default();
|
||||
RentState::from_account(&account, &rent)
|
||||
};
|
||||
|
||||
// Assert starting RentStates
|
||||
assert_eq!(
|
||||
get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
|
||||
RentState::Uninitialized
|
||||
);
|
||||
assert_eq!(
|
||||
get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
|
||||
RentState::RentPaying {
|
||||
lamports: 42,
|
||||
data_size: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
|
||||
RentState::RentPaying {
|
||||
lamports: rent_exempt_minimum - RENT_PER_VALIDATOR,
|
||||
data_size: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
|
||||
RentState::RentExempt
|
||||
);
|
||||
|
||||
let old_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
|
||||
let old_rent_paying_validator_lamports =
|
||||
bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
|
||||
let old_becomes_rent_exempt_validator_lamports =
|
||||
bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
|
||||
let old_rent_exempt_validator_lamports =
|
||||
bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
|
||||
|
||||
bank.distribute_rent_to_validators(&bank.vote_accounts(), TOTAL_RENT);
|
||||
|
||||
let new_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
|
||||
let new_rent_paying_validator_lamports =
|
||||
bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
|
||||
let new_becomes_rent_exempt_validator_lamports =
|
||||
bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
|
||||
let new_rent_exempt_validator_lamports =
|
||||
bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
|
||||
|
||||
// Assert ending balances; rent should be withheld if test is active and ending RentState
|
||||
// is RentPaying, ie. empty_validator and rent_paying_validator
|
||||
assert_eq!(
|
||||
if deactivate_feature {
|
||||
old_empty_validator_lamports + RENT_PER_VALIDATOR
|
||||
} else {
|
||||
old_empty_validator_lamports
|
||||
},
|
||||
new_empty_validator_lamports
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
if deactivate_feature {
|
||||
old_rent_paying_validator_lamports + RENT_PER_VALIDATOR
|
||||
} else {
|
||||
old_rent_paying_validator_lamports
|
||||
},
|
||||
new_rent_paying_validator_lamports
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
old_becomes_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
|
||||
new_becomes_rent_exempt_validator_lamports
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
old_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
|
||||
new_rent_exempt_validator_lamports
|
||||
);
|
||||
|
||||
// Assert ending RentStates
|
||||
assert_eq!(
|
||||
if deactivate_feature {
|
||||
RentState::RentPaying {
|
||||
lamports: RENT_PER_VALIDATOR,
|
||||
data_size: 0,
|
||||
}
|
||||
} else {
|
||||
RentState::Uninitialized
|
||||
},
|
||||
get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
|
||||
);
|
||||
assert_eq!(
|
||||
if deactivate_feature {
|
||||
RentState::RentPaying {
|
||||
lamports: old_rent_paying_validator_lamports + RENT_PER_VALIDATOR,
|
||||
data_size: 0,
|
||||
}
|
||||
} else {
|
||||
RentState::RentPaying {
|
||||
lamports: old_rent_paying_validator_lamports,
|
||||
data_size: 0,
|
||||
}
|
||||
},
|
||||
get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
|
||||
);
|
||||
assert_eq!(
|
||||
RentState::RentExempt,
|
||||
get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
|
||||
);
|
||||
assert_eq!(
|
||||
RentState::RentExempt,
|
||||
get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rent_exempt_executable_account() {
|
||||
let (mut genesis_config, mint_keypair) = create_genesis_config(100_000);
|
||||
|
|
|
@ -606,6 +606,10 @@ pub mod enable_request_heap_frame_ix {
|
|||
solana_sdk::declare_id!("Hr1nUA9b7NJ6eChS26o7Vi8gYYDDwWD3YeBfzJkTbU86");
|
||||
}
|
||||
|
||||
pub mod prevent_rent_paying_rent_recipients {
|
||||
solana_sdk::declare_id!("Fab5oP3DmsLYCiQZXdjyqT3ukFFPrsmqhXU4WU1AWVVF");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -752,6 +756,7 @@ lazy_static! {
|
|||
(cap_transaction_accounts_data_size::id(), "cap transaction accounts data size up to a limit #27839"),
|
||||
(remove_congestion_multiplier_from_fee_calculation::id(), "Remove congestion multiplier from transaction fee calculation #29881"),
|
||||
(enable_request_heap_frame_ix::id(), "Enable transaction to request heap frame using compute budget instruction #30076"),
|
||||
(prevent_rent_paying_rent_recipients::id(), "prevent recipients of rent rewards from ending in rent-paying state #30???"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
Loading…
Reference in New Issue