Add StakeInstruction::Merge logging
(cherry picked from commit ff22091a98
)
This commit is contained in:
@ -259,6 +259,7 @@ impl RpcClient {
|
|||||||
for (i, log) in logs.iter().enumerate() {
|
for (i, log) in logs.iter().enumerate() {
|
||||||
debug!("{:>3}: {}", i + 1, log);
|
debug!("{:>3}: {}", i + 1, log);
|
||||||
}
|
}
|
||||||
|
debug!("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -447,7 +447,7 @@ pub fn process_instruction(
|
|||||||
_program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
keyed_accounts: &[KeyedAccount],
|
keyed_accounts: &[KeyedAccount],
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
_invoke_context: &mut dyn InvokeContext,
|
invoke_context: &mut dyn InvokeContext,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
trace!("process_instruction: {:?}", data);
|
trace!("process_instruction: {:?}", data);
|
||||||
trace!("keyed_accounts: {:?}", keyed_accounts);
|
trace!("keyed_accounts: {:?}", keyed_accounts);
|
||||||
@ -498,6 +498,7 @@ pub fn process_instruction(
|
|||||||
StakeInstruction::Merge => {
|
StakeInstruction::Merge => {
|
||||||
let source_stake = &next_keyed_account(keyed_accounts)?;
|
let source_stake = &next_keyed_account(keyed_accounts)?;
|
||||||
me.merge(
|
me.merge(
|
||||||
|
invoke_context,
|
||||||
source_stake,
|
source_stake,
|
||||||
&from_keyed_account::<Clock>(next_keyed_account(keyed_accounts)?)?,
|
&from_keyed_account::<Clock>(next_keyed_account(keyed_accounts)?)?,
|
||||||
&from_keyed_account::<StakeHistory>(next_keyed_account(keyed_accounts)?)?,
|
&from_keyed_account::<StakeHistory>(next_keyed_account(keyed_accounts)?)?,
|
||||||
|
@ -13,8 +13,10 @@ use solana_sdk::{
|
|||||||
account::Account,
|
account::Account,
|
||||||
account_utils::{State, StateMut},
|
account_utils::{State, StateMut},
|
||||||
clock::{Clock, Epoch, UnixTimestamp},
|
clock::{Clock, Epoch, UnixTimestamp},
|
||||||
|
ic_msg,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
keyed_account::KeyedAccount,
|
keyed_account::KeyedAccount,
|
||||||
|
process_instruction::InvokeContext,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
|
rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
|
||||||
stake_history::{StakeHistory, StakeHistoryEntry},
|
stake_history::{StakeHistory, StakeHistoryEntry},
|
||||||
@ -800,6 +802,7 @@ pub trait StakeAccount {
|
|||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
fn merge(
|
fn merge(
|
||||||
&self,
|
&self,
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
source_stake: &KeyedAccount,
|
source_stake: &KeyedAccount,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
@ -1077,6 +1080,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
|
|
||||||
fn merge(
|
fn merge(
|
||||||
&self,
|
&self,
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
source_account: &KeyedAccount,
|
source_account: &KeyedAccount,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
@ -1091,15 +1095,20 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
return Err(InstructionError::InvalidArgument);
|
return Err(InstructionError::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stake_merge_kind = MergeKind::get_if_mergeable(self, clock, stake_history)?;
|
ic_msg!(invoke_context, "Checking if destination stake is mergeable");
|
||||||
|
let stake_merge_kind =
|
||||||
|
MergeKind::get_if_mergeable(invoke_context, self, clock, stake_history)?;
|
||||||
let meta = stake_merge_kind.meta();
|
let meta = stake_merge_kind.meta();
|
||||||
|
|
||||||
// Authorized staker is allowed to split/merge accounts
|
// Authorized staker is allowed to split/merge accounts
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
|
|
||||||
let source_merge_kind = MergeKind::get_if_mergeable(source_account, clock, stake_history)?;
|
ic_msg!(invoke_context, "Checking if source stake is mergeable");
|
||||||
|
let source_merge_kind =
|
||||||
|
MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?;
|
||||||
|
|
||||||
if let Some(merged_state) = stake_merge_kind.merge(source_merge_kind)? {
|
ic_msg!(invoke_context, "Merging stake accounts");
|
||||||
|
if let Some(merged_state) = stake_merge_kind.merge(invoke_context, source_merge_kind)? {
|
||||||
self.set_state(&merged_state)?;
|
self.set_state(&merged_state)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1218,6 +1227,7 @@ impl MergeKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_if_mergeable(
|
fn get_if_mergeable(
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
stake_keyed_account: &KeyedAccount,
|
stake_keyed_account: &KeyedAccount,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
@ -1236,7 +1246,11 @@ impl MergeKind {
|
|||||||
(0, 0, 0) => Ok(Self::Inactive(meta, stake_keyed_account.lamports()?)),
|
(0, 0, 0) => Ok(Self::Inactive(meta, stake_keyed_account.lamports()?)),
|
||||||
(0, _, _) => Ok(Self::ActivationEpoch(meta, stake)),
|
(0, _, _) => Ok(Self::ActivationEpoch(meta, stake)),
|
||||||
(_, 0, 0) => Ok(Self::FullyActive(meta, stake)),
|
(_, 0, 0) => Ok(Self::FullyActive(meta, stake)),
|
||||||
_ => Err(StakeError::MergeTransientStake.into()),
|
_ => {
|
||||||
|
let err = StakeError::MergeTransientStake;
|
||||||
|
ic_msg!(invoke_context, "{}", err);
|
||||||
|
Err(err.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StakeState::Initialized(meta) => {
|
StakeState::Initialized(meta) => {
|
||||||
@ -1246,7 +1260,11 @@ impl MergeKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metas_can_merge(stake: &Meta, source: &Meta) -> Result<(), InstructionError> {
|
fn metas_can_merge(
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
|
stake: &Meta,
|
||||||
|
source: &Meta,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
// `rent_exempt_reserve` has no bearing on the mergeability of accounts,
|
// `rent_exempt_reserve` has no bearing on the mergeability of accounts,
|
||||||
// as the source account will be culled by runtime once the operation
|
// as the source account will be culled by runtime once the operation
|
||||||
// succeeds. Considering it here would needlessly prevent merging stake
|
// succeeds. Considering it here would needlessly prevent merging stake
|
||||||
@ -1255,27 +1273,36 @@ impl MergeKind {
|
|||||||
if stake.authorized == source.authorized && stake.lockup == source.lockup {
|
if stake.authorized == source.authorized && stake.lockup == source.lockup {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
ic_msg!(invoke_context, "Unable to merge due to metadata mismatch");
|
||||||
Err(StakeError::MergeMismatch.into())
|
Err(StakeError::MergeMismatch.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_delegations_can_merge(
|
fn active_delegations_can_merge(
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
stake: &Delegation,
|
stake: &Delegation,
|
||||||
source: &Delegation,
|
source: &Delegation,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if stake.voter_pubkey == source.voter_pubkey
|
if stake.voter_pubkey != source.voter_pubkey {
|
||||||
&& (stake.warmup_cooldown_rate - source.warmup_cooldown_rate).abs() < f64::EPSILON
|
ic_msg!(invoke_context, "Unable to merge due to voter mismatch");
|
||||||
|
Err(StakeError::MergeMismatch.into())
|
||||||
|
} else if (stake.warmup_cooldown_rate - source.warmup_cooldown_rate).abs() < f64::EPSILON
|
||||||
&& stake.deactivation_epoch == Epoch::MAX
|
&& stake.deactivation_epoch == Epoch::MAX
|
||||||
&& source.deactivation_epoch == Epoch::MAX
|
&& source.deactivation_epoch == Epoch::MAX
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
ic_msg!(invoke_context, "Unable to merge due to stake deactivation");
|
||||||
Err(StakeError::MergeMismatch.into())
|
Err(StakeError::MergeMismatch.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_stakes_can_merge(stake: &Stake, source: &Stake) -> Result<(), InstructionError> {
|
fn active_stakes_can_merge(
|
||||||
Self::active_delegations_can_merge(&stake.delegation, &source.delegation)?;
|
invoke_context: &dyn InvokeContext,
|
||||||
|
stake: &Stake,
|
||||||
|
source: &Stake,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
Self::active_delegations_can_merge(invoke_context, &stake.delegation, &source.delegation)?;
|
||||||
// `credits_observed` MUST match to prevent earning multiple rewards
|
// `credits_observed` MUST match to prevent earning multiple rewards
|
||||||
// from a stake account by merging it into another stake account that
|
// from a stake account by merging it into another stake account that
|
||||||
// is small enough to not be paid out every epoch. This would effectively
|
// is small enough to not be paid out every epoch. This would effectively
|
||||||
@ -1284,15 +1311,23 @@ impl MergeKind {
|
|||||||
if stake.credits_observed == source.credits_observed {
|
if stake.credits_observed == source.credits_observed {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Unable to merge due to credits observed mismatch"
|
||||||
|
);
|
||||||
Err(StakeError::MergeMismatch.into())
|
Err(StakeError::MergeMismatch.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge(self, source: Self) -> Result<Option<StakeState>, InstructionError> {
|
fn merge(
|
||||||
Self::metas_can_merge(self.meta(), source.meta())?;
|
self,
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
|
source: Self,
|
||||||
|
) -> Result<Option<StakeState>, InstructionError> {
|
||||||
|
Self::metas_can_merge(invoke_context, self.meta(), source.meta())?;
|
||||||
self.active_stake()
|
self.active_stake()
|
||||||
.zip(source.active_stake())
|
.zip(source.active_stake())
|
||||||
.map(|(stake, source)| Self::active_stakes_can_merge(stake, source))
|
.map(|(stake, source)| Self::active_stakes_can_merge(invoke_context, stake, source))
|
||||||
.unwrap_or(Ok(()))?;
|
.unwrap_or(Ok(()))?;
|
||||||
let merged_state = match (self, source) {
|
let merged_state = match (self, source) {
|
||||||
(Self::Inactive(_, _), Self::Inactive(_, _)) => None,
|
(Self::Inactive(_, _), Self::Inactive(_, _)) => None,
|
||||||
@ -1580,7 +1615,10 @@ fn do_create_account(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::id;
|
use crate::id;
|
||||||
use solana_sdk::{account::Account, native_token, pubkey::Pubkey, system_program};
|
use solana_sdk::{
|
||||||
|
account::Account, native_token, process_instruction::MockInvokeContext, pubkey::Pubkey,
|
||||||
|
system_program,
|
||||||
|
};
|
||||||
use solana_vote_program::vote_state;
|
use solana_vote_program::vote_state;
|
||||||
use std::{cell::RefCell, iter::FromIterator};
|
use std::{cell::RefCell, iter::FromIterator};
|
||||||
|
|
||||||
@ -4763,6 +4801,7 @@ mod tests {
|
|||||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
|
|
||||||
let signers = vec![authorized_pubkey].into_iter().collect();
|
let signers = vec![authorized_pubkey].into_iter().collect();
|
||||||
|
|
||||||
@ -4802,6 +4841,7 @@ mod tests {
|
|||||||
// Authorized staker signature required...
|
// Authorized staker signature required...
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -4812,6 +4852,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -4870,6 +4911,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_self_fails() {
|
fn test_merge_self_fails() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let stake_address = Pubkey::new_unique();
|
let stake_address = Pubkey::new_unique();
|
||||||
let authority_pubkey = Pubkey::new_unique();
|
let authority_pubkey = Pubkey::new_unique();
|
||||||
let signers = HashSet::from_iter(vec![authority_pubkey]);
|
let signers = HashSet::from_iter(vec![authority_pubkey]);
|
||||||
@ -4901,6 +4943,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -4912,6 +4955,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_incorrect_authorized_staker() {
|
fn test_merge_incorrect_authorized_staker() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
@ -4956,6 +5000,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -4966,6 +5011,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -4979,6 +5025,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_invalid_account_data() {
|
fn test_merge_invalid_account_data() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
@ -5016,6 +5063,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -5029,6 +5077,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_fake_stake_source() {
|
fn test_merge_fake_stake_source() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
@ -5063,6 +5112,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.merge(
|
stake_keyed_account.merge(
|
||||||
|
&invoke_context,
|
||||||
&source_stake_keyed_account,
|
&source_stake_keyed_account,
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
&StakeHistory::default(),
|
&StakeHistory::default(),
|
||||||
@ -5074,6 +5124,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_active_stake() {
|
fn test_merge_active_stake() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let base_lamports = 4242424242;
|
let base_lamports = 4242424242;
|
||||||
let stake_address = Pubkey::new_unique();
|
let stake_address = Pubkey::new_unique();
|
||||||
let source_address = Pubkey::new_unique();
|
let source_address = Pubkey::new_unique();
|
||||||
@ -5142,6 +5193,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn try_merge(
|
fn try_merge(
|
||||||
|
invoke_context: &dyn InvokeContext,
|
||||||
stake_account: &KeyedAccount,
|
stake_account: &KeyedAccount,
|
||||||
source_account: &KeyedAccount,
|
source_account: &KeyedAccount,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
@ -5155,7 +5207,13 @@ mod tests {
|
|||||||
let test_source_keyed =
|
let test_source_keyed =
|
||||||
KeyedAccount::new(source_account.unsigned_key(), true, &test_source_account);
|
KeyedAccount::new(source_account.unsigned_key(), true, &test_source_account);
|
||||||
|
|
||||||
let result = test_stake_keyed.merge(&test_source_keyed, clock, stake_history, signers);
|
let result = test_stake_keyed.merge(
|
||||||
|
invoke_context,
|
||||||
|
&test_source_keyed,
|
||||||
|
clock,
|
||||||
|
stake_history,
|
||||||
|
signers,
|
||||||
|
);
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
assert_eq!(test_source_keyed.state(), Ok(StakeState::Uninitialized),);
|
assert_eq!(test_source_keyed.state(), Ok(StakeState::Uninitialized),);
|
||||||
}
|
}
|
||||||
@ -5164,6 +5222,7 @@ mod tests {
|
|||||||
|
|
||||||
// stake activation epoch, source initialized succeeds
|
// stake activation epoch, source initialized succeeds
|
||||||
assert!(try_merge(
|
assert!(try_merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5172,6 +5231,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.is_ok(),);
|
.is_ok(),);
|
||||||
assert!(try_merge(
|
assert!(try_merge(
|
||||||
|
&invoke_context,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5205,6 +5265,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
try_merge(
|
try_merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5216,6 +5277,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
try_merge(
|
try_merge(
|
||||||
|
&invoke_context,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5228,6 +5290,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
// Both fully activated works
|
// Both fully activated works
|
||||||
assert!(try_merge(
|
assert!(try_merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5288,6 +5351,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
try_merge(
|
try_merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5299,6 +5363,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
try_merge(
|
try_merge(
|
||||||
|
&invoke_context,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5312,6 +5377,7 @@ mod tests {
|
|||||||
|
|
||||||
// Both fully deactivated works
|
// Both fully deactivated works
|
||||||
assert!(try_merge(
|
assert!(try_merge(
|
||||||
|
&invoke_context,
|
||||||
&stake_keyed_account,
|
&stake_keyed_account,
|
||||||
&source_keyed_account,
|
&source_keyed_account,
|
||||||
&clock,
|
&clock,
|
||||||
@ -5769,6 +5835,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_things_can_merge() {
|
fn test_things_can_merge() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let good_stake = Stake {
|
let good_stake = Stake {
|
||||||
credits_observed: 4242,
|
credits_observed: 4242,
|
||||||
delegation: Delegation {
|
delegation: Delegation {
|
||||||
@ -5780,28 +5847,39 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let identical = good_stake;
|
let identical = good_stake;
|
||||||
assert!(MergeKind::active_stakes_can_merge(&good_stake, &identical).is_ok());
|
assert!(
|
||||||
|
MergeKind::active_stakes_can_merge(&invoke_context, &good_stake, &identical).is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
let bad_credits_observed = Stake {
|
let bad_credits_observed = Stake {
|
||||||
credits_observed: good_stake.credits_observed + 1,
|
credits_observed: good_stake.credits_observed + 1,
|
||||||
..good_stake
|
..good_stake
|
||||||
};
|
};
|
||||||
assert!(MergeKind::active_stakes_can_merge(&good_stake, &bad_credits_observed).is_err());
|
assert!(MergeKind::active_stakes_can_merge(
|
||||||
|
&invoke_context,
|
||||||
|
&good_stake,
|
||||||
|
&bad_credits_observed
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
let good_delegation = good_stake.delegation;
|
let good_delegation = good_stake.delegation;
|
||||||
let different_stake_ok = Delegation {
|
let different_stake_ok = Delegation {
|
||||||
stake: good_delegation.stake + 1,
|
stake: good_delegation.stake + 1,
|
||||||
..good_delegation
|
..good_delegation
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
MergeKind::active_delegations_can_merge(&good_delegation, &different_stake_ok).is_ok()
|
&invoke_context,
|
||||||
);
|
&good_delegation,
|
||||||
|
&different_stake_ok
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
let different_activation_epoch_ok = Delegation {
|
let different_activation_epoch_ok = Delegation {
|
||||||
activation_epoch: good_delegation.activation_epoch + 1,
|
activation_epoch: good_delegation.activation_epoch + 1,
|
||||||
..good_delegation
|
..good_delegation
|
||||||
};
|
};
|
||||||
assert!(MergeKind::active_delegations_can_merge(
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
|
&invoke_context,
|
||||||
&good_delegation,
|
&good_delegation,
|
||||||
&different_activation_epoch_ok
|
&different_activation_epoch_ok
|
||||||
)
|
)
|
||||||
@ -5811,18 +5889,25 @@ mod tests {
|
|||||||
voter_pubkey: Pubkey::new_unique(),
|
voter_pubkey: Pubkey::new_unique(),
|
||||||
..good_delegation
|
..good_delegation
|
||||||
};
|
};
|
||||||
assert!(MergeKind::active_delegations_can_merge(&good_delegation, &bad_voter).is_err());
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
|
&invoke_context,
|
||||||
|
&good_delegation,
|
||||||
|
&bad_voter
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
let bad_warmup_cooldown_rate = Delegation {
|
let bad_warmup_cooldown_rate = Delegation {
|
||||||
warmup_cooldown_rate: good_delegation.warmup_cooldown_rate + f64::EPSILON,
|
warmup_cooldown_rate: good_delegation.warmup_cooldown_rate + f64::EPSILON,
|
||||||
..good_delegation
|
..good_delegation
|
||||||
};
|
};
|
||||||
assert!(MergeKind::active_delegations_can_merge(
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
|
&invoke_context,
|
||||||
&good_delegation,
|
&good_delegation,
|
||||||
&bad_warmup_cooldown_rate
|
&bad_warmup_cooldown_rate
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
assert!(MergeKind::active_delegations_can_merge(
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
|
&invoke_context,
|
||||||
&bad_warmup_cooldown_rate,
|
&bad_warmup_cooldown_rate,
|
||||||
&good_delegation
|
&good_delegation
|
||||||
)
|
)
|
||||||
@ -5832,17 +5917,23 @@ mod tests {
|
|||||||
deactivation_epoch: 43,
|
deactivation_epoch: 43,
|
||||||
..good_delegation
|
..good_delegation
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
MergeKind::active_delegations_can_merge(&good_delegation, &bad_deactivation_epoch)
|
&invoke_context,
|
||||||
.is_err()
|
&good_delegation,
|
||||||
);
|
&bad_deactivation_epoch
|
||||||
assert!(
|
)
|
||||||
MergeKind::active_delegations_can_merge(&bad_deactivation_epoch, &good_delegation)
|
.is_err());
|
||||||
.is_err()
|
assert!(MergeKind::active_delegations_can_merge(
|
||||||
);
|
&invoke_context,
|
||||||
|
&bad_deactivation_epoch,
|
||||||
|
&good_delegation
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
// Identical Metas can merge
|
// Identical Metas can merge
|
||||||
assert!(MergeKind::metas_can_merge(&Meta::default(), &Meta::default()).is_ok());
|
assert!(
|
||||||
|
MergeKind::metas_can_merge(&invoke_context, &Meta::default(), &Meta::default()).is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
let mismatched_rent_exempt_reserve_ok = Meta {
|
let mismatched_rent_exempt_reserve_ok = Meta {
|
||||||
rent_exempt_reserve: 42,
|
rent_exempt_reserve: 42,
|
||||||
@ -5852,14 +5943,18 @@ mod tests {
|
|||||||
mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
|
mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
|
||||||
Meta::default().rent_exempt_reserve
|
Meta::default().rent_exempt_reserve
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(MergeKind::metas_can_merge(
|
||||||
MergeKind::metas_can_merge(&Meta::default(), &mismatched_rent_exempt_reserve_ok)
|
&invoke_context,
|
||||||
.is_ok()
|
&Meta::default(),
|
||||||
);
|
&mismatched_rent_exempt_reserve_ok
|
||||||
assert!(
|
)
|
||||||
MergeKind::metas_can_merge(&mismatched_rent_exempt_reserve_ok, &Meta::default())
|
.is_ok());
|
||||||
.is_ok()
|
assert!(MergeKind::metas_can_merge(
|
||||||
);
|
&invoke_context,
|
||||||
|
&mismatched_rent_exempt_reserve_ok,
|
||||||
|
&Meta::default()
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
let mismatched_authorized_fails = Meta {
|
let mismatched_authorized_fails = Meta {
|
||||||
authorized: Authorized {
|
authorized: Authorized {
|
||||||
@ -5872,12 +5967,18 @@ mod tests {
|
|||||||
mismatched_authorized_fails.authorized,
|
mismatched_authorized_fails.authorized,
|
||||||
Meta::default().authorized
|
Meta::default().authorized
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(MergeKind::metas_can_merge(
|
||||||
MergeKind::metas_can_merge(&Meta::default(), &mismatched_authorized_fails).is_err()
|
&invoke_context,
|
||||||
);
|
&Meta::default(),
|
||||||
assert!(
|
&mismatched_authorized_fails
|
||||||
MergeKind::metas_can_merge(&mismatched_authorized_fails, &Meta::default()).is_err()
|
)
|
||||||
);
|
.is_err());
|
||||||
|
assert!(MergeKind::metas_can_merge(
|
||||||
|
&invoke_context,
|
||||||
|
&mismatched_authorized_fails,
|
||||||
|
&Meta::default()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
let mismatched_lockup_fails = Meta {
|
let mismatched_lockup_fails = Meta {
|
||||||
lockup: Lockup {
|
lockup: Lockup {
|
||||||
@ -5888,12 +5989,23 @@ mod tests {
|
|||||||
..Meta::default()
|
..Meta::default()
|
||||||
};
|
};
|
||||||
assert_ne!(mismatched_lockup_fails.lockup, Meta::default().lockup);
|
assert_ne!(mismatched_lockup_fails.lockup, Meta::default().lockup);
|
||||||
assert!(MergeKind::metas_can_merge(&Meta::default(), &mismatched_lockup_fails).is_err());
|
assert!(MergeKind::metas_can_merge(
|
||||||
assert!(MergeKind::metas_can_merge(&mismatched_lockup_fails, &Meta::default()).is_err());
|
&invoke_context,
|
||||||
|
&Meta::default(),
|
||||||
|
&mismatched_lockup_fails
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
assert!(MergeKind::metas_can_merge(
|
||||||
|
&invoke_context,
|
||||||
|
&mismatched_lockup_fails,
|
||||||
|
&Meta::default()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_kind_get_if_mergeable() {
|
fn test_merge_kind_get_if_mergeable() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let authority_pubkey = Pubkey::new_unique();
|
let authority_pubkey = Pubkey::new_unique();
|
||||||
let initial_lamports = 4242424242;
|
let initial_lamports = 4242424242;
|
||||||
let rent = Rent::default();
|
let rent = Rent::default();
|
||||||
@ -5918,7 +6030,13 @@ mod tests {
|
|||||||
|
|
||||||
// Uninitialized state fails
|
// Uninitialized state fails
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap_err(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
InstructionError::InvalidAccountData
|
InstructionError::InvalidAccountData
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -5927,7 +6045,13 @@ mod tests {
|
|||||||
.set_state(&StakeState::RewardsPool)
|
.set_state(&StakeState::RewardsPool)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap_err(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
InstructionError::InvalidAccountData
|
InstructionError::InvalidAccountData
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -5936,7 +6060,13 @@ mod tests {
|
|||||||
.set_state(&StakeState::Initialized(meta))
|
.set_state(&StakeState::Initialized(meta))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
MergeKind::Inactive(meta, stake_lamports)
|
MergeKind::Inactive(meta, stake_lamports)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -5978,7 +6108,13 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
// activation_epoch succeeds
|
// activation_epoch succeeds
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
MergeKind::ActivationEpoch(meta, stake),
|
MergeKind::ActivationEpoch(meta, stake),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -6001,8 +6137,13 @@ mod tests {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history)
|
MergeKind::get_if_mergeable(
|
||||||
.unwrap_err(),
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
InstructionError::from(StakeError::MergeTransientStake),
|
InstructionError::from(StakeError::MergeTransientStake),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6019,7 +6160,13 @@ mod tests {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
MergeKind::FullyActive(meta, stake),
|
MergeKind::FullyActive(meta, stake),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6036,7 +6183,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// deactivation epoch fails, fully transient/deactivating
|
// deactivation epoch fails, fully transient/deactivating
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap_err(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
InstructionError::from(StakeError::MergeTransientStake),
|
InstructionError::from(StakeError::MergeTransientStake),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -6059,21 +6212,33 @@ mod tests {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history)
|
MergeKind::get_if_mergeable(
|
||||||
.unwrap_err(),
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
InstructionError::from(StakeError::MergeTransientStake),
|
InstructionError::from(StakeError::MergeTransientStake),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// first fully-deactivated epoch succeeds
|
// first fully-deactivated epoch succeeds
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MergeKind::get_if_mergeable(&stake_keyed_account, &clock, &stake_history).unwrap(),
|
MergeKind::get_if_mergeable(
|
||||||
|
&invoke_context,
|
||||||
|
&stake_keyed_account,
|
||||||
|
&clock,
|
||||||
|
&stake_history
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
MergeKind::Inactive(meta, stake_lamports),
|
MergeKind::Inactive(meta, stake_lamports),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_kind_merge() {
|
fn test_merge_kind_merge() {
|
||||||
|
let invoke_context = MockInvokeContext::default();
|
||||||
let lamports = 424242;
|
let lamports = 424242;
|
||||||
let meta = Meta {
|
let meta = Meta {
|
||||||
rent_exempt_reserve: 42,
|
rent_exempt_reserve: 42,
|
||||||
@ -6090,29 +6255,48 @@ mod tests {
|
|||||||
let activation_epoch = MergeKind::ActivationEpoch(meta, stake);
|
let activation_epoch = MergeKind::ActivationEpoch(meta, stake);
|
||||||
let fully_active = MergeKind::FullyActive(meta, stake);
|
let fully_active = MergeKind::FullyActive(meta, stake);
|
||||||
|
|
||||||
assert_eq!(inactive.clone().merge(inactive.clone()).unwrap(), None);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inactive.clone().merge(activation_epoch.clone()).unwrap(),
|
inactive
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, inactive.clone())
|
||||||
|
.unwrap(),
|
||||||
None
|
None
|
||||||
);
|
);
|
||||||
assert!(inactive.clone().merge(fully_active.clone()).is_err());
|
assert_eq!(
|
||||||
|
inactive
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, activation_epoch.clone())
|
||||||
|
.unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert!(inactive
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, fully_active.clone())
|
||||||
|
.is_err());
|
||||||
assert!(activation_epoch
|
assert!(activation_epoch
|
||||||
.clone()
|
.clone()
|
||||||
.merge(fully_active.clone())
|
.merge(&invoke_context, fully_active.clone())
|
||||||
.is_err());
|
.is_err());
|
||||||
assert!(fully_active.clone().merge(inactive.clone()).is_err());
|
|
||||||
assert!(fully_active
|
assert!(fully_active
|
||||||
.clone()
|
.clone()
|
||||||
.merge(activation_epoch.clone())
|
.merge(&invoke_context, inactive.clone())
|
||||||
|
.is_err());
|
||||||
|
assert!(fully_active
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, activation_epoch.clone())
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
let new_state = activation_epoch.clone().merge(inactive).unwrap().unwrap();
|
let new_state = activation_epoch
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, inactive)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
let delegation = new_state.delegation().unwrap();
|
let delegation = new_state.delegation().unwrap();
|
||||||
assert_eq!(delegation.stake, stake.delegation.stake + lamports);
|
assert_eq!(delegation.stake, stake.delegation.stake + lamports);
|
||||||
|
|
||||||
let new_state = activation_epoch
|
let new_state = activation_epoch
|
||||||
.clone()
|
.clone()
|
||||||
.merge(activation_epoch)
|
.merge(&invoke_context, activation_epoch)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let delegation = new_state.delegation().unwrap();
|
let delegation = new_state.delegation().unwrap();
|
||||||
@ -6121,7 +6305,11 @@ mod tests {
|
|||||||
2 * stake.delegation.stake + meta.rent_exempt_reserve
|
2 * stake.delegation.stake + meta.rent_exempt_reserve
|
||||||
);
|
);
|
||||||
|
|
||||||
let new_state = fully_active.clone().merge(fully_active).unwrap().unwrap();
|
let new_state = fully_active
|
||||||
|
.clone()
|
||||||
|
.merge(&invoke_context, fully_active)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
let delegation = new_state.delegation().unwrap();
|
let delegation = new_state.delegation().unwrap();
|
||||||
assert_eq!(delegation.stake, 2 * stake.delegation.stake);
|
assert_eq!(delegation.stake, 2 * stake.delegation.stake);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user