From 17af3ab10aa6f737b2a23e42f3d41e8a03ff0a35 Mon Sep 17 00:00:00 2001 From: HaoranYi Date: Mon, 24 Jul 2023 09:09:40 -0500 Subject: [PATCH] Add stake_flags to stake state (#32524) add stake_flags to stake state Co-authored-by: HaoranYi --- account-decoder/src/parse_stake.rs | 6 +- cli/src/cluster_query.rs | 2 +- cli/src/stake.rs | 3 +- cli/tests/stake.rs | 4 +- ledger-tool/src/main.rs | 2 +- programs/stake/src/stake_instruction.rs | 92 ++++++++++++-------- programs/stake/src/stake_state.rs | 110 ++++++++++++++---------- runtime/src/non_circulating_supply.rs | 2 +- runtime/src/stake_account.rs | 10 ++- runtime/tests/stake.rs | 6 +- sdk/program/src/stake/state.rs | 25 +++--- 11 files changed, 153 insertions(+), 109 deletions(-) diff --git a/account-decoder/src/parse_stake.rs b/account-decoder/src/parse_stake.rs index 4db60fecd6..50197cb0d3 100644 --- a/account-decoder/src/parse_stake.rs +++ b/account-decoder/src/parse_stake.rs @@ -19,7 +19,7 @@ pub fn parse_stake(data: &[u8]) -> Result { meta: meta.into(), stake: None, }), - StakeState::Stake(meta, stake) => StakeAccountType::Delegated(UiStakeAccount { + StakeState::Stake(meta, stake, _) => StakeAccountType::Delegated(UiStakeAccount { meta: meta.into(), stake: Some(stake.into()), }), @@ -136,7 +136,7 @@ impl From for UiDelegation { #[cfg(test)] mod test { - use {super::*, bincode::serialize}; + use {super::*, bincode::serialize, solana_sdk::stake::stake_flags::StakeFlags}; #[test] fn test_parse_stake() { @@ -194,7 +194,7 @@ mod test { credits_observed: 10, }; - let stake_state = StakeState::Stake(meta, stake); + 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/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 0155646496..208517edd4 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1819,7 +1819,7 @@ pub fn process_show_stakes( }); } } - StakeState::Stake(_, stake) => { + StakeState::Stake(_, stake, _) => { if vote_account_pubkeys.is_none() || vote_account_pubkeys .unwrap() diff --git a/cli/src/stake.rs b/cli/src/stake.rs index c1e04a3af5..9e1726744b 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -1628,7 +1628,7 @@ pub fn process_deactivate_stake_account( let vote_account_address = match stake_account.state() { Ok(stake_state) => match stake_state { - StakeState::Stake(_, stake) => stake.delegation.voter_pubkey, + StakeState::Stake(_, stake, _) => stake.delegation.voter_pubkey, _ => { return Err(CliError::BadParameter(format!( "{stake_account_address} is not a delegated stake account", @@ -2195,6 +2195,7 @@ pub fn build_stake_state( lockup, }, stake, + _, ) => { let current_epoch = clock.epoch; let StakeActivationStatus { diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index 8a538e195c..481c163c68 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -165,7 +165,7 @@ fn test_stake_redelegation() { let stake_state: StakeState = stake_account.state().unwrap(); let rent_exempt_reserve = match stake_state { - StakeState::Stake(meta, stake) => { + StakeState::Stake(meta, stake, _) => { assert_eq!(stake.delegation.voter_pubkey, vote_keypair.pubkey()); meta.rent_exempt_reserve } @@ -271,7 +271,7 @@ fn test_stake_redelegation() { let stake2_state: StakeState = stake2_account.state().unwrap(); match stake2_state { - StakeState::Stake(_meta, stake) => { + StakeState::Stake(_meta, stake, _) => { assert_eq!(stake.delegation.voter_pubkey, vote2_keypair.pubkey()); } _ => panic!("Unexpected stake2 state!"), diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 6843ddeeb2..d975c379b2 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -2965,7 +2965,7 @@ fn main() { .unwrap() .into_iter() { - if let Ok(StakeState::Stake(meta, stake)) = account.state() { + if let Ok(StakeState::Stake(meta, stake, _)) = account.state() { if vote_accounts_to_destake .contains(&stake.delegation.voter_pubkey) { diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 185c7e8f30..743b7d8d0f 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -481,6 +481,7 @@ mod tests { set_lockup_checked, AuthorizeCheckedWithSeedArgs, AuthorizeWithSeedArgs, LockupArgs, StakeError, }, + stake_flags::StakeFlags, state::{Authorized, Lockup, StakeActivationStatus, StakeAuthorize}, MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION, }, @@ -610,6 +611,7 @@ mod tests { }, ..Stake::default() }, + StakeFlags::empty(), ) } @@ -2721,7 +2723,7 @@ mod tests { StakeState::Initialized(_meta) => { assert_eq!(from(&accounts[0]).unwrap(), state); } - StakeState::Stake(_meta, _stake) => { + StakeState::Stake(_meta, _stake, _) => { let stake_0 = from(&accounts[0]).unwrap().stake(); assert_eq!(stake_0.unwrap().delegation.stake, stake_lamports / 2); } @@ -4316,8 +4318,8 @@ mod tests { // *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() { - assert_matches!(accounts[0].state().unwrap(), StakeState::Stake(_, _)); - if let StakeState::Stake(_, destination_stake) = accounts[1].state().unwrap() { + assert_matches!(accounts[0].state().unwrap(), StakeState::Stake(_, _, _)); + if let StakeState::Stake(_, destination_stake, _) = accounts[1].state().unwrap() { let destination_initial_rent_deficit = rent_exempt_reserve.saturating_sub(destination_starting_balance); let expected_destination_stake_delegation = @@ -4737,7 +4739,7 @@ mod tests { for split_to_state in &[ StakeState::Initialized(Meta::default()), - StakeState::Stake(Meta::default(), Stake::default()), + StakeState::Stake(Meta::default(), Stake::default(), StakeFlags::default()), StakeState::RewardsPool, ] { let split_to_account = AccountSharedData::new_data_with_space( @@ -4903,7 +4905,7 @@ mod tests { ); // verify no stake leakage in the case of a stake - if let StakeState::Stake(meta, stake) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { assert_eq!( accounts[1].state(), Ok(StakeState::Stake( @@ -4914,7 +4916,8 @@ mod tests { ..stake.delegation }, ..*stake - } + }, + *stake_flags, )) ); assert_eq!(accounts[0].lamports(), *minimum_balance,); @@ -5005,7 +5008,7 @@ mod tests { stake_lamports + initial_balance, ); - if let StakeState::Stake(meta, stake) = 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!( @@ -5018,7 +5021,8 @@ mod tests { ..stake.delegation }, ..stake - } + }, + stake_flags )), accounts[1].state(), ); @@ -5037,7 +5041,8 @@ mod tests { ..stake.delegation }, ..stake - } + }, + stake_flags, )), accounts[0].state(), ); @@ -5128,7 +5133,7 @@ mod tests { stake_lamports + initial_balance ); - if let StakeState::Stake(meta, stake) = 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, @@ -5146,7 +5151,8 @@ mod tests { ..stake.delegation }, ..stake - } + }, + stake_flags, )), accounts[1].state() ); @@ -5165,7 +5171,8 @@ mod tests { ..stake.delegation }, ..stake - } + }, + stake_flags, )), accounts[0].state() ); @@ -5321,7 +5328,7 @@ mod tests { assert_eq!(Ok(*state), accounts[1].state()); assert_eq!(Ok(StakeState::Uninitialized), accounts[0].state()); } - StakeState::Stake(meta, stake) => { + StakeState::Stake(meta, stake, stake_flags) => { assert_eq!( Ok(StakeState::Stake( *meta, @@ -5331,7 +5338,8 @@ mod tests { ..stake.delegation }, ..*stake - } + }, + *stake_flags )), accounts[1].state() ); @@ -5416,7 +5424,7 @@ mod tests { stake_lamports + initial_balance ); - if let StakeState::Stake(meta, stake) = state { + if let StakeState::Stake(meta, stake, stake_flags) = state { assert_eq!( Ok(StakeState::Stake( meta, @@ -5426,7 +5434,8 @@ mod tests { ..stake.delegation }, ..stake - } + }, + stake_flags, )), accounts[1].state() ); @@ -5538,7 +5547,7 @@ mod tests { ); assert_eq!(Ok(StakeState::Uninitialized), accounts[0].state()); } - StakeState::Stake(_meta, stake) => { + 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; @@ -5552,7 +5561,8 @@ mod tests { ..stake.delegation }, ..*stake - } + }, + *stake_flags, )), accounts[1].state() ); @@ -5667,7 +5677,7 @@ mod tests { StakeState::Initialized(meta) => { assert_eq!(accounts[0].state(), Ok(StakeState::Initialized(*meta)),); } - StakeState::Stake(meta, stake) => { + StakeState::Stake(meta, stake, stake_flags) => { let expected_stake = stake.delegation.stake + merge_from_state .stake() @@ -5686,7 +5696,8 @@ mod tests { ..stake.delegation }, ..*stake - } + }, + *stake_flags, )), ); } @@ -5720,7 +5731,7 @@ mod tests { }; let stake_account = AccountSharedData::new_data_with_space( stake_lamports, - &StakeState::Stake(meta, stake), + &StakeState::Stake(meta, stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6047,7 +6058,7 @@ mod tests { }; let stake_account = AccountSharedData::new_data_with_space( stake_lamports, - &StakeState::Stake(meta, stake), + &StakeState::Stake(meta, stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6063,7 +6074,7 @@ mod tests { }; let merge_from_account = AccountSharedData::new_data_with_space( merge_from_lamports, - &StakeState::Stake(meta, merge_from_stake), + &StakeState::Stake(meta, merge_from_stake, StakeFlags::empty()), StakeState::size_of(), &id(), ) @@ -6219,7 +6230,7 @@ mod tests { }; transaction_accounts[0] .1 - .set_state(&StakeState::Stake(meta, stake)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); } if clock.epoch == merge_from_deactivation_epoch { @@ -6233,7 +6244,11 @@ mod tests { }; transaction_accounts[1] .1 - .set_state(&StakeState::Stake(meta, merge_from_stake)) + .set_state(&StakeState::Stake( + meta, + merge_from_stake, + StakeFlags::empty(), + )) .unwrap(); } stake_history.add( @@ -6394,6 +6409,7 @@ mod tests { 1, /* activation_epoch */ &stake_config::Config::default(), ), + StakeFlags::empty(), ); let stake_account = AccountSharedData::new_data_with_space( @@ -6593,6 +6609,7 @@ mod tests { 1, /* activation_epoch */ &stake_config::Config::default(), ), + StakeFlags::empty(), )) .unwrap(); @@ -6686,6 +6703,7 @@ mod tests { activation_epoch, &stake_config::Config::default(), ), + StakeFlags::empty(), ); if let Some(expected_stake_activation_status) = expected_stake_activation_status { @@ -6816,7 +6834,7 @@ mod tests { ); assert_eq!(output_accounts[0].lamports(), rent_exempt_reserve); - if let StakeState::Stake(meta, stake) = output_accounts[0].deserialize_data().unwrap() { + if let StakeState::Stake(meta, stake, _) = output_accounts[0].deserialize_data().unwrap() { assert_eq!(meta.rent_exempt_reserve, rent_exempt_reserve); assert_eq!( stake.delegation.stake, @@ -6831,7 +6849,7 @@ mod tests { output_accounts[1].lamports(), minimum_delegation + rent_exempt_reserve ); - if let StakeState::Stake(meta, stake) = output_accounts[1].deserialize_data().unwrap() { + if let StakeState::Stake(meta, stake, _) = output_accounts[1].deserialize_data().unwrap() { assert_eq!(meta.rent_exempt_reserve, rent_exempt_reserve); assert_eq!(stake.delegation.stake, minimum_delegation); assert_eq!(stake.delegation.activation_epoch, current_epoch); @@ -6974,7 +6992,7 @@ mod tests { output_accounts[1].lamports(), minimum_delegation + rent_exempt_reserve + 42 ); - if let StakeState::Stake(meta, stake) = output_accounts[1].deserialize_data().unwrap() { + if let StakeState::Stake(meta, stake, _) = output_accounts[1].deserialize_data().unwrap() { assert_eq!(meta.rent_exempt_reserve, rent_exempt_reserve); assert_eq!(stake.delegation.stake, minimum_delegation + 42); assert_eq!(stake.delegation.activation_epoch, current_epoch); @@ -6988,12 +7006,12 @@ mod tests { // let mut stake_account_over_allocated = prepare_stake_account(0 /*activation_epoch:*/, None); - if let StakeState::Stake(mut meta, stake) = + if let StakeState::Stake(mut meta, stake, stake_flags) = stake_account_over_allocated.deserialize_data().unwrap() { meta.rent_exempt_reserve += 42; stake_account_over_allocated - .set_state(&StakeState::Stake(meta, stake)) + .set_state(&StakeState::Stake(meta, stake, stake_flags)) .unwrap(); } stake_account_over_allocated @@ -7016,7 +7034,7 @@ mod tests { ); assert_eq!(output_accounts[0].lamports(), rent_exempt_reserve + 42); - if let StakeState::Stake(meta, _stake) = output_accounts[0].deserialize_data().unwrap() { + if let StakeState::Stake(meta, _stake, _) = output_accounts[0].deserialize_data().unwrap() { assert_eq!(meta.rent_exempt_reserve, rent_exempt_reserve + 42); } else { panic!("Invalid output_accounts[0] data"); @@ -7025,7 +7043,7 @@ mod tests { output_accounts[1].lamports(), minimum_delegation + rent_exempt_reserve, ); - if let StakeState::Stake(meta, stake) = output_accounts[1].deserialize_data().unwrap() { + if let StakeState::Stake(meta, stake, _) = output_accounts[1].deserialize_data().unwrap() { assert_eq!(meta.rent_exempt_reserve, rent_exempt_reserve); assert_eq!(stake.delegation.stake, minimum_delegation); } else { @@ -7163,7 +7181,7 @@ mod tests { let mut deactivating_stake_account = prepare_stake_account(0 /*activation_epoch:*/, None); - if let StakeState::Stake(meta, mut stake) = + if let StakeState::Stake(meta, mut stake, _stake_flags) = deactivating_stake_account.deserialize_data().unwrap() { stake.deactivate(current_epoch).unwrap(); @@ -7179,7 +7197,7 @@ mod tests { ); deactivating_stake_account - .set_state(&StakeState::Stake(meta, stake)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); } let _ = process_instruction_redelegate( @@ -7198,7 +7216,7 @@ 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) = + if let StakeState::Stake(meta, mut stake, stake_flags) = stake_account_too_few_lamports.deserialize_data().unwrap() { stake.delegation.stake -= 1; @@ -7207,7 +7225,7 @@ mod tests { minimum_delegation + rent_exempt_reserve - 1 ); stake_account_too_few_lamports - .set_state(&StakeState::Stake(meta, stake)) + .set_state(&StakeState::Stake(meta, stake, stake_flags)) .unwrap(); } else { panic!("Invalid stake_account"); @@ -7232,7 +7250,7 @@ mod tests { ); // - // Failure: redelegate to same vote addresss + // Failure: redelegate to same vote address // let _ = process_instruction_redelegate( &stake_address, diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index f1b8ebc7a3..448a4d811f 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -25,6 +25,7 @@ use { config::Config, instruction::{LockupArgs, StakeError}, program::id, + stake_flags::StakeFlags, tools::{acceptable_reference_epoch_credits, eligible_for_deactivate_delinquent}, }, stake_history::{StakeHistory, StakeHistoryEntry}, @@ -487,7 +488,7 @@ pub fn authorize( custodian: Option<&Pubkey>, ) -> Result<(), InstructionError> { match stake_account.get_state()? { - StakeState::Stake(mut meta, stake) => { + StakeState::Stake(mut meta, stake, stake_flags) => { meta.authorized.authorize( signers, new_authority, @@ -498,7 +499,7 @@ pub fn authorize( None }, )?; - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } StakeState::Initialized(mut meta) => { meta.authorized.authorize( @@ -590,9 +591,9 @@ pub fn delegate( clock.epoch, config, ); - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) } - StakeState::Stake(meta, mut stake) => { + 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)?; @@ -606,7 +607,7 @@ pub fn delegate( stake_history, config, )?; - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } _ => Err(InstructionError::InvalidAccountData), } @@ -617,11 +618,11 @@ pub fn deactivate( clock: &Clock, signers: &HashSet, ) -> Result<(), InstructionError> { - if let StakeState::Stake(meta, mut stake) = stake_account.get_state()? { + if let StakeState::Stake(meta, mut stake, stake_flags) = stake_account.get_state()? { meta.authorized.check(signers, StakeAuthorize::Staker)?; stake.deactivate(clock.epoch)?; - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } else { Err(InstructionError::InvalidAccountData) } @@ -638,9 +639,9 @@ pub fn set_lockup( meta.set_lockup(lockup, signers, clock)?; stake_account.set_state(&StakeState::Initialized(meta)) } - StakeState::Stake(mut meta, stake) => { + StakeState::Stake(mut meta, stake, stake_flags) => { meta.set_lockup(lockup, signers, clock)?; - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } _ => Err(InstructionError::InvalidAccountData), } @@ -677,7 +678,7 @@ pub fn split( drop(stake_account); match stake_state { - StakeState::Stake(meta, mut stake) => { + 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( @@ -747,11 +748,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))?; + 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))?; + split.set_state(&StakeState::Stake(split_meta, split_stake, stake_flags))?; } StakeState::Initialized(meta) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; @@ -927,7 +928,7 @@ pub fn redelegate( let vote_state = vote_account.get_state::()?; let (stake_meta, effective_stake) = - if let StakeState::Stake(meta, stake) = 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 @@ -983,6 +984,7 @@ pub fn redelegate( clock.epoch, config, ), + StakeFlags::empty(), ))?; Ok(()) @@ -1013,7 +1015,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) => { + StakeState::Stake(meta, stake, _stake_flag) => { meta.authorized .check(&signers, StakeAuthorize::Withdrawer)?; // if we have a deactivation epoch and we're in cooldown @@ -1126,7 +1128,7 @@ pub(crate) fn deactivate_delinquent( return Err(StakeError::InsufficientReferenceVotes.into()); } - if let StakeState::Stake(meta, mut stake) = stake_account.get_state()? { + if let StakeState::Stake(meta, mut stake, stake_flags) = stake_account.get_state()? { if stake.delegation.voter_pubkey != *delinquent_vote_account_pubkey { return Err(StakeError::VoteAddressMismatch.into()); } @@ -1135,7 +1137,7 @@ 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) { stake.deactivate(current_epoch)?; - stake_account.set_state(&StakeState::Stake(meta, stake)) + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags)) } else { Err(StakeError::MinimumDelinquentEpochsForDeactivationNotMet.into()) } @@ -1272,24 +1274,24 @@ fn validate_split_amount( #[derive(Clone, Debug, PartialEq)] enum MergeKind { - Inactive(Meta, u64), - ActivationEpoch(Meta, Stake), + Inactive(Meta, u64, StakeFlags), + ActivationEpoch(Meta, Stake, StakeFlags), FullyActive(Meta, Stake), } impl MergeKind { fn meta(&self) -> &Meta { match self { - Self::Inactive(meta, _) => meta, - Self::ActivationEpoch(meta, _) => meta, + Self::Inactive(meta, _, _) => meta, + Self::ActivationEpoch(meta, _, _) => meta, Self::FullyActive(meta, _) => meta, } } fn active_stake(&self) -> Option<&Stake> { match self { - Self::Inactive(_, _) => None, - Self::ActivationEpoch(_, stake) => Some(stake), + Self::Inactive(_, _, _) => None, + Self::ActivationEpoch(_, stake, _) => Some(stake), Self::FullyActive(_, stake) => Some(stake), } } @@ -1302,7 +1304,7 @@ impl MergeKind { stake_history: &StakeHistory, ) -> Result { match stake_state { - StakeState::Stake(meta, stake) => { + 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 @@ -1310,8 +1312,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)), - (0, _, _) => Ok(Self::ActivationEpoch(*meta, *stake)), + (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; @@ -1320,7 +1322,9 @@ impl MergeKind { } } } - StakeState::Initialized(meta) => Ok(Self::Inactive(*meta, stake_lamports)), + StakeState::Initialized(meta) => { + Ok(Self::Inactive(*meta, stake_lamports, StakeFlags::empty())) + } _ => Err(InstructionError::InvalidAccountData), } } @@ -1414,15 +1418,22 @@ impl MergeKind { }) .unwrap_or(Ok(()))?; let merged_state = match (self, source) { - (Self::Inactive(_, _), Self::Inactive(_, _)) => None, - (Self::Inactive(_, _), Self::ActivationEpoch(_, _)) => None, - (Self::ActivationEpoch(meta, mut stake), Self::Inactive(_, source_lamports)) => { + (Self::Inactive(_, _, _), Self::Inactive(_, _, _)) => None, + (Self::Inactive(_, _, _), Self::ActivationEpoch(_, _, _)) => None, + ( + 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)) + Some(StakeState::Stake( + meta, + stake, + stake_flags.union(source_stake_flags), + )) } ( - Self::ActivationEpoch(meta, mut stake), - Self::ActivationEpoch(source_meta, source_stake), + 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, @@ -1434,7 +1445,11 @@ impl MergeKind { source_lamports, source_stake.credits_observed, )?; - Some(StakeState::Stake(meta, stake)) + Some(StakeState::Stake( + meta, + stake, + stake_flags.union(source_stake_flags), + )) } (Self::FullyActive(meta, mut stake), Self::FullyActive(_, source_stake)) => { // Don't stake the source account's `rent_exempt_reserve` to @@ -1447,7 +1462,7 @@ impl MergeKind { source_stake.delegation.stake, source_stake.credits_observed, )?; - Some(StakeState::Stake(meta, stake)) + Some(StakeState::Stake(meta, stake, StakeFlags::empty())) } _ => return Err(StakeError::MergeMismatch.into()), }; @@ -1533,7 +1548,7 @@ pub fn redeem_rewards( stake_history: Option<&StakeHistory>, inflation_point_calc_tracer: Option, ) -> Result<(u64, u64), InstructionError> { - if let StakeState::Stake(meta, mut stake) = 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( @@ -1557,7 +1572,7 @@ pub fn redeem_rewards( inflation_point_calc_tracer, ) { stake_account.checked_add_lamports(stakers_reward)?; - stake_account.set_state(&StakeState::Stake(meta, stake))?; + stake_account.set_state(&StakeState::Stake(meta, stake, stake_flags))?; Ok((stakers_reward, voters_reward)) } else { @@ -1575,7 +1590,7 @@ pub fn calculate_points( vote_state: &VoteState, stake_history: Option<&StakeHistory>, ) -> Result { - if let StakeState::Stake(_meta, stake) = stake_state { + if let StakeState::Stake(_meta, stake, _stake_flags) = stake_state { Ok(calculate_stake_points( stake, vote_state, @@ -1724,6 +1739,7 @@ fn do_create_account( activation_epoch, &Config::default(), ), + StakeFlags::empty(), )) .expect("set_state"); @@ -3221,7 +3237,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::Inactive(meta, stake_lamports) + MergeKind::Inactive(meta, stake_lamports, StakeFlags::empty()) ); clock.epoch = 0; @@ -3258,7 +3274,7 @@ mod tests { ..Stake::default() }; stake_account - .set_state(&StakeState::Stake(meta, stake)) + .set_state(&StakeState::Stake(meta, stake, StakeFlags::empty())) .unwrap(); // activation_epoch succeeds assert_eq!( @@ -3270,7 +3286,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::ActivationEpoch(meta, stake), + MergeKind::ActivationEpoch(meta, stake, StakeFlags::empty()), ); // all paritially activated, transient epochs fail @@ -3392,7 +3408,7 @@ mod tests { &stake_history ) .unwrap(), - MergeKind::Inactive(meta, stake_lamports), + MergeKind::Inactive(meta, stake_lamports, StakeFlags::empty()), ); } @@ -3412,8 +3428,8 @@ mod tests { }, ..Stake::default() }; - let inactive = MergeKind::Inactive(Meta::default(), lamports); - let activation_epoch = MergeKind::ActivationEpoch(meta, stake); + 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!( @@ -3507,8 +3523,8 @@ mod tests { }; // activating stake merge, match credits observed - let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a); - let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b); + 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() @@ -3542,8 +3558,8 @@ mod tests { }, credits_observed: credits_b, }; - let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a); - let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b); + 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 67e6a4ff1b..7cde838c4d 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 e4cd79e48c..379a5d3d19 100644 --- a/runtime/src/stake_account.rs +++ b/runtime/src/stake_account.rs @@ -110,11 +110,15 @@ impl AbiExample for StakeAccount { fn example() -> Self { use solana_sdk::{ account::Account, - stake::state::{Meta, Stake}, + stake::{ + stake_flags::StakeFlags, + state::{Meta, Stake}, + }, }; - let stake_state = StakeState::Stake(Meta::example(), Stake::example()); + let stake_state = + StakeState::Stake(Meta::example(), Stake::example(), StakeFlags::example()); let mut account = Account::example(); - account.data.resize(196, 0u8); + account.data.resize(200, 0u8); account.owner = solana_stake_program::id(); account.set_state(&stake_state).unwrap(); Self::try_from(AccountSharedData::from(account)).unwrap() diff --git a/runtime/tests/stake.rs b/runtime/tests/stake.rs index 5b8a34c2cc..1feacee426 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) = 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) = 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") @@ -636,7 +636,7 @@ fn test_create_stake_account_from_seed() { // 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) = stake_state { + if let StakeState::Stake(_meta, stake, _) = stake_state { assert_eq!(stake.delegation.stake, delegation); } else { panic!("wrong account type found") diff --git a/sdk/program/src/stake/state.rs b/sdk/program/src/stake/state.rs index 24dea16dda..3dbd34efab 100644 --- a/sdk/program/src/stake/state.rs +++ b/sdk/program/src/stake/state.rs @@ -7,6 +7,7 @@ use { stake::{ config::Config, instruction::{LockupArgs, StakeError}, + stake_flags::StakeFlags, }, stake_history::{StakeHistory, StakeHistoryEntry}, }, @@ -22,7 +23,7 @@ pub enum StakeState { #[default] Uninitialized, Initialized(Meta), - Stake(Meta, Stake), + Stake(Meta, Stake, StakeFlags), RewardsPool, } @@ -36,9 +37,10 @@ impl BorshDeserialize for StakeState { Ok(StakeState::Initialized(meta)) } 2 => { - let meta = Meta::deserialize_reader(reader)?; - let stake = Stake::deserialize_reader(reader)?; - Ok(StakeState::Stake(meta, stake)) + let meta: Meta = BorshDeserialize::deserialize_reader(reader)?; + let stake: Stake = BorshDeserialize::deserialize_reader(reader)?; + let stake_flags: StakeFlags = BorshDeserialize::deserialize_reader(reader)?; + Ok(StakeState::Stake(meta, stake, stake_flags)) } 3 => Ok(StakeState::RewardsPool), _ => Err(io::Error::new( @@ -57,10 +59,11 @@ impl BorshSerialize for StakeState { writer.write_all(&1u32.to_le_bytes())?; meta.serialize(writer) } - StakeState::Stake(meta, stake) => { + StakeState::Stake(meta, stake, stake_flags) => { writer.write_all(&2u32.to_le_bytes())?; meta.serialize(writer)?; - stake.serialize(writer) + stake.serialize(writer)?; + stake_flags.serialize(writer) } StakeState::RewardsPool => writer.write_all(&3u32.to_le_bytes()), } @@ -75,21 +78,21 @@ impl StakeState { pub fn stake(&self) -> Option { match self { - StakeState::Stake(_meta, stake) => Some(*stake), + StakeState::Stake(_meta, stake, _stake_flags) => Some(*stake), _ => None, } } pub fn delegation(&self) -> Option { match self { - StakeState::Stake(_meta, stake) => Some(stake.delegation), + StakeState::Stake(_meta, stake, _stake_flags) => Some(stake.delegation), _ => None, } } pub fn authorized(&self) -> Option { match self { - StakeState::Stake(meta, _stake) => Some(meta.authorized), + StakeState::Stake(meta, _stake, _stake_flags) => Some(meta.authorized), StakeState::Initialized(meta) => Some(meta.authorized), _ => None, } @@ -101,7 +104,7 @@ impl StakeState { pub fn meta(&self) -> Option { match self { - StakeState::Stake(meta, _stake) => Some(*meta), + StakeState::Stake(meta, _stake, _stake_flags) => Some(*meta), StakeState::Initialized(meta) => Some(*meta), _ => None, } @@ -629,6 +632,7 @@ mod test { }, credits_observed: 1, }, + StakeFlags::empty(), )); } @@ -663,6 +667,7 @@ mod test { }, credits_observed: 1, }, + StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, )); }