Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stake-pool: Use stake program minimum delegation #3547

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions stake-pool/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ use {
self, find_stake_program_address, find_transient_stake_program_address,
find_withdraw_authority_program_address,
instruction::{FundingType, PreferredValidatorType},
minimum_delegation,
state::{Fee, FeeType, StakePool, ValidatorList},
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
MINIMUM_RESERVE_LAMPORTS,
},
std::cmp::Ordering,
std::{process::exit, sync::Arc},
Expand Down Expand Up @@ -1219,9 +1220,11 @@ fn prepare_withdraw_accounts(
stake_pool_address: &Pubkey,
skip_fee: bool,
) -> Result<Vec<WithdrawAccount>, Error> {
let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation()?;
let stake_pool_minimum_delegation = minimum_delegation(stake_minimum_delegation);
let min_balance = rpc_client
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
.saturating_add(MINIMUM_ACTIVE_STAKE);
.saturating_add(stake_pool_minimum_delegation);
let pool_mint = get_token_mint(rpc_client, &stake_pool.pool_mint)?;
let validator_list: ValidatorList = get_validator_list(rpc_client, &stake_pool.validator_list)?;

Expand Down Expand Up @@ -1386,6 +1389,9 @@ fn command_withdraw_stake(
})
.flatten();

let stake_minimum_delegation = config.rpc_client.get_stake_minimum_delegation()?;
let stake_pool_minimum_delegation = minimum_delegation(stake_minimum_delegation);

