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 {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
account_overrides::AccountOverrides,
|
account_overrides::AccountOverrides,
|
||||||
|
account_rent_state::RentState,
|
||||||
accounts::{
|
accounts::{
|
||||||
AccountAddressFilter, Accounts, LoadedTransaction, PubkeyAccountSlot,
|
AccountAddressFilter, Accounts, LoadedTransaction, PubkeyAccountSlot,
|
||||||
TransactionLoadResult,
|
TransactionLoadResult,
|
||||||
|
@ -5032,8 +5033,27 @@ impl Bank {
|
||||||
let mut account = self
|
let mut account = self
|
||||||
.get_account_with_fixed_root(&pubkey)
|
.get_account_with_fixed_root(&pubkey)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if account.checked_add_lamports(rent_to_be_paid).is_err() {
|
let rent = self.rent_collector().rent;
|
||||||
// overflow adding lamports
|
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);
|
self.capitalization.fetch_sub(rent_to_be_paid, Relaxed);
|
||||||
error!(
|
error!(
|
||||||
"Burned {} rent lamports instead of sending to {}",
|
"Burned {} rent lamports instead of sending to {}",
|
||||||
|
@ -7363,6 +7383,11 @@ impl Bank {
|
||||||
.is_active(&feature_set::no_overflow_rent_distribution::id())
|
.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 {
|
pub fn versioned_tx_message_enabled(&self) -> bool {
|
||||||
self.feature_set
|
self.feature_set
|
||||||
.is_active(&feature_set::versioned_tx_message_enabled::id())
|
.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]
|
#[test]
|
||||||
fn test_rent_exempt_executable_account() {
|
fn test_rent_exempt_executable_account() {
|
||||||
let (mut genesis_config, mint_keypair) = create_genesis_config(100_000);
|
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");
|
solana_sdk::declare_id!("Hr1nUA9b7NJ6eChS26o7Vi8gYYDDwWD3YeBfzJkTbU86");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod prevent_rent_paying_rent_recipients {
|
||||||
|
solana_sdk::declare_id!("Fab5oP3DmsLYCiQZXdjyqT3ukFFPrsmqhXU4WU1AWVVF");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
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"),
|
(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"),
|
(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"),
|
(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 ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in New Issue