diff --git a/account-decoder/src/parse_stake.rs b/account-decoder/src/parse_stake.rs index f28443a05f4658..22a6aa506c66f4 100644 --- a/account-decoder/src/parse_stake.rs +++ b/account-decoder/src/parse_stake.rs @@ -136,7 +136,7 @@ impl From for UiDelegation { #[cfg(test)] mod test { - use {super::*, bincode::serialize, solana_sdk::stake::state::DeactivationFlag}; + use {super::*, bincode::serialize, solana_sdk::stake::state::StakeFlags}; #[test] fn test_parse_stake() { @@ -194,7 +194,7 @@ mod test { credits_observed: 10, }; - let stake_state = StakeState::Stake(meta, stake, DeactivationFlag::Empty); + let stake_state = StakeState::Stake(meta, stake, StakeFlags::empty()); let stake_data = serialize(&stake_state).unwrap(); assert_eq!( parse_stake(&stake_data).unwrap(), diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 6796815856b955..192fcf8c4a3229 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -526,9 +526,7 @@ mod tests { set_lockup_checked, AuthorizeCheckedWithSeedArgs, AuthorizeWithSeedArgs, LockupArgs, StakeError, }, - state::{ - Authorized, DeactivationFlag, Lockup, StakeActivationStatus, StakeAuthorize, - }, + state::{Authorized, Lockup, StakeActivationStatus, StakeAuthorize, StakeFlags}, MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION, }, stake_history::{StakeHistory, StakeHistoryEntry}, @@ -672,7 +670,7 @@ mod tests { }, ..Stake::default() }, - DeactivationFlag::Empty, + StakeFlags::empty(), ) } @@ -4808,11 +4806,7 @@ mod tests { for split_to_state in &[ StakeState::Initialized(Meta::default()), - StakeState::Stake( - Meta::default(), - Stake::default(), - DeactivationFlag::default(), - ), + StakeState::Stake(Meta::default(), Stake::default(), StakeFlags::default()), StakeState::RewardsPool, ] { let split_to_account = AccountSharedData::new_data_with_space( @@ -4978,7 +4972,7 @@ mod tests { ); // verify no stake leakage in the case of a stake - if let StakeState::Stake(meta, stake, deactivation_flag) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { assert_eq!( accounts[1].state(), Ok(StakeState::Stake( @@ -4990,7 +4984,7 @@ mod tests { }, ..*stake }, - *deactivation_flag, + *stake_flags, )) ); assert_eq!(accounts[0].lamports(), *minimum_balance,); @@ -5081,7 +5075,7 @@ mod tests { stake_lamports + initial_balance, ); - if let StakeState::Stake(meta, stake, deactivation_flag) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { let expected_stake = stake_lamports / 2 - (rent_exempt_reserve.saturating_sub(initial_balance)); assert_eq!( @@ -5095,7 +5089,7 @@ mod tests { }, ..stake }, - deactivation_flag + stake_flags )), accounts[1].state(), ); @@ -5115,7 +5109,7 @@ mod tests { }, ..stake }, - deactivation_flag, + stake_flags, )), accounts[0].state(), ); @@ -5206,7 +5200,7 @@ mod tests { stake_lamports + initial_balance ); - if let StakeState::Stake(meta, stake, deactivation_flag) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { let expected_split_meta = Meta { authorized: Authorized::auto(&stake_address), rent_exempt_reserve: split_rent_exempt_reserve, @@ -5225,7 +5219,7 @@ mod tests { }, ..stake }, - deactivation_flag, + stake_flags, )), accounts[1].state() ); @@ -5245,7 +5239,7 @@ mod tests { }, ..stake }, - deactivation_flag, + stake_flags, )), accounts[0].state() ); @@ -5401,7 +5395,7 @@ mod tests { assert_eq!(Ok(*state), accounts[1].state()); assert_eq!(Ok(StakeState::Uninitialized), accounts[0].state()); } - StakeState::Stake(meta, stake, deactivation_flag) => { + StakeState::Stake(meta, stake, stake_flags) => { assert_eq!( Ok(StakeState::Stake( *meta, @@ -5412,7 +5406,7 @@ mod tests { }, ..*stake }, - *deactivation_flag + *stake_flags )), accounts[1].state() ); @@ -5497,7 +5491,7 @@ mod tests { stake_lamports + initial_balance ); - if let StakeState::Stake(meta, stake, deactivation_flag) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { assert_eq!( Ok(StakeState::Stake( meta, @@ -5508,7 +5502,7 @@ mod tests { }, ..stake }, - deactivation_flag, + stake_flags, )), accounts[1].state() ); @@ -5620,7 +5614,7 @@ mod tests { ); assert_eq!(Ok(StakeState::Uninitialized), accounts[0].state()); } - StakeState::Stake(_meta, stake, deactivation_flag) => { + StakeState::Stake(_meta, stake, stake_flags) => { // Expected stake should reflect original stake amount so that extra lamports // from the rent_exempt_reserve inequality do not magically activate let expected_stake = stake_lamports - source_rent_exempt_reserve; @@ -5635,7 +5629,7 @@ mod tests { }, ..*stake }, - *deactivation_flag, + *stake_flags, )), accounts[1].state() ); @@ -5750,7 +5744,7 @@ mod tests { StakeState::Initialized(meta) => { assert_eq!(accounts[0].state(), Ok(StakeState::Initialized(*meta)),); } - StakeState::Stake(meta, stake, deactivation_flag) => { + StakeState::Stake(meta, stake, stake_flags) => { let expected_stake = stake.delegation.stake + merge_from_state .stake() @@ -5770,7 +5764,7 @@ mod tests { }, ..*stake }, - *deactivation_flag, + *stake_flags, )), ); } @@ -5804,7 +5798,7 @@ mod tests { }; let stake_account = AccountSharedData::new_data_with_space( stake_lamports, - &StakeState::Stake(meta, stake, DeactivationFlag::Empty), + &StakeState::Stake(meta, stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6131,7 +6125,7 @@ mod tests { }; let stake_account = AccountSharedData::new_data_with_space( stake_lamports, - &StakeState::Stake(meta, stake, DeactivationFlag::Empty), + &StakeState::Stake(meta, stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6147,7 +6141,7 @@ mod tests { }; let merge_from_account = AccountSharedData::new_data_with_space( merge_from_lamports, - &StakeState::Stake(meta, merge_from_stake, DeactivationFlag::Empty), + &StakeState::Stake(meta, merge_from_stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6303,7 +6297,7 @@ mod tests { }; transaction_accounts[0] .1 - .set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); } if clock.epoch == merge_from_deactivation_epoch { @@ -6320,7 +6314,7 @@ mod tests { .set_state(&StakeState::Stake( meta, merge_from_stake, - DeactivationFlag::Empty, + StakeFlags::empty(), )) .unwrap(); } @@ -6532,7 +6526,7 @@ mod tests { 1, /* activation_epoch */ &stake_config::Config::default(), ), - DeactivationFlag::Empty, + StakeFlags::empty(), ); let stake_account = AccountSharedData::new_data_with_space( @@ -6739,7 +6733,7 @@ mod tests { 1, /* activation_epoch */ &stake_config::Config::default(), ), - DeactivationFlag::Empty, + StakeFlags::empty(), )) .unwrap(); @@ -6833,7 +6827,7 @@ mod tests { activation_epoch, &stake_config::Config::default(), ), - DeactivationFlag::Empty, + StakeFlags::empty(), ); if let Some(expected_stake_activation_status) = expected_stake_activation_status { @@ -7142,14 +7136,14 @@ mod tests { // let mut stake_account_over_allocated = prepare_stake_account(0 /*activation_epoch:*/, None); - if let StakeState::Stake(mut meta, stake, deactivation_flag) = stake_account_over_allocated + if let StakeState::Stake(mut meta, stake, stake_flags) = stake_account_over_allocated .borrow_mut() .deserialize_data() .unwrap() { meta.rent_exempt_reserve += 42; stake_account_over_allocated - .set_state(&StakeState::Stake(meta, stake, deactivation_flag)) + .set_state(&StakeState::Stake(meta, stake, stake_flags)) .unwrap(); } stake_account_over_allocated @@ -7343,7 +7337,7 @@ mod tests { ); deactivating_stake_account - .set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); } let _ = process_instruction_redelegate( @@ -7362,11 +7356,10 @@ mod tests { // (less than `minimum_delegation + rent_exempt_reserve`) // let mut stake_account_too_few_lamports = stake_account.clone(); - if let StakeState::Stake(meta, mut stake, deactivation_flag) = - stake_account_too_few_lamports - .borrow_mut() - .deserialize_data() - .unwrap() + if let StakeState::Stake(meta, mut stake, stake_flags) = stake_account_too_few_lamports + .borrow_mut() + .deserialize_data() + .unwrap() { stake.delegation.stake -= 1; assert_eq!( @@ -7374,7 +7367,7 @@ mod tests { minimum_delegation + rent_exempt_reserve - 1 ); stake_account_too_few_lamports - .set_state(&StakeState::Stake(meta, stake, deactivation_flag)) + .set_state(&StakeState::Stake(meta, stake, stake_flags)) .unwrap(); } else { panic!("Invalid stake_account"); diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 5728dc1511b10f..0626226623ff9f 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -609,9 +609,9 @@ pub fn delegate( clock.epoch, config, ); - stake_account.set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + stake_account.set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) } - StakeState::Stake(meta, mut stake, deactivation_flag) => { + StakeState::Stake(meta, mut stake, stake_flags) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; let ValidatedDelegatedInfo { stake_amount } = validate_delegated_amount(&stake_account, &meta, feature_set)?; @@ -625,7 +625,7 @@ pub fn delegate( stake_history, config, )?; - stake_account.set_state(&StakeState::Stake(meta, stake, deactivation_flag)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } _ => Err(InstructionError::InvalidAccountData), } @@ -637,13 +637,14 @@ pub fn deactivate( clock: &Clock, signers: &HashSet, ) -> Result<(), InstructionError> { - if let StakeState::Stake(meta, mut stake, deactivate_flag) = stake_account.get_state()? { + if let StakeState::Stake(meta, mut stake, mut stake_flags) = stake_account.get_state()? { meta.authorized.check(signers, StakeAuthorize::Staker)?; let stake_history = invoke_context.get_sysvar_cache().get_stake_history()?; - stake.deactivate(clock.epoch, deactivate_flag, Some(&stake_history))?; + stake.deactivate(clock.epoch, stake_flags, Some(&stake_history))?; // After deactivation, need to clear DeactivationFlag to Empty. // So that future activation and deactivation are not subject to the `MustFullyActivateBeforeDeactivationIsPermitted` restriction. - stake_account.set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + stake_flags.remove(StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED); + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } else { Err(InstructionError::InvalidAccountData) } @@ -660,9 +661,9 @@ pub fn set_lockup( meta.set_lockup(lockup, signers, clock)?; stake_account.set_state(&StakeState::Initialized(meta)) } - StakeState::Stake(mut meta, stake, deactivation_flag) => { + StakeState::Stake(mut meta, stake, stake_flags) => { meta.set_lockup(lockup, signers, clock)?; - stake_account.set_state(&StakeState::Stake(meta, stake, deactivation_flag)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } _ => Err(InstructionError::InvalidAccountData), } @@ -699,7 +700,7 @@ pub fn split( drop(stake_account); match stake_state { - StakeState::Stake(meta, mut stake, deactivation_flag) => { + StakeState::Stake(meta, mut stake, stake_flags) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; let minimum_delegation = crate::get_minimum_delegation(&invoke_context.feature_set); let validated_split_info = validate_split_amount( @@ -769,15 +770,11 @@ pub fn split( let mut stake_account = instruction_context .try_borrow_instruction_account(transaction_context, stake_account_index)?; - stake_account.set_state(&StakeState::Stake(meta, stake, deactivation_flag))?; + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags))?; drop(stake_account); let mut split = instruction_context .try_borrow_instruction_account(transaction_context, split_index)?; - split.set_state(&StakeState::Stake( - split_meta, - split_stake, - deactivation_flag, - ))?; + split.set_state(&StakeState::Stake(split_meta, split_stake, stake_flags))?; } StakeState::Initialized(meta) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; @@ -961,7 +958,7 @@ pub fn redelegate( let vote_state = vote_account.get_state::()?; let (stake_meta, effective_stake) = - if let StakeState::Stake(meta, stake, _deactivation_flag) = stake_account.get_state()? { + if let StakeState::Stake(meta, stake, _stake_flags) = stake_account.get_state()? { let stake_history = invoke_context.get_sysvar_cache().get_stake_history()?; let status = stake .delegation @@ -1017,7 +1014,7 @@ pub fn redelegate( clock.epoch, config, ), - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, + StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, ))?; Ok(()) @@ -1049,7 +1046,7 @@ pub fn withdraw( let mut stake_account = instruction_context .try_borrow_instruction_account(transaction_context, stake_account_index)?; let (lockup, reserve, is_staked) = match stake_account.get_state()? { - StakeState::Stake(meta, stake, _deactivation_flag) => { + StakeState::Stake(meta, stake, _stake_flag) => { meta.authorized .check(&signers, StakeAuthorize::Withdrawer)?; // if we have a deactivation epoch and we're in cooldown @@ -1172,7 +1169,7 @@ pub(crate) fn deactivate_delinquent( return Err(StakeError::InsufficientReferenceVotes.into()); } - if let StakeState::Stake(meta, mut stake, deactivate_flag) = stake_account.get_state()? { + if let StakeState::Stake(meta, mut stake, mut stake_flags) = stake_account.get_state()? { if stake.delegation.voter_pubkey != *delinquent_vote_account_pubkey { return Err(StakeError::VoteAddressMismatch.into()); } @@ -1181,9 +1178,10 @@ pub(crate) fn deactivate_delinquent( // voted in the last `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` if eligible_for_deactivate_delinquent(&delinquent_vote_state.epoch_credits, current_epoch) { let stake_history = invoke_context.get_sysvar_cache().get_stake_history()?; - stake.deactivate(current_epoch, deactivate_flag, Some(&stake_history))?; + stake.deactivate(current_epoch, stake_flags, Some(&stake_history))?; // After deactivation, need to clear DeactivationFlag to Empty. - stake_account.set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + stake_flags.remove(StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED); + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } else { Err(StakeError::MinimumDelinquentEpochsForDeactivationNotMet.into()) } @@ -1335,8 +1333,8 @@ fn validate_split_amount( #[derive(Clone, Debug, PartialEq)] enum MergeKind { - Inactive(Meta, u64, DeactivationFlag), - ActivationEpoch(Meta, Stake, DeactivationFlag), + Inactive(Meta, u64, StakeFlags), + ActivationEpoch(Meta, Stake, StakeFlags), FullyActive(Meta, Stake), } @@ -1365,7 +1363,7 @@ impl MergeKind { stake_history: &StakeHistory, ) -> Result { match stake_state { - StakeState::Stake(meta, stake, deactivation_flag) => { + StakeState::Stake(meta, stake, stake_flags) => { // stake must not be in a transient state. Transient here meaning // activating or deactivating with non-zero effective stake. let status = stake @@ -1373,8 +1371,8 @@ impl MergeKind { .stake_activating_and_deactivating(clock.epoch, Some(stake_history)); match (status.effective, status.activating, status.deactivating) { - (0, 0, 0) => Ok(Self::Inactive(*meta, stake_lamports, *deactivation_flag)), - (0, _, _) => Ok(Self::ActivationEpoch(*meta, *stake, *deactivation_flag)), + (0, 0, 0) => Ok(Self::Inactive(*meta, stake_lamports, *stake_flags)), + (0, _, _) => Ok(Self::ActivationEpoch(*meta, *stake, *stake_flags)), (_, 0, 0) => Ok(Self::FullyActive(*meta, *stake)), _ => { let err = StakeError::MergeTransientStake; @@ -1383,11 +1381,9 @@ impl MergeKind { } } } - StakeState::Initialized(meta) => Ok(Self::Inactive( - *meta, - stake_lamports, - DeactivationFlag::Empty, - )), + StakeState::Initialized(meta) => { + Ok(Self::Inactive(*meta, stake_lamports, StakeFlags::empty())) + } _ => Err(InstructionError::InvalidAccountData), } } @@ -1484,19 +1480,19 @@ impl MergeKind { (Self::Inactive(_, _, _), Self::Inactive(_, _, _)) => None, (Self::Inactive(_, _, _), Self::ActivationEpoch(_, _, _)) => None, ( - Self::ActivationEpoch(meta, mut stake, deactivation_flag), - Self::Inactive(_, source_lamports, source_deactivation_flag), + Self::ActivationEpoch(meta, mut stake, stake_flags), + Self::Inactive(_, source_lamports, source_stake_flags), ) => { stake.delegation.stake = checked_add(stake.delegation.stake, source_lamports)?; Some(StakeState::Stake( meta, stake, - DeactivationFlag::merge(deactivation_flag, source_deactivation_flag), + stake_flags.union(source_stake_flags), )) } ( - Self::ActivationEpoch(meta, mut stake, deactivation_flag), - Self::ActivationEpoch(source_meta, source_stake, source_deactivation_flag), + Self::ActivationEpoch(meta, mut stake, stake_flags), + Self::ActivationEpoch(source_meta, source_stake, source_stake_flags), ) => { let source_lamports = checked_add( source_meta.rent_exempt_reserve, @@ -1508,11 +1504,10 @@ impl MergeKind { source_lamports, source_stake.credits_observed, )?; - Some(StakeState::Stake( meta, stake, - DeactivationFlag::merge(deactivation_flag, source_deactivation_flag), + stake_flags.union(source_stake_flags), )) } (Self::FullyActive(meta, mut stake), Self::FullyActive(_, source_stake)) => { @@ -1526,7 +1521,7 @@ impl MergeKind { source_stake.delegation.stake, source_stake.credits_observed, )?; - Some(StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + Some(StakeState::Stake(meta, stake, StakeFlags::empty())) } _ => return Err(StakeError::MergeMismatch.into()), }; @@ -1613,7 +1608,7 @@ pub fn redeem_rewards( inflation_point_calc_tracer: Option, credits_auto_rewind: bool, ) -> Result<(u64, u64), InstructionError> { - if let StakeState::Stake(meta, mut stake, deactivation_flag) = stake_state { + if let StakeState::Stake(meta, mut stake, stake_flags) = stake_state { if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer.as_ref() { inflation_point_calc_tracer( &InflationPointCalculationEvent::EffectiveStakeAtRewardedEpoch( @@ -1638,7 +1633,7 @@ pub fn redeem_rewards( credits_auto_rewind, ) { stake_account.checked_add_lamports(stakers_reward)?; - stake_account.set_state(&StakeState::Stake(meta, stake, deactivation_flag))?; + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags))?; Ok((stakers_reward, voters_reward)) } else { @@ -1656,7 +1651,7 @@ pub fn calculate_points( vote_state: &VoteState, stake_history: Option<&StakeHistory>, ) -> Result { - if let StakeState::Stake(_meta, stake, _deactivation_flag) = stake_state { + if let StakeState::Stake(_meta, stake, _stake_flags) = stake_state { Ok(calculate_stake_points( stake, vote_state, @@ -1818,7 +1813,7 @@ fn do_create_account( activation_epoch, &Config::default(), ), - DeactivationFlag::Empty, + StakeFlags::empty(), )) .expect("set_state"); @@ -3377,7 +3372,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::Inactive(meta, stake_lamports, DeactivationFlag::Empty) + MergeKind::Inactive(meta, stake_lamports, StakeFlags::empty()) ); clock.epoch = 0; @@ -3414,7 +3409,7 @@ mod tests { ..Stake::default() }; stake_account - .set_state(&StakeState::Stake(meta, stake, DeactivationFlag::Empty)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); // activation_epoch succeeds assert_eq!( @@ -3426,7 +3421,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::ActivationEpoch(meta, stake, DeactivationFlag::Empty), + MergeKind::ActivationEpoch(meta, stake, StakeFlags::empty()), ); // all paritially activated, transient epochs fail @@ -3548,7 +3543,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::Inactive(meta, stake_lamports, DeactivationFlag::Empty), + MergeKind::Inactive(meta, stake_lamports, StakeFlags::empty()), ); } @@ -3568,8 +3563,8 @@ mod tests { }, ..Stake::default() }; - let inactive = MergeKind::Inactive(Meta::default(), lamports, DeactivationFlag::Empty); - let activation_epoch = MergeKind::ActivationEpoch(meta, stake, DeactivationFlag::Empty); + let inactive = MergeKind::Inactive(Meta::default(), lamports, StakeFlags::empty()); + let activation_epoch = MergeKind::ActivationEpoch(meta, stake, StakeFlags::empty()); let fully_active = MergeKind::FullyActive(meta, stake); assert_eq!( @@ -3663,8 +3658,8 @@ mod tests { }; // activating stake merge, match credits observed - let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a, DeactivationFlag::Empty); - let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b, DeactivationFlag::Empty); + let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a, StakeFlags::empty()); + let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b, StakeFlags::empty()); let new_stake = activation_epoch_a .merge(&invoke_context, activation_epoch_b, &clock) .unwrap() @@ -3698,8 +3693,8 @@ mod tests { }, credits_observed: credits_b, }; - let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a, DeactivationFlag::Empty); - let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b, DeactivationFlag::Empty); + let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a, StakeFlags::empty()); + let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b, StakeFlags::empty()); let new_stake = activation_epoch_a .merge(&invoke_context, activation_epoch_b, &clock) .unwrap() diff --git a/runtime/src/non_circulating_supply.rs b/runtime/src/non_circulating_supply.rs index 13ab3b21e31a28..7cde838c4d72dc 100644 --- a/runtime/src/non_circulating_supply.rs +++ b/runtime/src/non_circulating_supply.rs @@ -60,7 +60,7 @@ pub fn calculate_non_circulating_supply(bank: &Arc) -> ScanResult { + StakeState::Stake(meta, _stake, _stake_flags) => { if meta.lockup.is_in_force(&clock, None) || withdraw_authority_list.contains(&meta.authorized.withdrawer) { diff --git a/runtime/src/stake_account.rs b/runtime/src/stake_account.rs index 70c9fcc025db81..4ff960cf538ec0 100644 --- a/runtime/src/stake_account.rs +++ b/runtime/src/stake_account.rs @@ -125,13 +125,10 @@ impl AbiExample for StakeAccount { fn example() -> Self { use solana_sdk::{ account::Account, - stake::state::{DeactivationFlag, Meta, Stake}, + stake::state::{Meta, Stake, StakeFlags}, }; - let stake_state = StakeState::Stake( - Meta::example(), - Stake::example(), - DeactivationFlag::example(), - ); + let stake_state = + StakeState::Stake(Meta::example(), Stake::example(), StakeFlags::example()); let mut account = Account::example(); account.data.resize(200, 0u8); account.owner = solana_stake_program::id(); diff --git a/runtime/tests/stake.rs b/runtime/tests/stake.rs index df0e5aece8acdb..7240971391b527 100755 --- a/runtime/tests/stake.rs +++ b/runtime/tests/stake.rs @@ -362,7 +362,7 @@ fn test_stake_account_lifetime() { // Test that correct lamports are staked let account = bank.get_account(&stake_pubkey).expect("account not found"); let stake_state = account.state().expect("couldn't unpack account data"); - if let StakeState::Stake(_meta, stake, _deactivation_flag) = stake_state { + if let StakeState::Stake(_meta, stake, _stake_flags) = stake_state { assert_eq!(stake.delegation.stake, stake_starting_delegation,); } else { panic!("wrong account type found") @@ -386,7 +386,7 @@ fn test_stake_account_lifetime() { // Test that lamports are still staked let account = bank.get_account(&stake_pubkey).expect("account not found"); let stake_state = account.state().expect("couldn't unpack account data"); - if let StakeState::Stake(_meta, stake, _deactivation_flag) = stake_state { + if let StakeState::Stake(_meta, stake, _stake_flags) = stake_state { assert_eq!(stake.delegation.stake, stake_starting_delegation,); } else { panic!("wrong account type found") diff --git a/sdk/program/src/stake/state.rs b/sdk/program/src/stake/state.rs index b1e1a597fd8049..a16da52458f826 100644 --- a/sdk/program/src/stake/state.rs +++ b/sdk/program/src/stake/state.rs @@ -10,44 +10,33 @@ use { }, stake_history::{StakeHistory, StakeHistoryEntry}, }, + bitflags::bitflags, borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize}, std::collections::HashSet, }; pub type StakeActivationStatus = StakeHistoryEntry; -#[repr(u8)] -#[derive( - Debug, - Default, - Serialize, - Deserialize, - PartialEq, - Clone, - Copy, - AbiExample, - BorshDeserialize, - BorshSchema, - BorshSerialize, -)] - -pub enum DeactivationFlag { - #[default] - Empty = 0, - MustFullyActivateBeforeDeactivationIsPermitted = 1, +bitflags! { + #[derive( + Serialize, + Deserialize, + AbiExample, + BorshDeserialize, + BorshSchema, + BorshSerialize, + )] + // Additional flags for stake state. + // Currently only bit 1 is used. The other 7 bits are reserved for future. + pub struct StakeFlags : u8 { + // Stake must be fully activated before deactivation is allowed + const MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED = 0b0000_0001; + } } -impl DeactivationFlag { - /// Given two deactivation flags from two stake states to merge, Return the deactivationFlag - /// for the merged stake state. - pub fn merge(deactivation_flag1: Self, deactivation_flag2: Self) -> Self { - if deactivation_flag1 == DeactivationFlag::Empty - && deactivation_flag2 == DeactivationFlag::Empty - { - DeactivationFlag::Empty - } else { - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted - } +impl Default for StakeFlags { + fn default() -> Self { + StakeFlags::empty() } } @@ -57,7 +46,7 @@ pub enum StakeState { #[default] Uninitialized, Initialized(Meta), - Stake(Meta, Stake, DeactivationFlag), + Stake(Meta, Stake, StakeFlags), RewardsPool, } @@ -73,12 +62,8 @@ impl BorshDeserialize for StakeState { 2 => { let meta: Meta = BorshDeserialize::deserialize(buf)?; let stake: Stake = BorshDeserialize::deserialize(buf)?; - let deactivate_flag: DeactivationFlag = BorshDeserialize::deserialize(buf)?; - - // To make BorshSerializer compatible with bincode serializer, 3 padding bytes were added during serialization. - // So consume those 3 bytes here. - let _pad: [u8; 3] = BorshDeserialize::deserialize(buf)?; - Ok(StakeState::Stake(meta, stake, deactivate_flag)) + let stake_flags: StakeFlags = BorshDeserialize::deserialize(buf)?; + Ok(StakeState::Stake(meta, stake, stake_flags)) } 3 => Ok(StakeState::RewardsPool), _ => Err(io::Error::new( @@ -97,16 +82,11 @@ impl BorshSerialize for StakeState { writer.write_all(&1u32.to_le_bytes())?; meta.serialize(writer) } - StakeState::Stake(meta, stake, deactivate_flag) => { + StakeState::Stake(meta, stake, stake_flags) => { writer.write_all(&2u32.to_le_bytes())?; meta.serialize(writer)?; stake.serialize(writer)?; - deactivate_flag.serialize(writer)?; - - // bincode serializer add 3 more padding bytes for `Stake` variant to pad the last - // u8 enum increase the size from 197 bytes to 200 bytes. - // To make BorshSerializer compatible with bincode serializer, add 3 padding bytes here. - writer.write_all(&[0; 3]) // padding + stake_flags.serialize(writer) } StakeState::RewardsPool => writer.write_all(&3u32.to_le_bytes()), } @@ -121,21 +101,21 @@ impl StakeState { pub fn stake(&self) -> Option { match self { - StakeState::Stake(_meta, stake, _deactivation_flag) => Some(*stake), + StakeState::Stake(_meta, stake, _stake_flags) => Some(*stake), _ => None, } } pub fn delegation(&self) -> Option { match self { - StakeState::Stake(_meta, stake, _deactivation_flag) => Some(stake.delegation), + StakeState::Stake(_meta, stake, _stake_flags) => Some(stake.delegation), _ => None, } } pub fn authorized(&self) -> Option { match self { - StakeState::Stake(meta, _stake, _deactivation_flag) => Some(meta.authorized), + StakeState::Stake(meta, _stake, _stake_flags) => Some(meta.authorized), StakeState::Initialized(meta) => Some(meta.authorized), _ => None, } @@ -147,7 +127,7 @@ impl StakeState { pub fn meta(&self) -> Option { match self { - StakeState::Stake(meta, _stake, _deactivation_flag) => Some(*meta), + StakeState::Stake(meta, _stake, _stake_flags) => Some(*meta), StakeState::Initialized(meta) => Some(*meta), _ => None, } @@ -613,17 +593,16 @@ impl Stake { pub fn deactivate( &mut self, epoch: Epoch, - deactivation_flag: DeactivationFlag, + stake_flags: StakeFlags, history: Option<&StakeHistory>, ) -> Result<(), StakeError> { if self.delegation.deactivation_epoch != std::u64::MAX { return Err(StakeError::AlreadyDeactivated); } - // when deactivation_flag is set to - // MustFullyActivateBeforeDeactivationIsPermittedFlag, deactivation is - // only permitted when the stake delegation activating amount is zero. - if deactivation_flag == DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted { + // when MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED flag is set on stake_flags, + // deactivation is only permitted when the stake delegation activating amount is zero. + if stake_flags.contains(StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED) { let status = self .delegation .stake_activating_and_deactivating(epoch, history); @@ -655,6 +634,7 @@ mod test { fn check_borsh_serialization(stake: StakeState) { let bincode_serialized = serialize(&stake).unwrap(); let borsh_serialized = StakeState::try_to_vec(&stake).unwrap(); + assert_eq!(bincode_serialized, borsh_serialized); } @@ -694,7 +674,7 @@ mod test { }, credits_observed: 1, }, - DeactivationFlag::Empty, + StakeFlags::empty(), )); } @@ -729,7 +709,7 @@ mod test { }, credits_observed: 1, }, - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, + StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, )); } @@ -791,41 +771,9 @@ mod test { assert_eq!(borsh_serialized[FLAG_OFFSET], expected); }; check_flag( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, + StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, 1, ); - check_flag(DeactivationFlag::Empty, 0); - } - - #[test] - fn test_deactivation_flag_add() { - assert_eq!( - DeactivationFlag::Empty, - DeactivationFlag::merge(DeactivationFlag::Empty, DeactivationFlag::Empty) - ); - - assert_eq!( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, - DeactivationFlag::merge( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted - ) - ); - - assert_eq!( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, - DeactivationFlag::merge( - DeactivationFlag::Empty, - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted - ) - ); - - assert_eq!( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, - DeactivationFlag::merge( - DeactivationFlag::MustFullyActivateBeforeDeactivationIsPermitted, - DeactivationFlag::Empty - ) - ); + check_flag(StakeFlags::empty(), 0); } }