Add test to check destination for stake splitting (#23303)
This commit is contained in:
parent
856d29c7b7
commit
fcaf01e243
|
@ -6666,6 +6666,151 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// Ensure that `split()` correctly handles prefunded destination accounts. When a destination
|
||||
/// account already has funds, ensure the minimum split amount reduces accordingly.
|
||||
#[test]
|
||||
fn test_split_destination_minimum_stake_delegation() {
|
||||
let rent = Rent::default();
|
||||
let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
||||
|
||||
for (destination_starting_balance, split_amount, expected_result) in [
|
||||
// split amount must be non zero
|
||||
(
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION,
|
||||
0,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
// any split amount is OK when destination account is already fully funded
|
||||
(rent_exempt_reserve + MINIMUM_STAKE_DELEGATION, 1, Ok(())),
|
||||
// if destination is only short by 1 lamport, then split amount can be 1 lamport
|
||||
(
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION - 1,
|
||||
1,
|
||||
Ok(()),
|
||||
),
|
||||
// destination short by 2 lamports, so 1 isn't enough (non-zero split amount)
|
||||
(
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION - 2,
|
||||
1,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
// destination is rent exempt, so split enough for minimum delegation
|
||||
(rent_exempt_reserve, MINIMUM_STAKE_DELEGATION, Ok(())),
|
||||
// destination is rent exempt, but split amount less than minimum delegation
|
||||
(
|
||||
rent_exempt_reserve,
|
||||
MINIMUM_STAKE_DELEGATION - 1,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
// destination is not rent exempt, so split enough for rent and minimum delegation
|
||||
(
|
||||
rent_exempt_reserve - 1,
|
||||
MINIMUM_STAKE_DELEGATION + 1,
|
||||
Ok(()),
|
||||
),
|
||||
// destination is not rent exempt, but split amount only for minimum delegation
|
||||
(
|
||||
rent_exempt_reserve - 1,
|
||||
MINIMUM_STAKE_DELEGATION,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
// destination has smallest non-zero balance, so can split the minimum balance
|
||||
// requirements minus what destination already has
|
||||
(
|
||||
1,
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION - 1,
|
||||
Ok(()),
|
||||
),
|
||||
// destination has smallest non-zero balance, but cannot split less than the minimum
|
||||
// balance requirements minus what destination already has
|
||||
(
|
||||
1,
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION - 2,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
// destination has zero lamports, so split must be at least rent exempt reserve plus
|
||||
// minimum delegation
|
||||
(0, rent_exempt_reserve + MINIMUM_STAKE_DELEGATION, Ok(())),
|
||||
// destination has zero lamports, but split amount is less than rent exempt reserve
|
||||
// plus minimum delegation
|
||||
(
|
||||
0,
|
||||
rent_exempt_reserve + MINIMUM_STAKE_DELEGATION - 1,
|
||||
Err(InstructionError::InsufficientFunds),
|
||||
),
|
||||
] {
|
||||
let source_pubkey = Pubkey::new_unique();
|
||||
let source_meta = Meta {
|
||||
rent_exempt_reserve,
|
||||
..Meta::auto(&source_pubkey)
|
||||
};
|
||||
|
||||
// Set the source's starting balance and stake delegation amount to something large
|
||||
// to ensure its post-split balance meets all the requirements
|
||||
let source_balance = u64::MAX;
|
||||
let source_stake_delegation = source_balance - rent_exempt_reserve;
|
||||
|
||||
for source_stake_state in &[
|
||||
StakeState::Initialized(source_meta),
|
||||
StakeState::Stake(source_meta, just_stake(source_stake_delegation)),
|
||||
] {
|
||||
let source_account = AccountSharedData::new_ref_data_with_space(
|
||||
source_balance,
|
||||
&source_stake_state,
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.unwrap();
|
||||
let source_keyed_account = KeyedAccount::new(&source_pubkey, true, &source_account);
|
||||
|
||||
let destination_pubkey = Pubkey::new_unique();
|
||||
let destination_account = AccountSharedData::new_ref_data_with_space(
|
||||
destination_starting_balance,
|
||||
&StakeState::Uninitialized,
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.unwrap();
|
||||
let destination_keyed_account =
|
||||
KeyedAccount::new(&destination_pubkey, true, &destination_account);
|
||||
|
||||
assert_eq!(
|
||||
expected_result,
|
||||
source_keyed_account.split(
|
||||
split_amount,
|
||||
&destination_keyed_account,
|
||||
&HashSet::from([source_pubkey]),
|
||||
),
|
||||
);
|
||||
|
||||
// For the expected OK cases, when the source's StakeState is Stake, then the
|
||||
// destination's StakeState *must* also end up as Stake as well. Additionally,
|
||||
// check to ensure the destination's delegation amount is correct. If the
|
||||
// destination is already rent exempt, then the destination's stake delegation
|
||||
// *must* equal the split amount. Otherwise, the split amount must first be used to
|
||||
// make the destination rent exempt, and then the leftover lamports are delegated.
|
||||
if expected_result.is_ok() {
|
||||
if let StakeState::Stake(_, _) = source_keyed_account.state().unwrap() {
|
||||
if let StakeState::Stake(_, destination_stake) =
|
||||
destination_keyed_account.state().unwrap()
|
||||
{
|
||||
let destination_initial_rent_deficit =
|
||||
rent_exempt_reserve.saturating_sub(destination_starting_balance);
|
||||
let expected_destination_stake_delegation =
|
||||
split_amount - destination_initial_rent_deficit;
|
||||
assert_eq!(
|
||||
expected_destination_stake_delegation,
|
||||
destination_stake.delegation.stake
|
||||
);
|
||||
} else {
|
||||
panic!("destination state must be StakeStake::Stake after successful split when source is also StakeState::Stake!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that `withdraw()` respects the MINIMUM_STAKE_DELEGATION requirements
|
||||
/// - Assert 1: withdrawing so remaining stake is equal-to the minimum is OK
|
||||
/// - Assert 2: withdrawing so remaining stake is less-than the minimum is not OK
|
||||
|
|
Loading…
Reference in New Issue