Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit a1c827d

Browse files
authored
stake-pool: Update program to work with minimum delegation (#3127)
* stake-pool: Update program to work with minimum delegation * Update minimum reserve lamports in the CLI * Cargo fmt * Add another test for hijacked account * Fix python and JS constants
1 parent 3db53e2 commit a1c827d

38 files changed

+475
-188
lines changed

stake-pool/cli/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use {
4545
find_withdraw_authority_program_address,
4646
instruction::{FundingType, PreferredValidatorType},
4747
state::{Fee, FeeType, StakePool, ValidatorList},
48-
MINIMUM_ACTIVE_STAKE,
48+
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
4949
},
5050
std::cmp::Ordering,
5151
std::{process::exit, sync::Arc},
@@ -250,7 +250,7 @@ fn command_create_pool(
250250
let reserve_stake_balance = config
251251
.rpc_client
252252
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
253-
+ 1;
253+
+ MINIMUM_RESERVE_LAMPORTS;
254254
let mint_account_balance = config
255255
.rpc_client
256256
.get_minimum_balance_for_rent_exemption(spl_token::state::Mint::LEN)?;
@@ -1058,7 +1058,7 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
10581058
let minimum_reserve_stake_balance = config
10591059
.rpc_client
10601060
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
1061-
+ 1;
1061+
+ MINIMUM_RESERVE_LAMPORTS;
10621062
let cli_stake_pool_stake_account_infos = validator_list
10631063
.validators
10641064
.iter()
@@ -1271,7 +1271,7 @@ fn prepare_withdraw_accounts(
12711271
stake_pool.reserve_stake,
12721272
reserve_stake.lamports
12731273
- rpc_client.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
1274-
- 1,
1274+
- MINIMUM_RESERVE_LAMPORTS,
12751275
None,
12761276
));
12771277

stake-pool/js/src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient');
1212

1313
// Minimum amount of staked SOL required in a validator stake account to allow
1414
// for merges without a mismatch on credits observed
15-
export const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL / 1_000;
15+
export const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL;

stake-pool/program/src/entrypoint.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
33
#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]
44

5-
use crate::{error::StakePoolError, processor::Processor};
6-
use solana_program::{
7-
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
8-
program_error::PrintProgramError, pubkey::Pubkey,
5+
use {
6+
crate::{error::StakePoolError, processor::Processor},
7+
solana_program::{
8+
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
9+
program_error::PrintProgramError, pubkey::Pubkey,
10+
},
911
};
1012

1113
entrypoint!(process_instruction);

stake-pool/program/src/error.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Error types
22
3-
use num_derive::FromPrimitive;
4-
use solana_program::{decode_error::DecodeError, program_error::ProgramError};
5-
use thiserror::Error;
3+
use {
4+
num_derive::FromPrimitive,
5+
solana_program::{decode_error::DecodeError, program_error::ProgramError},
6+
thiserror::Error,
7+
};
68

79
/// Errors that may be returned by the StakePool program.
810
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]

stake-pool/program/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ const TRANSIENT_STAKE_SEED_PREFIX: &[u8] = b"transient";
2929

3030
/// Minimum amount of staked SOL required in a validator stake account to allow
3131
/// for merges without a mismatch on credits observed
32-
pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL / 1_000;
32+
pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL;
33+
34+
/// Minimum amount of SOL in the reserve
35+
pub const MINIMUM_RESERVE_LAMPORTS: u64 = LAMPORTS_PER_SOL;
3336

3437
/// Maximum amount of validator stake accounts to update per
3538
/// `UpdateValidatorListBalance` instruction, based on compute limits
@@ -64,7 +67,8 @@ pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
6467
/// conversions
6568
#[inline]
6669
pub fn minimum_reserve_lamports(meta: &Meta) -> u64 {
67-
meta.rent_exempt_reserve.saturating_add(1)
70+
meta.rent_exempt_reserve
71+
.saturating_add(MINIMUM_RESERVE_LAMPORTS)
6872
}
6973

7074
/// Generates the deposit authority program address for the stake pool

stake-pool/program/src/processor.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@ use {
1515
borsh::{BorshDeserialize, BorshSerialize},
1616
num_traits::FromPrimitive,
1717
solana_program::{
18-
account_info::next_account_info,
19-
account_info::AccountInfo,
18+
account_info::{next_account_info, AccountInfo},
2019
borsh::try_from_slice_unchecked,
2120
clock::{Clock, Epoch},
2221
decode_error::DecodeError,
2322
entrypoint::ProgramResult,
2423
msg,
2524
program::{invoke, invoke_signed},
26-
program_error::PrintProgramError,
27-
program_error::ProgramError,
25+
program_error::{PrintProgramError, ProgramError},
2826
program_pack::Pack,
2927
pubkey::Pubkey,
3028
rent::Rent,
@@ -136,6 +134,17 @@ fn check_account_owner(
136134
}
137135
}
138136

137+
/// Checks if a stake acount can be managed by the pool
138+
fn stake_is_usable_by_pool(
139+
meta: &stake::state::Meta,
140+
expected_authority: &Pubkey,
141+
expected_lockup: &stake::state::Lockup,
142+
) -> bool {
143+
meta.authorized.staker == *expected_authority
144+
&& meta.authorized.withdrawer == *expected_authority
145+
&& meta.lockup == *expected_lockup
146+
}
147+
139148
/// Create a transient stake account without transferring lamports
140149
fn create_transient_stake_account<'a>(
141150
transient_stake_account_info: AccountInfo<'a>,
@@ -747,7 +756,7 @@ impl Processor {
747756
stake_pool.manager_fee_account = *manager_fee_info.key;
748757
stake_pool.token_program_id = *token_program_info.key;
749758
stake_pool.total_lamports = total_lamports;
750-
stake_pool.pool_token_supply = 0;
759+
stake_pool.pool_token_supply = total_lamports;
751760
stake_pool.last_update_epoch = Clock::get()?.epoch;
752761
stake_pool.lockup = stake::state::Lockup::default();
753762
stake_pool.epoch_fee = epoch_fee;
@@ -1543,10 +1552,11 @@ impl Processor {
15431552
// * not a stake -> ignore
15441553
match transient_stake_state {
15451554
Some(stake::state::StakeState::Initialized(meta)) => {
1546-
// if transient account was hijacked, ignore it
1547-
if meta.authorized.staker == *withdraw_authority_info.key
1548-
&& meta.authorized.withdrawer == *withdraw_authority_info.key
1549-
{
1555+
if stake_is_usable_by_pool(
1556+
&meta,
1557+
withdraw_authority_info.key,
1558+
&stake_pool.lockup,
1559+
) {
15501560
if no_merge {
15511561
transient_stake_lamports = transient_stake_info.lamports();
15521562
} else {
@@ -1571,10 +1581,11 @@ impl Processor {
15711581
}
15721582
}
15731583
Some(stake::state::StakeState::Stake(meta, stake)) => {
1574-
// if transient account was hijacked, ignore it
1575-
if meta.authorized.staker == *withdraw_authority_info.key
1576-
&& meta.authorized.withdrawer == *withdraw_authority_info.key
1577-
{
1584+
if stake_is_usable_by_pool(
1585+
&meta,
1586+
withdraw_authority_info.key,
1587+
&stake_pool.lockup,
1588+
) {
15781589
let account_stake = meta
15791590
.rent_exempt_reserve
15801591
.saturating_add(stake.delegation.stake);

stake-pool/program/src/state.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! State transition types
22
3-
use spl_token::state::{Account, AccountState};
43
use {
54
crate::{
65
big_vec::BigVec, error::StakePoolError, MAX_WITHDRAWAL_FEE_INCREASE,
@@ -20,6 +19,7 @@ use {
2019
stake::state::Lockup,
2120
},
2221
spl_math::checked_ceil_div::CheckedCeilDiv,
22+
spl_token::state::{Account, AccountState},
2323
std::{convert::TryFrom, fmt, matches},
2424
};
2525

@@ -535,7 +535,7 @@ impl Default for StakeStatus {
535535
pub struct ValidatorStakeInfo {
536536
/// Amount of active stake delegated to this validator, minus the minimum
537537
/// required stake amount of rent-exemption + `crate::MINIMUM_ACTIVE_STAKE`
538-
/// (currently 0.001 SOL).
538+
/// (currently 1 SOL).
539539
///
540540
/// Note that if `last_update_epoch` does not match the current epoch then
541541
/// this field may not be accurate
@@ -823,10 +823,8 @@ mod test {
823823
use {
824824
super::*,
825825
proptest::prelude::*,
826-
solana_program::borsh::{
827-
get_instance_packed_len, get_packed_len, try_from_slice_unchecked,
828-
},
829826
solana_program::{
827+
borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked},
830828
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_S_PER_SLOT, SECONDS_PER_DAY},
831829
native_token::LAMPORTS_PER_SOL,
832830
},

stake-pool/program/tests/decrease.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use {
1515
},
1616
spl_stake_pool::{
1717
error::StakePoolError, find_transient_stake_program_address, id, instruction,
18+
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
1819
},
1920
};
2021

@@ -28,9 +29,16 @@ async fn setup() -> (
2829
u64,
2930
) {
3031
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
32+
let rent = banks_client.get_rent().await.unwrap();
33+
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake::state::StakeState>());
3134
let stake_pool_accounts = StakePoolAccounts::new();
3235
stake_pool_accounts
33-
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
36+
.initialize_stake_pool(
37+
&mut banks_client,
38+
&payer,
39+
&recent_blockhash,
40+
MINIMUM_RESERVE_LAMPORTS,
41+
)
3442
.await
3543
.unwrap();
3644

@@ -48,12 +56,12 @@ async fn setup() -> (
4856
&recent_blockhash,
4957
&stake_pool_accounts,
5058
&validator_stake_account,
51-
100_000_000,
59+
MINIMUM_ACTIVE_STAKE * 2 + stake_rent,
5260
)
5361
.await
5462
.unwrap();
5563

56-
let lamports = deposit_info.stake_lamports / 2;
64+
let decrease_lamports = MINIMUM_ACTIVE_STAKE + stake_rent;
5765

5866
(
5967
banks_client,
@@ -62,7 +70,7 @@ async fn setup() -> (
6270
stake_pool_accounts,
6371
validator_stake_account,
6472
deposit_info,
65-
lamports,
73+
decrease_lamports,
6674
)
6775
}
6876

@@ -297,7 +305,7 @@ async fn fail_decrease_twice() {
297305
&recent_blockhash,
298306
&validator_stake.stake_account,
299307
&validator_stake.transient_stake_account,
300-
decrease_lamports / 3,
308+
decrease_lamports,
301309
validator_stake.transient_stake_seed,
302310
)
303311
.await;
@@ -318,7 +326,7 @@ async fn fail_decrease_twice() {
318326
&recent_blockhash,
319327
&validator_stake.stake_account,
320328
&transient_stake_address,
321-
decrease_lamports / 2,
329+
decrease_lamports,
322330
transient_stake_seed,
323331
)
324332
.await

stake-pool/program/tests/deposit.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ use {
1515
solana_program_test::*,
1616
solana_sdk::{
1717
signature::{Keypair, Signer},
18-
transaction::Transaction,
19-
transaction::TransactionError,
18+
transaction::{Transaction, TransactionError},
2019
transport::TransportError,
2120
},
22-
spl_stake_pool::{error::StakePoolError, id, instruction, minimum_stake_lamports, state},
21+
spl_stake_pool::{
22+
error::StakePoolError, id, instruction, minimum_stake_lamports, state,
23+
MINIMUM_RESERVE_LAMPORTS,
24+
},
2325
spl_token::error as token_error,
2426
};
2527

@@ -40,7 +42,7 @@ async fn setup() -> (
4042
&mut context.banks_client,
4143
&context.payer,
4244
&context.last_blockhash,
43-
1,
45+
MINIMUM_RESERVE_LAMPORTS,
4446
)
4547
.await
4648
.unwrap();
@@ -616,7 +618,12 @@ async fn fail_with_unknown_validator() {
616618
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
617619
let stake_pool_accounts = StakePoolAccounts::new();
618620
stake_pool_accounts
619-
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
621+
.initialize_stake_pool(
622+
&mut banks_client,
623+
&payer,
624+
&recent_blockhash,
625+
MINIMUM_RESERVE_LAMPORTS,
626+
)
620627
.await
621628
.unwrap();
622629

stake-pool/program/tests/deposit_authority.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use {
1111
signature::{Keypair, Signer},
1212
transaction::TransactionError,
1313
},
14-
spl_stake_pool::{error::StakePoolError, state::StakePool},
14+
spl_stake_pool::{error::StakePoolError, state::StakePool, MINIMUM_RESERVE_LAMPORTS},
1515
};
1616

1717
#[tokio::test]
@@ -21,7 +21,12 @@ async fn success_initialize() {
2121
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
2222
let deposit_authority = stake_pool_accounts.stake_deposit_authority;
2323
stake_pool_accounts
24-
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
24+
.initialize_stake_pool(
25+
&mut banks_client,
26+
&payer,
27+
&recent_blockhash,
28+
MINIMUM_RESERVE_LAMPORTS,
29+
)
2530
.await
2631
.unwrap();
2732

@@ -41,7 +46,12 @@ async fn success_deposit() {
4146
let stake_pool_accounts =
4247
StakePoolAccounts::new_with_deposit_authority(stake_deposit_authority);
4348
stake_pool_accounts
44-
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
49+
.initialize_stake_pool(
50+
&mut banks_client,
51+
&payer,
52+
&recent_blockhash,
53+
MINIMUM_RESERVE_LAMPORTS,
54+
)
4555
.await
4656
.unwrap();
4757

@@ -116,7 +126,12 @@ async fn fail_deposit_without_authority_signature() {
116126
let mut stake_pool_accounts =
117127
StakePoolAccounts::new_with_deposit_authority(stake_deposit_authority);
118128
stake_pool_accounts
119-
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
129+
.initialize_stake_pool(
130+
&mut banks_client,
131+
&payer,
132+
&recent_blockhash,
133+
MINIMUM_RESERVE_LAMPORTS,
134+
)
120135
.await
121136
.unwrap();
122137

0 commit comments

Comments
 (0)