Skip to content

Commit

Permalink
stake-pool: Update program to work with minimum delegation (#3127)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
joncinque authored May 2, 2022
1 parent 3db53e2 commit a1c827d
Show file tree
Hide file tree
Showing 38 changed files with 475 additions and 188 deletions.
8 changes: 4 additions & 4 deletions stake-pool/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use {
find_withdraw_authority_program_address,
instruction::{FundingType, PreferredValidatorType},
state::{Fee, FeeType, StakePool, ValidatorList},
MINIMUM_ACTIVE_STAKE,
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
},
std::cmp::Ordering,
std::{process::exit, sync::Arc},
Expand Down Expand Up @@ -250,7 +250,7 @@ fn command_create_pool(
let reserve_stake_balance = config
.rpc_client
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
+ 1;
+ MINIMUM_RESERVE_LAMPORTS;
let mint_account_balance = config
.rpc_client
.get_minimum_balance_for_rent_exemption(spl_token::state::Mint::LEN)?;
Expand Down Expand Up @@ -1058,7 +1058,7 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
let minimum_reserve_stake_balance = config
.rpc_client
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
+ 1;
+ MINIMUM_RESERVE_LAMPORTS;
let cli_stake_pool_stake_account_infos = validator_list
.validators
.iter()
Expand Down Expand Up @@ -1271,7 +1271,7 @@ fn prepare_withdraw_accounts(
stake_pool.reserve_stake,
reserve_stake.lamports
- rpc_client.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?
- 1,
- MINIMUM_RESERVE_LAMPORTS,
None,
));

Expand Down
2 changes: 1 addition & 1 deletion stake-pool/js/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient');

// Minimum amount of staked SOL required in a validator stake account to allow
// for merges without a mismatch on credits observed
export const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL / 1_000;
export const MINIMUM_ACTIVE_STAKE = LAMPORTS_PER_SOL;
10 changes: 6 additions & 4 deletions stake-pool/program/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]

use crate::{error::StakePoolError, processor::Processor};
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
program_error::PrintProgramError, pubkey::Pubkey,
use {
crate::{error::StakePoolError, processor::Processor},
solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
program_error::PrintProgramError, pubkey::Pubkey,
},
};

entrypoint!(process_instruction);
Expand Down
8 changes: 5 additions & 3 deletions stake-pool/program/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Error types
use num_derive::FromPrimitive;
use solana_program::{decode_error::DecodeError, program_error::ProgramError};
use thiserror::Error;
use {
num_derive::FromPrimitive,
solana_program::{decode_error::DecodeError, program_error::ProgramError},
thiserror::Error,
};

/// Errors that may be returned by the StakePool program.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
Expand Down
8 changes: 6 additions & 2 deletions stake-pool/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ 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 / 1_000;
pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL;

/// Minimum amount of SOL in the reserve
pub const MINIMUM_RESERVE_LAMPORTS: u64 = LAMPORTS_PER_SOL;

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

