solana-program-library/stake-pool/program/tests/update_stake_pool_balance.rs

347 lines
11 KiB
Rust

#![cfg(feature = "test-bpf")]
mod helpers;
use {
helpers::*,
solana_program::{
borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey,
},
solana_program_test::*,
solana_sdk::{
signature::{Keypair, Signer},
transaction::TransactionError,
},
spl_stake_pool::{error::StakePoolError, state::StakePool},
};
async fn setup() -> (
ProgramTestContext,
StakePoolAccounts,
Vec<ValidatorStakeAccount>,
) {
let mut context = program_test().start_with_context().await;
let stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
1,
)
.await
.unwrap();
// Add several accounts
let mut stake_accounts: Vec<ValidatorStakeAccount> = vec![];
const STAKE_ACCOUNTS: u64 = 3;
for _ in 0..STAKE_ACCOUNTS {
let validator_stake_account = simple_add_validator_to_pool(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
&stake_pool_accounts,
)
.await;
let _deposit_info = simple_deposit_stake(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
&stake_pool_accounts,
&validator_stake_account,
TEST_STAKE_AMOUNT,
)
.await
.unwrap();
stake_accounts.push(validator_stake_account);
}
(context, stake_pool_accounts, stake_accounts)
}
#[tokio::test]
async fn success() {
let (mut context, stake_pool_accounts, stake_accounts) = setup().await;
let pre_fee = get_token_balance(
&mut context.banks_client,
&stake_pool_accounts.pool_fee_account.pubkey(),
)
.await;
let pre_balance = get_validator_list_sum(
&mut context.banks_client,
&stake_pool_accounts.reserve_stake.pubkey(),
&stake_pool_accounts.validator_list.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::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(pre_balance, stake_pool.total_lamports);
let pre_token_supply = get_token_supply(
&mut context.banks_client,
&stake_pool_accounts.pool_mint.pubkey(),
)
.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
const VOTE_CREDITS: u64 = 1_000;
for stake_account in &stake_accounts {
context.increment_vote_account_credits(&stake_account.vote.pubkey(), VOTE_CREDITS);
}
// Update epoch
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
context
.warp_to_slot(first_normal_slot + slots_per_epoch)
.unwrap();
// Update list and pool
let error = 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;
assert!(error.is_none());
// Check fee
let post_balance = get_validator_list_sum(
&mut context.banks_client,
&stake_pool_accounts.reserve_stake.pubkey(),
&stake_pool_accounts.validator_list.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::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(post_balance, stake_pool.total_lamports);
let post_fee = get_token_balance(
&mut context.banks_client,
&stake_pool_accounts.pool_fee_account.pubkey(),
)
.await;
let pool_token_supply = get_token_supply(
&mut context.banks_client,
&stake_pool_accounts.pool_mint.pubkey(),
)
.await;
let actual_fee = post_fee - pre_fee;
assert_eq!(pool_token_supply - pre_token_supply, actual_fee);
let expected_fee_lamports = (post_balance - pre_balance) * stake_pool.epoch_fee.numerator
/ stake_pool.epoch_fee.denominator;
let actual_fee_lamports = stake_pool.calc_pool_tokens_for_deposit(actual_fee).unwrap();
assert_eq!(actual_fee_lamports, expected_fee_lamports);
let expected_fee = expected_fee_lamports * pool_token_supply / post_balance;
assert_eq!(expected_fee, actual_fee);
assert_eq!(pool_token_supply, stake_pool.pool_token_supply);
assert_eq!(pre_token_supply, stake_pool.last_epoch_pool_token_supply);
assert_eq!(pre_balance, stake_pool.last_epoch_total_lamports);
}
#[tokio::test]
async fn success_ignoring_extra_lamports() {
let (mut context, stake_pool_accounts, stake_accounts) = setup().await;
let pre_balance = get_validator_list_sum(
&mut context.banks_client,
&stake_pool_accounts.reserve_stake.pubkey(),
&stake_pool_accounts.validator_list.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::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(pre_balance, stake_pool.total_lamports);
let pre_token_supply = get_token_supply(
&mut context.banks_client,
&stake_pool_accounts.pool_mint.pubkey(),
)
.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, should not be taken into account
const EXTRA_STAKE_AMOUNT: u64 = 1_000_000;
for stake_account in &stake_accounts {
transfer(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
&stake_account.stake_account,
EXTRA_STAKE_AMOUNT,
)
.await;
}
// Update epoch
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
context
.warp_to_slot(first_normal_slot + slots_per_epoch)
.unwrap();
// Update list and pool
let error = 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;
assert!(error.is_none());
// Check fee
let post_balance = get_validator_list_sum(
&mut context.banks_client,
&stake_pool_accounts.reserve_stake.pubkey(),
&stake_pool_accounts.validator_list.pubkey(),
)
.await;
assert_eq!(post_balance, pre_balance);
let pool_token_supply = get_token_supply(
&mut context.banks_client,
&stake_pool_accounts.pool_mint.pubkey(),
)
.await;
assert_eq!(pool_token_supply, pre_token_supply);
}
#[tokio::test]
async fn fail_with_wrong_validator_list() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let mut stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
let wrong_validator_list = Keypair::new();
stake_pool_accounts.validator_list = wrong_validator_list;
let error = stake_pool_accounts
.update_stake_pool_balance(&mut banks_client, &payer, &recent_blockhash)
.await
.unwrap()
.unwrap();
match error {
TransactionError::InstructionError(
_,
InstructionError::Custom(error_index),
) => {
let program_error = StakePoolError::InvalidValidatorStakeList as u32;
assert_eq!(error_index, program_error);
}
_ => panic!("Wrong error occurs while try to update pool balance with wrong validator stake list account"),
}
}
#[tokio::test]
async fn fail_with_wrong_pool_fee_account() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let mut stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
let wrong_fee_account = Keypair::new();
stake_pool_accounts.pool_fee_account = wrong_fee_account;
let error = stake_pool_accounts
.update_stake_pool_balance(&mut banks_client, &payer, &recent_blockhash)
.await
.unwrap()
.unwrap();
match error {
TransactionError::InstructionError(
_,
InstructionError::Custom(error_index),
) => {
let program_error = StakePoolError::InvalidFeeAccount as u32;
assert_eq!(error_index, program_error);
}
_ => panic!("Wrong error occurs while try to update pool balance with wrong validator stake list account"),
}
}
#[tokio::test]
async fn fail_with_wrong_reserve() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let mut stake_pool_accounts = StakePoolAccounts::new();
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
let wrong_reserve_stake = Keypair::new();
stake_pool_accounts.reserve_stake = wrong_reserve_stake;
let error = stake_pool_accounts
.update_stake_pool_balance(&mut banks_client, &payer, &recent_blockhash)
.await
.unwrap()
.unwrap();
assert_eq!(
error,
TransactionError::InstructionError(
0,
InstructionError::Custom(StakePoolError::InvalidProgramAddress as u32),
)
);
}
#[tokio::test]
async fn test_update_stake_pool_balance_with_uninitialized_validator_list() {} // TODO
#[tokio::test]
async fn test_update_stake_pool_balance_with_out_of_dated_validators_balances() {} // TODO