let withdraw_accounts = if use_reserve {
vec![WithdrawAccount {
stake_address: stake_pool.reserve_stake,
Expand All @@ -1400,7 +1406,7 @@ fn command_withdraw_stake(
.voter_pubkey;
if let Some(vote_account_address) = vote_account_address {
if *vote_account_address != vote_account {
return Err(format!("Provided withdrawal vote account {} does not match delegation on stake receiver account {},
return Err(format!("Provided withdrawal vote account {} does not match delegation on stake receiver account {},
remove this flag or provide a different stake account delegated to {}", vote_account_address, vote_account, vote_account_address).into());
}
}
Expand All @@ -1422,7 +1428,7 @@ fn command_withdraw_stake(
.calc_lamports_withdraw_amount(
stake_account
.lamports
.saturating_sub(MINIMUM_ACTIVE_STAKE)
.saturating_sub(stake_pool_minimum_delegation)
.saturating_sub(stake_account_rent_exemption),
)
.unwrap();
Expand Down Expand Up @@ -1454,7 +1460,7 @@ fn command_withdraw_stake(
.calc_lamports_withdraw_amount(
stake_account
.lamports
.saturating_sub(MINIMUM_ACTIVE_STAKE)
.saturating_sub(stake_pool_minimum_delegation)
.saturating_sub(stake_account_rent_exemption),
)
.unwrap();
Expand Down
14 changes: 7 additions & 7 deletions stake-pool/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub enum StakePoolInstruction {
/// list of managed validators.
///
/// The stake account will have the rent-exempt amount plus
/// `crate::MINIMUM_ACTIVE_STAKE` (currently 0.001 SOL).
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
///
/// 0. `[w]` Stake pool
/// 1. `[s]` Staker
Expand All @@ -103,8 +103,8 @@ pub enum StakePoolInstruction {
/// (Staker only) Removes validator from the pool
///
/// Only succeeds if the validator stake account has the minimum of
/// `crate::MINIMUM_ACTIVE_STAKE` (currently 0.001 SOL) plus the rent-exempt
/// amount.
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
/// plus the rent-exempt amount.
///
/// 0. `[w]` Stake pool
/// 1. `[s]` Staker
Expand Down Expand Up @@ -158,9 +158,8 @@ pub enum StakePoolInstruction {
/// will do the work of merging once it's ready.
///
/// This instruction only succeeds if the transient stake account does not exist.
/// The minimum amount to move is rent-exemption plus `crate::MINIMUM_ACTIVE_STAKE`
/// (currently 0.001 SOL) in order to avoid issues on credits observed when
/// merging active stakes later.
/// The minimum amount to move is rent-exemption plus
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
///
/// 0. `[]` Stake pool
/// 1. `[s]` Stake pool staker
Expand Down Expand Up @@ -281,7 +280,8 @@ pub enum StakePoolInstruction {
///
/// Succeeds if the stake account has enough SOL to cover the desired amount
/// of pool tokens, and if the withdrawal keeps the total staked amount
/// above the minimum of rent-exempt amount + 0.001 SOL.
/// above the minimum of rent-exempt amount +
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
///
/// When allowing withdrawals, the order of priority goes:
///
Expand Down
18 changes: 13 additions & 5 deletions stake-pool/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod entrypoint;
pub use solana_program;
use {
crate::state::Fee,
solana_program::{native_token::LAMPORTS_PER_SOL, pubkey::Pubkey, stake::state::Meta},
solana_program::{pubkey::Pubkey, stake::state::Meta},
};

/// Seed for deposit authority seed
Expand All @@ -29,10 +29,12 @@ const TRANSIENT_STAKE_SEED_PREFIX: &[u8] = b"transient";

/// Minimum amount of staked SOL required in a validator stake account to allow
/// for merges without a mismatch on credits observed
pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL;
pub const MINIMUM_ACTIVE_STAKE: u64 = 1_000_000;
brooksprumo marked this conversation as resolved.
Show resolved Hide resolved

/// Minimum amount of SOL in the reserve
pub const MINIMUM_RESERVE_LAMPORTS: u64 = LAMPORTS_PER_SOL;
/// NOTE: This can be changed to 0 once the `stake_allow_zero_undelegated_amount`
/// feature is enabled on all clusters
pub const MINIMUM_RESERVE_LAMPORTS: u64 = 1;
brooksprumo marked this conversation as resolved.
Show resolved Hide resolved

/// Maximum amount of validator stake accounts to update per
/// `UpdateValidatorListBalance` instruction, based on compute limits
Expand All @@ -58,9 +60,15 @@ pub const MAX_TRANSIENT_STAKE_ACCOUNTS: usize = 10;
/// Get the stake amount under consideration when calculating pool token
/// conversions
#[inline]
pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
pub fn minimum_stake_lamports(meta: &Meta, stake_program_minimum_delegation: u64) -> u64 {
meta.rent_exempt_reserve
.saturating_add(MINIMUM_ACTIVE_STAKE)
.saturating_add(minimum_delegation(stake_program_minimum_delegation))
}

/// Get the minimum delegation required by a stake account in a stake pool
#[inline]
pub fn minimum_delegation(stake_program_minimum_delegation: u64) -> u64 {
std::cmp::max(stake_program_minimum_delegation, MINIMUM_ACTIVE_STAKE)
}

/// Get the stake amount under consideration when calculating pool token
Expand Down
45 changes: 30 additions & 15 deletions stake-pool/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use {
error::StakePoolError,
find_deposit_authority_program_address,
instruction::{FundingType, PreferredValidatorType, StakePoolInstruction},
minimum_reserve_lamports, minimum_stake_lamports,
minimum_delegation, minimum_reserve_lamports, minimum_stake_lamports,
state::{
AccountType, Fee, FeeType, StakePool, StakeStatus, ValidatorList, ValidatorListHeader,
ValidatorStakeInfo,
},
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED_PREFIX,
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, TRANSIENT_STAKE_SEED_PREFIX,
},
borsh::{BorshDeserialize, BorshSerialize},
mpl_token_metadata::{
Expand Down Expand Up @@ -907,7 +907,9 @@ impl Processor {

// Fund the stake account with the minimum + rent-exempt balance
let space = std::mem::size_of::<stake::state::StakeState>();
let required_lamports = MINIMUM_ACTIVE_STAKE + rent.minimum_balance(space);
let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let required_lamports = minimum_delegation(stake_minimum_delegation)
.saturating_add(rent.minimum_balance(space));

// Create new stake account
create_pda_account(
Expand Down Expand Up @@ -1031,7 +1033,8 @@ impl Processor {
let mut validator_stake_info = maybe_validator_stake_info.unwrap();

let stake_lamports = **stake_account_info.lamports.borrow();
let required_lamports = minimum_stake_lamports(&meta);
let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let required_lamports = minimum_stake_lamports(&meta, stake_minimum_delegation);
if stake_lamports != required_lamports {
msg!(
"Attempting to remove validator account with {} lamports, must have {} lamports",
Expand All @@ -1041,11 +1044,12 @@ impl Processor {
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
}

if stake.delegation.stake != MINIMUM_ACTIVE_STAKE {
let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
if stake.delegation.stake != current_minimum_delegation {
msg!(
"Error: attempting to remove stake with delegation of {} lamports, must have {} lamports",
stake.delegation.stake,
MINIMUM_ACTIVE_STAKE
current_minimum_delegation
);
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
}
Expand Down Expand Up @@ -1224,7 +1228,8 @@ impl Processor {
.lamports()
.checked_sub(lamports)
.ok_or(ProgramError::InsufficientFunds)?;
let required_lamports = minimum_stake_lamports(&meta);
let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let required_lamports = minimum_stake_lamports(&meta, stake_minimum_delegation);
if remaining_lamports < required_lamports {
msg!("Need at least {} lamports in the stake account after decrease, {} requested, {} is the current possible maximum",
required_lamports,
Expand Down Expand Up @@ -1394,13 +1399,17 @@ impl Processor {
}

let stake_rent = rent.minimum_balance(std::mem::size_of::<stake::state::StakeState>());
if lamports < MINIMUM_ACTIVE_STAKE {
let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
if lamports < current_minimum_delegation {
msg!(
"Need more than {} lamports for transient stake to be rent-exempt and mergeable, {} provided",
MINIMUM_ACTIVE_STAKE,
"Need more than {} lamports for transient stake to meet minimum delegation requirement, {} provided",
current_minimum_delegation,
lamports
);
return Err(ProgramError::AccountNotRentExempt);
return Err(ProgramError::Custom(
stake::instruction::StakeError::InsufficientDelegation as u32,
));
}

// the stake account rent exemption is withdrawn after the merge, so
Expand Down Expand Up @@ -1577,6 +1586,8 @@ impl Processor {
return Err(StakePoolError::InvalidState.into());
}

let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
let validator_iter = &mut validator_slice
.iter_mut()
.zip(validator_stake_accounts.chunks_exact(2));
Expand Down Expand Up @@ -1750,7 +1761,7 @@ impl Processor {
active_stake_lamports = stake
.delegation
.stake
.checked_sub(MINIMUM_ACTIVE_STAKE)
.checked_sub(current_minimum_delegation)
.ok_or(StakePoolError::CalculationFailure)?;
} else {
msg!("Validator stake account no longer part of the pool, ignoring");
Expand Down Expand Up @@ -2195,10 +2206,12 @@ impl Processor {
.ok_or(StakePoolError::CalculationFailure)?;
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;

let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
validator_stake_info.active_stake_lamports = post_validator_stake
.delegation
.stake
.checked_sub(MINIMUM_ACTIVE_STAKE)
.checked_sub(current_minimum_delegation)
.ok_or(StakePoolError::CalculationFailure)?;

Ok(())
Expand Down Expand Up @@ -2508,8 +2521,10 @@ impl Processor {
}

let remaining_lamports = stake.delegation.stake.saturating_sub(withdraw_lamports);
if remaining_lamports < MINIMUM_ACTIVE_STAKE {
msg!("Attempting to withdraw {} lamports from validator account with {} stake lamports, {} must remain", withdraw_lamports, stake.delegation.stake, MINIMUM_ACTIVE_STAKE);
let stake_minimum_delegation = stake::tools::get_minimum_delegation()?;
let current_minimum_delegation = minimum_delegation(stake_minimum_delegation);
if remaining_lamports < current_minimum_delegation {
msg!("Attempting to withdraw {} lamports from validator account with {} stake lamports, {} must remain", withdraw_lamports, stake.delegation.stake, current_minimum_delegation);
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
}
Some((validator_stake_info, withdrawing_from_transient_stake))
Expand Down
4 changes: 2 additions & 2 deletions stake-pool/program/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,8 @@ impl Default for StakeStatus {
#[derive(Clone, Copy, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ValidatorStakeInfo {
/// Amount of active stake delegated to this validator, minus the minimum
/// required stake amount of rent-exemption + `crate::MINIMUM_ACTIVE_STAKE`
/// (currently 1 SOL).
/// required stake amount of rent-exemption +
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
///
/// Note that if `last_update_epoch` does not match the current epoch then
/// this field may not be accurate
Expand Down
9 changes: 6 additions & 3 deletions stake-pool/program/tests/decrease.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {
},
spl_stake_pool::{
error::StakePoolError, find_transient_stake_program_address, id, instruction,
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
MINIMUM_RESERVE_LAMPORTS,
},
};

Expand Down Expand Up @@ -50,18 +50,21 @@ async fn setup() -> (
)
.await;

let current_minimum_delegation =
stake_pool_get_minimum_delegation(&mut banks_client, &payer, &recent_blockhash).await;

let deposit_info = simple_deposit_stake(
&mut banks_client,
&payer,
&recent_blockhash,
&stake_pool_accounts,
&validator_stake_account,
MINIMUM_ACTIVE_STAKE * 2 + stake_rent,
current_minimum_delegation * 2 + stake_rent,
)
.await
.unwrap();

let decrease_lamports = MINIMUM_ACTIVE_STAKE + stake_rent;
let decrease_lamports = current_minimum_delegation + stake_rent;

(
banks_client,
Expand Down
16 changes: 14 additions & 2 deletions stake-pool/program/tests/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,14 @@ async fn success() {
let stake_state =
deserialize::<stake::state::StakeState>(&validator_stake_account.data).unwrap();
let meta = stake_state.meta().unwrap();
let stake_minimum_delegation = stake_get_minimum_delegation(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
)
.await;
assert_eq!(
validator_stake_account.lamports - minimum_stake_lamports(&meta),
validator_stake_account.lamports - minimum_stake_lamports(&meta, stake_minimum_delegation),
post_validator_stake_item.stake_lamports()
);
assert_eq!(post_validator_stake_item.transient_stake_lamports, 0);
Expand Down Expand Up @@ -443,8 +449,14 @@ async fn success_with_extra_stake_lamports() {
let stake_state =
deserialize::<stake::state::StakeState>(&validator_stake_account.data).unwrap();
let meta = stake_state.meta().unwrap();
let stake_minimum_delegation = stake_get_minimum_delegation(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
)
.await;
assert_eq!(
validator_stake_account.lamports - minimum_stake_lamports(&meta),
validator_stake_account.lamports - minimum_stake_lamports(&meta, stake_minimum_delegation),
post_validator_stake_item.stake_lamports()
);
assert_eq!(post_validator_stake_item.transient_stake_lamports, 0);
Expand Down
Loading