/// Generates the deposit authority program address for the stake pool
Expand Down
37 changes: 24 additions & 13 deletions stake-pool/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ use {
borsh::{BorshDeserialize, BorshSerialize},
num_traits::FromPrimitive,
solana_program::{
account_info::next_account_info,
account_info::AccountInfo,
account_info::{next_account_info, AccountInfo},
borsh::try_from_slice_unchecked,
clock::{Clock, Epoch},
decode_error::DecodeError,
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
program_error::PrintProgramError,
program_error::ProgramError,
program_error::{PrintProgramError, ProgramError},
program_pack::Pack,
pubkey::Pubkey,
rent::Rent,
Expand Down Expand Up @@ -136,6 +134,17 @@ fn check_account_owner(
}
}

/// Checks if a stake acount can be managed by the pool
fn stake_is_usable_by_pool(
meta: &stake::state::Meta,
expected_authority: &Pubkey,
expected_lockup: &stake::state::Lockup,
) -> bool {
meta.authorized.staker == *expected_authority
&& meta.authorized.withdrawer == *expected_authority
&& meta.lockup == *expected_lockup
}

/// Create a transient stake account without transferring lamports
fn create_transient_stake_account<'a>(
transient_stake_account_info: AccountInfo<'a>,
Expand Down Expand Up @@ -747,7 +756,7 @@ impl Processor {
stake_pool.manager_fee_account = *manager_fee_info.key;
stake_pool.token_program_id = *token_program_info.key;
stake_pool.total_lamports = total_lamports;
stake_pool.pool_token_supply = 0;
stake_pool.pool_token_supply = total_lamports;
stake_pool.last_update_epoch = Clock::get()?.epoch;
stake_pool.lockup = stake::state::Lockup::default();
stake_pool.epoch_fee = epoch_fee;
Expand Down Expand Up @@ -1543,10 +1552,11 @@ impl Processor {
// * not a stake -> ignore
match transient_stake_state {
Some(stake::state::StakeState::Initialized(meta)) => {
// if transient account was hijacked, ignore it
if meta.authorized.staker == *withdraw_authority_info.key
&& meta.authorized.withdrawer == *withdraw_authority_info.key
{
if stake_is_usable_by_pool(
&meta,
withdraw_authority_info.key,
&stake_pool.lockup,
) {
if no_merge {
transient_stake_lamports = transient_stake_info.lamports();
} else {
Expand All @@ -1571,10 +1581,11 @@ impl Processor {
}
}
Some(stake::state::StakeState::Stake(meta, stake)) => {
// if transient account was hijacked, ignore it
if meta.authorized.staker == *withdraw_authority_info.key
&& meta.authorized.withdrawer == *withdraw_authority_info.key
{
if stake_is_usable_by_pool(
&meta,
withdraw_authority_info.key,
&stake_pool.lockup,
) {
let account_stake = meta
.rent_exempt_reserve
.saturating_add(stake.delegation.stake);
Expand Down
8 changes: 3 additions & 5 deletions stake-pool/program/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! State transition types
use spl_token::state::{Account, AccountState};
use {
crate::{
big_vec::BigVec, error::StakePoolError, MAX_WITHDRAWAL_FEE_INCREASE,
Expand All @@ -20,6 +19,7 @@ use {
stake::state::Lockup,
},
spl_math::checked_ceil_div::CheckedCeilDiv,
spl_token::state::{Account, AccountState},
std::{convert::TryFrom, fmt, matches},
};

Expand Down Expand Up @@ -535,7 +535,7 @@ impl Default for StakeStatus {
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 0.001 SOL).
/// (currently 1 SOL).
///
/// Note that if `last_update_epoch` does not match the current epoch then
/// this field may not be accurate
Expand Down Expand Up @@ -823,10 +823,8 @@ mod test {
use {
super::*,
proptest::prelude::*,
solana_program::borsh::{
get_instance_packed_len, get_packed_len, try_from_slice_unchecked,
},
solana_program::{
borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked},
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_S_PER_SLOT, SECONDS_PER_DAY},
native_token::LAMPORTS_PER_SOL,
},
Expand Down
20 changes: 14 additions & 6 deletions stake-pool/program/tests/decrease.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use {
},
spl_stake_pool::{
error::StakePoolError, find_transient_stake_program_address, id, instruction,
MINIMUM_ACTIVE_STAKE, MINIMUM_RESERVE_LAMPORTS,
},
};

Expand All @@ -28,9 +29,16 @@ async fn setup() -> (
u64,
) {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let rent = banks_client.get_rent().await.unwrap();
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake::state::StakeState>());
let stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.initialize_stake_pool(
&mut banks_client,
&payer,
&recent_blockhash,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();

Expand All @@ -48,12 +56,12 @@ async fn setup() -> (
&recent_blockhash,
&stake_pool_accounts,
&validator_stake_account,
100_000_000,
MINIMUM_ACTIVE_STAKE * 2 + stake_rent,
)
.await
.unwrap();

let lamports = deposit_info.stake_lamports / 2;
let decrease_lamports = MINIMUM_ACTIVE_STAKE + stake_rent;

(
banks_client,
Expand All @@ -62,7 +70,7 @@ async fn setup() -> (
stake_pool_accounts,
validator_stake_account,
deposit_info,
lamports,
decrease_lamports,
)
}

Expand Down Expand Up @@ -297,7 +305,7 @@ async fn fail_decrease_twice() {
&recent_blockhash,
&validator_stake.stake_account,
&validator_stake.transient_stake_account,
decrease_lamports / 3,
decrease_lamports,
validator_stake.transient_stake_seed,
)
.await;
Expand All @@ -318,7 +326,7 @@ async fn fail_decrease_twice() {
&recent_blockhash,
&validator_stake.stake_account,
&transient_stake_address,
decrease_lamports / 2,
decrease_lamports,
transient_stake_seed,
)
.await
Expand Down
17 changes: 12 additions & 5 deletions stake-pool/program/tests/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ use {
solana_program_test::*,
solana_sdk::{
signature::{Keypair, Signer},
transaction::Transaction,
transaction::TransactionError,
transaction::{Transaction, TransactionError},
transport::TransportError,
},
spl_stake_pool::{error::StakePoolError, id, instruction, minimum_stake_lamports, state},
spl_stake_pool::{
error::StakePoolError, id, instruction, minimum_stake_lamports, state,
MINIMUM_RESERVE_LAMPORTS,
},
spl_token::error as token_error,
};

Expand All @@ -40,7 +42,7 @@ async fn setup() -> (
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
1,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();
Expand Down Expand Up @@ -616,7 +618,12 @@ async fn fail_with_unknown_validator() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.initialize_stake_pool(
&mut banks_client,
&payer,
&recent_blockhash,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();

Expand Down
23 changes: 19 additions & 4 deletions stake-pool/program/tests/deposit_authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use {
signature::{Keypair, Signer},
transaction::TransactionError,
},
spl_stake_pool::{error::StakePoolError, state::StakePool},
spl_stake_pool::{error::StakePoolError, state::StakePool, MINIMUM_RESERVE_LAMPORTS},
};

#[tokio::test]
Expand All @@ -21,7 +21,12 @@ async fn success_initialize() {
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
let deposit_authority = stake_pool_accounts.stake_deposit_authority;
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.initialize_stake_pool(
&mut banks_client,
&payer,
&recent_blockhash,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();

Expand All @@ -41,7 +46,12 @@ async fn success_deposit() {
let stake_pool_accounts =
StakePoolAccounts::new_with_deposit_authority(stake_deposit_authority);
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.initialize_stake_pool(
&mut banks_client,
&payer,
&recent_blockhash,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();

Expand Down Expand Up @@ -116,7 +126,12 @@ async fn fail_deposit_without_authority_signature() {
let mut stake_pool_accounts =
StakePoolAccounts::new_with_deposit_authority(stake_deposit_authority);
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.initialize_stake_pool(
&mut banks_client,
&payer,
&recent_blockhash,
MINIMUM_RESERVE_LAMPORTS,
)
.await
.unwrap();

Expand Down
Loading

0 comments on commit a1c827d

Please sign in to comment.