stake-pool: Split up more tests to help CI, avoid warping (#3852)
* stake-pool: Split up more tests to help CI, avoid warping * Remove unnecessary updates * Split preferred test that keeps failing
This commit is contained in:
parent
792ffbc518
commit
7d4ab6028b
|
@ -56,11 +56,6 @@ async fn setup(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
|
||||||
let mut slot = first_normal_slot;
|
|
||||||
context.warp_to_slot(slot).unwrap();
|
|
||||||
|
|
||||||
let user = Keypair::new();
|
let user = Keypair::new();
|
||||||
// make stake account
|
// make stake account
|
||||||
let deposit_stake = Keypair::new();
|
let deposit_stake = Keypair::new();
|
||||||
|
@ -92,8 +87,8 @@ async fn setup(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
slot += slots_per_epoch;
|
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
context.warp_to_slot(slot).unwrap();
|
context.warp_to_slot(first_normal_slot).unwrap();
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
.update_all(
|
.update_all(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
|
@ -805,226 +800,3 @@ async fn fail_with_wrong_mint_for_receiver_acc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fail_with_uninitialized_validator_list() {} // TODO
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fail_with_out_of_dated_pool_balances() {} // TODO
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_with_preferred_deposit() {
|
|
||||||
let (
|
|
||||||
mut context,
|
|
||||||
stake_pool_accounts,
|
|
||||||
validator_stake,
|
|
||||||
user,
|
|
||||||
deposit_stake,
|
|
||||||
pool_token_account,
|
|
||||||
_stake_lamports,
|
|
||||||
) = setup(spl_token::id()).await;
|
|
||||||
|
|
||||||
stake_pool_accounts
|
|
||||||
.set_preferred_validator(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
instruction::PreferredValidatorType::Deposit,
|
|
||||||
Some(validator_stake.vote.pubkey()),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.deposit_stake(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&deposit_stake,
|
|
||||||
&pool_token_account,
|
|
||||||
&validator_stake.stake_account,
|
|
||||||
&user,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fail_with_wrong_preferred_deposit() {
|
|
||||||
let (
|
|
||||||
mut context,
|
|
||||||
stake_pool_accounts,
|
|
||||||
validator_stake,
|
|
||||||
user,
|
|
||||||
deposit_stake,
|
|
||||||
pool_token_account,
|
|
||||||
_stake_lamports,
|
|
||||||
) = setup(spl_token::id()).await;
|
|
||||||
|
|
||||||
let preferred_validator = simple_add_validator_to_pool(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_pool_accounts,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
stake_pool_accounts
|
|
||||||
.set_preferred_validator(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
instruction::PreferredValidatorType::Deposit,
|
|
||||||
Some(preferred_validator.vote.pubkey()),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.deposit_stake(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&deposit_stake,
|
|
||||||
&pool_token_account,
|
|
||||||
&validator_stake.stake_account,
|
|
||||||
&user,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
match error {
|
|
||||||
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
|
||||||
assert_eq!(
|
|
||||||
error_index,
|
|
||||||
StakePoolError::IncorrectDepositVoteAddress as u32
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_with_referral_fee() {
|
|
||||||
let (
|
|
||||||
mut context,
|
|
||||||
stake_pool_accounts,
|
|
||||||
validator_stake_account,
|
|
||||||
user,
|
|
||||||
deposit_stake,
|
|
||||||
pool_token_account,
|
|
||||||
stake_lamports,
|
|
||||||
) = setup(spl_token::id()).await;
|
|
||||||
|
|
||||||
let referrer = Keypair::new();
|
|
||||||
let referrer_token_account = Keypair::new();
|
|
||||||
create_token_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_pool_accounts.token_program_id,
|
|
||||||
&referrer_token_account,
|
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
|
||||||
&referrer,
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let referrer_balance_pre =
|
|
||||||
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
|
||||||
|
|
||||||
let mut transaction = Transaction::new_with_payer(
|
|
||||||
&instruction::deposit_stake(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&deposit_stake,
|
|
||||||
&user.pubkey(),
|
|
||||||
&validator_stake_account.stake_account,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&pool_token_account,
|
|
||||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
|
||||||
&referrer_token_account.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
|
||||||
&spl_token::id(),
|
|
||||||
),
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
);
|
|
||||||
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
|
||||||
context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let referrer_balance_post =
|
|
||||||
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
|
||||||
let stake_pool = get_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let stake_pool =
|
|
||||||
try_from_slice_unchecked::<state::StakePool>(stake_pool.data.as_slice()).unwrap();
|
|
||||||
let rent = context.banks_client.get_rent().await.unwrap();
|
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake::state::StakeState>());
|
|
||||||
let fee_tokens = stake_pool
|
|
||||||
.calc_pool_tokens_sol_deposit_fee(stake_rent)
|
|
||||||
.unwrap()
|
|
||||||
+ stake_pool
|
|
||||||
.calc_pool_tokens_stake_deposit_fee(stake_lamports - stake_rent)
|
|
||||||
.unwrap();
|
|
||||||
let referral_fee = stake_pool_accounts.calculate_referral_fee(fee_tokens);
|
|
||||||
assert!(referral_fee > 0);
|
|
||||||
assert_eq!(referrer_balance_pre + referral_fee, referrer_balance_post);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fail_with_invalid_referrer() {
|
|
||||||
let (
|
|
||||||
mut context,
|
|
||||||
stake_pool_accounts,
|
|
||||||
validator_stake_account,
|
|
||||||
user,
|
|
||||||
deposit_stake,
|
|
||||||
pool_token_account,
|
|
||||||
_stake_lamports,
|
|
||||||
) = setup(spl_token::id()).await;
|
|
||||||
|
|
||||||
let invalid_token_account = Keypair::new();
|
|
||||||
|
|
||||||
let mut transaction = Transaction::new_with_payer(
|
|
||||||
&instruction::deposit_stake(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&deposit_stake,
|
|
||||||
&user.pubkey(),
|
|
||||||
&validator_stake_account.stake_account,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&pool_token_account,
|
|
||||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
|
||||||
&invalid_token_account.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
|
||||||
&spl_token::id(),
|
|
||||||
),
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
);
|
|
||||||
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
|
||||||
let transaction_error = context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.err()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match transaction_error {
|
|
||||||
TransactionError::InstructionError(_, InstructionError::InvalidAccountData) => (),
|
|
||||||
_ => panic!(
|
|
||||||
"Wrong error occurs while try to make a deposit with an invalid referrer account"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
#![allow(clippy::integer_arithmetic)]
|
||||||
|
#![cfg(feature = "test-sbf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
helpers::*,
|
||||||
|
solana_program::{
|
||||||
|
borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake,
|
||||||
|
},
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
transaction::{Transaction, TransactionError},
|
||||||
|
},
|
||||||
|
spl_stake_pool::{error::StakePoolError, id, instruction, state, MINIMUM_RESERVE_LAMPORTS},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup(
|
||||||
|
token_program_id: Pubkey,
|
||||||
|
) -> (
|
||||||
|
ProgramTestContext,
|
||||||
|
StakePoolAccounts,
|
||||||
|
ValidatorStakeAccount,
|
||||||
|
Keypair,
|
||||||
|
Pubkey,
|
||||||
|
Pubkey,
|
||||||
|
u64,
|
||||||
|
) {
|
||||||
|
let mut context = program_test().start_with_context().await;
|
||||||
|
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::new_with_token_program(token_program_id);
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
MINIMUM_RESERVE_LAMPORTS,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let validator_stake_account = simple_add_validator_to_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_pool_accounts,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let user = Keypair::new();
|
||||||
|
// make stake account
|
||||||
|
let deposit_stake = Keypair::new();
|
||||||
|
let lockup = stake::state::Lockup::default();
|
||||||
|
|
||||||
|
let authorized = stake::state::Authorized {
|
||||||
|
staker: user.pubkey(),
|
||||||
|
withdrawer: user.pubkey(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let stake_lamports = create_independent_stake_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&deposit_stake,
|
||||||
|
&authorized,
|
||||||
|
&lockup,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
delegate_stake_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&deposit_stake.pubkey(),
|
||||||
|
&user,
|
||||||
|
&validator_stake_account.vote.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
|
context.warp_to_slot(first_normal_slot).unwrap();
|
||||||
|
stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&[validator_stake_account.vote.pubkey()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// make pool token account
|
||||||
|
let pool_token_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_pool_accounts.token_program_id,
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake_account,
|
||||||
|
user,
|
||||||
|
deposit_stake.pubkey(),
|
||||||
|
pool_token_account.pubkey(),
|
||||||
|
stake_lamports,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_preferred_deposit() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
_stake_lamports,
|
||||||
|
) = setup(spl_token::id()).await;
|
||||||
|
|
||||||
|
stake_pool_accounts
|
||||||
|
.set_preferred_validator(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
instruction::PreferredValidatorType::Deposit,
|
||||||
|
Some(validator_stake.vote.pubkey()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_stake(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&deposit_stake,
|
||||||
|
&pool_token_account,
|
||||||
|
&validator_stake.stake_account,
|
||||||
|
&user,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_wrong_preferred_deposit() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
_stake_lamports,
|
||||||
|
) = setup(spl_token::id()).await;
|
||||||
|
|
||||||
|
let preferred_validator = simple_add_validator_to_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_pool_accounts,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
stake_pool_accounts
|
||||||
|
.set_preferred_validator(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
instruction::PreferredValidatorType::Deposit,
|
||||||
|
Some(preferred_validator.vote.pubkey()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_stake(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&deposit_stake,
|
||||||
|
&pool_token_account,
|
||||||
|
&validator_stake.stake_account,
|
||||||
|
&user,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
assert_eq!(
|
||||||
|
error_index,
|
||||||
|
StakePoolError::IncorrectDepositVoteAddress as u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_referral_fee() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake_account,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
stake_lamports,
|
||||||
|
) = setup(spl_token::id()).await;
|
||||||
|
|
||||||
|
let referrer = Keypair::new();
|
||||||
|
let referrer_token_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_pool_accounts.token_program_id,
|
||||||
|
&referrer_token_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&referrer,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_pre =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_stake(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&deposit_stake,
|
||||||
|
&user.pubkey(),
|
||||||
|
&validator_stake_account.stake_account,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&referrer_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_post =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(stake_pool.data.as_slice()).unwrap();
|
||||||
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake::state::StakeState>());
|
||||||
|
let fee_tokens = stake_pool
|
||||||
|
.calc_pool_tokens_sol_deposit_fee(stake_rent)
|
||||||
|
.unwrap()
|
||||||
|
+ stake_pool
|
||||||
|
.calc_pool_tokens_stake_deposit_fee(stake_lamports - stake_rent)
|
||||||
|
.unwrap();
|
||||||
|
let referral_fee = stake_pool_accounts.calculate_referral_fee(fee_tokens);
|
||||||
|
assert!(referral_fee > 0);
|
||||||
|
assert_eq!(referrer_balance_pre + referral_fee, referrer_balance_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_invalid_referrer() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake_account,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
_stake_lamports,
|
||||||
|
) = setup(spl_token::id()).await;
|
||||||
|
|
||||||
|
let invalid_token_account = Keypair::new();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_stake(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&deposit_stake,
|
||||||
|
&user.pubkey(),
|
||||||
|
&validator_stake_account.stake_account,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&invalid_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
||||||
|
let transaction_error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::InvalidAccountData) => (),
|
||||||
|
_ => panic!(
|
||||||
|
"Wrong error occurs while try to make a deposit with an invalid referrer account"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,12 +26,8 @@ async fn setup(
|
||||||
ProgramTestContext,
|
ProgramTestContext,
|
||||||
StakePoolAccounts,
|
StakePoolAccounts,
|
||||||
Vec<ValidatorStakeAccount>,
|
Vec<ValidatorStakeAccount>,
|
||||||
u64,
|
|
||||||
) {
|
) {
|
||||||
let mut context = program_test().start_with_context().await;
|
let mut context = program_test().start_with_context().await;
|
||||||
let slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
|
||||||
context.warp_to_slot(slot).unwrap();
|
|
||||||
|
|
||||||
let stake_pool_accounts = StakePoolAccounts::default();
|
let stake_pool_accounts = StakePoolAccounts::default();
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
.initialize_stake_pool(
|
.initialize_stake_pool(
|
||||||
|
@ -118,12 +114,12 @@ async fn setup(
|
||||||
stake_accounts.push(stake_account);
|
stake_accounts.push(stake_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
(context, stake_pool_accounts, stake_accounts, slot)
|
(context, stake_pool_accounts, stake_accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success() {
|
async fn success() {
|
||||||
let (mut context, stake_pool_accounts, stake_accounts, slot) = setup(NUM_VALIDATORS).await;
|
let (mut context, stake_pool_accounts, stake_accounts) = setup(NUM_VALIDATORS).await;
|
||||||
|
|
||||||
let pre_fee = get_token_balance(
|
let pre_fee = get_token_balance(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
|
@ -151,15 +147,6 @@ async fn success() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.update_stake_pool_balance(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
// Increment vote credits to earn rewards
|
// Increment vote credits to earn rewards
|
||||||
const VOTE_CREDITS: u64 = 1_000;
|
const VOTE_CREDITS: u64 = 1_000;
|
||||||
for stake_account in &stake_accounts {
|
for stake_account in &stake_accounts {
|
||||||
|
@ -167,8 +154,8 @@ async fn success() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update epoch
|
// Update epoch
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
let slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
context.warp_to_slot(slot + slots_per_epoch).unwrap();
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
// Update list and pool
|
// Update list and pool
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
|
@ -229,7 +216,7 @@ async fn success() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success_absorbing_extra_lamports() {
|
async fn success_absorbing_extra_lamports() {
|
||||||
let (mut context, stake_pool_accounts, stake_accounts, slot) = setup(NUM_VALIDATORS).await;
|
let (mut context, stake_pool_accounts, stake_accounts) = setup(NUM_VALIDATORS).await;
|
||||||
|
|
||||||
let pre_balance = get_validator_list_sum(
|
let pre_balance = get_validator_list_sum(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
|
@ -251,15 +238,6 @@ async fn success_absorbing_extra_lamports() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.update_stake_pool_balance(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
// Transfer extra funds, will be absorbed during update
|
// Transfer extra funds, will be absorbed during update
|
||||||
const EXTRA_STAKE_AMOUNT: u64 = 1_000_000;
|
const EXTRA_STAKE_AMOUNT: u64 = 1_000_000;
|
||||||
for stake_account in &stake_accounts {
|
for stake_account in &stake_accounts {
|
||||||
|
@ -277,8 +255,8 @@ async fn success_absorbing_extra_lamports() {
|
||||||
let expected_fee = stake_pool.calc_epoch_fee_amount(extra_lamports).unwrap();
|
let expected_fee = stake_pool.calc_epoch_fee_amount(extra_lamports).unwrap();
|
||||||
|
|
||||||
// Update epoch
|
// Update epoch
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
let slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
context.warp_to_slot(slot + slots_per_epoch).unwrap();
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
// Update list and pool
|
// Update list and pool
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
|
|
|
@ -5,19 +5,10 @@ mod helpers;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
helpers::*,
|
helpers::*,
|
||||||
solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey, stake},
|
solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey},
|
||||||
solana_program_test::*,
|
solana_program_test::*,
|
||||||
solana_sdk::{
|
solana_sdk::{signature::Signer, stake::state::StakeState},
|
||||||
instruction::InstructionError,
|
|
||||||
signature::Signer,
|
|
||||||
stake::state::{Authorized, Lockup, StakeState},
|
|
||||||
system_instruction,
|
|
||||||
transaction::{Transaction, TransactionError},
|
|
||||||
},
|
|
||||||
spl_stake_pool::{
|
spl_stake_pool::{
|
||||||
error::StakePoolError,
|
|
||||||
find_stake_program_address, find_transient_stake_program_address,
|
|
||||||
find_withdraw_authority_program_address, id, instruction,
|
|
||||||
state::{StakePool, StakeStatus, ValidatorList},
|
state::{StakePool, StakeStatus, ValidatorList},
|
||||||
MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS,
|
MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS,
|
||||||
},
|
},
|
||||||
|
@ -719,371 +710,6 @@ async fn success_with_burned_tokens() {
|
||||||
assert_eq!(mint.supply, stake_pool.pool_token_supply);
|
assert_eq!(mint.supply, stake_pool.pool_token_supply);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_ignoring_hijacked_transient_stake_with_authorized() {
|
|
||||||
let hijacker = Pubkey::new_unique();
|
|
||||||
check_ignored_hijacked_transient_stake(Some(&Authorized::auto(&hijacker)), None).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_ignoring_hijacked_transient_stake_with_lockup() {
|
|
||||||
let hijacker = Pubkey::new_unique();
|
|
||||||
check_ignored_hijacked_transient_stake(
|
|
||||||
None,
|
|
||||||
Some(&Lockup {
|
|
||||||
custodian: hijacker,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_ignored_hijacked_transient_stake(
|
|
||||||
hijack_authorized: Option<&Authorized>,
|
|
||||||
hijack_lockup: Option<&Lockup>,
|
|
||||||
) {
|
|
||||||
let num_validators = 1;
|
|
||||||
let (mut context, stake_pool_accounts, stake_accounts, _, lamports, _, mut slot) =
|
|
||||||
setup(num_validators).await;
|
|
||||||
|
|
||||||
let rent = context.banks_client.get_rent().await.unwrap();
|
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
|
||||||
|
|
||||||
let pre_lamports = get_validator_list_sum(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let (withdraw_authority, _) =
|
|
||||||
find_withdraw_authority_program_address(&id(), &stake_pool_accounts.stake_pool.pubkey());
|
|
||||||
|
|
||||||
println!("Decrease from all validators");
|
|
||||||
let stake_account = &stake_accounts[0];
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.decrease_validator_stake(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_account.stake_account,
|
|
||||||
&stake_account.transient_stake_account,
|
|
||||||
lamports,
|
|
||||||
stake_account.transient_stake_seed,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
println!("Warp one epoch so the stakes deactivate and merge");
|
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
|
||||||
slot += slots_per_epoch;
|
|
||||||
context.warp_to_slot(slot).unwrap();
|
|
||||||
|
|
||||||
println!("During update, hijack the transient stake account");
|
|
||||||
let validator_list = stake_pool_accounts
|
|
||||||
.get_validator_list(&mut context.banks_client)
|
|
||||||
.await;
|
|
||||||
let transient_stake_address = find_transient_stake_program_address(
|
|
||||||
&id(),
|
|
||||||
&stake_account.vote.pubkey(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
stake_account.transient_stake_seed,
|
|
||||||
)
|
|
||||||
.0;
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
|
||||||
&[
|
|
||||||
instruction::update_validator_list_balance(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&validator_list,
|
|
||||||
&[stake_account.vote.pubkey()],
|
|
||||||
0,
|
|
||||||
/* no_merge = */ false,
|
|
||||||
),
|
|
||||||
system_instruction::transfer(
|
|
||||||
&context.payer.pubkey(),
|
|
||||||
&transient_stake_address,
|
|
||||||
stake_rent + MINIMUM_RESERVE_LAMPORTS,
|
|
||||||
),
|
|
||||||
stake::instruction::initialize(
|
|
||||||
&transient_stake_address,
|
|
||||||
hijack_authorized.unwrap_or(&Authorized::auto(&withdraw_authority)),
|
|
||||||
hijack_lockup.unwrap_or(&Lockup::default()),
|
|
||||||
),
|
|
||||||
instruction::update_stake_pool_balance(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
|
||||||
&spl_token::id(),
|
|
||||||
),
|
|
||||||
instruction::cleanup_removed_validator_entries(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
&[&context.payer],
|
|
||||||
context.last_blockhash,
|
|
||||||
);
|
|
||||||
let error = context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.err();
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
println!("Update again normally, should be no change in the lamports");
|
|
||||||
stake_pool_accounts
|
|
||||||
.update_all(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
stake_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.vote.pubkey())
|
|
||||||
.collect::<Vec<Pubkey>>()
|
|
||||||
.as_slice(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let expected_lamports = get_validator_list_sum(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(pre_lamports, expected_lamports);
|
|
||||||
|
|
||||||
let stake_pool_info = get_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
|
||||||
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_ignoring_hijacked_validator_stake_with_authorized() {
|
|
||||||
let hijacker = Pubkey::new_unique();
|
|
||||||
check_ignored_hijacked_transient_stake(Some(&Authorized::auto(&hijacker)), None).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn success_ignoring_hijacked_validator_stake_with_lockup() {
|
|
||||||
let hijacker = Pubkey::new_unique();
|
|
||||||
check_ignored_hijacked_validator_stake(
|
|
||||||
None,
|
|
||||||
Some(&Lockup {
|
|
||||||
custodian: hijacker,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_ignored_hijacked_validator_stake(
|
|
||||||
hijack_authorized: Option<&Authorized>,
|
|
||||||
hijack_lockup: Option<&Lockup>,
|
|
||||||
) {
|
|
||||||
let num_validators = 1;
|
|
||||||
let (mut context, stake_pool_accounts, stake_accounts, _, lamports, _, mut slot) =
|
|
||||||
setup(num_validators).await;
|
|
||||||
|
|
||||||
let rent = context.banks_client.get_rent().await.unwrap();
|
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
|
||||||
|
|
||||||
let pre_lamports = get_validator_list_sum(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let (withdraw_authority, _) =
|
|
||||||
find_withdraw_authority_program_address(&id(), &stake_pool_accounts.stake_pool.pubkey());
|
|
||||||
|
|
||||||
let stake_account = &stake_accounts[0];
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.decrease_validator_stake(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_account.stake_account,
|
|
||||||
&stake_account.transient_stake_account,
|
|
||||||
lamports,
|
|
||||||
stake_account.transient_stake_seed,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.remove_validator_from_pool(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_account.stake_account,
|
|
||||||
&stake_account.transient_stake_account,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
println!("Warp one epoch so the stakes deactivate and merge");
|
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
|
||||||
slot += slots_per_epoch;
|
|
||||||
context.warp_to_slot(slot).unwrap();
|
|
||||||
|
|
||||||
println!("During update, hijack the validator stake account");
|
|
||||||
let validator_list = stake_pool_accounts
|
|
||||||
.get_validator_list(&mut context.banks_client)
|
|
||||||
.await;
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
|
||||||
&[
|
|
||||||
instruction::update_validator_list_balance(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&validator_list,
|
|
||||||
&[stake_account.vote.pubkey()],
|
|
||||||
0,
|
|
||||||
/* no_merge = */ false,
|
|
||||||
),
|
|
||||||
system_instruction::transfer(
|
|
||||||
&context.payer.pubkey(),
|
|
||||||
&stake_account.stake_account,
|
|
||||||
stake_rent + MINIMUM_RESERVE_LAMPORTS,
|
|
||||||
),
|
|
||||||
stake::instruction::initialize(
|
|
||||||
&stake_account.stake_account,
|
|
||||||
hijack_authorized.unwrap_or(&Authorized::auto(&withdraw_authority)),
|
|
||||||
hijack_lockup.unwrap_or(&Lockup::default()),
|
|
||||||
),
|
|
||||||
instruction::update_stake_pool_balance(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.withdraw_authority,
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
|
||||||
&spl_token::id(),
|
|
||||||
),
|
|
||||||
instruction::cleanup_removed_validator_entries(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
&[&context.payer],
|
|
||||||
context.last_blockhash,
|
|
||||||
);
|
|
||||||
let error = context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.err();
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
println!("Update again normally, should be no change in the lamports");
|
|
||||||
stake_pool_accounts
|
|
||||||
.update_all(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
stake_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.vote.pubkey())
|
|
||||||
.collect::<Vec<Pubkey>>()
|
|
||||||
.as_slice(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let expected_lamports = get_validator_list_sum(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(pre_lamports, expected_lamports);
|
|
||||||
|
|
||||||
let stake_pool_info = get_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
|
||||||
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
|
||||||
|
|
||||||
println!("Fail adding validator back in with first seed");
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.add_validator_to_pool(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_account.stake_account,
|
|
||||||
&stake_account.vote.pubkey(),
|
|
||||||
stake_account.validator_stake_seed,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
error,
|
|
||||||
TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::Custom(StakePoolError::AlreadyInUse as u32),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("Succeed adding validator back in with new seed");
|
|
||||||
let seed = NonZeroU32::new(1);
|
|
||||||
let validator = stake_account.vote.pubkey();
|
|
||||||
let (stake_account, _) = find_stake_program_address(
|
|
||||||
&id(),
|
|
||||||
&validator,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
seed,
|
|
||||||
);
|
|
||||||
let error = stake_pool_accounts
|
|
||||||
.add_validator_to_pool(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&context.payer,
|
|
||||||
&context.last_blockhash,
|
|
||||||
&stake_account,
|
|
||||||
&validator,
|
|
||||||
seed,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(error.is_none());
|
|
||||||
|
|
||||||
let stake_pool_info = get_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
|
||||||
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
|
||||||
|
|
||||||
let expected_lamports = get_validator_list_sum(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(pre_lamports, expected_lamports);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_with_uninitialized_validator_list() {} // TODO
|
async fn fail_with_uninitialized_validator_list() {} // TODO
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,521 @@
|
||||||
|
#![allow(clippy::integer_arithmetic)]
|
||||||
|
#![cfg(feature = "test-sbf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
helpers::*,
|
||||||
|
solana_program::{borsh::try_from_slice_unchecked, pubkey::Pubkey, stake},
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
instruction::InstructionError,
|
||||||
|
signature::Signer,
|
||||||
|
stake::state::{Authorized, Lockup, StakeState},
|
||||||
|
system_instruction,
|
||||||
|
transaction::{Transaction, TransactionError},
|
||||||
|
},
|
||||||
|
spl_stake_pool::{
|
||||||
|
error::StakePoolError, find_stake_program_address, find_transient_stake_program_address,
|
||||||
|
find_withdraw_authority_program_address, id, instruction, state::StakePool,
|
||||||
|
MINIMUM_RESERVE_LAMPORTS,
|
||||||
|
},
|
||||||
|
std::num::NonZeroU32,
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup(
|
||||||
|
num_validators: usize,
|
||||||
|
) -> (
|
||||||
|
ProgramTestContext,
|
||||||
|
StakePoolAccounts,
|
||||||
|
Vec<ValidatorStakeAccount>,
|
||||||
|
Vec<DepositStakeAccount>,
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
) {
|
||||||
|
let mut context = program_test().start_with_context().await;
|
||||||
|
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
let mut slot = first_normal_slot;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
let reserve_stake_amount = TEST_STAKE_AMOUNT * 2 * num_validators as u64;
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::default();
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
reserve_stake_amount + MINIMUM_RESERVE_LAMPORTS,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Add several accounts with some stake
|
||||||
|
let mut stake_accounts: Vec<ValidatorStakeAccount> = vec![];
|
||||||
|
let mut deposit_accounts: Vec<DepositStakeAccount> = vec![];
|
||||||
|
for i in 0..num_validators {
|
||||||
|
let stake_account = ValidatorStakeAccount::new(
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
NonZeroU32::new(i as u32),
|
||||||
|
u64::MAX,
|
||||||
|
);
|
||||||
|
create_vote(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.validator,
|
||||||
|
&stake_account.vote,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.add_validator_to_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.vote.pubkey(),
|
||||||
|
stake_account.validator_stake_seed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let deposit_account = DepositStakeAccount::new_with_vote(
|
||||||
|
stake_account.vote.pubkey(),
|
||||||
|
stake_account.stake_account,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
);
|
||||||
|
deposit_account
|
||||||
|
.create_and_delegate(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
stake_accounts.push(stake_account);
|
||||||
|
deposit_accounts.push(deposit_account);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warp forward so the stakes properly activate, and deposit
|
||||||
|
slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
stake_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.vote.pubkey())
|
||||||
|
.collect::<Vec<Pubkey>>()
|
||||||
|
.as_slice(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
for deposit_account in &mut deposit_accounts {
|
||||||
|
deposit_account
|
||||||
|
.deposit_stake(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_pool_accounts,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
stake_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.vote.pubkey())
|
||||||
|
.collect::<Vec<Pubkey>>()
|
||||||
|
.as_slice(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
stake_accounts,
|
||||||
|
deposit_accounts,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
reserve_stake_amount,
|
||||||
|
slot,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_ignoring_hijacked_transient_stake_with_authorized() {
|
||||||
|
let hijacker = Pubkey::new_unique();
|
||||||
|
check_ignored_hijacked_transient_stake(Some(&Authorized::auto(&hijacker)), None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_ignoring_hijacked_transient_stake_with_lockup() {
|
||||||
|
let hijacker = Pubkey::new_unique();
|
||||||
|
check_ignored_hijacked_transient_stake(
|
||||||
|
None,
|
||||||
|
Some(&Lockup {
|
||||||
|
custodian: hijacker,
|
||||||
|
..Lockup::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_ignored_hijacked_transient_stake(
|
||||||
|
hijack_authorized: Option<&Authorized>,
|
||||||
|
hijack_lockup: Option<&Lockup>,
|
||||||
|
) {
|
||||||
|
let num_validators = 1;
|
||||||
|
let (mut context, stake_pool_accounts, stake_accounts, _, lamports, _, mut slot) =
|
||||||
|
setup(num_validators).await;
|
||||||
|
|
||||||
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
||||||
|
|
||||||
|
let pre_lamports = get_validator_list_sum(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let (withdraw_authority, _) =
|
||||||
|
find_withdraw_authority_program_address(&id(), &stake_pool_accounts.stake_pool.pubkey());
|
||||||
|
|
||||||
|
println!("Decrease from all validators");
|
||||||
|
let stake_account = &stake_accounts[0];
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.decrease_validator_stake(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.transient_stake_account,
|
||||||
|
lamports,
|
||||||
|
stake_account.transient_stake_seed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
println!("Warp one epoch so the stakes deactivate and merge");
|
||||||
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
println!("During update, hijack the transient stake account");
|
||||||
|
let validator_list = stake_pool_accounts
|
||||||
|
.get_validator_list(&mut context.banks_client)
|
||||||
|
.await;
|
||||||
|
let transient_stake_address = find_transient_stake_program_address(
|
||||||
|
&id(),
|
||||||
|
&stake_account.vote.pubkey(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
stake_account.transient_stake_seed,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[
|
||||||
|
instruction::update_validator_list_balance(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&validator_list,
|
||||||
|
&[stake_account.vote.pubkey()],
|
||||||
|
0,
|
||||||
|
/* no_merge = */ false,
|
||||||
|
),
|
||||||
|
system_instruction::transfer(
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&transient_stake_address,
|
||||||
|
stake_rent + MINIMUM_RESERVE_LAMPORTS,
|
||||||
|
),
|
||||||
|
stake::instruction::initialize(
|
||||||
|
&transient_stake_address,
|
||||||
|
hijack_authorized.unwrap_or(&Authorized::auto(&withdraw_authority)),
|
||||||
|
hijack_lockup.unwrap_or(&Lockup::default()),
|
||||||
|
),
|
||||||
|
instruction::update_stake_pool_balance(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
instruction::cleanup_removed_validator_entries(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err();
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
println!("Update again normally, should be no change in the lamports");
|
||||||
|
stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
stake_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.vote.pubkey())
|
||||||
|
.collect::<Vec<Pubkey>>()
|
||||||
|
.as_slice(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let expected_lamports = get_validator_list_sum(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(pre_lamports, expected_lamports);
|
||||||
|
|
||||||
|
let stake_pool_info = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
||||||
|
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_ignoring_hijacked_validator_stake_with_authorized() {
|
||||||
|
let hijacker = Pubkey::new_unique();
|
||||||
|
check_ignored_hijacked_transient_stake(Some(&Authorized::auto(&hijacker)), None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_ignoring_hijacked_validator_stake_with_lockup() {
|
||||||
|
let hijacker = Pubkey::new_unique();
|
||||||
|
check_ignored_hijacked_validator_stake(
|
||||||
|
None,
|
||||||
|
Some(&Lockup {
|
||||||
|
custodian: hijacker,
|
||||||
|
..Lockup::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_ignored_hijacked_validator_stake(
|
||||||
|
hijack_authorized: Option<&Authorized>,
|
||||||
|
hijack_lockup: Option<&Lockup>,
|
||||||
|
) {
|
||||||
|
let num_validators = 1;
|
||||||
|
let (mut context, stake_pool_accounts, stake_accounts, _, lamports, _, mut slot) =
|
||||||
|
setup(num_validators).await;
|
||||||
|
|
||||||
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
||||||
|
|
||||||
|
let pre_lamports = get_validator_list_sum(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let (withdraw_authority, _) =
|
||||||
|
find_withdraw_authority_program_address(&id(), &stake_pool_accounts.stake_pool.pubkey());
|
||||||
|
|
||||||
|
let stake_account = &stake_accounts[0];
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.decrease_validator_stake(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.transient_stake_account,
|
||||||
|
lamports,
|
||||||
|
stake_account.transient_stake_seed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.remove_validator_from_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.transient_stake_account,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
println!("Warp one epoch so the stakes deactivate and merge");
|
||||||
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
println!("During update, hijack the validator stake account");
|
||||||
|
let validator_list = stake_pool_accounts
|
||||||
|
.get_validator_list(&mut context.banks_client)
|
||||||
|
.await;
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[
|
||||||
|
instruction::update_validator_list_balance(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&validator_list,
|
||||||
|
&[stake_account.vote.pubkey()],
|
||||||
|
0,
|
||||||
|
/* no_merge = */ false,
|
||||||
|
),
|
||||||
|
system_instruction::transfer(
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&stake_account.stake_account,
|
||||||
|
stake_rent + MINIMUM_RESERVE_LAMPORTS,
|
||||||
|
),
|
||||||
|
stake::instruction::initialize(
|
||||||
|
&stake_account.stake_account,
|
||||||
|
hijack_authorized.unwrap_or(&Authorized::auto(&withdraw_authority)),
|
||||||
|
hijack_lockup.unwrap_or(&Lockup::default()),
|
||||||
|
),
|
||||||
|
instruction::update_stake_pool_balance(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
instruction::cleanup_removed_validator_entries(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err();
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
println!("Update again normally, should be no change in the lamports");
|
||||||
|
stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
stake_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.vote.pubkey())
|
||||||
|
.collect::<Vec<Pubkey>>()
|
||||||
|
.as_slice(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let expected_lamports = get_validator_list_sum(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(pre_lamports, expected_lamports);
|
||||||
|
|
||||||
|
let stake_pool_info = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
||||||
|
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
||||||
|
|
||||||
|
println!("Fail adding validator back in with first seed");
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.add_validator_to_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.vote.pubkey(),
|
||||||
|
stake_account.validator_stake_seed,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
error,
|
||||||
|
TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::Custom(StakePoolError::AlreadyInUse as u32),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Succeed adding validator back in with new seed");
|
||||||
|
let seed = NonZeroU32::new(1);
|
||||||
|
let validator = stake_account.vote.pubkey();
|
||||||
|
let (stake_account, _) = find_stake_program_address(
|
||||||
|
&id(),
|
||||||
|
&validator,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
seed,
|
||||||
|
);
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.add_validator_to_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&stake_account,
|
||||||
|
&validator,
|
||||||
|
seed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let stake_pool_info = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
||||||
|
assert_eq!(pre_lamports, stake_pool.total_lamports);
|
||||||
|
|
||||||
|
let expected_lamports = get_validator_list_sum(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(pre_lamports, expected_lamports);
|
||||||
|
}
|
|
@ -474,7 +474,7 @@ async fn success_with_reserve() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success_and_fail_with_preferred_withdraw() {
|
async fn success_with_empty_preferred_withdraw() {
|
||||||
let (
|
let (
|
||||||
mut context,
|
mut context,
|
||||||
stake_pool_accounts,
|
stake_pool_accounts,
|
||||||
|
@ -520,17 +520,39 @@ async fn success_and_fail_with_preferred_withdraw() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
// Deposit into preferred, then fail
|
#[tokio::test]
|
||||||
let user_stake_recipient = Keypair::new();
|
async fn success_and_fail_with_preferred_withdraw() {
|
||||||
create_blank_stake_account(
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake,
|
||||||
|
deposit_info,
|
||||||
|
user_transfer_authority,
|
||||||
|
user_stake_recipient,
|
||||||
|
tokens_to_burn,
|
||||||
|
) = setup(spl_token::id()).await;
|
||||||
|
|
||||||
|
let preferred_validator = simple_add_validator_to_pool(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
&context.last_blockhash,
|
&context.last_blockhash,
|
||||||
&user_stake_recipient,
|
&stake_pool_accounts,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
stake_pool_accounts
|
||||||
|
.set_preferred_validator(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
instruction::PreferredValidatorType::Withdraw,
|
||||||
|
Some(preferred_validator.vote.pubkey()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let _preferred_deposit = simple_deposit_stake(
|
let _preferred_deposit = simple_deposit_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
|
@ -542,6 +564,7 @@ async fn success_and_fail_with_preferred_withdraw() {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let new_authority = Pubkey::new_unique();
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
.withdraw_stake(
|
.withdraw_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
|
|
Loading…
Reference in New Issue