Add stake_flags to stake state (#32524)

add stake_flags to stake state

Co-authored-by: HaoranYi <haoran.yi@solana.com>
This commit is contained in:
HaoranYi 2023-07-24 09:09:40 -05:00 committed by GitHub
parent 8e5805797e
commit 17af3ab10a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 153 additions and 109 deletions

View File

@ -19,7 +19,7 @@ pub fn parse_stake(data: &[u8]) -> Result<StakeAccountType, ParseAccountError> {
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<Delegation> 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(),

View File

@ -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()

View File

@ -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 {

View File

@ -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!"),

View File

@ -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)
{

View File

@ -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,

View File

@ -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<Pubkey>,
) -> 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::<VoteStateVersions>()?;
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<Self, InstructionError> {
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<impl Fn(&InflationPointCalculationEvent)>,
) -> 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<u128, InstructionError> {
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()

View File

@ -60,7 +60,7 @@ pub fn calculate_non_circulating_supply(bank: &Arc<Bank>) -> ScanResult<NonCircu
non_circulating_accounts_set.insert(*pubkey);
}
}
StakeState::Stake(meta, _stake) => {
StakeState::Stake(meta, _stake, _stake_flags) => {
if meta.lockup.is_in_force(&clock, None)
|| withdraw_authority_list.contains(&meta.authorized.withdrawer)
{

View File

@ -110,11 +110,15 @@ impl AbiExample for StakeAccount<Delegation> {
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()

View File

@ -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")

View File

@ -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<Stake> {
match self {
StakeState::Stake(_meta, stake) => Some(*stake),
StakeState::Stake(_meta, stake, _stake_flags) => Some(*stake),
_ => None,
}
}
pub fn delegation(&self) -> Option<Delegation> {
match self {
StakeState::Stake(_meta, stake) => Some(stake.delegation),
StakeState::Stake(_meta, stake, _stake_flags) => Some(stake.delegation),
_ => None,
}
}
pub fn authorized(&self) -> Option<Authorized> {
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<Meta> {
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,
));
}