Refactor: Remove trait from stake keyed account (#24148)

Removes trait from StakeAccount.
This commit is contained in:
Alexander Meißner
2022-04-06 22:58:09 +02:00
committed by GitHub
parent 25304ce485
commit efb9cbd8e7
2 changed files with 445 additions and 493 deletions

View File

@@ -1,10 +1,11 @@
#[deprecated(
since = "1.8.0",
note = "Please use `solana_sdk::stake::instruction` or `solana_program::stake::instruction` instead"
)]
pub use solana_sdk::stake::instruction::*;
use { use {
crate::{config, stake_state::StakeAccount}, crate::{
config,
stake_state::{
authorize, authorize_with_seed, deactivate, delegate, initialize, merge, set_lockup,
split, withdraw,
},
},
log::*, log::*,
solana_program_runtime::{ solana_program_runtime::{
invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check, invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check,
@@ -15,7 +16,7 @@ use {
keyed_account::keyed_account_at_index, keyed_account::keyed_account_at_index,
program_utils::limited_deserialize, program_utils::limited_deserialize,
stake::{ stake::{
instruction::StakeInstruction, instruction::{LockupArgs, StakeInstruction},
program::id, program::id,
state::{Authorized, Lockup}, state::{Authorized, Lockup},
}, },
@@ -44,7 +45,7 @@ pub fn process_instruction(
match limited_deserialize(data)? { match limited_deserialize(data)? {
StakeInstruction::Initialize(authorized, lockup) => { StakeInstruction::Initialize(authorized, lockup) => {
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
me.initialize(&authorized, &lockup, &rent, &invoke_context.feature_set) initialize(me, &authorized, &lockup, &rent, &invoke_context.feature_set)
} }
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => { StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
instruction_context.check_number_of_instruction_accounts(3)?; instruction_context.check_number_of_instruction_accounts(3)?;
@@ -62,7 +63,8 @@ pub fn process_instruction(
.ok() .ok()
.map(|ka| ka.unsigned_key()); .map(|ka| ka.unsigned_key());
me.authorize( authorize(
me,
&signers, &signers,
&authorized_pubkey, &authorized_pubkey,
stake_authorize, stake_authorize,
@@ -71,7 +73,8 @@ pub fn process_instruction(
custodian, custodian,
) )
} else { } else {
me.authorize( authorize(
me,
&signers, &signers,
&authorized_pubkey, &authorized_pubkey,
stake_authorize, stake_authorize,
@@ -97,7 +100,8 @@ pub fn process_instruction(
.ok() .ok()
.map(|ka| ka.unsigned_key()); .map(|ka| ka.unsigned_key());
me.authorize_with_seed( authorize_with_seed(
me,
authority_base, authority_base,
&args.authority_seed, &args.authority_seed,
&args.authority_owner, &args.authority_owner,
@@ -108,7 +112,8 @@ pub fn process_instruction(
custodian, custodian,
) )
} else { } else {
me.authorize_with_seed( authorize_with_seed(
me,
authority_base, authority_base,
&args.authority_seed, &args.authority_seed,
&args.authority_owner, &args.authority_owner,
@@ -138,13 +143,13 @@ pub fn process_instruction(
} }
let config = config::from(&*config_account.try_account_ref()?) let config = config::from(&*config_account.try_account_ref()?)
.ok_or(InstructionError::InvalidArgument)?; .ok_or(InstructionError::InvalidArgument)?;
me.delegate(vote, &clock, &stake_history, &config, &signers) delegate(me, vote, &clock, &stake_history, &config, &signers)
} }
StakeInstruction::Split(lamports) => { StakeInstruction::Split(lamports) => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let split_stake = let split_stake =
&keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; &keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
me.split(invoke_context, lamports, split_stake, &signers) split(me, invoke_context, lamports, split_stake, &signers)
} }
StakeInstruction::Merge => { StakeInstruction::Merge => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
@@ -157,7 +162,8 @@ pub fn process_instruction(
instruction_context, instruction_context,
3, 3,
)?; )?;
me.merge( merge(
me,
invoke_context, invoke_context,
source_stake, source_stake,
&clock, &clock,
@@ -176,7 +182,8 @@ pub fn process_instruction(
3, 3,
)?; )?;
instruction_context.check_number_of_instruction_accounts(5)?; instruction_context.check_number_of_instruction_accounts(5)?;
me.withdraw( withdraw(
me,
lamports, lamports,
to, to,
&clock, &clock,
@@ -189,11 +196,11 @@ pub fn process_instruction(
StakeInstruction::Deactivate => { StakeInstruction::Deactivate => {
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
me.deactivate(&clock, &signers) deactivate(me, &clock, &signers)
} }
StakeInstruction::SetLockup(lockup) => { StakeInstruction::SetLockup(lockup) => {
let clock = invoke_context.get_sysvar_cache().get_clock()?; let clock = invoke_context.get_sysvar_cache().get_clock()?;
me.set_lockup(&lockup, &signers, &clock) set_lockup(me, &lockup, &signers, &clock)
} }
StakeInstruction::InitializeChecked => { StakeInstruction::InitializeChecked => {
if invoke_context if invoke_context
@@ -214,7 +221,8 @@ pub fn process_instruction(
let rent = let rent =
get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
me.initialize( initialize(
me,
&authorized, &authorized,
&Lockup::default(), &Lockup::default(),
&rent, &rent,
@@ -243,7 +251,8 @@ pub fn process_instruction(
.ok() .ok()
.map(|ka| ka.unsigned_key()); .map(|ka| ka.unsigned_key());
me.authorize( authorize(
me,
&signers, &signers,
authorized_pubkey, authorized_pubkey,
stake_authorize, stake_authorize,
@@ -275,7 +284,8 @@ pub fn process_instruction(
.ok() .ok()
.map(|ka| ka.unsigned_key()); .map(|ka| ka.unsigned_key());
me.authorize_with_seed( authorize_with_seed(
me,
authority_base, authority_base,
&args.authority_seed, &args.authority_seed,
&args.authority_owner, &args.authority_owner,
@@ -312,7 +322,7 @@ pub fn process_instruction(
custodian, custodian,
}; };
let clock = invoke_context.get_sysvar_cache().get_clock()?; let clock = invoke_context.get_sysvar_cache().get_clock()?;
me.set_lockup(&lockup, &signers, &clock) set_lockup(me, &lockup, &signers, &clock)
} else { } else {
Err(InstructionError::InvalidInstructionData) Err(InstructionError::InvalidInstructionData)
} }
@@ -356,7 +366,11 @@ mod tests {
rent::Rent, rent::Rent,
stake::{ stake::{
config as stake_config, config as stake_config,
instruction::{self, LockupArgs}, instruction::{
self, authorize_checked, authorize_checked_with_seed, initialize_checked,
set_lockup_checked, AuthorizeCheckedWithSeedArgs, AuthorizeWithSeedArgs,
LockupArgs, StakeError,
},
state::{Authorized, Lockup, StakeAuthorize}, state::{Authorized, Lockup, StakeAuthorize},
}, },
stake_history::{StakeHistory, StakeHistoryEntry}, stake_history::{StakeHistory, StakeHistoryEntry},

View File

@@ -365,94 +365,23 @@ fn calculate_stake_rewards(
Some((staker_rewards, voter_rewards, credits_observed)) Some((staker_rewards, voter_rewards, credits_observed))
} }
pub trait StakeAccount { pub fn initialize(
fn initialize( stake_account: &KeyedAccount,
&self,
authorized: &Authorized,
lockup: &Lockup,
rent: &Rent,
feature_set: &FeatureSet,
) -> Result<(), InstructionError>;
fn authorize(
&self,
signers: &HashSet<Pubkey>,
new_authority: &Pubkey,
stake_authorize: StakeAuthorize,
require_custodian_for_locked_stake_authorize: bool,
clock: &Clock,
custodian: Option<&Pubkey>,
) -> Result<(), InstructionError>;
fn authorize_with_seed(
&self,
authority_base: &KeyedAccount,
authority_seed: &str,
authority_owner: &Pubkey,
new_authority: &Pubkey,
stake_authorize: StakeAuthorize,
require_custodian_for_locked_stake_authorize: bool,
clock: &Clock,
custodian: Option<&Pubkey>,
) -> Result<(), InstructionError>;
fn delegate(
&self,
vote_account: &KeyedAccount,
clock: &Clock,
stake_history: &StakeHistory,
config: &Config,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError>;
fn set_lockup(
&self,
lockup: &LockupArgs,
signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError>;
fn split(
&self,
invoke_context: &InvokeContext,
lamports: u64,
split_stake: &KeyedAccount,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn merge(
&self,
invoke_context: &InvokeContext,
source_stake: &KeyedAccount,
clock: &Clock,
stake_history: &StakeHistory,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn withdraw(
&self,
lamports: u64,
to: &KeyedAccount,
clock: &Clock,
stake_history: &StakeHistory,
withdraw_authority: &KeyedAccount,
custodian: Option<&KeyedAccount>,
feature_set: &FeatureSet,
) -> Result<(), InstructionError>;
}
impl<'a> StakeAccount for KeyedAccount<'a> {
fn initialize(
&self,
authorized: &Authorized, authorized: &Authorized,
lockup: &Lockup, lockup: &Lockup,
rent: &Rent, rent: &Rent,
feature_set: &FeatureSet, feature_set: &FeatureSet,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if self.data_len()? != std::mem::size_of::<StakeState>() { if stake_account.data_len()? != std::mem::size_of::<StakeState>() {
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
if let StakeState::Uninitialized = self.state()? { if let StakeState::Uninitialized = stake_account.state()? {
let rent_exempt_reserve = rent.minimum_balance(self.data_len()?); let rent_exempt_reserve = rent.minimum_balance(stake_account.data_len()?);
let minimum_delegation = crate::get_minimum_delegation(feature_set); let minimum_delegation = crate::get_minimum_delegation(feature_set);
let minimum_balance = rent_exempt_reserve + minimum_delegation; let minimum_balance = rent_exempt_reserve + minimum_delegation;
if self.lamports()? >= minimum_balance { if stake_account.lamports()? >= minimum_balance {
self.set_state(&StakeState::Initialized(Meta { stake_account.set_state(&StakeState::Initialized(Meta {
rent_exempt_reserve, rent_exempt_reserve,
authorized: *authorized, authorized: *authorized,
lockup: *lockup, lockup: *lockup,
@@ -468,8 +397,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
/// Authorize the given pubkey to manage stake (deactivate, withdraw). This may be called /// Authorize the given pubkey to manage stake (deactivate, withdraw). This may be called
/// multiple times, but will implicitly withdraw authorization from the previously authorized /// multiple times, but will implicitly withdraw authorization from the previously authorized
/// staker. The default staker is the owner of the stake account's pubkey. /// staker. The default staker is the owner of the stake account's pubkey.
fn authorize( pub fn authorize(
&self, stake_account: &KeyedAccount,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
new_authority: &Pubkey, new_authority: &Pubkey,
stake_authorize: StakeAuthorize, stake_authorize: StakeAuthorize,
@@ -477,7 +406,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
clock: &Clock, clock: &Clock,
custodian: Option<&Pubkey>, custodian: Option<&Pubkey>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
match self.state()? { match stake_account.state()? {
StakeState::Stake(mut meta, stake) => { StakeState::Stake(mut meta, stake) => {
meta.authorized.authorize( meta.authorized.authorize(
signers, signers,
@@ -489,7 +418,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
None None
}, },
)?; )?;
self.set_state(&StakeState::Stake(meta, stake)) stake_account.set_state(&StakeState::Stake(meta, stake))
} }
StakeState::Initialized(mut meta) => { StakeState::Initialized(mut meta) => {
meta.authorized.authorize( meta.authorized.authorize(
@@ -502,13 +431,14 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
None None
}, },
)?; )?;
self.set_state(&StakeState::Initialized(meta)) stake_account.set_state(&StakeState::Initialized(meta))
} }
_ => Err(InstructionError::InvalidAccountData), _ => Err(InstructionError::InvalidAccountData),
} }
} }
fn authorize_with_seed(
&self, pub fn authorize_with_seed(
stake_account: &KeyedAccount,
authority_base: &KeyedAccount, authority_base: &KeyedAccount,
authority_seed: &str, authority_seed: &str,
authority_owner: &Pubkey, authority_owner: &Pubkey,
@@ -526,7 +456,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
authority_owner, authority_owner,
)?); )?);
} }
self.authorize( authorize(
stake_account,
&signers, &signers,
new_authority, new_authority,
stake_authorize, stake_authorize,
@@ -535,8 +466,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
custodian, custodian,
) )
} }
fn delegate(
&self, pub fn delegate(
stake_account: &KeyedAccount,
vote_account: &KeyedAccount, vote_account: &KeyedAccount,
clock: &Clock, clock: &Clock,
stake_history: &StakeHistory, stake_history: &StakeHistory,
@@ -547,11 +479,11 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
match self.state()? { match stake_account.state()? {
StakeState::Initialized(meta) => { StakeState::Initialized(meta) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?; meta.authorized.check(signers, StakeAuthorize::Staker)?;
let ValidatedDelegatedInfo { stake_amount } = let ValidatedDelegatedInfo { stake_amount } =
validate_delegated_amount(self, &meta)?; validate_delegated_amount(stake_account, &meta)?;
let stake = new_stake( let stake = new_stake(
stake_amount, stake_amount,
vote_account.unsigned_key(), vote_account.unsigned_key(),
@@ -559,12 +491,12 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
clock.epoch, clock.epoch,
config, config,
); );
self.set_state(&StakeState::Stake(meta, stake)) stake_account.set_state(&StakeState::Stake(meta, stake))
} }
StakeState::Stake(meta, mut stake) => { StakeState::Stake(meta, mut stake) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?; meta.authorized.check(signers, StakeAuthorize::Staker)?;
let ValidatedDelegatedInfo { stake_amount } = let ValidatedDelegatedInfo { stake_amount } =
validate_delegated_amount(self, &meta)?; validate_delegated_amount(stake_account, &meta)?;
redelegate( redelegate(
&mut stake, &mut stake,
stake_amount, stake_amount,
@@ -574,42 +506,48 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
stake_history, stake_history,
config, config,
)?; )?;
self.set_state(&StakeState::Stake(meta, stake)) stake_account.set_state(&StakeState::Stake(meta, stake))
} }
_ => Err(InstructionError::InvalidAccountData), _ => Err(InstructionError::InvalidAccountData),
} }
} }
fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError> {
if let StakeState::Stake(meta, mut stake) = self.state()? { pub fn deactivate(
stake_account: &KeyedAccount,
clock: &Clock,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> {
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
meta.authorized.check(signers, StakeAuthorize::Staker)?; meta.authorized.check(signers, StakeAuthorize::Staker)?;
stake.deactivate(clock.epoch)?; stake.deactivate(clock.epoch)?;
self.set_state(&StakeState::Stake(meta, stake)) stake_account.set_state(&StakeState::Stake(meta, stake))
} else { } else {
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
} }
} }
fn set_lockup(
&self, pub fn set_lockup(
stake_account: &KeyedAccount,
lockup: &LockupArgs, lockup: &LockupArgs,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
clock: &Clock, clock: &Clock,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
match self.state()? { match stake_account.state()? {
StakeState::Initialized(mut meta) => { StakeState::Initialized(mut meta) => {
meta.set_lockup(lockup, signers, clock)?; meta.set_lockup(lockup, signers, clock)?;
self.set_state(&StakeState::Initialized(meta)) stake_account.set_state(&StakeState::Initialized(meta))
} }
StakeState::Stake(mut meta, stake) => { StakeState::Stake(mut meta, stake) => {
meta.set_lockup(lockup, signers, clock)?; meta.set_lockup(lockup, signers, clock)?;
self.set_state(&StakeState::Stake(meta, stake)) stake_account.set_state(&StakeState::Stake(meta, stake))
} }
_ => Err(InstructionError::InvalidAccountData), _ => Err(InstructionError::InvalidAccountData),
} }
} }
fn split( pub fn split(
&self, stake_account: &KeyedAccount,
invoke_context: &InvokeContext, invoke_context: &InvokeContext,
lamports: u64, lamports: u64,
split: &KeyedAccount, split: &KeyedAccount,
@@ -624,16 +562,16 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
if !matches!(split.state()?, StakeState::Uninitialized) { if !matches!(split.state()?, StakeState::Uninitialized) {
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
if lamports > self.lamports()? { if lamports > stake_account.lamports()? {
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
match self.state()? { match stake_account.state()? {
StakeState::Stake(meta, mut stake) => { StakeState::Stake(meta, mut stake) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?; meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = validate_split_amount( let validated_split_info = validate_split_amount(
invoke_context, invoke_context,
self, stake_account,
split, split,
lamports, lamports,
&meta, &meta,
@@ -657,8 +595,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
// original rent_exempt_reserve and the split_rent_exempt_reserve, in order // original rent_exempt_reserve and the split_rent_exempt_reserve, in order
// to prevent magic activation of stake by splitting between accounts of // to prevent magic activation of stake by splitting between accounts of
// different sizes. // different sizes.
let remaining_stake_delta = let remaining_stake_delta = lamports.saturating_sub(meta.rent_exempt_reserve);
lamports.saturating_sub(meta.rent_exempt_reserve);
(remaining_stake_delta, remaining_stake_delta) (remaining_stake_delta, remaining_stake_delta)
} else { } else {
// Otherwise, the new split stake should reflect the entire split // Otherwise, the new split stake should reflect the entire split
@@ -674,23 +611,21 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
}; };
let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?; let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?;
let mut split_meta = meta; let mut split_meta = meta;
split_meta.rent_exempt_reserve = split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
validated_split_info.destination_rent_exempt_reserve;
self.set_state(&StakeState::Stake(meta, stake))?; stake_account.set_state(&StakeState::Stake(meta, stake))?;
split.set_state(&StakeState::Stake(split_meta, split_stake))?; split.set_state(&StakeState::Stake(split_meta, split_stake))?;
} }
StakeState::Initialized(meta) => { StakeState::Initialized(meta) => {
meta.authorized.check(signers, StakeAuthorize::Staker)?; meta.authorized.check(signers, StakeAuthorize::Staker)?;
let validated_split_info = let validated_split_info =
validate_split_amount(invoke_context, self, split, lamports, &meta, None)?; validate_split_amount(invoke_context, stake_account, split, lamports, &meta, None)?;
let mut split_meta = meta; let mut split_meta = meta;
split_meta.rent_exempt_reserve = split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
validated_split_info.destination_rent_exempt_reserve;
split.set_state(&StakeState::Initialized(split_meta))?; split.set_state(&StakeState::Initialized(split_meta))?;
} }
StakeState::Uninitialized => { StakeState::Uninitialized => {
if !signers.contains(self.unsigned_key()) { if !signers.contains(stake_account.unsigned_key()) {
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
} }
@@ -698,19 +633,21 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
} }
// Deinitialize state upon zero balance // Deinitialize state upon zero balance
if lamports == self.lamports()? { if lamports == stake_account.lamports()? {
self.set_state(&StakeState::Uninitialized)?; stake_account.set_state(&StakeState::Uninitialized)?;
} }
split split
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_add_lamports(lamports)?; .checked_add_lamports(lamports)?;
self.try_account_ref_mut()?.checked_sub_lamports(lamports)?; stake_account
.try_account_ref_mut()?
.checked_sub_lamports(lamports)?;
Ok(()) Ok(())
} }
fn merge( pub fn merge(
&self, stake_account: &KeyedAccount,
invoke_context: &InvokeContext, invoke_context: &InvokeContext,
source_account: &KeyedAccount, source_account: &KeyedAccount,
clock: &Clock, clock: &Clock,
@@ -721,14 +658,14 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
if source_account.owner()? != id() { if source_account.owner()? != id() {
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
// Close the self-reference loophole // Close the stake_account-reference loophole
if source_account.unsigned_key() == self.unsigned_key() { if source_account.unsigned_key() == stake_account.unsigned_key() {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
ic_msg!(invoke_context, "Checking if destination stake is mergeable"); ic_msg!(invoke_context, "Checking if destination stake is mergeable");
let stake_merge_kind = let stake_merge_kind =
MergeKind::get_if_mergeable(invoke_context, self, clock, stake_history)?; MergeKind::get_if_mergeable(invoke_context, stake_account, clock, stake_history)?;
let meta = stake_merge_kind.meta(); let meta = stake_merge_kind.meta();
// Authorized staker is allowed to split/merge accounts // Authorized staker is allowed to split/merge accounts
@@ -739,10 +676,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?; MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?;
ic_msg!(invoke_context, "Merging stake accounts"); ic_msg!(invoke_context, "Merging stake accounts");
if let Some(merged_state) = if let Some(merged_state) = stake_merge_kind.merge(invoke_context, source_merge_kind, clock)? {
stake_merge_kind.merge(invoke_context, source_merge_kind, clock)? stake_account.set_state(&merged_state)?;
{
self.set_state(&merged_state)?;
} }
// Source is about to be drained, deinitialize its state // Source is about to be drained, deinitialize its state
@@ -753,12 +688,14 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
source_account source_account
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_sub_lamports(lamports)?; .checked_sub_lamports(lamports)?;
self.try_account_ref_mut()?.checked_add_lamports(lamports)?; stake_account
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
Ok(()) Ok(())
} }
fn withdraw( pub fn withdraw(
&self, stake_account: &KeyedAccount,
lamports: u64, lamports: u64,
to: &KeyedAccount, to: &KeyedAccount,
clock: &Clock, clock: &Clock,
@@ -773,7 +710,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
.ok_or(InstructionError::MissingRequiredSignature)?; .ok_or(InstructionError::MissingRequiredSignature)?;
signers.insert(*withdraw_authority_pubkey); signers.insert(*withdraw_authority_pubkey);
let (lockup, reserve, is_staked) = match self.state()? { let (lockup, reserve, is_staked) = match stake_account.state()? {
StakeState::Stake(meta, stake) => { StakeState::Stake(meta, stake) => {
meta.authorized meta.authorized
.check(&signers, StakeAuthorize::Withdrawer)?; .check(&signers, StakeAuthorize::Withdrawer)?;
@@ -802,7 +739,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
(meta.lockup, reserve, false) (meta.lockup, reserve, false)
} }
StakeState::Uninitialized => { StakeState::Uninitialized => {
if !signers.contains(self.unsigned_key()) { if !signers.contains(stake_account.unsigned_key()) {
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
(Lockup::default(), 0, false) // no lockup, no restrictions (Lockup::default(), 0, false) // no lockup, no restrictions
@@ -820,28 +757,29 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
let lamports_and_reserve = checked_add(lamports, reserve)?; let lamports_and_reserve = checked_add(lamports, reserve)?;
// if the stake is active, we mustn't allow the account to go away // if the stake is active, we mustn't allow the account to go away
if is_staked // line coverage for branch coverage if is_staked // line coverage for branch coverage
&& lamports_and_reserve > self.lamports()? && lamports_and_reserve > stake_account.lamports()?
{ {
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
if lamports != self.lamports()? // not a full withdrawal if lamports != stake_account.lamports()? // not a full withdrawal
&& lamports_and_reserve > self.lamports()? && lamports_and_reserve > stake_account.lamports()?
{ {
assert!(!is_staked); assert!(!is_staked);
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
// Deinitialize state upon zero balance // Deinitialize state upon zero balance
if lamports == self.lamports()? { if lamports == stake_account.lamports()? {
self.set_state(&StakeState::Uninitialized)?; stake_account.set_state(&StakeState::Uninitialized)?;
} }
self.try_account_ref_mut()?.checked_sub_lamports(lamports)?; stake_account
.try_account_ref_mut()?
.checked_sub_lamports(lamports)?;
to.try_account_ref_mut()?.checked_add_lamports(lamports)?; to.try_account_ref_mut()?.checked_add_lamports(lamports)?;
Ok(()) Ok(())
} }
}
/// After calling `validate_delegated_amount()`, this struct contains calculated values that are used /// After calling `validate_delegated_amount()`, this struct contains calculated values that are used
/// by the caller. /// by the caller.