diff --git a/docs/src/stake-pool.md b/docs/src/stake-pool.md index 902ac2f60e4..6414f670563 100644 --- a/docs/src/stake-pool.md +++ b/docs/src/stake-pool.md @@ -1097,12 +1097,6 @@ reserve. In this way, a user's funds are never at risk, and always redeemable. -### Staking Credits Observed on Deposit - -A deposited stake account's "credits observed" must match the destination -account's "credits observed". Typically, this means you must wait an additional -epoch after activation for your stake account to match up with the stake pool's account. - ### Transaction sizes The Solana transaction processor has two important limitations: diff --git a/stake-pool/program/src/lib.rs b/stake-pool/program/src/lib.rs index d8a53453380..3c726fa3545 100644 --- a/stake-pool/program/src/lib.rs +++ b/stake-pool/program/src/lib.rs @@ -6,7 +6,6 @@ pub mod big_vec; pub mod error; pub mod instruction; pub mod processor; -pub mod stake_program; pub mod state; #[cfg(not(feature = "no-entrypoint"))] diff --git a/stake-pool/program/src/processor.rs b/stake-pool/program/src/processor.rs index 948d2450427..29ed14eb924 100644 --- a/stake-pool/program/src/processor.rs +++ b/stake-pool/program/src/processor.rs @@ -5,7 +5,7 @@ use { error::StakePoolError, find_deposit_authority_program_address, instruction::{FundingType, PreferredValidatorType, StakePoolInstruction}, - minimum_reserve_lamports, minimum_stake_lamports, stake_program, + minimum_reserve_lamports, minimum_stake_lamports, state::{ AccountType, Fee, FeeType, StakePool, StakeStatus, ValidatorList, ValidatorListHeader, ValidatorStakeInfo, @@ -674,7 +674,7 @@ impl Processor { stake_pool.total_lamports = total_lamports; stake_pool.pool_token_supply = 0; stake_pool.last_update_epoch = Clock::get()?.epoch; - stake_pool.lockup = stake_program::Lockup::default(); + stake_pool.lockup = stake::state::Lockup::default(); stake_pool.epoch_fee = epoch_fee; stake_pool.next_epoch_fee = None; stake_pool.preferred_deposit_validator_vote_address = None; @@ -1530,9 +1530,7 @@ impl Processor { if let Some(stake::state::StakeState::Stake(_, validator_stake)) = validator_stake_state { - if stake_program::active_stakes_can_merge(&stake, &validator_stake) - .is_ok() - { + if validator_stake.delegation.activation_epoch < clock.epoch { let additional_lamports = transient_stake_info .lamports() .saturating_sub(stake.delegation.stake); diff --git a/stake-pool/program/src/stake_program.rs b/stake-pool/program/src/stake_program.rs deleted file mode 100644 index 093f2cdf76b..00000000000 --- a/stake-pool/program/src/stake_program.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! FIXME copied from the solana stake program - -use { - borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, - serde_derive::{Deserialize, Serialize}, - solana_program::{ - clock::{Epoch, UnixTimestamp}, - msg, - program_error::ProgramError, - pubkey::Pubkey, - stake, - }, -}; - -/// FIXME copied from the stake program, once https://github.com/solana-labs/solana/pull/20784 -/// lands this can be removed -#[derive( - BorshSerialize, - BorshDeserialize, - BorshSchema, - Default, - Debug, - Serialize, - Deserialize, - PartialEq, - Clone, - Copy, -)] -pub struct Lockup { - /// UnixTimestamp at which this stake will allow withdrawal, unless the - /// transaction is signed by the custodian - pub unix_timestamp: UnixTimestamp, - /// epoch height at which this stake will allow withdrawal, unless the - /// transaction is signed by the custodian - pub epoch: Epoch, - /// custodian signature on a transaction exempts the operation from - /// lockup constraints - pub custodian: Pubkey, -} - -/// FIXME copied from stake program -/// Checks if two active delegations are mergeable, required since we cannot recover -/// from a CPI error. -pub fn active_delegations_can_merge( - stake: &stake::state::Delegation, - source: &stake::state::Delegation, -) -> Result<(), ProgramError> { - if stake.voter_pubkey != source.voter_pubkey { - msg!("Unable to merge due to voter mismatch"); - Err(ProgramError::InvalidAccountData) - } else if (stake.warmup_cooldown_rate - source.warmup_cooldown_rate).abs() < f64::EPSILON - && stake.deactivation_epoch == Epoch::MAX - && source.deactivation_epoch == Epoch::MAX - { - Ok(()) - } else { - msg!("Unable to merge due to stake deactivation"); - Err(ProgramError::InvalidAccountData) - } -} - -/// FIXME copied from stake program -/// Checks if two active stakes are mergeable, required since we cannot recover -/// from a CPI error. -pub fn active_stakes_can_merge( - stake: &stake::state::Stake, - source: &stake::state::Stake, -) -> Result<(), ProgramError> { - active_delegations_can_merge(&stake.delegation, &source.delegation)?; - - if stake.credits_observed == source.credits_observed { - Ok(()) - } else { - msg!("Unable to merge due to credits observed mismatch"); - Err(ProgramError::InvalidAccountData) - } -} diff --git a/stake-pool/program/src/state.rs b/stake-pool/program/src/state.rs index 792e18cc1c6..bcbf6f8cb20 100644 --- a/stake-pool/program/src/state.rs +++ b/stake-pool/program/src/state.rs @@ -3,7 +3,7 @@ use spl_token::state::{Account, AccountState}; use { crate::{ - big_vec::BigVec, error::StakePoolError, stake_program::Lockup, MAX_WITHDRAWAL_FEE_INCREASE, + big_vec::BigVec, error::StakePoolError, MAX_WITHDRAWAL_FEE_INCREASE, WITHDRAWAL_BASELINE_FEE, }, borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, @@ -17,6 +17,7 @@ use { program_memory::sol_memcmp, program_pack::{Pack, Sealed}, pubkey::{Pubkey, PUBKEY_BYTES}, + stake::state::Lockup, }, spl_math::checked_ceil_div::CheckedCeilDiv, std::{convert::TryFrom, fmt, matches}, diff --git a/stake-pool/program/tests/deposit.rs b/stake-pool/program/tests/deposit.rs index 0d84b64d6fd..0b7eda1e7ca 100644 --- a/stake-pool/program/tests/deposit.rs +++ b/stake-pool/program/tests/deposit.rs @@ -89,7 +89,7 @@ async fn setup() -> ( ) .await; - slot += 2 * slots_per_epoch; + slot += slots_per_epoch; context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( diff --git a/stake-pool/program/tests/huge_pool.rs b/stake-pool/program/tests/huge_pool.rs index 48b7e74114c..d85f362ad21 100644 --- a/stake-pool/program/tests/huge_pool.rs +++ b/stake-pool/program/tests/huge_pool.rs @@ -24,7 +24,6 @@ use { find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, id, instruction::{self, PreferredValidatorType}, - stake_program, state::{AccountType, Fee, StakePool, StakeStatus, ValidatorList, ValidatorStakeInfo}, MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE, }, @@ -72,7 +71,7 @@ async fn setup( total_lamports: 0, pool_token_supply: 0, last_update_epoch: 0, - lockup: stake_program::Lockup::default(), + lockup: stake::state::Lockup::default(), epoch_fee: stake_pool_accounts.epoch_fee, next_epoch_fee: None, preferred_deposit_validator_vote_address: None, diff --git a/stake-pool/program/tests/set_epoch_fee.rs b/stake-pool/program/tests/set_epoch_fee.rs index 8eb87366ab8..95d3a46c36f 100644 --- a/stake-pool/program/tests/set_epoch_fee.rs +++ b/stake-pool/program/tests/set_epoch_fee.rs @@ -190,7 +190,11 @@ async fn fail_not_updated() { }; // move forward so an update is required - context.warp_to_slot(50_000).unwrap(); + 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(); let transaction = Transaction::new_signed_with_payer( &[instruction::set_fee( diff --git a/stake-pool/program/tests/set_withdrawal_fee.rs b/stake-pool/program/tests/set_withdrawal_fee.rs index 5fb475e0f27..10438662b87 100644 --- a/stake-pool/program/tests/set_withdrawal_fee.rs +++ b/stake-pool/program/tests/set_withdrawal_fee.rs @@ -633,7 +633,11 @@ async fn fail_not_updated() { }; // move forward so an update is required - context.warp_to_slot(50_000).unwrap(); + 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(); let transaction = Transaction::new_signed_with_payer( &[instruction::set_fee( diff --git a/stake-pool/program/tests/update_stake_pool_balance.rs b/stake-pool/program/tests/update_stake_pool_balance.rs index 9bbb558f1c6..aa031690ad5 100644 --- a/stake-pool/program/tests/update_stake_pool_balance.rs +++ b/stake-pool/program/tests/update_stake_pool_balance.rs @@ -107,7 +107,11 @@ async fn success() { } // Update epoch - context.warp_to_slot(50_000).unwrap(); + 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 @@ -213,7 +217,11 @@ async fn success_ignoring_extra_lamports() { } // Update epoch - context.warp_to_slot(50_000).unwrap(); + 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 diff --git a/stake-pool/program/tests/update_validator_list_balance.rs b/stake-pool/program/tests/update_validator_list_balance.rs index 7c8c270a8fa..44cb093efad 100644 --- a/stake-pool/program/tests/update_validator_list_balance.rs +++ b/stake-pool/program/tests/update_validator_list_balance.rs @@ -92,7 +92,7 @@ async fn setup( } // Warp forward so the stakes properly activate, and deposit - slot += 2 * slots_per_epoch; + slot += slots_per_epoch; context.warp_to_slot(slot).unwrap(); stake_pool_accounts diff --git a/stake-pool/program/tests/vsa_remove.rs b/stake-pool/program/tests/vsa_remove.rs index acbb0d8857f..560da5da3ff 100644 --- a/stake-pool/program/tests/vsa_remove.rs +++ b/stake-pool/program/tests/vsa_remove.rs @@ -696,8 +696,10 @@ async fn success_with_hijacked_transient_account() { assert!(error.is_none()); // warp forward to merge + 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(slots_per_epoch * 2).unwrap(); + let mut slot = first_normal_slot + slots_per_epoch; + context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -723,7 +725,8 @@ async fn success_with_hijacked_transient_account() { assert!(error.is_none()); // warp forward to merge - context.warp_to_slot(slots_per_epoch * 4).unwrap(); + slot += slots_per_epoch; + context.warp_to_slot(slot).unwrap(); // hijack let validator_list = stake_pool_accounts