stake-pool: Add ability to remove a validator that has deactivating transient stake (#1624)
* Add status enum * Add ability to remove validator with transient stake * Only account validator stake if active * Fix merge conflicts
This commit is contained in:
parent
7d666b86ce
commit
14bdbdc3ac
|
@ -519,7 +519,7 @@ pub fn update_stake_pool_balance(
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new(*stake_pool, false),
|
AccountMeta::new(*stake_pool, false),
|
||||||
AccountMeta::new_readonly(*withdraw_authority, false),
|
AccountMeta::new_readonly(*withdraw_authority, false),
|
||||||
AccountMeta::new_readonly(*validator_list_storage, false),
|
AccountMeta::new(*validator_list_storage, false),
|
||||||
AccountMeta::new_readonly(*reserve_stake, false),
|
AccountMeta::new_readonly(*reserve_stake, false),
|
||||||
AccountMeta::new(*manager_fee_account, false),
|
AccountMeta::new(*manager_fee_account, false),
|
||||||
AccountMeta::new(*stake_pool_mint, false),
|
AccountMeta::new(*stake_pool_mint, false),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use {
|
||||||
find_deposit_authority_program_address,
|
find_deposit_authority_program_address,
|
||||||
instruction::StakePoolInstruction,
|
instruction::StakePoolInstruction,
|
||||||
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
|
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
|
||||||
state::{AccountType, Fee, StakePool, ValidatorList, ValidatorStakeInfo},
|
state::{AccountType, Fee, StakePool, StakeStatus, ValidatorList, ValidatorStakeInfo},
|
||||||
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED,
|
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED,
|
||||||
},
|
},
|
||||||
borsh::{BorshDeserialize, BorshSerialize},
|
borsh::{BorshDeserialize, BorshSerialize},
|
||||||
|
@ -15,7 +15,7 @@ use {
|
||||||
solana_program::{
|
solana_program::{
|
||||||
account_info::next_account_info,
|
account_info::next_account_info,
|
||||||
account_info::AccountInfo,
|
account_info::AccountInfo,
|
||||||
clock::Clock,
|
clock::{Clock, Epoch},
|
||||||
decode_error::DecodeError,
|
decode_error::DecodeError,
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
msg,
|
msg,
|
||||||
|
@ -747,6 +747,7 @@ impl Processor {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
validator_list.validators.push(ValidatorStakeInfo {
|
validator_list.validators.push(ValidatorStakeInfo {
|
||||||
|
status: StakeStatus::Active,
|
||||||
vote_account_address,
|
vote_account_address,
|
||||||
stake_lamports: stake_lamports.saturating_sub(minimum_lamport_amount),
|
stake_lamports: stake_lamports.saturating_sub(minimum_lamport_amount),
|
||||||
last_update_epoch: clock.epoch,
|
last_update_epoch: clock.epoch,
|
||||||
|
@ -814,20 +815,16 @@ impl Processor {
|
||||||
transient_stake_account_info.key,
|
transient_stake_account_info.key,
|
||||||
&vote_account_address,
|
&vote_account_address,
|
||||||
)?;
|
)?;
|
||||||
// check that the transient stake account doesn't exist
|
|
||||||
if get_stake_state(transient_stake_account_info).is_ok() {
|
let maybe_validator_list_entry = validator_list.find_mut(&vote_account_address);
|
||||||
|
if maybe_validator_list_entry.is_none() {
|
||||||
msg!(
|
msg!(
|
||||||
"Transient stake {} exists, can't remove stake {} on validator {}",
|
"Vote account {} not found in stake pool",
|
||||||
transient_stake_account_info.key,
|
|
||||||
stake_account_info.key,
|
|
||||||
vote_account_address
|
vote_account_address
|
||||||
);
|
);
|
||||||
return Err(StakePoolError::WrongStakeState.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validator_list.contains(&vote_account_address) {
|
|
||||||
return Err(StakePoolError::ValidatorNotFound.into());
|
return Err(StakePoolError::ValidatorNotFound.into());
|
||||||
}
|
}
|
||||||
|
let mut validator_list_entry = maybe_validator_list_entry.unwrap();
|
||||||
|
|
||||||
let stake_lamports = **stake_account_info.lamports.borrow();
|
let stake_lamports = **stake_account_info.lamports.borrow();
|
||||||
let required_lamports = minimum_stake_lamports(&meta);
|
let required_lamports = minimum_stake_lamports(&meta);
|
||||||
|
@ -840,6 +837,24 @@ impl Processor {
|
||||||
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check that the transient stake account doesn't exist
|
||||||
|
let new_status = if let Ok((_meta, stake)) = get_stake_state(transient_stake_account_info) {
|
||||||
|
if stake.delegation.deactivation_epoch == Epoch::MAX {
|
||||||
|
msg!(
|
||||||
|
"Transient stake {} activating, can't remove stake {} on validator {}",
|
||||||
|
transient_stake_account_info.key,
|
||||||
|
stake_account_info.key,
|
||||||
|
vote_account_address
|
||||||
|
);
|
||||||
|
return Err(StakePoolError::WrongStakeState.into());
|
||||||
|
} else {
|
||||||
|
// stake is deactivating, mark the entry as such
|
||||||
|
StakeStatus::DeactivatingTransient
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StakeStatus::ReadyForRemoval
|
||||||
|
};
|
||||||
|
|
||||||
Self::stake_authorize_signed(
|
Self::stake_authorize_signed(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
stake_account_info.clone(),
|
stake_account_info.clone(),
|
||||||
|
@ -851,9 +866,13 @@ impl Processor {
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
validator_list
|
match new_status {
|
||||||
.validators
|
StakeStatus::DeactivatingTransient => validator_list_entry.status = new_status,
|
||||||
.retain(|item| item.vote_account_address != vote_account_address);
|
StakeStatus::ReadyForRemoval => validator_list
|
||||||
|
.validators
|
||||||
|
.retain(|item| item.vote_account_address != vote_account_address),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1239,6 +1258,11 @@ impl Processor {
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
if validator_stake_record.status == StakeStatus::DeactivatingTransient {
|
||||||
|
// the validator stake was previously removed, and
|
||||||
|
// now this entry can be removed totally
|
||||||
|
validator_stake_record.status = StakeStatus::ReadyForRemoval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(stake_program::StakeState::Stake(_, stake)) => {
|
Some(stake_program::StakeState::Stake(_, stake)) => {
|
||||||
|
@ -1257,6 +1281,11 @@ impl Processor {
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
if validator_stake_record.status == StakeStatus::DeactivatingTransient {
|
||||||
|
// the validator stake was previously removed, and
|
||||||
|
// now this entry can be removed totally
|
||||||
|
validator_stake_record.status = StakeStatus::ReadyForRemoval;
|
||||||
|
}
|
||||||
} else if stake.delegation.activation_epoch < clock.epoch {
|
} else if stake.delegation.activation_epoch < clock.epoch {
|
||||||
if let Some(stake_program::StakeState::Stake(_, validator_stake)) =
|
if let Some(stake_program::StakeState::Stake(_, validator_stake)) =
|
||||||
validator_stake_state
|
validator_stake_state
|
||||||
|
@ -1298,15 +1327,19 @@ impl Processor {
|
||||||
// * any other state / not a stake -> error state, but account for transient stake
|
// * any other state / not a stake -> error state, but account for transient stake
|
||||||
match validator_stake_state {
|
match validator_stake_state {
|
||||||
Some(stake_program::StakeState::Stake(meta, _)) => {
|
Some(stake_program::StakeState::Stake(meta, _)) => {
|
||||||
stake_lamports += validator_stake_info
|
if validator_stake_record.status == StakeStatus::Active {
|
||||||
.lamports()
|
stake_lamports += validator_stake_info
|
||||||
.saturating_sub(minimum_stake_lamports(&meta));
|
.lamports()
|
||||||
|
.saturating_sub(minimum_stake_lamports(&meta));
|
||||||
|
} else {
|
||||||
|
msg!("Validator stake account no longer part of the pool, ignoring");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(stake_program::StakeState::Initialized(_))
|
Some(stake_program::StakeState::Initialized(_))
|
||||||
| Some(stake_program::StakeState::Uninitialized)
|
| Some(stake_program::StakeState::Uninitialized)
|
||||||
| Some(stake_program::StakeState::RewardsPool)
|
| Some(stake_program::StakeState::RewardsPool)
|
||||||
| None => {
|
| None => {
|
||||||
msg!("Validator stake account no longer part of the pool, not considering");
|
msg!("Validator stake account no longer part of the pool, ignoring");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1355,7 +1388,7 @@ impl Processor {
|
||||||
return Err(ProgramError::IncorrectProgramId);
|
return Err(ProgramError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let validator_list =
|
let mut validator_list =
|
||||||
try_from_slice_unchecked::<ValidatorList>(&validator_list_info.data.borrow())?;
|
try_from_slice_unchecked::<ValidatorList>(&validator_list_info.data.borrow())?;
|
||||||
if !validator_list.is_valid() {
|
if !validator_list.is_valid() {
|
||||||
return Err(StakePoolError::InvalidState.into());
|
return Err(StakePoolError::InvalidState.into());
|
||||||
|
@ -1375,7 +1408,7 @@ impl Processor {
|
||||||
msg!("Reserve stake account in unknown state, aborting");
|
msg!("Reserve stake account in unknown state, aborting");
|
||||||
return Err(StakePoolError::WrongStakeState.into());
|
return Err(StakePoolError::WrongStakeState.into());
|
||||||
};
|
};
|
||||||
for validator_stake_record in validator_list.validators {
|
for validator_stake_record in &validator_list.validators {
|
||||||
if validator_stake_record.last_update_epoch < clock.epoch {
|
if validator_stake_record.last_update_epoch < clock.epoch {
|
||||||
return Err(StakePoolError::StakeListOutOfDate.into());
|
return Err(StakePoolError::StakeListOutOfDate.into());
|
||||||
}
|
}
|
||||||
|
@ -1406,6 +1439,10 @@ impl Processor {
|
||||||
.checked_add(fee)
|
.checked_add(fee)
|
||||||
.ok_or(StakePoolError::CalculationFailure)?;
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
}
|
}
|
||||||
|
validator_list
|
||||||
|
.validators
|
||||||
|
.retain(|item| item.status != StakeStatus::ReadyForRemoval);
|
||||||
|
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
||||||
stake_pool.total_stake_lamports = total_stake_lamports;
|
stake_pool.total_stake_lamports = total_stake_lamports;
|
||||||
stake_pool.last_update_epoch = clock.epoch;
|
stake_pool.last_update_epoch = clock.epoch;
|
||||||
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
||||||
|
@ -1507,6 +1544,11 @@ impl Processor {
|
||||||
.find_mut(&vote_account_address)
|
.find_mut(&vote_account_address)
|
||||||
.ok_or(StakePoolError::ValidatorNotFound)?;
|
.ok_or(StakePoolError::ValidatorNotFound)?;
|
||||||
|
|
||||||
|
if validator_list_item.status != StakeStatus::Active {
|
||||||
|
msg!("Validator is marked for removal and no longer accepting deposits");
|
||||||
|
return Err(StakePoolError::ValidatorNotFound.into());
|
||||||
|
}
|
||||||
|
|
||||||
let stake_lamports = **stake_info.lamports.borrow();
|
let stake_lamports = **stake_info.lamports.borrow();
|
||||||
let new_pool_tokens = stake_pool
|
let new_pool_tokens = stake_pool
|
||||||
.calc_pool_tokens_for_deposit(stake_lamports)
|
.calc_pool_tokens_for_deposit(stake_lamports)
|
||||||
|
@ -1684,6 +1726,11 @@ impl Processor {
|
||||||
.find_mut(&vote_account_address)
|
.find_mut(&vote_account_address)
|
||||||
.ok_or(StakePoolError::ValidatorNotFound)?;
|
.ok_or(StakePoolError::ValidatorNotFound)?;
|
||||||
|
|
||||||
|
if validator_list_item.status != StakeStatus::Active {
|
||||||
|
msg!("Validator is marked for removal and no longer allowing withdrawals");
|
||||||
|
return Err(StakePoolError::ValidatorNotFound.into());
|
||||||
|
}
|
||||||
|
|
||||||
let required_lamports = minimum_stake_lamports(&meta);
|
let required_lamports = minimum_stake_lamports(&meta);
|
||||||
let current_lamports = stake_split_from.lamports();
|
let current_lamports = stake_split_from.lamports();
|
||||||
let remaining_lamports = current_lamports.saturating_sub(withdraw_lamports);
|
let remaining_lamports = current_lamports.saturating_sub(withdraw_lamports);
|
||||||
|
|
|
@ -285,10 +285,32 @@ pub struct ValidatorList {
|
||||||
pub validators: Vec<ValidatorStakeInfo>,
|
pub validators: Vec<ValidatorStakeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Status of the stake account in the validator list, for accounting
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
|
pub enum StakeStatus {
|
||||||
|
/// Stake account is active, there may be a transient stake as well
|
||||||
|
Active,
|
||||||
|
/// Only transient stake account exists, when a transient stake is
|
||||||
|
/// deactivating during validator removal
|
||||||
|
DeactivatingTransient,
|
||||||
|
/// No more validator stake accounts exist, entry ready for removal during
|
||||||
|
/// `UpdateStakePoolBalance`
|
||||||
|
ReadyForRemoval,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StakeStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about the singe validator stake account
|
/// Information about the singe validator stake account
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct ValidatorStakeInfo {
|
pub struct ValidatorStakeInfo {
|
||||||
|
/// Status of the validator stake account
|
||||||
|
pub status: StakeStatus,
|
||||||
|
|
||||||
/// Validator vote account address
|
/// Validator vote account address
|
||||||
pub vote_account_address: Pubkey,
|
pub vote_account_address: Pubkey,
|
||||||
|
|
||||||
|
@ -314,7 +336,7 @@ impl ValidatorList {
|
||||||
/// Calculate the number of validator entries that fit in the provided length
|
/// Calculate the number of validator entries that fit in the provided length
|
||||||
pub fn calculate_max_validators(buffer_length: usize) -> usize {
|
pub fn calculate_max_validators(buffer_length: usize) -> usize {
|
||||||
let header_size = 1 + 4 + 4;
|
let header_size = 1 + 4 + 4;
|
||||||
buffer_length.saturating_sub(header_size) / 48
|
buffer_length.saturating_sub(header_size) / 49
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if contains validator with particular pubkey
|
/// Check if contains validator with particular pubkey
|
||||||
|
@ -402,16 +424,19 @@ mod test {
|
||||||
max_validators,
|
max_validators,
|
||||||
validators: vec![
|
validators: vec![
|
||||||
ValidatorStakeInfo {
|
ValidatorStakeInfo {
|
||||||
|
status: StakeStatus::Active,
|
||||||
vote_account_address: Pubkey::new_from_array([1; 32]),
|
vote_account_address: Pubkey::new_from_array([1; 32]),
|
||||||
stake_lamports: 123456789,
|
stake_lamports: 123456789,
|
||||||
last_update_epoch: 987654321,
|
last_update_epoch: 987654321,
|
||||||
},
|
},
|
||||||
ValidatorStakeInfo {
|
ValidatorStakeInfo {
|
||||||
|
status: StakeStatus::DeactivatingTransient,
|
||||||
vote_account_address: Pubkey::new_from_array([2; 32]),
|
vote_account_address: Pubkey::new_from_array([2; 32]),
|
||||||
stake_lamports: 998877665544,
|
stake_lamports: 998877665544,
|
||||||
last_update_epoch: 11223445566,
|
last_update_epoch: 11223445566,
|
||||||
},
|
},
|
||||||
ValidatorStakeInfo {
|
ValidatorStakeInfo {
|
||||||
|
status: StakeStatus::ReadyForRemoval,
|
||||||
vote_account_address: Pubkey::new_from_array([3; 32]),
|
vote_account_address: Pubkey::new_from_array([3; 32]),
|
||||||
stake_lamports: 0,
|
stake_lamports: 0,
|
||||||
last_update_epoch: 999999999999999,
|
last_update_epoch: 999999999999999,
|
||||||
|
|
|
@ -49,7 +49,8 @@ async fn setup() -> (
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
100_000_000,
|
100_000_000,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let lamports = deposit_info.stake_lamports / 2;
|
let lamports = deposit_info.stake_lamports / 2;
|
||||||
|
|
||||||
|
|
|
@ -1010,7 +1010,7 @@ pub async fn simple_deposit(
|
||||||
stake_pool_accounts: &StakePoolAccounts,
|
stake_pool_accounts: &StakePoolAccounts,
|
||||||
validator_stake_account: &ValidatorStakeAccount,
|
validator_stake_account: &ValidatorStakeAccount,
|
||||||
stake_lamports: u64,
|
stake_lamports: u64,
|
||||||
) -> DepositStakeAccount {
|
) -> Option<DepositStakeAccount> {
|
||||||
let authority = Keypair::new();
|
let authority = Keypair::new();
|
||||||
// make stake account
|
// make stake account
|
||||||
let stake = Keypair::new();
|
let stake = Keypair::new();
|
||||||
|
@ -1064,11 +1064,11 @@ pub async fn simple_deposit(
|
||||||
&authority,
|
&authority,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok()?;
|
||||||
|
|
||||||
let pool_tokens = get_token_balance(banks_client, &pool_account.pubkey()).await;
|
let pool_tokens = get_token_balance(banks_client, &pool_account.pubkey()).await;
|
||||||
|
|
||||||
DepositStakeAccount {
|
Some(DepositStakeAccount {
|
||||||
authority,
|
authority,
|
||||||
stake,
|
stake,
|
||||||
pool_account,
|
pool_account,
|
||||||
|
@ -1076,7 +1076,7 @@ pub async fn simple_deposit(
|
||||||
pool_tokens,
|
pool_tokens,
|
||||||
vote_account,
|
vote_account,
|
||||||
validator_stake_account,
|
validator_stake_account,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_validator_list_sum(
|
pub async fn get_validator_list_sum(
|
||||||
|
|
|
@ -54,7 +54,8 @@ async fn setup() -> (
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
5_000_000,
|
5_000_000,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
(
|
(
|
||||||
banks_client,
|
banks_client,
|
||||||
|
|
|
@ -51,7 +51,8 @@ async fn setup() -> (
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
TEST_STAKE_AMOUNT,
|
TEST_STAKE_AMOUNT,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
stake_accounts.push(validator_stake_account);
|
stake_accounts.push(validator_stake_account);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ use {
|
||||||
solana_program_test::*,
|
solana_program_test::*,
|
||||||
solana_sdk::signature::{Keypair, Signer},
|
solana_sdk::signature::{Keypair, Signer},
|
||||||
spl_stake_pool::{
|
spl_stake_pool::{
|
||||||
stake_program, state::StakePool, MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE,
|
borsh::try_from_slice_unchecked,
|
||||||
|
stake_program,
|
||||||
|
state::{StakePool, StakeStatus, ValidatorList},
|
||||||
|
MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -474,7 +477,139 @@ async fn merge_into_validator_stake() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn max_validators() {}
|
#[ignore]
|
||||||
|
async fn merge_transient_stake_after_remove() {
|
||||||
|
let (mut context, stake_pool_accounts, stake_accounts, lamports, reserve_lamports, mut slot) =
|
||||||
|
setup(1).await;
|
||||||
|
|
||||||
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
|
let deactivated_lamports = lamports + stake_rent;
|
||||||
|
let new_authority = Pubkey::new_unique();
|
||||||
|
// Decrease and remove all validators
|
||||||
|
for stake_account in &stake_accounts {
|
||||||
|
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,
|
||||||
|
deactivated_lamports,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.remove_validator_from_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&new_authority,
|
||||||
|
&stake_account.stake_account,
|
||||||
|
&stake_account.transient_stake_account,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warp forward to merge time
|
||||||
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(slot).unwrap();
|
||||||
|
|
||||||
|
// Update without merge, status should be DeactivatingTransient
|
||||||
|
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(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.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(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
validator_list.validators[0].status,
|
||||||
|
StakeStatus::DeactivatingTransient
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
validator_list.validators[0].stake_lamports,
|
||||||
|
deactivated_lamports
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update with merge, status should be ReadyForRemoval and no lamports
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.update_validator_list_balance(
|
||||||
|
&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());
|
||||||
|
|
||||||
|
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(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
validator_list.validators[0].status,
|
||||||
|
StakeStatus::ReadyForRemoval
|
||||||
|
);
|
||||||
|
assert_eq!(validator_list.validators[0].stake_lamports, 0);
|
||||||
|
|
||||||
|
let reserve_stake = context
|
||||||
|
.banks_client
|
||||||
|
.get_account(stake_pool_accounts.reserve_stake.pubkey())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
reserve_stake.lamports,
|
||||||
|
reserve_lamports + deactivated_lamports + stake_rent + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update stake pool balance, should be gone
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.update_stake_pool_balance(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
)
|
||||||
|
.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(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_with_uninitialized_validator_list() {} // TODO
|
async fn fail_with_uninitialized_validator_list() {} // TODO
|
||||||
|
|
|
@ -86,6 +86,7 @@ async fn success() {
|
||||||
account_type: state::AccountType::ValidatorList,
|
account_type: state::AccountType::ValidatorList,
|
||||||
max_validators: stake_pool_accounts.max_validators,
|
max_validators: stake_pool_accounts.max_validators,
|
||||||
validators: vec![state::ValidatorStakeInfo {
|
validators: vec![state::ValidatorStakeInfo {
|
||||||
|
status: state::StakeStatus::Active,
|
||||||
vote_account_address: user_stake.vote.pubkey(),
|
vote_account_address: user_stake.vote.pubkey(),
|
||||||
last_update_epoch: 0,
|
last_update_epoch: 0,
|
||||||
stake_lamports: 0,
|
stake_lamports: 0,
|
||||||
|
|
|
@ -38,8 +38,8 @@ async fn setup() -> (
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
|
let validator_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
|
||||||
user_stake
|
validator_stake
|
||||||
.create_and_delegate(
|
.create_and_delegate(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
|
@ -53,7 +53,7 @@ async fn setup() -> (
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
@ -63,13 +63,13 @@ async fn setup() -> (
|
||||||
payer,
|
payer,
|
||||||
recent_blockhash,
|
recent_blockhash,
|
||||||
stake_pool_accounts,
|
stake_pool_accounts,
|
||||||
user_stake,
|
validator_stake,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success() {
|
async fn success() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let new_authority = Pubkey::new_unique();
|
let new_authority = Pubkey::new_unique();
|
||||||
|
@ -79,8 +79,8 @@ async fn success() {
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
@ -103,7 +103,7 @@ async fn success() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check of stake account authority has changed
|
// Check of stake account authority has changed
|
||||||
let stake = get_account(&mut banks_client, &user_stake.stake_account).await;
|
let stake = get_account(&mut banks_client, &validator_stake.stake_account).await;
|
||||||
let stake_state = deserialize::<stake_program::StakeState>(&stake.data).unwrap();
|
let stake_state = deserialize::<stake_program::StakeState>(&stake.data).unwrap();
|
||||||
match stake_state {
|
match stake_state {
|
||||||
stake_program::StakeState::Stake(meta, _) => {
|
stake_program::StakeState::Stake(meta, _) => {
|
||||||
|
@ -116,7 +116,7 @@ async fn success() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_with_wrong_stake_program_id() {
|
async fn fail_with_wrong_stake_program_id() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let wrong_stake_program = Pubkey::new_unique();
|
let wrong_stake_program = Pubkey::new_unique();
|
||||||
|
@ -128,8 +128,8 @@ async fn fail_with_wrong_stake_program_id() {
|
||||||
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
||||||
AccountMeta::new_readonly(new_authority, false),
|
AccountMeta::new_readonly(new_authority, false),
|
||||||
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
||||||
AccountMeta::new(user_stake.stake_account, false),
|
AccountMeta::new(validator_stake.stake_account, false),
|
||||||
AccountMeta::new_readonly(user_stake.transient_stake_account, false),
|
AccountMeta::new_readonly(validator_stake.transient_stake_account, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(wrong_stake_program, false),
|
AccountMeta::new_readonly(wrong_stake_program, false),
|
||||||
];
|
];
|
||||||
|
@ -162,7 +162,7 @@ async fn fail_with_wrong_stake_program_id() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_with_wrong_validator_list_account() {
|
async fn fail_with_wrong_validator_list_account() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let wrong_validator_list = Keypair::new();
|
let wrong_validator_list = Keypair::new();
|
||||||
|
@ -176,8 +176,8 @@ async fn fail_with_wrong_validator_list_account() {
|
||||||
&stake_pool_accounts.withdraw_authority,
|
&stake_pool_accounts.withdraw_authority,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&wrong_validator_list.pubkey(),
|
&wrong_validator_list.pubkey(),
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.unwrap()],
|
.unwrap()],
|
||||||
Some(&payer.pubkey()),
|
Some(&payer.pubkey()),
|
||||||
|
@ -203,14 +203,14 @@ async fn fail_with_wrong_validator_list_account() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_not_at_minimum() {
|
async fn fail_not_at_minimum() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
transfer(
|
transfer(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
1_000_001,
|
1_000_001,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -222,8 +222,8 @@ async fn fail_not_at_minimum() {
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -239,7 +239,7 @@ async fn fail_not_at_minimum() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_double_remove() {
|
async fn fail_double_remove() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let new_authority = Pubkey::new_unique();
|
let new_authority = Pubkey::new_unique();
|
||||||
|
@ -249,8 +249,8 @@ async fn fail_double_remove() {
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
@ -263,8 +263,8 @@ async fn fail_double_remove() {
|
||||||
&payer,
|
&payer,
|
||||||
&latest_blockhash,
|
&latest_blockhash,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -285,7 +285,7 @@ async fn fail_double_remove() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_wrong_staker() {
|
async fn fail_wrong_staker() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let malicious = Keypair::new();
|
let malicious = Keypair::new();
|
||||||
|
@ -299,8 +299,8 @@ async fn fail_wrong_staker() {
|
||||||
&stake_pool_accounts.withdraw_authority,
|
&stake_pool_accounts.withdraw_authority,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.unwrap()],
|
.unwrap()],
|
||||||
Some(&payer.pubkey()),
|
Some(&payer.pubkey()),
|
||||||
|
@ -328,7 +328,7 @@ async fn fail_wrong_staker() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_no_signature() {
|
async fn fail_no_signature() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let new_authority = Pubkey::new_unique();
|
let new_authority = Pubkey::new_unique();
|
||||||
|
@ -339,8 +339,8 @@ async fn fail_no_signature() {
|
||||||
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
||||||
AccountMeta::new_readonly(new_authority, false),
|
AccountMeta::new_readonly(new_authority, false),
|
||||||
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
||||||
AccountMeta::new(user_stake.stake_account, false),
|
AccountMeta::new(validator_stake.stake_account, false),
|
||||||
AccountMeta::new_readonly(user_stake.transient_stake_account, false),
|
AccountMeta::new_readonly(validator_stake.transient_stake_account, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(stake_program::id(), false),
|
AccountMeta::new_readonly(stake_program::id(), false),
|
||||||
];
|
];
|
||||||
|
@ -378,7 +378,7 @@ async fn fail_no_signature() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_with_activating_transient_stake() {
|
async fn fail_with_activating_transient_stake() {
|
||||||
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, user_stake) =
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
// increase the validator stake
|
// increase the validator stake
|
||||||
|
@ -387,8 +387,8 @@ async fn fail_with_activating_transient_stake() {
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
&user_stake.vote.pubkey(),
|
&validator_stake.vote.pubkey(),
|
||||||
2_000_000_000,
|
2_000_000_000,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -401,8 +401,8 @@ async fn fail_with_activating_transient_stake() {
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&new_authority,
|
&new_authority,
|
||||||
&user_stake.stake_account,
|
&validator_stake.stake_account,
|
||||||
&user_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -419,6 +419,129 @@ async fn fail_with_activating_transient_stake() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_deactivating_transient_stake() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake) =
|
||||||
|
setup().await;
|
||||||
|
|
||||||
|
let rent = banks_client.get_rent().await.unwrap();
|
||||||
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
|
let deposit_info = simple_deposit(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&stake_pool_accounts,
|
||||||
|
&validator_stake,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// increase the validator stake
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.decrease_validator_stake(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&validator_stake.stake_account,
|
||||||
|
&validator_stake.transient_stake_account,
|
||||||
|
TEST_STAKE_AMOUNT + stake_rent,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let new_authority = Pubkey::new_unique();
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.remove_validator_from_pool(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&new_authority,
|
||||||
|
&validator_stake.stake_account,
|
||||||
|
&validator_stake.transient_stake_account,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
// fail deposit
|
||||||
|
let maybe_deposit = simple_deposit(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&stake_pool_accounts,
|
||||||
|
&validator_stake,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(maybe_deposit.is_none());
|
||||||
|
|
||||||
|
// fail withdraw
|
||||||
|
let user_stake_recipient = Keypair::new();
|
||||||
|
create_blank_stake_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_stake_recipient,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let new_authority = Pubkey::new_unique();
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.withdraw_stake(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_stake_recipient.pubkey(),
|
||||||
|
&deposit_info.pool_account.pubkey(),
|
||||||
|
&validator_stake.stake_account,
|
||||||
|
&new_authority,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_some());
|
||||||
|
|
||||||
|
// check validator has changed
|
||||||
|
let validator_list = get_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let validator_list =
|
||||||
|
try_from_slice_unchecked::<state::ValidatorList>(validator_list.data.as_slice()).unwrap();
|
||||||
|
let expected_list = state::ValidatorList {
|
||||||
|
account_type: state::AccountType::ValidatorList,
|
||||||
|
max_validators: stake_pool_accounts.max_validators,
|
||||||
|
validators: vec![state::ValidatorStakeInfo {
|
||||||
|
status: state::StakeStatus::DeactivatingTransient,
|
||||||
|
vote_account_address: validator_stake.vote.pubkey(),
|
||||||
|
last_update_epoch: 0,
|
||||||
|
stake_lamports: TEST_STAKE_AMOUNT + stake_rent,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
assert_eq!(validator_list, expected_list);
|
||||||
|
|
||||||
|
// Update, should not change, no merges yet
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.update_all(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&[validator_stake.vote.pubkey()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let validator_list = get_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let validator_list =
|
||||||
|
try_from_slice_unchecked::<state::ValidatorList>(validator_list.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(validator_list, expected_list);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_not_updated_stake_pool() {} // TODO
|
async fn fail_not_updated_stake_pool() {} // TODO
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,8 @@ async fn setup() -> (
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
TEST_STAKE_AMOUNT,
|
TEST_STAKE_AMOUNT,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
||||||
|
|
||||||
|
@ -607,7 +608,8 @@ async fn fail_without_token_approval() {
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
TEST_STAKE_AMOUNT,
|
TEST_STAKE_AMOUNT,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
||||||
|
|
||||||
|
@ -675,7 +677,8 @@ async fn fail_with_low_delegation() {
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
TEST_STAKE_AMOUNT,
|
TEST_STAKE_AMOUNT,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
let tokens_to_burn = deposit_info.pool_tokens / 4;
|
||||||
|
|
||||||
|
@ -819,7 +822,8 @@ async fn success_with_reserve() {
|
||||||
&validator_stake,
|
&validator_stake,
|
||||||
deposit_lamports,
|
deposit_lamports,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// decrease some stake
|
// decrease some stake
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
|
|
Loading…
Reference in New Issue