748 lines
24 KiB
Rust
748 lines
24 KiB
Rust
#![cfg(feature = "test-bpf")]
|
|
|
|
mod helpers;
|
|
|
|
use {
|
|
borsh::BorshSerialize,
|
|
helpers::*,
|
|
solana_program::{
|
|
borsh::try_from_slice_unchecked, program_option::COption, program_pack::Pack,
|
|
pubkey::Pubkey, stake,
|
|
},
|
|
solana_program_test::*,
|
|
solana_sdk::{
|
|
account::{Account, WritableAccount},
|
|
clock::{Clock, Epoch},
|
|
signature::{Keypair, Signer},
|
|
transaction::Transaction,
|
|
},
|
|
solana_vote_program::{
|
|
self,
|
|
vote_state::{VoteInit, VoteState, VoteStateVersions},
|
|
},
|
|
spl_stake_pool::{
|
|
find_stake_program_address, find_transient_stake_program_address,
|
|
find_withdraw_authority_program_address, id,
|
|
instruction::{self, PreferredValidatorType},
|
|
state::{AccountType, Fee, StakePool, StakeStatus, ValidatorList, ValidatorStakeInfo},
|
|
MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE,
|
|
},
|
|
spl_token::state::{Account as SplAccount, AccountState as SplAccountState, Mint},
|
|
};
|
|
|
|
const HUGE_POOL_SIZE: u32 = 2_950;
|
|
const ACCOUNT_RENT_EXEMPTION: u64 = 1_000_000_000; // go with something big to be safe
|
|
const STAKE_AMOUNT: u64 = 200_000_000_000;
|
|
const STAKE_ACCOUNT_RENT_EXEMPTION: u64 = 2_282_880;
|
|
|
|
async fn setup(
|
|
max_validators: u32,
|
|
num_validators: u32,
|
|
stake_amount: u64,
|
|
) -> (
|
|
ProgramTestContext,
|
|
StakePoolAccounts,
|
|
Vec<Pubkey>,
|
|
Pubkey,
|
|
Keypair,
|
|
Pubkey,
|
|
Pubkey,
|
|
) {
|
|
let mut program_test = program_test();
|
|
let mut vote_account_pubkeys = vec![];
|
|
let mut stake_pool_accounts = StakePoolAccounts::new();
|
|
stake_pool_accounts.max_validators = max_validators;
|
|
|
|
let stake_pool_pubkey = stake_pool_accounts.stake_pool.pubkey();
|
|
let (_, stake_withdraw_bump_seed) =
|
|
find_withdraw_authority_program_address(&id(), &stake_pool_pubkey);
|
|
|
|
let mut stake_pool = StakePool {
|
|
account_type: AccountType::StakePool,
|
|
manager: stake_pool_accounts.manager.pubkey(),
|
|
staker: stake_pool_accounts.staker.pubkey(),
|
|
stake_deposit_authority: stake_pool_accounts.stake_deposit_authority,
|
|
stake_withdraw_bump_seed,
|
|
validator_list: stake_pool_accounts.validator_list.pubkey(),
|
|
reserve_stake: stake_pool_accounts.reserve_stake.pubkey(),
|
|
pool_mint: stake_pool_accounts.pool_mint.pubkey(),
|
|
manager_fee_account: stake_pool_accounts.pool_fee_account.pubkey(),
|
|
token_program_id: spl_token::id(),
|
|
total_lamports: 0,
|
|
pool_token_supply: 0,
|
|
last_update_epoch: 0,
|
|
lockup: stake::state::Lockup::default(),
|
|
epoch_fee: stake_pool_accounts.epoch_fee,
|
|
next_epoch_fee: None,
|
|
preferred_deposit_validator_vote_address: None,
|
|
preferred_withdraw_validator_vote_address: None,
|
|
stake_deposit_fee: Fee::default(),
|
|
sol_deposit_fee: Fee::default(),
|
|
stake_withdrawal_fee: Fee::default(),
|
|
next_stake_withdrawal_fee: None,
|
|
stake_referral_fee: 0,
|
|
sol_referral_fee: 0,
|
|
sol_deposit_authority: None,
|
|
sol_withdraw_authority: None,
|
|
sol_withdrawal_fee: Fee::default(),
|
|
next_sol_withdrawal_fee: None,
|
|
last_epoch_pool_token_supply: 0,
|
|
last_epoch_total_lamports: 0,
|
|
};
|
|
|
|
let mut validator_list = ValidatorList::new(max_validators);
|
|
validator_list.validators = vec![];
|
|
|
|
let authorized_voter = Pubkey::new_unique();
|
|
let authorized_withdrawer = Pubkey::new_unique();
|
|
let commission = 1;
|
|
|
|
let meta = stake::state::Meta {
|
|
rent_exempt_reserve: STAKE_ACCOUNT_RENT_EXEMPTION,
|
|
authorized: stake::state::Authorized {
|
|
staker: stake_pool_accounts.withdraw_authority,
|
|
withdrawer: stake_pool_accounts.withdraw_authority,
|
|
},
|
|
lockup: stake::state::Lockup::default(),
|
|
};
|
|
|
|
for _ in 0..max_validators {
|
|
// create vote account
|
|
let vote_pubkey = Pubkey::new_unique();
|
|
vote_account_pubkeys.push(vote_pubkey);
|
|
let node_pubkey = Pubkey::new_unique();
|
|
let vote_state = VoteStateVersions::new_current(VoteState::new(
|
|
&VoteInit {
|
|
node_pubkey,
|
|
authorized_voter,
|
|
authorized_withdrawer,
|
|
commission,
|
|
},
|
|
&Clock::default(),
|
|
));
|
|
let vote_account = Account::create(
|
|
ACCOUNT_RENT_EXEMPTION,
|
|
bincode::serialize::<VoteStateVersions>(&vote_state).unwrap(),
|
|
solana_vote_program::id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(vote_pubkey, vote_account);
|
|
}
|
|
|
|
for vote_account_address in vote_account_pubkeys.iter().take(num_validators as usize) {
|
|
// create validator stake account
|
|
let stake = stake::state::Stake {
|
|
delegation: stake::state::Delegation {
|
|
voter_pubkey: *vote_account_address,
|
|
stake: stake_amount,
|
|
activation_epoch: 0,
|
|
deactivation_epoch: u64::MAX,
|
|
warmup_cooldown_rate: 0.25, // default
|
|
},
|
|
credits_observed: 0,
|
|
};
|
|
|
|
let stake_account = Account::create(
|
|
stake_amount + STAKE_ACCOUNT_RENT_EXEMPTION,
|
|
bincode::serialize::<stake::state::StakeState>(&stake::state::StakeState::Stake(
|
|
meta, stake,
|
|
))
|
|
.unwrap(),
|
|
stake::program::id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
|
|
let (stake_address, _) =
|
|
find_stake_program_address(&id(), vote_account_address, &stake_pool_pubkey);
|
|
program_test.add_account(stake_address, stake_account);
|
|
let active_stake_lamports = stake_amount - MINIMUM_ACTIVE_STAKE;
|
|
// add to validator list
|
|
validator_list.validators.push(ValidatorStakeInfo {
|
|
status: StakeStatus::Active,
|
|
vote_account_address: *vote_account_address,
|
|
active_stake_lamports,
|
|
transient_stake_lamports: 0,
|
|
last_update_epoch: 0,
|
|
transient_seed_suffix_start: 0,
|
|
transient_seed_suffix_end: 0,
|
|
});
|
|
|
|
stake_pool.total_lamports += active_stake_lamports;
|
|
stake_pool.pool_token_supply += active_stake_lamports;
|
|
}
|
|
|
|
let mut validator_list_bytes = validator_list.try_to_vec().unwrap();
|
|
|
|
// add extra room if needed
|
|
for _ in num_validators..max_validators {
|
|
validator_list_bytes.append(&mut ValidatorStakeInfo::default().try_to_vec().unwrap());
|
|
}
|
|
|
|
let reserve_stake_account = Account::create(
|
|
stake_amount + STAKE_ACCOUNT_RENT_EXEMPTION,
|
|
bincode::serialize::<stake::state::StakeState>(&stake::state::StakeState::Initialized(
|
|
meta,
|
|
))
|
|
.unwrap(),
|
|
stake::program::id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(
|
|
stake_pool_accounts.reserve_stake.pubkey(),
|
|
reserve_stake_account,
|
|
);
|
|
|
|
let mut stake_pool_bytes = stake_pool.try_to_vec().unwrap();
|
|
// more room for optionals
|
|
stake_pool_bytes.extend_from_slice(&Pubkey::default().to_bytes());
|
|
stake_pool_bytes.extend_from_slice(&Pubkey::default().to_bytes());
|
|
let stake_pool_account = Account::create(
|
|
ACCOUNT_RENT_EXEMPTION,
|
|
stake_pool_bytes,
|
|
id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(stake_pool_pubkey, stake_pool_account);
|
|
|
|
let validator_list_account = Account::create(
|
|
ACCOUNT_RENT_EXEMPTION,
|
|
validator_list_bytes,
|
|
id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(
|
|
stake_pool_accounts.validator_list.pubkey(),
|
|
validator_list_account,
|
|
);
|
|
|
|
let mut mint_vec = vec![0u8; Mint::LEN];
|
|
let mint = Mint {
|
|
mint_authority: COption::Some(stake_pool_accounts.withdraw_authority),
|
|
supply: stake_pool.pool_token_supply,
|
|
decimals: 9,
|
|
is_initialized: true,
|
|
freeze_authority: COption::None,
|
|
};
|
|
Pack::pack(mint, &mut mint_vec).unwrap();
|
|
let stake_pool_mint = Account::create(
|
|
ACCOUNT_RENT_EXEMPTION,
|
|
mint_vec,
|
|
spl_token::id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(stake_pool_accounts.pool_mint.pubkey(), stake_pool_mint);
|
|
|
|
let mut fee_account_vec = vec![0u8; SplAccount::LEN];
|
|
let fee_account_data = SplAccount {
|
|
mint: stake_pool_accounts.pool_mint.pubkey(),
|
|
owner: stake_pool_accounts.manager.pubkey(),
|
|
amount: 0,
|
|
delegate: COption::None,
|
|
state: SplAccountState::Initialized,
|
|
is_native: COption::None,
|
|
delegated_amount: 0,
|
|
close_authority: COption::None,
|
|
};
|
|
Pack::pack(fee_account_data, &mut fee_account_vec).unwrap();
|
|
let fee_account = Account::create(
|
|
ACCOUNT_RENT_EXEMPTION,
|
|
fee_account_vec,
|
|
spl_token::id(),
|
|
false,
|
|
Epoch::default(),
|
|
);
|
|
program_test.add_account(stake_pool_accounts.pool_fee_account.pubkey(), fee_account);
|
|
|
|
let mut context = program_test.start_with_context().await;
|
|
|
|
let vote_pubkey = vote_account_pubkeys[HUGE_POOL_SIZE as usize - 1];
|
|
// make stake account
|
|
let user = Keypair::new();
|
|
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,
|
|
stake_amount,
|
|
)
|
|
.await;
|
|
|
|
delegate_stake_account(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&deposit_stake.pubkey(),
|
|
&user,
|
|
&vote_pubkey,
|
|
)
|
|
.await;
|
|
|
|
// make pool token account
|
|
let pool_token_account = Keypair::new();
|
|
create_token_account(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&pool_token_account,
|
|
&stake_pool_accounts.pool_mint.pubkey(),
|
|
&user.pubkey(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
(
|
|
context,
|
|
stake_pool_accounts,
|
|
vote_account_pubkeys,
|
|
vote_pubkey,
|
|
user,
|
|
deposit_stake.pubkey(),
|
|
pool_token_account.pubkey(),
|
|
)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn update() {
|
|
let (mut context, stake_pool_accounts, vote_account_pubkeys, _, _, _, _) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
|
|
|
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,
|
|
&vote_account_pubkeys[0..MAX_VALIDATORS_TO_UPDATE],
|
|
0,
|
|
/* no_merge = */ false,
|
|
)],
|
|
Some(&context.payer.pubkey()),
|
|
&[&context.payer],
|
|
context.last_blockhash,
|
|
);
|
|
let error = context
|
|
.banks_client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.err();
|
|
assert!(error.is_none());
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[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(),
|
|
)],
|
|
Some(&context.payer.pubkey()),
|
|
&[&context.payer],
|
|
context.last_blockhash,
|
|
);
|
|
let error = context
|
|
.banks_client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.err();
|
|
assert!(error.is_none());
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[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());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn remove_validator_from_pool() {
|
|
let (mut context, stake_pool_accounts, vote_account_pubkeys, _, _, _, _) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, MINIMUM_ACTIVE_STAKE).await;
|
|
|
|
let first_vote = vote_account_pubkeys[0];
|
|
let (stake_address, _) =
|
|
find_stake_program_address(&id(), &first_vote, &stake_pool_accounts.stake_pool.pubkey());
|
|
let transient_stake_seed = u64::MAX;
|
|
let (transient_stake_address, _) = find_transient_stake_program_address(
|
|
&id(),
|
|
&first_vote,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
transient_stake_seed,
|
|
);
|
|
|
|
let new_authority = Pubkey::new_unique();
|
|
let destination_stake = Keypair::new();
|
|
let error = stake_pool_accounts
|
|
.remove_validator_from_pool(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&new_authority,
|
|
&stake_address,
|
|
&transient_stake_address,
|
|
&destination_stake,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
let middle_index = HUGE_POOL_SIZE as usize / 2;
|
|
let middle_vote = vote_account_pubkeys[middle_index];
|
|
let (stake_address, _) = find_stake_program_address(
|
|
&id(),
|
|
&middle_vote,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
);
|
|
let (transient_stake_address, _) = find_transient_stake_program_address(
|
|
&id(),
|
|
&middle_vote,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
transient_stake_seed,
|
|
);
|
|
|
|
let new_authority = Pubkey::new_unique();
|
|
let destination_stake = Keypair::new();
|
|
let error = stake_pool_accounts
|
|
.remove_validator_from_pool(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&new_authority,
|
|
&stake_address,
|
|
&transient_stake_address,
|
|
&destination_stake,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
let last_index = HUGE_POOL_SIZE as usize - 1;
|
|
let last_vote = vote_account_pubkeys[last_index];
|
|
let (stake_address, _) =
|
|
find_stake_program_address(&id(), &last_vote, &stake_pool_accounts.stake_pool.pubkey());
|
|
let (transient_stake_address, _) = find_transient_stake_program_address(
|
|
&id(),
|
|
&last_vote,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
transient_stake_seed,
|
|
);
|
|
|
|
let new_authority = Pubkey::new_unique();
|
|
let destination_stake = Keypair::new();
|
|
let error = stake_pool_accounts
|
|
.remove_validator_from_pool(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&new_authority,
|
|
&stake_address,
|
|
&transient_stake_address,
|
|
&destination_stake,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
let validator_list = get_account(
|
|
&mut context.banks_client,
|
|
&stake_pool_accounts.validator_list.pubkey(),
|
|
)
|
|
.await;
|
|
let validator_list =
|
|
try_from_slice_unchecked::<ValidatorList>(validator_list.data.as_slice()).unwrap();
|
|
let first_element = &validator_list.validators[0];
|
|
assert_eq!(first_element.status, StakeStatus::ReadyForRemoval);
|
|
assert_eq!(first_element.active_stake_lamports, 0);
|
|
assert_eq!(first_element.transient_stake_lamports, 0);
|
|
|
|
let middle_element = &validator_list.validators[middle_index];
|
|
assert_eq!(middle_element.status, StakeStatus::ReadyForRemoval);
|
|
assert_eq!(middle_element.active_stake_lamports, 0);
|
|
assert_eq!(middle_element.transient_stake_lamports, 0);
|
|
|
|
let last_element = &validator_list.validators[last_index];
|
|
assert_eq!(last_element.status, StakeStatus::ReadyForRemoval);
|
|
assert_eq!(last_element.active_stake_lamports, 0);
|
|
assert_eq!(last_element.transient_stake_lamports, 0);
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[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());
|
|
|
|
let validator_list = get_account(
|
|
&mut context.banks_client,
|
|
&stake_pool_accounts.validator_list.pubkey(),
|
|
)
|
|
.await;
|
|
let validator_list =
|
|
try_from_slice_unchecked::<ValidatorList>(validator_list.data.as_slice()).unwrap();
|
|
assert_eq!(validator_list.validators.len() as u32, HUGE_POOL_SIZE - 3);
|
|
// assert they're gone
|
|
assert!(!validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == first_vote));
|
|
assert!(!validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == middle_vote));
|
|
assert!(!validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == last_vote));
|
|
|
|
// but that we didn't remove too many
|
|
assert!(validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == vote_account_pubkeys[1]));
|
|
assert!(validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == vote_account_pubkeys[middle_index - 1]));
|
|
assert!(validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == vote_account_pubkeys[middle_index + 1]));
|
|
assert!(validator_list
|
|
.validators
|
|
.iter()
|
|
.any(|x| x.vote_account_address == vote_account_pubkeys[last_index - 1]));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn add_validator_to_pool() {
|
|
let (mut context, stake_pool_accounts, _, test_vote_address, _, _, _) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE - 1, STAKE_AMOUNT).await;
|
|
|
|
let last_index = HUGE_POOL_SIZE as usize - 1;
|
|
let stake_pool_pubkey = stake_pool_accounts.stake_pool.pubkey();
|
|
let (stake_address, _) =
|
|
find_stake_program_address(&id(), &test_vote_address, &stake_pool_pubkey);
|
|
|
|
let error = stake_pool_accounts
|
|
.add_validator_to_pool(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&stake_address,
|
|
&test_vote_address,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
let validator_list = get_account(
|
|
&mut context.banks_client,
|
|
&stake_pool_accounts.validator_list.pubkey(),
|
|
)
|
|
.await;
|
|
let validator_list =
|
|
try_from_slice_unchecked::<ValidatorList>(validator_list.data.as_slice()).unwrap();
|
|
assert_eq!(validator_list.validators.len(), last_index + 1);
|
|
let last_element = validator_list.validators[last_index];
|
|
assert_eq!(last_element.status, StakeStatus::Active);
|
|
assert_eq!(last_element.active_stake_lamports, 0);
|
|
assert_eq!(last_element.transient_stake_lamports, 0);
|
|
assert_eq!(last_element.vote_account_address, test_vote_address);
|
|
|
|
let transient_stake_seed = u64::MAX;
|
|
let (transient_stake_address, _) = find_transient_stake_program_address(
|
|
&id(),
|
|
&test_vote_address,
|
|
&stake_pool_pubkey,
|
|
transient_stake_seed,
|
|
);
|
|
let increase_amount = MINIMUM_ACTIVE_STAKE;
|
|
let error = stake_pool_accounts
|
|
.increase_validator_stake(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&transient_stake_address,
|
|
&test_vote_address,
|
|
increase_amount,
|
|
transient_stake_seed,
|
|
)
|
|
.await;
|
|
assert!(error.is_none(), "{:?}", error);
|
|
|
|
let validator_list = get_account(
|
|
&mut context.banks_client,
|
|
&stake_pool_accounts.validator_list.pubkey(),
|
|
)
|
|
.await;
|
|
let validator_list =
|
|
try_from_slice_unchecked::<ValidatorList>(validator_list.data.as_slice()).unwrap();
|
|
let last_element = validator_list.validators[last_index];
|
|
assert_eq!(last_element.status, StakeStatus::Active);
|
|
assert_eq!(last_element.active_stake_lamports, 0);
|
|
assert_eq!(
|
|
last_element.transient_stake_lamports,
|
|
increase_amount + STAKE_ACCOUNT_RENT_EXEMPTION
|
|
);
|
|
assert_eq!(last_element.vote_account_address, test_vote_address);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn set_preferred() {
|
|
let (mut context, stake_pool_accounts, _, vote_account_address, _, _, _) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
|
|
|
let error = stake_pool_accounts
|
|
.set_preferred_validator(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
PreferredValidatorType::Deposit,
|
|
Some(vote_account_address),
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
let error = stake_pool_accounts
|
|
.set_preferred_validator(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
PreferredValidatorType::Withdraw,
|
|
Some(vote_account_address),
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
let stake_pool = get_account(
|
|
&mut context.banks_client,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
)
|
|
.await;
|
|
let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap();
|
|
|
|
assert_eq!(
|
|
stake_pool.preferred_deposit_validator_vote_address,
|
|
Some(vote_account_address)
|
|
);
|
|
assert_eq!(
|
|
stake_pool.preferred_withdraw_validator_vote_address,
|
|
Some(vote_account_address)
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deposit_stake() {
|
|
let (mut context, stake_pool_accounts, _, vote_pubkey, user, stake_pubkey, pool_account_pubkey) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
|
|
|
let (stake_address, _) = find_stake_program_address(
|
|
&id(),
|
|
&vote_pubkey,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
);
|
|
|
|
let error = stake_pool_accounts
|
|
.deposit_stake(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&stake_pubkey,
|
|
&pool_account_pubkey,
|
|
&stake_address,
|
|
&user,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn withdraw() {
|
|
let (mut context, stake_pool_accounts, _, vote_pubkey, user, stake_pubkey, pool_account_pubkey) =
|
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
|
|
|
let (stake_address, _) = find_stake_program_address(
|
|
&id(),
|
|
&vote_pubkey,
|
|
&stake_pool_accounts.stake_pool.pubkey(),
|
|
);
|
|
|
|
let error = stake_pool_accounts
|
|
.deposit_stake(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&stake_pubkey,
|
|
&pool_account_pubkey,
|
|
&stake_address,
|
|
&user,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
// Create stake account to withdraw to
|
|
let user_stake_recipient = Keypair::new();
|
|
create_blank_stake_account(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&user_stake_recipient,
|
|
)
|
|
.await;
|
|
|
|
let error = stake_pool_accounts
|
|
.withdraw_stake(
|
|
&mut context.banks_client,
|
|
&context.payer,
|
|
&context.last_blockhash,
|
|
&user_stake_recipient.pubkey(),
|
|
&user,
|
|
&pool_account_pubkey,
|
|
&stake_address,
|
|
&user.pubkey(),
|
|
STAKE_AMOUNT,
|
|
)
|
|
.await;
|
|
assert!(error.is_none(), "{:?}", error);
|
|
}
|