Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 0 additions & 42 deletions program/src/helpers/delegate.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use {
crate::PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
solana_account_info::AccountInfo,
solana_clock::Epoch,
solana_program_error::ProgramError,
solana_pubkey::Pubkey,
solana_stake_interface::{
error::StakeError,
state::{Delegation, Meta, Stake},
sysvar::stake_history::StakeHistorySysvar,
},
};

Expand All @@ -29,46 +27,6 @@ pub(crate) fn new_stake(
}
}

pub(crate) fn redelegate_stake(
stake: &mut Stake,
stake_lamports: u64,
voter_pubkey: &Pubkey,
credits_observed: u64,
epoch: Epoch,
stake_history: &StakeHistorySysvar,
) -> Result<(), ProgramError> {
// If stake is currently active:
if stake.stake(
epoch,
stake_history,
PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
) != 0
{
// If pubkey of new voter is the same as current,
// and we are scheduled to start deactivating this epoch,
// we rescind deactivation
if stake.delegation.voter_pubkey == *voter_pubkey
&& epoch == stake.delegation.deactivation_epoch
{
stake.delegation.deactivation_epoch = u64::MAX;
return Ok(());
} else {
// can't redelegate to another pubkey if stake is active.
return Err(StakeError::TooSoonToRedelegate.into());
}
}
// Either the stake is freshly activated, is active but has been
// deactivated this epoch, or has fully de-activated.
// Redelegation implies either re-activation or un-deactivation

stake.delegation.stake = stake_lamports;
stake.delegation.activation_epoch = epoch;
stake.delegation.deactivation_epoch = u64::MAX;
stake.delegation.voter_pubkey = *voter_pubkey;
stake.credits_observed = credits_observed;
Ok(())
}

/// Ensure the stake delegation amount is valid. This checks that the account
/// meets the minimum balance requirements of delegated stake. If not, return
/// an error.
Expand Down
35 changes: 29 additions & 6 deletions program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,22 +404,45 @@ impl Processor {
)
}
StakeStateV2::Stake(meta, mut stake, flags) => {
// Only the staker may (re)delegate
meta.authorized
.check(&signers, StakeAuthorize::Staker)
.map_err(to_program_error)?;

// Compute the maximum stake allowed to (re)delegate
let ValidatedDelegatedInfo { stake_amount } =
validate_delegated_amount(stake_account_info, &meta)?;

redelegate_stake(
&mut stake,
stake_amount,
vote_account_info.key,
vote_state.credits(),
// Get current activation status at this epoch
let effective_stake = stake.delegation.stake(
clock.epoch,
stake_history,
)?;
PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
);

if effective_stake == 0 {
// The stake has no effective voting power this epoch. This means it is either:
// 1. Inactive (fully cooled down after a previous deactivation)
// 2. Still activating (was delegated for the first time this epoch)
stake = new_stake(
stake_amount,
vote_account_info.key,
vote_state.credits(),
clock.epoch,
);
} else if clock.epoch == stake.delegation.deactivation_epoch
&& stake.delegation.voter_pubkey == *vote_account_info.key
{
if stake_amount < stake.delegation.stake {
return Err(StakeError::InsufficientDelegation.into());
}
stake.delegation.deactivation_epoch = u64::MAX;
} else {
// Not a valid state for redelegation
return Err(StakeError::TooSoonToRedelegate.into());
}

// Persist the updated stake state back to the account.
set_stake_state(stake_account_info, &StakeStateV2::Stake(meta, stake, flags))
}
_ => Err(ProgramError::InvalidAccountData),
Expand Down