Skip to content

Commit

Permalink
pr: fixup split() checking lamports > self.lamports()
Browse files Browse the repository at this point in the history
  • Loading branch information
brooksprumo committed Mar 1, 2022
1 parent cc91ad3 commit b26cf9d
Showing 1 changed file with 72 additions and 70 deletions.
142 changes: 72 additions & 70 deletions programs/stake/src/stake_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,81 +614,83 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
if split.data_len()? != std::mem::size_of::<StakeState>() {
return Err(InstructionError::InvalidAccountData);
}
if !matches!(split.state()?, StakeState::Uninitialized) {
return Err(InstructionError::InvalidAccountData);
}
if lamports > self.lamports()? {
return Err(InstructionError::InsufficientFunds);
}

if let StakeState::Uninitialized = split.state()? {
match self.state()? {
StakeState::Stake(meta, mut stake) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = validate_split_amount(self, split, lamports, &meta)?;

// split the stake, subtract rent_exempt_balance unless
// the destination account already has those lamports
// in place.
// this means that the new stake account will have a stake equivalent to
// lamports minus rent_exempt_reserve if it starts out with a zero balance
let (remaining_stake_delta, split_stake_amount) =
if validated_split_info.source_remaining_balance == 0 {
// If split amount equals the full source stake, the new split stake must
// equal the same amount, regardless of any current lamport balance in the
// split account. Since split accounts retain the state of their source
// account, this prevents any magic activation of stake by prefunding the
// split account.
// The new split stake also needs to ignore any positive delta between the
// original rent_exempt_reserve and the split_rent_exempt_reserve, in order
// to prevent magic activation of stake by splitting between accounts of
// different sizes.
let remaining_stake_delta =
lamports.saturating_sub(meta.rent_exempt_reserve);
(remaining_stake_delta, remaining_stake_delta)
} else {
// Otherwise, the new split stake should reflect the entire split
// requested, less any lamports needed to cover the split_rent_exempt_reserve
(
lamports,
lamports.saturating_sub(
validated_split_info
.destination_rent_exempt_reserve
.saturating_sub(split.lamports()?),
),
)
};
let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?;
let mut split_meta = meta;
split_meta.rent_exempt_reserve =
validated_split_info.destination_rent_exempt_reserve;

self.set_state(&StakeState::Stake(meta, stake))?;
split.set_state(&StakeState::Stake(split_meta, split_stake))?;
}
StakeState::Initialized(meta) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = validate_split_amount(self, split, lamports, &meta)?;
let mut split_meta = meta;
split_meta.rent_exempt_reserve =
validated_split_info.destination_rent_exempt_reserve;
split.set_state(&StakeState::Initialized(split_meta))?;
}
StakeState::Uninitialized => {
if !signers.contains(self.unsigned_key()) {
return Err(InstructionError::MissingRequiredSignature);
}
}
_ => return Err(InstructionError::InvalidAccountData),
match self.state()? {
StakeState::Stake(meta, mut stake) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = validate_split_amount(self, split, lamports, &meta)?;

// split the stake, subtract rent_exempt_balance unless
// the destination account already has those lamports
// in place.
// this means that the new stake account will have a stake equivalent to
// lamports minus rent_exempt_reserve if it starts out with a zero balance
let (remaining_stake_delta, split_stake_amount) =
if validated_split_info.source_remaining_balance == 0 {
// If split amount equals the full source stake, the new split stake must
// equal the same amount, regardless of any current lamport balance in the
// split account. Since split accounts retain the state of their source
// account, this prevents any magic activation of stake by prefunding the
// split account.
// The new split stake also needs to ignore any positive delta between the
// original rent_exempt_reserve and the split_rent_exempt_reserve, in order
// to prevent magic activation of stake by splitting between accounts of
// different sizes.
let remaining_stake_delta =
lamports.saturating_sub(meta.rent_exempt_reserve);
(remaining_stake_delta, remaining_stake_delta)
} else {
// Otherwise, the new split stake should reflect the entire split
// requested, less any lamports needed to cover the split_rent_exempt_reserve
(
lamports,
lamports.saturating_sub(
validated_split_info
.destination_rent_exempt_reserve
.saturating_sub(split.lamports()?),
),
)
};
let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?;
let mut split_meta = meta;
split_meta.rent_exempt_reserve =
validated_split_info.destination_rent_exempt_reserve;

self.set_state(&StakeState::Stake(meta, stake))?;
split.set_state(&StakeState::Stake(split_meta, split_stake))?;
}

// Deinitialize state upon zero balance
if lamports == self.lamports()? {
self.set_state(&StakeState::Uninitialized)?;
StakeState::Initialized(meta) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = validate_split_amount(self, split, lamports, &meta)?;
let mut split_meta = meta;
split_meta.rent_exempt_reserve =
validated_split_info.destination_rent_exempt_reserve;
split.set_state(&StakeState::Initialized(split_meta))?;
}
StakeState::Uninitialized => {
if !signers.contains(self.unsigned_key()) {
return Err(InstructionError::MissingRequiredSignature);
}
}
_ => return Err(InstructionError::InvalidAccountData),
}

split
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
self.try_account_ref_mut()?.checked_sub_lamports(lamports)?;
Ok(())
} else {
Err(InstructionError::InvalidAccountData)
// Deinitialize state upon zero balance
if lamports == self.lamports()? {
self.set_state(&StakeState::Uninitialized)?;
}

split
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
self.try_account_ref_mut()?.checked_sub_lamports(lamports)?;
Ok(())
}

fn merge(
Expand Down

0 comments on commit b26cf9d

Please sign in to comment.