stake: plumb `can_merge_expired_lockups` feature flag

This commit is contained in:
Trent Nelson 2021-05-20 20:23:36 -06:00 committed by Trent Nelson
parent 8c073b2c94
commit 74ac6ab80f
2 changed files with 60 additions and 23 deletions

View File

@ -595,12 +595,15 @@ pub fn process_instruction(
}
StakeInstruction::Merge => {
let source_stake = &keyed_account_at_index(keyed_accounts, 1)?;
let can_merge_expired_lockups =
invoke_context.is_feature_active(&feature_set::stake_program_v4::id());
me.merge(
invoke_context,
source_stake,
&from_keyed_account::<Clock>(keyed_account_at_index(keyed_accounts, 2)?)?,
&from_keyed_account::<StakeHistory>(keyed_account_at_index(keyed_accounts, 3)?)?,
&signers,
can_merge_expired_lockups,
)
}

View File

@ -888,6 +888,7 @@ pub trait StakeAccount {
clock: &Clock,
stake_history: &StakeHistory,
signers: &HashSet<Pubkey>,
can_merge_expired_lockups: bool,
) -> Result<(), InstructionError>;
fn withdraw(
&self,
@ -1191,6 +1192,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
clock: &Clock,
stake_history: &StakeHistory,
signers: &HashSet<Pubkey>,
can_merge_expired_lockups: bool,
) -> Result<(), InstructionError> {
// Ensure source isn't spoofed
if source_account.owner()? != id() {
@ -1213,8 +1215,16 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
let source_merge_kind =
MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?;
let clock = if can_merge_expired_lockups {
Some(clock)
} else {
None
};
ic_msg!(invoke_context, "Merging stake accounts");
if let Some(merged_state) = stake_merge_kind.merge(invoke_context, source_merge_kind)? {
if let Some(merged_state) =
stake_merge_kind.merge(invoke_context, source_merge_kind, clock)?
{
self.set_state(&merged_state)?;
}
@ -1380,6 +1390,7 @@ impl MergeKind {
invoke_context: &dyn InvokeContext,
stake: &Meta,
source: &Meta,
_clock: Option<&Clock>,
) -> Result<(), InstructionError> {
// `rent_exempt_reserve` has no bearing on the mergeability of accounts,
// as the source account will be culled by runtime once the operation
@ -1439,8 +1450,9 @@ impl MergeKind {
self,
invoke_context: &dyn InvokeContext,
source: Self,
clock: Option<&Clock>,
) -> Result<Option<StakeState>, InstructionError> {
Self::metas_can_merge(invoke_context, self.meta(), source.meta())?;
Self::metas_can_merge(invoke_context, self.meta(), source.meta(), clock)?;
self.active_stake()
.zip(source.active_stake())
.map(|(stake, source)| Self::active_stakes_can_merge(invoke_context, stake, source))
@ -5380,7 +5392,8 @@ mod tests {
&source_stake_keyed_account,
&Clock::default(),
&StakeHistory::default(),
&HashSet::new()
&HashSet::new(),
false,
),
Err(InstructionError::MissingRequiredSignature)
);
@ -5391,7 +5404,8 @@ mod tests {
&source_stake_keyed_account,
&Clock::default(),
&StakeHistory::default(),
&signers
&signers,
false,
),
Ok(())
);
@ -5483,6 +5497,7 @@ mod tests {
&Clock::default(),
&StakeHistory::default(),
&signers,
false,
),
Err(InstructionError::InvalidArgument),
);
@ -5540,6 +5555,7 @@ mod tests {
&Clock::default(),
&StakeHistory::default(),
&wrong_signers,
false,
),
Err(InstructionError::MissingRequiredSignature)
);
@ -5551,6 +5567,7 @@ mod tests {
&Clock::default(),
&StakeHistory::default(),
&signers,
false,
),
Err(StakeError::MergeMismatch.into())
);
@ -5603,6 +5620,7 @@ mod tests {
&Clock::default(),
&StakeHistory::default(),
&signers,
false,
),
Err(InstructionError::InvalidAccountData)
);
@ -5651,7 +5669,8 @@ mod tests {
&source_stake_keyed_account,
&Clock::default(),
&StakeHistory::default(),
&signers
&signers,
false,
),
Err(InstructionError::IncorrectProgramId)
);
@ -5748,6 +5767,7 @@ mod tests {
clock,
stake_history,
signers,
false,
);
if result.is_ok() {
assert_eq!(test_source_keyed.state(), Ok(StakeState::Uninitialized),);
@ -6468,11 +6488,19 @@ mod tests {
&good_delegation
)
.is_err());
}
#[test]
fn test_metas_can_merge_pre_v4() {
let invoke_context = MockInvokeContext::new(vec![]);
// Identical Metas can merge
assert!(
MergeKind::metas_can_merge(&invoke_context, &Meta::default(), &Meta::default()).is_ok()
);
assert!(MergeKind::metas_can_merge(
&invoke_context,
&Meta::default(),
&Meta::default(),
None,
)
.is_ok());
let mismatched_rent_exempt_reserve_ok = Meta {
rent_exempt_reserve: 42,
@ -6485,13 +6513,15 @@ mod tests {
assert!(MergeKind::metas_can_merge(
&invoke_context,
&Meta::default(),
&mismatched_rent_exempt_reserve_ok
&mismatched_rent_exempt_reserve_ok,
None,
)
.is_ok());
assert!(MergeKind::metas_can_merge(
&invoke_context,
&mismatched_rent_exempt_reserve_ok,
&Meta::default()
&Meta::default(),
None,
)
.is_ok());
@ -6509,13 +6539,15 @@ mod tests {
assert!(MergeKind::metas_can_merge(
&invoke_context,
&Meta::default(),
&mismatched_authorized_fails
&mismatched_authorized_fails,
None,
)
.is_err());
assert!(MergeKind::metas_can_merge(
&invoke_context,
&mismatched_authorized_fails,
&Meta::default()
&Meta::default(),
None,
)
.is_err());
@ -6531,13 +6563,15 @@ mod tests {
assert!(MergeKind::metas_can_merge(
&invoke_context,
&Meta::default(),
&mismatched_lockup_fails
&mismatched_lockup_fails,
None,
)
.is_err());
assert!(MergeKind::metas_can_merge(
&invoke_context,
&mismatched_lockup_fails,
&Meta::default()
&Meta::default(),
None,
)
.is_err());
}
@ -6796,37 +6830,37 @@ mod tests {
assert_eq!(
inactive
.clone()
.merge(&invoke_context, inactive.clone())
.merge(&invoke_context, inactive.clone(), None)
.unwrap(),
None
);
assert_eq!(
inactive
.clone()
.merge(&invoke_context, activation_epoch.clone())
.merge(&invoke_context, activation_epoch.clone(), None)
.unwrap(),
None
);
assert!(inactive
.clone()
.merge(&invoke_context, fully_active.clone())
.merge(&invoke_context, fully_active.clone(), None)
.is_err());
assert!(activation_epoch
.clone()
.merge(&invoke_context, fully_active.clone())
.merge(&invoke_context, fully_active.clone(), None)
.is_err());
assert!(fully_active
.clone()
.merge(&invoke_context, inactive.clone())
.merge(&invoke_context, inactive.clone(), None)
.is_err());
assert!(fully_active
.clone()
.merge(&invoke_context, activation_epoch.clone())
.merge(&invoke_context, activation_epoch.clone(), None)
.is_err());
let new_state = activation_epoch
.clone()
.merge(&invoke_context, inactive)
.merge(&invoke_context, inactive, None)
.unwrap()
.unwrap();
let delegation = new_state.delegation().unwrap();
@ -6834,7 +6868,7 @@ mod tests {
let new_state = activation_epoch
.clone()
.merge(&invoke_context, activation_epoch)
.merge(&invoke_context, activation_epoch, None)
.unwrap()
.unwrap();
let delegation = new_state.delegation().unwrap();
@ -6845,7 +6879,7 @@ mod tests {
let new_state = fully_active
.clone()
.merge(&invoke_context, fully_active)
.merge(&invoke_context, fully_active, None)
.unwrap()
.unwrap();
let delegation = new_state.delegation().unwrap();