Skip to content

Commit ecde6df

Browse files
committed
program: use Rent over rent_exempt_reserve
1 parent aa54e9c commit ecde6df

4 files changed

Lines changed: 50 additions & 27 deletions

File tree

clients/cli/src/quarantine.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ pub async fn get_available_balances(
6363
stake_account_addresses: &[Pubkey],
6464
minimum_pool_balance: u64,
6565
) -> Result<Vec<(u64, u64)>, Error> {
66+
let rent_exempt_reseve = config
67+
.program_client
68+
.get_minimum_balance_for_rent_exemption(StakeStateV2::size_of())
69+
.await?;
70+
6671
let stake_accounts = config
6772
.rpc_client
6873
.get_multiple_accounts(stake_account_addresses)
@@ -71,16 +76,25 @@ pub async fn get_available_balances(
7176
let mut delegations = vec![];
7277
for stake_account in &stake_accounts {
7378
let delegation = if let Some(account) = stake_account {
79+
// if this assert ever triggers, multistake or another account change has landed
80+
// this function should be updated to use real stake account sizes
81+
// we may be fetching hundreds of accounts here so memoize the rents
82+
assert_eq!(
83+
account.data.len(),
84+
StakeStateV2::size_of(),
85+
"StakeStateV2 is no longer canonical, or StakeStateV2::size_of() is no longer 200."
86+
);
87+
7488
match bincode::deserialize::<StakeStateV2>(&account.data) {
7589
Ok(StakeStateV2::Stake(meta, stake, _)) => (
7690
stake.delegation.stake.saturating_sub(minimum_pool_balance),
7791
account
7892
.lamports
7993
.saturating_sub(stake.delegation.stake)
80-
.saturating_sub(meta.rent_exempt_reserve),
94+
.saturating_sub(rent_exempt_reserve),
8195
),
8296
Ok(StakeStateV2::Initialized(meta)) => {
83-
(0, account.lamports.saturating_sub(meta.rent_exempt_reserve))
97+
(0, account.lamports.saturating_sub(rent_exempt_reserve))
8498
}
8599
_ => unreachable!(),
86100
}
@@ -125,14 +139,14 @@ pub async fn create_uninitialized_stake_account_instruction(
125139
) -> Result<Instruction, Error> {
126140
let rent_amount = config
127141
.program_client
128-
.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeStateV2>())
142+
.get_minimum_balance_for_rent_exemption(StakeStateV2::size_of())
129143
.await?;
130144

131145
Ok(system_instruction::create_account(
132146
payer,
133147
stake_account,
134148
rent_amount,
135-
std::mem::size_of::<StakeStateV2>() as u64,
149+
StakeStateV2::size_of() as u64,
136150
&stake::program::id(),
137151
))
138152
}

program/src/processor.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use {
3434
sysvar::stake_history::StakeHistorySysvar,
3535
},
3636
solana_system_interface::{instruction as system_instruction, program as system_program},
37-
solana_sysvar::SysvarSerialize,
37+
solana_sysvar::{Sysvar, SysvarSerialize},
3838
solana_vote_interface::program as vote_program,
3939
spl_token_interface::{self as spl_token, state::Mint},
4040
};
@@ -766,8 +766,13 @@ impl Processor {
766766
)?;
767767
check_stake_program(stake_program_info.key)?;
768768

769+
// we expect these numbers to be equal but get them separately in case of future changes
770+
let rent = Rent::get()?;
771+
let pool_rent_exempt_reserve = rent.minimum_balance(pool_stake_info.data_len());
772+
let onramp_rent_exempt_reserve = rent.minimum_balance(pool_onramp_info.data_len());
773+
769774
// get main pool account, we require it to be fully active for most operations
770-
let (pool_stake_meta, pool_stake_state) = get_stake_state(pool_stake_info)?;
775+
let (_, pool_stake_state) = get_stake_state(pool_stake_info)?;
771776
let pool_stake_status = pool_stake_state
772777
.delegation
773778
.stake_activating_and_deactivating(
@@ -779,17 +784,16 @@ impl Processor {
779784

780785
// get on-ramp and its status. we have to match because unlike the main account it could be Initialized
781786
// if it doesnt exist, it must first be created with InitializePoolOnRamp
782-
let (option_onramp_status, onramp_deactivation_epoch, onramp_rent_exempt_reserve) =
787+
let (option_onramp_status, onramp_deactivation_epoch) =
783788
match try_from_slice_unchecked::<StakeStateV2>(&pool_onramp_info.data.borrow()) {
784-
Ok(StakeStateV2::Initialized(meta)) => (None, u64::MAX, meta.rent_exempt_reserve),
785-
Ok(StakeStateV2::Stake(meta, stake, _)) => (
789+
Ok(StakeStateV2::Initialized(_)) => (None, u64::MAX),
790+
Ok(StakeStateV2::Stake(_, stake, _)) => (
786791
Some(stake.delegation.stake_activating_and_deactivating(
787792
clock.epoch,
788793
stake_history,
789794
PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
790795
)),
791796
stake.delegation.deactivation_epoch,
792-
meta.rent_exempt_reserve,
793797
),
794798
_ => return Err(SinglePoolError::OnRampDoesntExist.into()),
795799
};
@@ -831,7 +835,7 @@ impl Processor {
831835
let pool_excess_lamports = pool_stake_info
832836
.lamports()
833837
.saturating_sub(pool_stake_state.delegation.stake)
834-
.saturating_sub(pool_stake_meta.rent_exempt_reserve);
838+
.saturating_sub(pool_rent_exempt_reserve);
835839

836840
// if the on-ramp is fully active, move its stake to the main pool account
837841
if let Some(ref onramp_status) = option_onramp_status {
@@ -975,17 +979,20 @@ impl Processor {
975979
return Err(SinglePoolError::InvalidPoolStakeAccountUsage.into());
976980
}
977981

982+
let rent = Rent::get()?;
983+
let pool_rent_exempt_reserve = rent.minimum_balance(pool_stake_info.data_len());
984+
978985
let minimum_pool_balance = minimum_pool_balance()?;
979986

980-
let (pool_stake_meta, pool_stake_state) = get_stake_state(pool_stake_info)?;
987+
let (_, pool_stake_state) = get_stake_state(pool_stake_info)?;
981988
let pre_pool_stake = pool_stake_state
982989
.delegation
983990
.stake
984991
.saturating_sub(minimum_pool_balance);
985992
let pre_pool_excess_lamports = pool_stake_info
986993
.lamports()
987994
.checked_sub(pool_stake_state.delegation.stake)
988-
.and_then(|amount| amount.checked_sub(pool_stake_meta.rent_exempt_reserve))
995+
.and_then(|amount| amount.checked_sub(pool_rent_exempt_reserve))
989996
.ok_or(SinglePoolError::ArithmeticOverflow)?;
990997
msg!("Available stake pre merge {}", pre_pool_stake);
991998

@@ -1013,7 +1020,7 @@ impl Processor {
10131020
stake_history_info.clone(),
10141021
)?;
10151022

1016-
let (pool_stake_meta, pool_stake_state) = get_stake_state(pool_stake_info)?;
1023+
let (_, pool_stake_state) = get_stake_state(pool_stake_info)?;
10171024
let post_pool_stake = pool_stake_state
10181025
.delegation
10191026
.stake
@@ -1030,7 +1037,7 @@ impl Processor {
10301037
// this includes their rent-exempt reserve if the pool is fully active
10311038
let user_excess_lamports = post_pool_lamports
10321039
.checked_sub(pool_stake_state.delegation.stake)
1033-
.and_then(|amount| amount.checked_sub(pool_stake_meta.rent_exempt_reserve))
1040+
.and_then(|amount| amount.checked_sub(pool_rent_exempt_reserve))
10341041
.and_then(|amount| amount.checked_sub(pre_pool_excess_lamports))
10351042
.ok_or(SinglePoolError::ArithmeticOverflow)?;
10361043

program/tests/deposit.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use {
66
helpers::*,
77
solana_program_test::*,
88
solana_sdk::{signature::Signer, signer::keypair::Keypair, transaction::Transaction},
9-
solana_stake_interface::state::{Authorized, Lockup},
9+
solana_stake_interface::state::{Authorized, Lockup, StakeStateV2},
1010
solana_system_interface::instruction as system_instruction,
1111
spl_associated_token_account_interface::address::get_associated_token_address,
1212
spl_single_pool::{error::SinglePoolError, id, instruction},
@@ -38,6 +38,9 @@ async fn success(
3838
};
3939
let mut context = program_test.start_with_context().await;
4040

41+
let rent = context.banks_client.get_rent().await.unwrap();
42+
let rent_exempt_reserve = rent.minimum_balance(StakeStateV2::size_of());
43+
4144
let accounts = SinglePoolAccounts::default();
4245
accounts
4346
.initialize_for_deposit(
@@ -107,7 +110,7 @@ async fn success(
107110
.unwrap();
108111
}
109112

110-
let (alice_meta_before_deposit, alice_stake_before_deposit, _) =
113+
let (_, alice_stake_before_deposit, _) =
111114
get_stake_account(&mut context.banks_client, &accounts.alice_stake.pubkey()).await;
112115
let alice_stake_before_deposit = alice_stake_before_deposit.unwrap().delegation.stake;
113116

@@ -141,7 +144,7 @@ async fn success(
141144
.await
142145
.lamports;
143146

144-
let (pool_meta_after, pool_stake_after, pool_lamports_after) =
147+
let (_, pool_stake_after, pool_lamports_after) =
145148
get_stake_account(&mut context.banks_client, &accounts.stake_account).await;
146149
let pool_stake_after = pool_stake_after.unwrap().delegation.stake;
147150

@@ -150,7 +153,7 @@ async fn success(
150153
let expected_deposit = if activate {
151154
alice_stake_before_deposit
152155
} else {
153-
alice_stake_before_deposit + alice_meta_before_deposit.rent_exempt_reserve
156+
alice_stake_before_deposit + rent_exempt_reserve
154157
};
155158

156159
// deposit stake account is closed
@@ -168,10 +171,7 @@ async fn success(
168171
assert_eq!(pool_lamports_after, pool_lamports_before + expected_deposit);
169172
assert_eq!(
170173
pool_lamports_after,
171-
pool_stake_before
172-
+ expected_deposit
173-
+ pool_meta_after.rent_exempt_reserve
174-
+ pool_extra_lamports,
174+
pool_stake_before + expected_deposit + rent_exempt_reserve + pool_extra_lamports,
175175
);
176176

177177
// alice got her rent and extra back if active, or just extra back otherwise

program/tests/replenish.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ async fn move_value_success(
218218
};
219219
let mut context = program_test.start_with_context().await;
220220

221+
let rent = context.banks_client.get_rent().await.unwrap();
222+
let pool_rent = rent.minimum_balance(StakeStateV2::size_of());
223+
let onramp_rent = pool_rent;
224+
221225
let accounts = SinglePoolAccounts::default();
222226
accounts
223227
.initialize_for_deposit(&mut context, TEST_STAKE_AMOUNT, None)
@@ -297,15 +301,14 @@ async fn move_value_success(
297301
.await
298302
.unwrap();
299303

300-
let (pool_meta, pool_stake, pool_lamports) =
304+
let (_, pool_stake, pool_lamports) =
301305
get_stake_account(&mut context.banks_client, &accounts.stake_account).await;
302306
let pool_status = pool_stake
303307
.unwrap()
304308
.delegation
305309
.stake_activating_and_deactivating(clock.epoch, &stake_history, Some(0));
306-
let pool_rent = pool_meta.rent_exempt_reserve;
307310

308-
let (onramp_meta, onramp_stake, onramp_lamports) =
311+
let (_, onramp_stake, onramp_lamports) =
309312
get_stake_account(&mut context.banks_client, &accounts.onramp_account).await;
310313
let onramp_status = onramp_stake
311314
.map(|stake| {
@@ -314,7 +317,6 @@ async fn move_value_success(
314317
.stake_activating_and_deactivating(clock.epoch, &stake_history, Some(0))
315318
})
316319
.unwrap_or_default();
317-
let onramp_rent = onramp_meta.rent_exempt_reserve;
318320

319321
match (onramp_state, move_lamports_to_onramp) {
320322
// stake moved already before test or because of test, new lamports were added to onramp

0 commit comments

Comments
 (0)