gemachain_stake_program/
stake_state.rs

1//! Stake state
2//! * delegate stakes to vote accounts
3//! * keep track of rewards
4//! * own mining pools
5
6use {
7    gemachain_sdk::{
8        account::{AccountSharedData, ReadableAccount, WritableAccount},
9        account_utils::{State, StateMut},
10        clock::{Clock, Epoch},
11        feature_set::stake_merge_with_unmatched_credits_observed,
12        ic_msg,
13        instruction::{checked_add, InstructionError},
14        keyed_account::KeyedAccount,
15        process_instruction::InvokeContext,
16        pubkey::Pubkey,
17        rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
18        stake::{
19            config::Config,
20            instruction::{LockupArgs, StakeError},
21            program::id,
22        },
23        stake_history::{StakeHistory, StakeHistoryEntry},
24    },
25    gemachain_vote_program::vote_state::{VoteState, VoteStateVersions},
26    std::{collections::HashSet, convert::TryFrom},
27};
28
29#[deprecated(
30    since = "1.8.0",
31    note = "Please use `gemachain_sdk::stake::state` or `gemachain_program::stake::state` instead"
32)]
33pub use gemachain_sdk::stake::state::*;
34
35#[derive(Debug)]
36pub enum SkippedReason {
37    ZeroPoints,
38    ZeroPointValue,
39    ZeroReward,
40    ZeroCreditsAndReturnZero,
41    ZeroCreditsAndReturnCurrent,
42}
43
44impl From<SkippedReason> for InflationPointCalculationEvent {
45    fn from(reason: SkippedReason) -> Self {
46        InflationPointCalculationEvent::Skipped(reason)
47    }
48}
49
50#[derive(Debug)]
51pub enum InflationPointCalculationEvent {
52    CalculatedPoints(u64, u128, u128, u128),
53    SplitRewards(u64, u64, u64, PointValue),
54    EffectiveStakeAtRewardedEpoch(u64),
55    RentExemptReserve(u64),
56    Delegation(Delegation, Pubkey),
57    Commission(u8),
58    CreditsObserved(u64, Option<u64>),
59    Skipped(SkippedReason),
60}
61
62pub(crate) fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
63    None::<fn(&_)>
64}
65
66// utility function, used by Stakes, tests
67pub fn from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<StakeState> {
68    account.state().ok()
69}
70
71pub fn stake_from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<Stake> {
72    from(account).and_then(|state: StakeState| state.stake())
73}
74
75pub fn delegation_from(account: &AccountSharedData) -> Option<Delegation> {
76    from(account).and_then(|state: StakeState| state.delegation())
77}
78
79pub fn authorized_from(account: &AccountSharedData) -> Option<Authorized> {
80    from(account).and_then(|state: StakeState| state.authorized())
81}
82
83pub fn lockup_from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<Lockup> {
84    from(account).and_then(|state: StakeState| state.lockup())
85}
86
87pub fn meta_from(account: &AccountSharedData) -> Option<Meta> {
88    from(account).and_then(|state: StakeState| state.meta())
89}
90
91fn redelegate(
92    stake: &mut Stake,
93    stake_carats: u64,
94    voter_pubkey: &Pubkey,
95    vote_state: &VoteState,
96    clock: &Clock,
97    stake_history: &StakeHistory,
98    config: &Config,
99    can_reverse_deactivation: bool,
100) -> Result<(), StakeError> {
101    // If stake is currently active:
102    if stake.stake(clock.epoch, Some(stake_history)) != 0 {
103        // If pubkey of new voter is the same as current,
104        // and we are scheduled to start deactivating this epoch,
105        // we rescind deactivation
106        if stake.delegation.voter_pubkey == *voter_pubkey
107            && clock.epoch == stake.delegation.deactivation_epoch
108            && can_reverse_deactivation
109        {
110            stake.delegation.deactivation_epoch = std::u64::MAX;
111            return Ok(());
112        } else {
113            // can't redelegate to another pubkey if stake is active.
114            return Err(StakeError::TooSoonToRedelegate);
115        }
116    }
117    // Either the stake is freshly activated, is active but has been
118    // deactivated this epoch, or has fully de-activated.
119    // Redelegation implies either re-activation or un-deactivation
120
121    stake.delegation.stake = stake_carats;
122    stake.delegation.activation_epoch = clock.epoch;
123    stake.delegation.deactivation_epoch = std::u64::MAX;
124    stake.delegation.voter_pubkey = *voter_pubkey;
125    stake.delegation.warmup_cooldown_rate = config.warmup_cooldown_rate;
126    stake.credits_observed = vote_state.credits();
127    Ok(())
128}
129
130fn new_stake(
131    stake: u64,
132    voter_pubkey: &Pubkey,
133    vote_state: &VoteState,
134    activation_epoch: Epoch,
135    config: &Config,
136) -> Stake {
137    Stake {
138        delegation: Delegation::new(
139            voter_pubkey,
140            stake,
141            activation_epoch,
142            config.warmup_cooldown_rate,
143        ),
144        credits_observed: vote_state.credits(),
145    }
146}
147
148/// captures a rewards round as carats to be awarded
149///  and the total points over which those carats
150///  are to be distributed
151//  basically read as rewards/points, but in integers instead of as an f64
152#[derive(Clone, Debug, PartialEq)]
153pub struct PointValue {
154    pub rewards: u64, // carats to split
155    pub points: u128, // over these points
156}
157
158fn redeem_stake_rewards(
159    rewarded_epoch: Epoch,
160    stake: &mut Stake,
161    point_value: &PointValue,
162    vote_state: &VoteState,
163    stake_history: Option<&StakeHistory>,
164    inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
165    fix_activating_credits_observed: bool,
166) -> Option<(u64, u64)> {
167    if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
168        inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
169            stake.credits_observed,
170            None,
171        ));
172    }
173    calculate_stake_rewards(
174        rewarded_epoch,
175        stake,
176        point_value,
177        vote_state,
178        stake_history,
179        inflation_point_calc_tracer,
180        fix_activating_credits_observed,
181    )
182    .map(|(stakers_reward, voters_reward, credits_observed)| {
183        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
184            inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
185                stake.credits_observed,
186                Some(credits_observed),
187            ));
188        }
189        stake.credits_observed = credits_observed;
190        stake.delegation.stake += stakers_reward;
191        (stakers_reward, voters_reward)
192    })
193}
194
195fn calculate_stake_points(
196    stake: &Stake,
197    vote_state: &VoteState,
198    stake_history: Option<&StakeHistory>,
199    inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
200) -> u128 {
201    calculate_stake_points_and_credits(
202        stake,
203        vote_state,
204        stake_history,
205        inflation_point_calc_tracer,
206    )
207    .0
208}
209
210/// for a given stake and vote_state, calculate how many
211///   points were earned (credits * stake) and new value
212///   for credits_observed were the points paid
213fn calculate_stake_points_and_credits(
214    stake: &Stake,
215    new_vote_state: &VoteState,
216    stake_history: Option<&StakeHistory>,
217    inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
218) -> (u128, u64) {
219    // if there is no newer credits since observed, return no point
220    if new_vote_state.credits() <= stake.credits_observed {
221        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
222            inflation_point_calc_tracer(&SkippedReason::ZeroCreditsAndReturnCurrent.into());
223        }
224        return (0, stake.credits_observed);
225    }
226
227    let mut points = 0;
228    let mut new_credits_observed = stake.credits_observed;
229
230    for (epoch, final_epoch_credits, initial_epoch_credits) in
231        new_vote_state.epoch_credits().iter().copied()
232    {
233        let stake_amount = u128::from(stake.delegation.stake(epoch, stake_history));
234
235        // figure out how much this stake has seen that
236        //   for which the vote account has a record
237        let earned_credits = if stake.credits_observed < initial_epoch_credits {
238            // the staker observed the entire epoch
239            final_epoch_credits - initial_epoch_credits
240        } else if stake.credits_observed < final_epoch_credits {
241            // the staker registered sometime during the epoch, partial credit
242            final_epoch_credits - new_credits_observed
243        } else {
244            // the staker has already observed or been redeemed this epoch
245            //  or was activated after this epoch
246            0
247        };
248        let earned_credits = u128::from(earned_credits);
249
250        // don't want to assume anything about order of the iterator...
251        new_credits_observed = new_credits_observed.max(final_epoch_credits);
252
253        // finally calculate points for this epoch
254        let earned_points = stake_amount * earned_credits;
255        points += earned_points;
256
257        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
258            inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints(
259                epoch,
260                stake_amount,
261                earned_credits,
262                earned_points,
263            ));
264        }
265    }
266
267    (points, new_credits_observed)
268}
269
270/// for a given stake and vote_state, calculate what distributions and what updates should be made
271/// returns a tuple in the case of a payout of:
272///   * staker_rewards to be distributed
273///   * voter_rewards to be distributed
274///   * new value for credits_observed in the stake
275/// returns None if there's no payout or if any deserved payout is < 1 carat
276fn calculate_stake_rewards(
277    rewarded_epoch: Epoch,
278    stake: &Stake,
279    point_value: &PointValue,
280    vote_state: &VoteState,
281    stake_history: Option<&StakeHistory>,
282    inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
283    fix_activating_credits_observed: bool,
284) -> Option<(u64, u64, u64)> {
285    let (points, credits_observed) = calculate_stake_points_and_credits(
286        stake,
287        vote_state,
288        stake_history,
289        inflation_point_calc_tracer,
290    );
291
292    // Drive credits_observed forward unconditionally when rewards are disabled
293    // or when this is the stake's activation epoch
294    if point_value.rewards == 0
295        || (fix_activating_credits_observed && stake.delegation.activation_epoch == rewarded_epoch)
296    {
297        return Some((0, 0, credits_observed));
298    }
299
300    if points == 0 {
301        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
302            inflation_point_calc_tracer(&SkippedReason::ZeroPoints.into());
303        }
304        return None;
305    }
306    if point_value.points == 0 {
307        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
308            inflation_point_calc_tracer(&SkippedReason::ZeroPointValue.into());
309        }
310        return None;
311    }
312
313    let rewards = points
314        .checked_mul(u128::from(point_value.rewards))
315        .unwrap()
316        .checked_div(point_value.points)
317        .unwrap();
318
319    let rewards = u64::try_from(rewards).unwrap();
320
321    // don't bother trying to split if fractional carats got truncated
322    if rewards == 0 {
323        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
324            inflation_point_calc_tracer(&SkippedReason::ZeroReward.into());
325        }
326        return None;
327    }
328    let (voter_rewards, staker_rewards, is_split) = vote_state.commission_split(rewards);
329    if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
330        inflation_point_calc_tracer(&InflationPointCalculationEvent::SplitRewards(
331            rewards,
332            voter_rewards,
333            staker_rewards,
334            (*point_value).clone(),
335        ));
336    }
337
338    if (voter_rewards == 0 || staker_rewards == 0) && is_split {
339        // don't collect if we lose a whole carat somewhere
340        //  is_split means there should be tokens on both sides,
341        //  uncool to move credits_observed if one side didn't get paid
342        return None;
343    }
344
345    Some((staker_rewards, voter_rewards, credits_observed))
346}
347
348pub trait StakeAccount {
349    fn initialize(
350        &self,
351        authorized: &Authorized,
352        lockup: &Lockup,
353        rent: &Rent,
354    ) -> Result<(), InstructionError>;
355    fn authorize(
356        &self,
357        signers: &HashSet<Pubkey>,
358        new_authority: &Pubkey,
359        stake_authorize: StakeAuthorize,
360        require_custodian_for_locked_stake_authorize: bool,
361        clock: &Clock,
362        custodian: Option<&Pubkey>,
363    ) -> Result<(), InstructionError>;
364    fn authorize_with_seed(
365        &self,
366        authority_base: &KeyedAccount,
367        authority_seed: &str,
368        authority_owner: &Pubkey,
369        new_authority: &Pubkey,
370        stake_authorize: StakeAuthorize,
371        require_custodian_for_locked_stake_authorize: bool,
372        clock: &Clock,
373        custodian: Option<&Pubkey>,
374    ) -> Result<(), InstructionError>;
375    fn delegate(
376        &self,
377        vote_account: &KeyedAccount,
378        clock: &Clock,
379        stake_history: &StakeHistory,
380        config: &Config,
381        signers: &HashSet<Pubkey>,
382        can_reverse_deactivation: bool,
383    ) -> Result<(), InstructionError>;
384    fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError>;
385    fn set_lockup(
386        &self,
387        lockup: &LockupArgs,
388        signers: &HashSet<Pubkey>,
389        clock: Option<&Clock>,
390    ) -> Result<(), InstructionError>;
391    fn split(
392        &self,
393        carats: u64,
394        split_stake: &KeyedAccount,
395        signers: &HashSet<Pubkey>,
396    ) -> Result<(), InstructionError>;
397    fn merge(
398        &self,
399        invoke_context: &dyn InvokeContext,
400        source_stake: &KeyedAccount,
401        clock: &Clock,
402        stake_history: &StakeHistory,
403        signers: &HashSet<Pubkey>,
404        can_merge_expired_lockups: bool,
405    ) -> Result<(), InstructionError>;
406    fn withdraw(
407        &self,
408        carats: u64,
409        to: &KeyedAccount,
410        clock: &Clock,
411        stake_history: &StakeHistory,
412        withdraw_authority: &KeyedAccount,
413        custodian: Option<&KeyedAccount>,
414        prevent_withdraw_to_zero: bool,
415    ) -> Result<(), InstructionError>;
416}
417
418impl<'a> StakeAccount for KeyedAccount<'a> {
419    fn initialize(
420        &self,
421        authorized: &Authorized,
422        lockup: &Lockup,
423        rent: &Rent,
424    ) -> Result<(), InstructionError> {
425        if self.data_len()? != std::mem::size_of::<StakeState>() {
426            return Err(InstructionError::InvalidAccountData);
427        }
428        if let StakeState::Uninitialized = self.state()? {
429            let rent_exempt_reserve = rent.minimum_balance(self.data_len()?);
430
431            if rent_exempt_reserve < self.carats()? {
432                self.set_state(&StakeState::Initialized(Meta {
433                    rent_exempt_reserve,
434                    authorized: *authorized,
435                    lockup: *lockup,
436                }))
437            } else {
438                Err(InstructionError::InsufficientFunds)
439            }
440        } else {
441            Err(InstructionError::InvalidAccountData)
442        }
443    }
444
445    /// Authorize the given pubkey to manage stake (deactivate, withdraw). This may be called
446    /// multiple times, but will implicitly withdraw authorization from the previously authorized
447    /// staker. The default staker is the owner of the stake account's pubkey.
448    fn authorize(
449        &self,
450        signers: &HashSet<Pubkey>,
451        new_authority: &Pubkey,
452        stake_authorize: StakeAuthorize,
453        require_custodian_for_locked_stake_authorize: bool,
454        clock: &Clock,
455        custodian: Option<&Pubkey>,
456    ) -> Result<(), InstructionError> {
457        match self.state()? {
458            StakeState::Stake(mut meta, stake) => {
459                meta.authorized.authorize(
460                    signers,
461                    new_authority,
462                    stake_authorize,
463                    if require_custodian_for_locked_stake_authorize {
464                        Some((&meta.lockup, clock, custodian))
465                    } else {
466                        None
467                    },
468                )?;
469                self.set_state(&StakeState::Stake(meta, stake))
470            }
471            StakeState::Initialized(mut meta) => {
472                meta.authorized.authorize(
473                    signers,
474                    new_authority,
475                    stake_authorize,
476                    if require_custodian_for_locked_stake_authorize {
477                        Some((&meta.lockup, clock, custodian))
478                    } else {
479                        None
480                    },
481                )?;
482                self.set_state(&StakeState::Initialized(meta))
483            }
484            _ => Err(InstructionError::InvalidAccountData),
485        }
486    }
487    fn authorize_with_seed(
488        &self,
489        authority_base: &KeyedAccount,
490        authority_seed: &str,
491        authority_owner: &Pubkey,
492        new_authority: &Pubkey,
493        stake_authorize: StakeAuthorize,
494        require_custodian_for_locked_stake_authorize: bool,
495        clock: &Clock,
496        custodian: Option<&Pubkey>,
497    ) -> Result<(), InstructionError> {
498        let mut signers = HashSet::default();
499        if let Some(base_pubkey) = authority_base.signer_key() {
500            signers.insert(Pubkey::create_with_seed(
501                base_pubkey,
502                authority_seed,
503                authority_owner,
504            )?);
505        }
506        self.authorize(
507            &signers,
508            new_authority,
509            stake_authorize,
510            require_custodian_for_locked_stake_authorize,
511            clock,
512            custodian,
513        )
514    }
515    fn delegate(
516        &self,
517        vote_account: &KeyedAccount,
518        clock: &Clock,
519        stake_history: &StakeHistory,
520        config: &Config,
521        signers: &HashSet<Pubkey>,
522        can_reverse_deactivation: bool,
523    ) -> Result<(), InstructionError> {
524        if vote_account.owner()? != gemachain_vote_program::id() {
525            return Err(InstructionError::IncorrectProgramId);
526        }
527
528        match self.state()? {
529            StakeState::Initialized(meta) => {
530                meta.authorized.check(signers, StakeAuthorize::Staker)?;
531                let stake = new_stake(
532                    self.carats()?.saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;)
533                    vote_account.unsigned_key(),
534                    &State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
535                    clock.epoch,
536                    config,
537                );
538                self.set_state(&StakeState::Stake(meta, stake))
539            }
540            StakeState::Stake(meta, mut stake) => {
541                meta.authorized.check(signers, StakeAuthorize::Staker)?;
542                redelegate(
543                    &mut stake,
544                    self.carats()?.saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;)
545                    vote_account.unsigned_key(),
546                    &State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
547                    clock,
548                    stake_history,
549                    config,
550                    can_reverse_deactivation,
551                )?;
552                self.set_state(&StakeState::Stake(meta, stake))
553            }
554            _ => Err(InstructionError::InvalidAccountData),
555        }
556    }
557    fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError> {
558        if let StakeState::Stake(meta, mut stake) = self.state()? {
559            meta.authorized.check(signers, StakeAuthorize::Staker)?;
560            stake.deactivate(clock.epoch)?;
561
562            self.set_state(&StakeState::Stake(meta, stake))
563        } else {
564            Err(InstructionError::InvalidAccountData)
565        }
566    }
567    fn set_lockup(
568        &self,
569        lockup: &LockupArgs,
570        signers: &HashSet<Pubkey>,
571        clock: Option<&Clock>,
572    ) -> Result<(), InstructionError> {
573        match self.state()? {
574            StakeState::Initialized(mut meta) => {
575                meta.set_lockup(lockup, signers, clock)?;
576                self.set_state(&StakeState::Initialized(meta))
577            }
578            StakeState::Stake(mut meta, stake) => {
579                meta.set_lockup(lockup, signers, clock)?;
580                self.set_state(&StakeState::Stake(meta, stake))
581            }
582            _ => Err(InstructionError::InvalidAccountData),
583        }
584    }
585
586    fn split(
587        &self,
588        carats: u64,
589        split: &KeyedAccount,
590        signers: &HashSet<Pubkey>,
591    ) -> Result<(), InstructionError> {
592        if split.owner()? != id() {
593            return Err(InstructionError::IncorrectProgramId);
594        }
595        if split.data_len()? != std::mem::size_of::<StakeState>() {
596            return Err(InstructionError::InvalidAccountData);
597        }
598
599        if let StakeState::Uninitialized = split.state()? {
600            // verify enough account carats
601            if carats > self.carats()? {
602                return Err(InstructionError::InsufficientFunds);
603            }
604
605            match self.state()? {
606                StakeState::Stake(meta, mut stake) => {
607                    meta.authorized.check(signers, StakeAuthorize::Staker)?;
608                    let split_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
609                        meta.rent_exempt_reserve,
610                        self.data_len()? as u64,
611                        split.data_len()? as u64,
612                    );
613
614                    // verify enough carats for rent and more than 0 stake in new split account
615                    if carats <= split_rent_exempt_reserve.saturating_sub(split.carats()?)
616                        // if not full withdrawal
617                        || (carats != self.carats()?
618                            // verify more than 0 stake left in previous stake
619                            && checked_add(carats, meta.rent_exempt_reserve)? >= self.carats()?)
620                    {
621                        return Err(InstructionError::InsufficientFunds);
622                    }
623                    // split the stake, subtract rent_exempt_balance unless
624                    // the destination account already has those carats
625                    // in place.
626                    // this means that the new stake account will have a stake equivalent to
627                    // carats minus rent_exempt_reserve if it starts out with a zero balance
628                    let (remaining_stake_delta, split_stake_amount) = if carats
629                        == self.carats()?
630                    {
631                        // If split amount equals the full source stake, the new split stake must
632                        // equal the same amount, regardless of any current carat balance in the
633                        // split account. Since split accounts retain the state of their source
634                        // account, this prevents any magic activation of stake by prefunding the
635                        // split account.
636                        // The new split stake also needs to ignore any positive delta between the
637                        // original rent_exempt_reserve and the split_rent_exempt_reserve, in order
638                        // to prevent magic activation of stake by splitting between accounts of
639                        // different sizes.
640                        let remaining_stake_delta =
641                            carats.saturating_sub(meta.rent_exempt_reserve);
642                        (remaining_stake_delta, remaining_stake_delta)
643                    } else {
644                        // Otherwise, the new split stake should reflect the entire split
645                        // requested, less any carats needed to cover the split_rent_exempt_reserve
646                        (
647                            carats,
648                            carats - split_rent_exempt_reserve.saturating_sub(split.carats()?),
649                        )
650                    };
651                    let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?;
652                    let mut split_meta = meta;
653                    split_meta.rent_exempt_reserve = split_rent_exempt_reserve;
654
655                    self.set_state(&StakeState::Stake(meta, stake))?;
656                    split.set_state(&StakeState::Stake(split_meta, split_stake))?;
657                }
658                StakeState::Initialized(meta) => {
659                    meta.authorized.check(signers, StakeAuthorize::Staker)?;
660                    let split_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
661                        meta.rent_exempt_reserve,
662                        self.data_len()? as u64,
663                        split.data_len()? as u64,
664                    );
665
666                    // enough carats for rent and more than 0 stake in new split account
667                    if carats <= split_rent_exempt_reserve.saturating_sub(split.carats()?)
668                        // if not full withdrawal
669                        || (carats != self.carats()?
670                            // verify more than 0 stake left in previous stake
671                            && checked_add(carats, meta.rent_exempt_reserve)? >= self.carats()?)
672                    {
673                        return Err(InstructionError::InsufficientFunds);
674                    }
675
676                    let mut split_meta = meta;
677                    split_meta.rent_exempt_reserve = split_rent_exempt_reserve;
678                    split.set_state(&StakeState::Initialized(split_meta))?;
679                }
680                StakeState::Uninitialized => {
681                    if !signers.contains(self.unsigned_key()) {
682                        return Err(InstructionError::MissingRequiredSignature);
683                    }
684                }
685                _ => return Err(InstructionError::InvalidAccountData),
686            }
687
688            // Deinitialize state upon zero balance
689            if carats == self.carats()? {
690                self.set_state(&StakeState::Uninitialized)?;
691            }
692
693            split
694                .try_account_ref_mut()?
695                .checked_add_carats(carats)?;
696            self.try_account_ref_mut()?.checked_sub_carats(carats)?;
697            Ok(())
698        } else {
699            Err(InstructionError::InvalidAccountData)
700        }
701    }
702
703    fn merge(
704        &self,
705        invoke_context: &dyn InvokeContext,
706        source_account: &KeyedAccount,
707        clock: &Clock,
708        stake_history: &StakeHistory,
709        signers: &HashSet<Pubkey>,
710        can_merge_expired_lockups: bool,
711    ) -> Result<(), InstructionError> {
712        // Ensure source isn't spoofed
713        if source_account.owner()? != id() {
714            return Err(InstructionError::IncorrectProgramId);
715        }
716        // Close the self-reference loophole
717        if source_account.unsigned_key() == self.unsigned_key() {
718            return Err(InstructionError::InvalidArgument);
719        }
720
721        ic_msg!(invoke_context, "Checking if destination stake is mergeable");
722        let stake_merge_kind =
723            MergeKind::get_if_mergeable(invoke_context, self, clock, stake_history)?;
724        let meta = stake_merge_kind.meta();
725
726        // Authorized staker is allowed to split/merge accounts
727        meta.authorized.check(signers, StakeAuthorize::Staker)?;
728
729        ic_msg!(invoke_context, "Checking if source stake is mergeable");
730        let source_merge_kind =
731            MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?;
732
733        let clock = if can_merge_expired_lockups {
734            Some(clock)
735        } else {
736            None
737        };
738
739        ic_msg!(invoke_context, "Merging stake accounts");
740        if let Some(merged_state) =
741            stake_merge_kind.merge(invoke_context, source_merge_kind, clock)?
742        {
743            self.set_state(&merged_state)?;
744        }
745
746        // Source is about to be drained, deinitialize its state
747        source_account.set_state(&StakeState::Uninitialized)?;
748
749        // Drain the source stake account
750        let carats = source_account.carats()?;
751        source_account
752            .try_account_ref_mut()?
753            .checked_sub_carats(carats)?;
754        self.try_account_ref_mut()?.checked_add_carats(carats)?;
755        Ok(())
756    }
757
758    fn withdraw(
759        &self,
760        carats: u64,
761        to: &KeyedAccount,
762        clock: &Clock,
763        stake_history: &StakeHistory,
764        withdraw_authority: &KeyedAccount,
765        custodian: Option<&KeyedAccount>,
766        prevent_withdraw_to_zero: bool,
767    ) -> Result<(), InstructionError> {
768        let mut signers = HashSet::new();
769        let withdraw_authority_pubkey = withdraw_authority
770            .signer_key()
771            .ok_or(InstructionError::MissingRequiredSignature)?;
772        signers.insert(*withdraw_authority_pubkey);
773
774        let (lockup, reserve, is_staked) = match self.state()? {
775            StakeState::Stake(meta, stake) => {
776                meta.authorized
777                    .check(&signers, StakeAuthorize::Withdrawer)?;
778                // if we have a deactivation epoch and we're in cooldown
779                let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
780                    stake.delegation.stake(clock.epoch, Some(stake_history))
781                } else {
782                    // Assume full stake if the stake account hasn't been
783                    //  de-activated, because in the future the exposed stake
784                    //  might be higher than stake.stake() due to warmup
785                    stake.delegation.stake
786                };
787
788                let staked_and_reserve = checked_add(staked, meta.rent_exempt_reserve)?;
789                (meta.lockup, staked_and_reserve, staked != 0)
790            }
791            StakeState::Initialized(meta) => {
792                meta.authorized
793                    .check(&signers, StakeAuthorize::Withdrawer)?;
794                let reserve = if prevent_withdraw_to_zero {
795                    checked_add(meta.rent_exempt_reserve, 1)? // stake accounts must have a balance > rent_exempt_reserve
796                } else {
797                    meta.rent_exempt_reserve
798                };
799
800                (meta.lockup, reserve, false)
801            }
802            StakeState::Uninitialized => {
803                if !signers.contains(self.unsigned_key()) {
804                    return Err(InstructionError::MissingRequiredSignature);
805                }
806                (Lockup::default(), 0, false) // no lockup, no restrictions
807            }
808            _ => return Err(InstructionError::InvalidAccountData),
809        };
810
811        // verify that lockup has expired or that the withdrawal is signed by
812        //   the custodian, both epoch and unix_timestamp must have passed
813        let custodian_pubkey = custodian.and_then(|keyed_account| keyed_account.signer_key());
814        if lockup.is_in_force(clock, custodian_pubkey) {
815            return Err(StakeError::LockupInForce.into());
816        }
817
818        let carats_and_reserve = checked_add(carats, reserve)?;
819        // if the stake is active, we mustn't allow the account to go away
820        if is_staked // line coverage for branch coverage
821            && carats_and_reserve > self.carats()?
822        {
823            return Err(InstructionError::InsufficientFunds);
824        }
825
826        if carats != self.carats()? // not a full withdrawal
827            && carats_and_reserve > self.carats()?
828        {
829            assert!(!is_staked);
830            return Err(InstructionError::InsufficientFunds);
831        }
832
833        // Deinitialize state upon zero balance
834        if carats == self.carats()? {
835            self.set_state(&StakeState::Uninitialized)?;
836        }
837
838        self.try_account_ref_mut()?.checked_sub_carats(carats)?;
839        to.try_account_ref_mut()?.checked_add_carats(carats)?;
840        Ok(())
841    }
842}
843
844#[derive(Clone, Debug, PartialEq)]
845enum MergeKind {
846    Inactive(Meta, u64),
847    ActivationEpoch(Meta, Stake),
848    FullyActive(Meta, Stake),
849}
850
851impl MergeKind {
852    fn meta(&self) -> &Meta {
853        match self {
854            Self::Inactive(meta, _) => meta,
855            Self::ActivationEpoch(meta, _) => meta,
856            Self::FullyActive(meta, _) => meta,
857        }
858    }
859
860    fn active_stake(&self) -> Option<&Stake> {
861        match self {
862            Self::Inactive(_, _) => None,
863            Self::ActivationEpoch(_, stake) => Some(stake),
864            Self::FullyActive(_, stake) => Some(stake),
865        }
866    }
867
868    fn get_if_mergeable(
869        invoke_context: &dyn InvokeContext,
870        stake_keyed_account: &KeyedAccount,
871        clock: &Clock,
872        stake_history: &StakeHistory,
873    ) -> Result<Self, InstructionError> {
874        match stake_keyed_account.state()? {
875            StakeState::Stake(meta, stake) => {
876                // stake must not be in a transient state. Transient here meaning
877                // activating or deactivating with non-zero effective stake.
878                match stake
879                    .delegation
880                    .stake_activating_and_deactivating(clock.epoch, Some(stake_history))
881                {
882                    /*
883                    (e, a, d): e - effective, a - activating, d - deactivating */
884                    (0, 0, 0) => Ok(Self::Inactive(meta, stake_keyed_account.carats()?)),
885                    (0, _, _) => Ok(Self::ActivationEpoch(meta, stake)),
886                    (_, 0, 0) => Ok(Self::FullyActive(meta, stake)),
887                    _ => {
888                        let err = StakeError::MergeTransientStake;
889                        ic_msg!(invoke_context, "{}", err);
890                        Err(err.into())
891                    }
892                }
893            }
894            StakeState::Initialized(meta) => {
895                Ok(Self::Inactive(meta, stake_keyed_account.carats()?))
896            }
897            _ => Err(InstructionError::InvalidAccountData),
898        }
899    }
900
901    fn metas_can_merge(
902        invoke_context: &dyn InvokeContext,
903        stake: &Meta,
904        source: &Meta,
905        clock: Option<&Clock>,
906    ) -> Result<(), InstructionError> {
907        let can_merge_lockups = match clock {
908            // pre-v4 behavior. lockups must match, even when expired
909            None => stake.lockup == source.lockup,
910            // v4 behavior. lockups may mismatch so long as both have expired
911            Some(clock) => {
912                stake.lockup == source.lockup
913                    || (!stake.lockup.is_in_force(clock, None)
914                        && !source.lockup.is_in_force(clock, None))
915            }
916        };
917        // `rent_exempt_reserve` has no bearing on the mergeability of accounts,
918        // as the source account will be culled by runtime once the operation
919        // succeeds. Considering it here would needlessly prevent merging stake
920        // accounts with differing data lengths, which already exist in the wild
921        // due to an SDK bug
922        if stake.authorized == source.authorized && can_merge_lockups {
923            Ok(())
924        } else {
925            ic_msg!(invoke_context, "Unable to merge due to metadata mismatch");
926            Err(StakeError::MergeMismatch.into())
927        }
928    }
929
930    fn active_delegations_can_merge(
931        invoke_context: &dyn InvokeContext,
932        stake: &Delegation,
933        source: &Delegation,
934    ) -> Result<(), InstructionError> {
935        if stake.voter_pubkey != source.voter_pubkey {
936            ic_msg!(invoke_context, "Unable to merge due to voter mismatch");
937            Err(StakeError::MergeMismatch.into())
938        } else if (stake.warmup_cooldown_rate - source.warmup_cooldown_rate).abs() < f64::EPSILON
939            && stake.deactivation_epoch == Epoch::MAX
940            && source.deactivation_epoch == Epoch::MAX
941        {
942            Ok(())
943        } else {
944            ic_msg!(invoke_context, "Unable to merge due to stake deactivation");
945            Err(StakeError::MergeMismatch.into())
946        }
947    }
948
949    // Remove this when the `stake_merge_with_unmatched_credits_observed` feature is removed
950    fn active_stakes_can_merge(
951        invoke_context: &dyn InvokeContext,
952        stake: &Stake,
953        source: &Stake,
954    ) -> Result<(), InstructionError> {
955        Self::active_delegations_can_merge(invoke_context, &stake.delegation, &source.delegation)?;
956        // `credits_observed` MUST match to prevent earning multiple rewards
957        // from a stake account by merging it into another stake account that
958        // is small enough to not be paid out every epoch. This would effectively
959        // reset the larger stake accounts `credits_observed` to that of the
960        // smaller account.
961        if stake.credits_observed == source.credits_observed {
962            Ok(())
963        } else {
964            ic_msg!(
965                invoke_context,
966                "Unable to merge due to credits observed mismatch"
967            );
968            Err(StakeError::MergeMismatch.into())
969        }
970    }
971
972    fn merge(
973        self,
974        invoke_context: &dyn InvokeContext,
975        source: Self,
976        clock: Option<&Clock>,
977    ) -> Result<Option<StakeState>, InstructionError> {
978        Self::metas_can_merge(invoke_context, self.meta(), source.meta(), clock)?;
979        self.active_stake()
980            .zip(source.active_stake())
981            .map(|(stake, source)| {
982                if invoke_context
983                    .is_feature_active(&stake_merge_with_unmatched_credits_observed::id())
984                {
985                    Self::active_delegations_can_merge(
986                        invoke_context,
987                        &stake.delegation,
988                        &source.delegation,
989                    )
990                } else {
991                    Self::active_stakes_can_merge(invoke_context, stake, source)
992                }
993            })
994            .unwrap_or(Ok(()))?;
995        let merged_state = match (self, source) {
996            (Self::Inactive(_, _), Self::Inactive(_, _)) => None,
997            (Self::Inactive(_, _), Self::ActivationEpoch(_, _)) => None,
998            (Self::ActivationEpoch(meta, mut stake), Self::Inactive(_, source_carats)) => {
999                stake.delegation.stake = checked_add(stake.delegation.stake, source_carats)?;
1000                Some(StakeState::Stake(meta, stake))
1001            }
1002            (
1003                Self::ActivationEpoch(meta, mut stake),
1004                Self::ActivationEpoch(source_meta, source_stake),
1005            ) => {
1006                let source_carats = checked_add(
1007                    source_meta.rent_exempt_reserve,
1008                    source_stake.delegation.stake,
1009                )?;
1010                merge_delegation_stake_and_credits_observed(
1011                    invoke_context,
1012                    &mut stake,
1013                    source_carats,
1014                    source_stake.credits_observed,
1015                )?;
1016                Some(StakeState::Stake(meta, stake))
1017            }
1018            (Self::FullyActive(meta, mut stake), Self::FullyActive(_, source_stake)) => {
1019                // Don't stake the source account's `rent_exempt_reserve` to
1020                // protect against the magic activation loophole. It will
1021                // instead be moved into the destination account as extra,
1022                // withdrawable `carats`
1023                merge_delegation_stake_and_credits_observed(
1024                    invoke_context,
1025                    &mut stake,
1026                    source_stake.delegation.stake,
1027                    source_stake.credits_observed,
1028                )?;
1029                Some(StakeState::Stake(meta, stake))
1030            }
1031            _ => return Err(StakeError::MergeMismatch.into()),
1032        };
1033        Ok(merged_state)
1034    }
1035}
1036
1037fn merge_delegation_stake_and_credits_observed(
1038    invoke_context: &dyn InvokeContext,
1039    stake: &mut Stake,
1040    absorbed_carats: u64,
1041    absorbed_credits_observed: u64,
1042) -> Result<(), InstructionError> {
1043    if invoke_context.is_feature_active(&stake_merge_with_unmatched_credits_observed::id()) {
1044        stake.credits_observed =
1045            stake_weighted_credits_observed(stake, absorbed_carats, absorbed_credits_observed)
1046                .ok_or(InstructionError::ArithmeticOverflow)?;
1047    }
1048    stake.delegation.stake = checked_add(stake.delegation.stake, absorbed_carats)?;
1049    Ok(())
1050}
1051
1052/// Calculate the effective credits observed for two stakes when merging
1053///
1054/// When merging two `ActivationEpoch` or `FullyActive` stakes, the credits
1055/// observed of the merged stake is the weighted average of the two stakes'
1056/// credits observed.
1057///
1058/// This is because we can derive the effective credits_observed by reversing the staking
1059/// rewards equation, _while keeping the rewards unchanged after merge (i.e. strong
1060/// requirement)_, like below:
1061///
1062/// a(N) => account, r => rewards, s => stake, c => credits:
1063/// assume:
1064///   a3 = merge(a1, a2)
1065/// then:
1066///   a3.s = a1.s + a2.s
1067///
1068/// Next, given:
1069///   aN.r = aN.c * aN.s (for every N)
1070/// finally:
1071///        a3.r = a1.r + a2.r
1072/// a3.c * a3.s = a1.c * a1.s + a2.c * a2.s
1073///        a3.c = (a1.c * a1.s + a2.c * a2.s) / (a1.s + a2.s)     // QED
1074///
1075/// (For this discussion, we omitted irrelevant variables, including distance
1076///  calculation against vote_account and point indirection.)
1077fn stake_weighted_credits_observed(
1078    stake: &Stake,
1079    absorbed_carats: u64,
1080    absorbed_credits_observed: u64,
1081) -> Option<u64> {
1082    if stake.credits_observed == absorbed_credits_observed {
1083        Some(stake.credits_observed)
1084    } else {
1085        let total_stake = u128::from(stake.delegation.stake.checked_add(absorbed_carats)?);
1086        let stake_weighted_credits =
1087            u128::from(stake.credits_observed).checked_mul(u128::from(stake.delegation.stake))?;
1088        let absorbed_weighted_credits =
1089            u128::from(absorbed_credits_observed).checked_mul(u128::from(absorbed_carats))?;
1090        // Discard fractional credits as a merge side-effect friction by taking
1091        // the ceiling, done by adding `denominator - 1` to the numerator.
1092        let total_weighted_credits = stake_weighted_credits
1093            .checked_add(absorbed_weighted_credits)?
1094            .checked_add(total_stake)?
1095            .checked_sub(1)?;
1096        u64::try_from(total_weighted_credits.checked_div(total_stake)?).ok()
1097    }
1098}
1099
1100// utility function, used by runtime
1101// returns a tuple of (stakers_reward,voters_reward)
1102pub fn redeem_rewards(
1103    rewarded_epoch: Epoch,
1104    stake_account: &mut AccountSharedData,
1105    vote_account: &mut AccountSharedData,
1106    vote_state: &VoteState,
1107    point_value: &PointValue,
1108    stake_history: Option<&StakeHistory>,
1109    inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
1110    fix_activating_credits_observed: bool,
1111) -> Result<(u64, u64), InstructionError> {
1112    if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
1113        if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
1114            inflation_point_calc_tracer(
1115                &InflationPointCalculationEvent::EffectiveStakeAtRewardedEpoch(
1116                    stake.stake(rewarded_epoch, stake_history),
1117                ),
1118            );
1119            inflation_point_calc_tracer(&InflationPointCalculationEvent::RentExemptReserve(
1120                meta.rent_exempt_reserve,
1121            ));
1122            inflation_point_calc_tracer(&InflationPointCalculationEvent::Commission(
1123                vote_state.commission,
1124            ));
1125        }
1126
1127        if let Some((stakers_reward, voters_reward)) = redeem_stake_rewards(
1128            rewarded_epoch,
1129            &mut stake,
1130            point_value,
1131            vote_state,
1132            stake_history,
1133            inflation_point_calc_tracer,
1134            fix_activating_credits_observed,
1135        ) {
1136            stake_account.checked_add_carats(stakers_reward)?;
1137            vote_account.checked_add_carats(voters_reward)?;
1138
1139            stake_account.set_state(&StakeState::Stake(meta, stake))?;
1140
1141            Ok((stakers_reward, voters_reward))
1142        } else {
1143            Err(StakeError::NoCreditsToRedeem.into())
1144        }
1145    } else {
1146        Err(InstructionError::InvalidAccountData)
1147    }
1148}
1149
1150// utility function, used by runtime
1151pub fn calculate_points(
1152    stake_account: &AccountSharedData,
1153    vote_account: &AccountSharedData,
1154    stake_history: Option<&StakeHistory>,
1155) -> Result<u128, InstructionError> {
1156    if let StakeState::Stake(_meta, stake) = stake_account.state()? {
1157        let vote_state: VoteState =
1158            StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
1159
1160        Ok(calculate_stake_points(
1161            &stake,
1162            &vote_state,
1163            stake_history,
1164            &mut null_tracer(),
1165        ))
1166    } else {
1167        Err(InstructionError::InvalidAccountData)
1168    }
1169}
1170
1171// utility function, used by Split
1172//This emulates current Rent math in order to preserve backward compatibility. In the future, and
1173//to support variable rent, the Split instruction should pass in the Rent sysvar instead.
1174fn calculate_split_rent_exempt_reserve(
1175    source_rent_exempt_reserve: u64,
1176    source_data_len: u64,
1177    split_data_len: u64,
1178) -> u64 {
1179    let carats_per_byte_year =
1180        source_rent_exempt_reserve / (source_data_len + ACCOUNT_STORAGE_OVERHEAD);
1181    carats_per_byte_year * (split_data_len + ACCOUNT_STORAGE_OVERHEAD)
1182}
1183
1184pub type RewriteStakeStatus = (&'static str, (u64, u64), (u64, u64));
1185
1186// utility function, used by runtime::Stakes, tests
1187pub fn new_stake_history_entry<'a, I>(
1188    epoch: Epoch,
1189    stakes: I,
1190    history: Option<&StakeHistory>,
1191) -> StakeHistoryEntry
1192where
1193    I: Iterator<Item = &'a Delegation>,
1194{
1195    // whatever the stake says they  had for the epoch
1196    //  and whatever the were still waiting for
1197    fn add(a: (u64, u64, u64), b: (u64, u64, u64)) -> (u64, u64, u64) {
1198        (a.0 + b.0, a.1 + b.1, a.2 + b.2)
1199    }
1200    let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
1201        add(sum, stake.stake_activating_and_deactivating(epoch, history))
1202    });
1203
1204    StakeHistoryEntry {
1205        effective,
1206        activating,
1207        deactivating,
1208    }
1209}
1210
1211// genesis investor accounts
1212pub fn create_lockup_stake_account(
1213    authorized: &Authorized,
1214    lockup: &Lockup,
1215    rent: &Rent,
1216    carats: u64,
1217) -> AccountSharedData {
1218    let mut stake_account =
1219        AccountSharedData::new(carats, std::mem::size_of::<StakeState>(), &id());
1220
1221    let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
1222    assert!(
1223        carats >= rent_exempt_reserve,
1224        "carats: {} is less than rent_exempt_reserve {}",
1225        carats,
1226        rent_exempt_reserve
1227    );
1228
1229    stake_account
1230        .set_state(&StakeState::Initialized(Meta {
1231            authorized: *authorized,
1232            lockup: *lockup,
1233            rent_exempt_reserve,
1234        }))
1235        .expect("set_state");
1236
1237    stake_account
1238}
1239
1240// utility function, used by Bank, tests, genesis for bootstrap
1241pub fn create_account(
1242    authorized: &Pubkey,
1243    voter_pubkey: &Pubkey,
1244    vote_account: &AccountSharedData,
1245    rent: &Rent,
1246    carats: u64,
1247) -> AccountSharedData {
1248    do_create_account(
1249        authorized,
1250        voter_pubkey,
1251        vote_account,
1252        rent,
1253        carats,
1254        Epoch::MAX,
1255    )
1256}
1257
1258// utility function, used by tests
1259pub fn create_account_with_activation_epoch(
1260    authorized: &Pubkey,
1261    voter_pubkey: &Pubkey,
1262    vote_account: &AccountSharedData,
1263    rent: &Rent,
1264    carats: u64,
1265    activation_epoch: Epoch,
1266) -> AccountSharedData {
1267    do_create_account(
1268        authorized,
1269        voter_pubkey,
1270        vote_account,
1271        rent,
1272        carats,
1273        activation_epoch,
1274    )
1275}
1276
1277fn do_create_account(
1278    authorized: &Pubkey,
1279    voter_pubkey: &Pubkey,
1280    vote_account: &AccountSharedData,
1281    rent: &Rent,
1282    carats: u64,
1283    activation_epoch: Epoch,
1284) -> AccountSharedData {
1285    let mut stake_account =
1286        AccountSharedData::new(carats, std::mem::size_of::<StakeState>(), &id());
1287
1288    let vote_state = VoteState::from(vote_account).expect("vote_state");
1289
1290    let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
1291
1292    stake_account
1293        .set_state(&StakeState::Stake(
1294            Meta {
1295                authorized: Authorized::auto(authorized),
1296                rent_exempt_reserve,
1297                ..Meta::default()
1298            },
1299            new_stake(
1300                carats - rent_exempt_reserve, // underflow is an error, is basically: assert!(carats > rent_exempt_reserve);
1301                voter_pubkey,
1302                &vote_state,
1303                activation_epoch,
1304                &Config::default(),
1305            ),
1306        ))
1307        .expect("set_state");
1308
1309    stake_account
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314    use super::*;
1315    use proptest::prelude::*;
1316    use gemachain_sdk::{
1317        account::{AccountSharedData, WritableAccount},
1318        clock::UnixTimestamp,
1319        native_token,
1320        process_instruction::MockInvokeContext,
1321        pubkey::Pubkey,
1322        system_program,
1323    };
1324    use gemachain_vote_program::vote_state;
1325    use std::{cell::RefCell, iter::FromIterator};
1326
1327    #[test]
1328    fn test_authorized_authorize() {
1329        let staker = gemachain_sdk::pubkey::new_rand();
1330        let mut authorized = Authorized::auto(&staker);
1331        let mut signers = HashSet::new();
1332        assert_eq!(
1333            authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
1334            Err(InstructionError::MissingRequiredSignature)
1335        );
1336        signers.insert(staker);
1337        assert_eq!(
1338            authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
1339            Ok(())
1340        );
1341    }
1342
1343    #[test]
1344    fn test_authorized_authorize_with_custodian() {
1345        let staker = gemachain_sdk::pubkey::new_rand();
1346        let custodian = gemachain_sdk::pubkey::new_rand();
1347        let invalid_custodian = gemachain_sdk::pubkey::new_rand();
1348        let mut authorized = Authorized::auto(&staker);
1349        let mut signers = HashSet::new();
1350        signers.insert(staker);
1351
1352        let lockup = Lockup {
1353            epoch: 1,
1354            unix_timestamp: 1,
1355            custodian,
1356        };
1357        let clock = Clock {
1358            epoch: 0,
1359            unix_timestamp: 0,
1360            ..Clock::default()
1361        };
1362
1363        // Legacy behaviour when the `require_custodian_for_locked_stake_authorize` feature is
1364        // inactive
1365        assert_eq!(
1366            authorized.authorize(&signers, &staker, StakeAuthorize::Withdrawer, None),
1367            Ok(())
1368        );
1369
1370        // No lockup, no custodian
1371        assert_eq!(
1372            authorized.authorize(
1373                &signers,
1374                &staker,
1375                StakeAuthorize::Withdrawer,
1376                Some((&Lockup::default(), &clock, None))
1377            ),
1378            Ok(())
1379        );
1380
1381        // No lockup, invalid custodian not a signer
1382        assert_eq!(
1383            authorized.authorize(
1384                &signers,
1385                &staker,
1386                StakeAuthorize::Withdrawer,
1387                Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1388            ),
1389            Ok(()) // <== invalid custodian doesn't matter, there's no lockup
1390        );
1391
1392        // Lockup active, invalid custodian not a signer
1393        assert_eq!(
1394            authorized.authorize(
1395                &signers,
1396                &staker,
1397                StakeAuthorize::Withdrawer,
1398                Some((&lockup, &clock, Some(&invalid_custodian)))
1399            ),
1400            Err(StakeError::CustodianSignatureMissing.into()),
1401        );
1402
1403        signers.insert(invalid_custodian);
1404
1405        // No lockup, invalid custodian is a signer
1406        assert_eq!(
1407            authorized.authorize(
1408                &signers,
1409                &staker,
1410                StakeAuthorize::Withdrawer,
1411                Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1412            ),
1413            Ok(()) // <== invalid custodian doesn't matter, there's no lockup
1414        );
1415
1416        // Lockup active, invalid custodian is a signer
1417        signers.insert(invalid_custodian);
1418        assert_eq!(
1419            authorized.authorize(
1420                &signers,
1421                &staker,
1422                StakeAuthorize::Withdrawer,
1423                Some((&lockup, &clock, Some(&invalid_custodian)))
1424            ),
1425            Err(StakeError::LockupInForce.into()), // <== invalid custodian rejected
1426        );
1427
1428        signers.remove(&invalid_custodian);
1429
1430        // Lockup active, no custodian
1431        assert_eq!(
1432            authorized.authorize(
1433                &signers,
1434                &staker,
1435                StakeAuthorize::Withdrawer,
1436                Some((&lockup, &clock, None))
1437            ),
1438            Err(StakeError::CustodianMissing.into()),
1439        );
1440
1441        // Lockup active, custodian not a signer
1442        assert_eq!(
1443            authorized.authorize(
1444                &signers,
1445                &staker,
1446                StakeAuthorize::Withdrawer,
1447                Some((&lockup, &clock, Some(&custodian)))
1448            ),
1449            Err(StakeError::CustodianSignatureMissing.into()),
1450        );
1451
1452        // Lockup active, custodian is a signer
1453        signers.insert(custodian);
1454        assert_eq!(
1455            authorized.authorize(
1456                &signers,
1457                &staker,
1458                StakeAuthorize::Withdrawer,
1459                Some((&lockup, &clock, Some(&custodian)))
1460            ),
1461            Ok(())
1462        );
1463    }
1464
1465    #[test]
1466    fn test_stake_state_stake_from_fail() {
1467        let mut stake_account = AccountSharedData::new(0, std::mem::size_of::<StakeState>(), &id());
1468
1469        stake_account
1470            .set_state(&StakeState::default())
1471            .expect("set_state");
1472
1473        assert_eq!(stake_from(&stake_account), None);
1474    }
1475
1476    #[test]
1477    fn test_stake_is_bootstrap() {
1478        assert!(Delegation {
1479            activation_epoch: std::u64::MAX,
1480            ..Delegation::default()
1481        }
1482        .is_bootstrap());
1483        assert!(!Delegation {
1484            activation_epoch: 0,
1485            ..Delegation::default()
1486        }
1487        .is_bootstrap());
1488    }
1489
1490    #[test]
1491    fn test_stake_delegate() {
1492        let mut clock = Clock {
1493            epoch: 1,
1494            ..Clock::default()
1495        };
1496
1497        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
1498        let vote_pubkey_2 = gemachain_sdk::pubkey::new_rand();
1499
1500        let mut vote_state = VoteState::default();
1501        for i in 0..1000 {
1502            vote_state.process_slot_vote_unchecked(i);
1503        }
1504        let mut vote_state_2 = VoteState::default();
1505        for i in 0..1000 {
1506            vote_state_2.process_slot_vote_unchecked(i);
1507        }
1508
1509        let vote_account = RefCell::new(vote_state::create_account(
1510            &vote_pubkey,
1511            &gemachain_sdk::pubkey::new_rand(),
1512            0,
1513            100,
1514        ));
1515        let vote_account_2 = RefCell::new(vote_state::create_account(
1516            &vote_pubkey_2,
1517            &gemachain_sdk::pubkey::new_rand(),
1518            0,
1519            100,
1520        ));
1521        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
1522        let vote_keyed_account_2 = KeyedAccount::new(&vote_pubkey_2, false, &vote_account_2);
1523
1524        let vote_state_credits = vote_state.credits();
1525        vote_keyed_account
1526            .set_state(&VoteStateVersions::new_current(vote_state))
1527            .unwrap();
1528        let vote_state_credits_2 = vote_state_2.credits();
1529        vote_keyed_account_2
1530            .set_state(&VoteStateVersions::new_current(vote_state_2))
1531            .unwrap();
1532
1533        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
1534        let stake_carats = 42;
1535        let stake_account = AccountSharedData::new_ref_data_with_space(
1536            stake_carats,
1537            &StakeState::Initialized(Meta {
1538                authorized: Authorized {
1539                    staker: stake_pubkey,
1540                    withdrawer: stake_pubkey,
1541                },
1542                ..Meta::default()
1543            }),
1544            std::mem::size_of::<StakeState>(),
1545            &id(),
1546        )
1547        .expect("stake_account");
1548
1549        // unsigned keyed account
1550        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
1551
1552        let mut signers = HashSet::default();
1553        assert_eq!(
1554            stake_keyed_account.delegate(
1555                &vote_keyed_account,
1556                &clock,
1557                &StakeHistory::default(),
1558                &Config::default(),
1559                &signers,
1560                true,
1561            ),
1562            Err(InstructionError::MissingRequiredSignature)
1563        );
1564
1565        // signed keyed account
1566        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
1567        signers.insert(stake_pubkey);
1568        assert!(stake_keyed_account
1569            .delegate(
1570                &vote_keyed_account,
1571                &clock,
1572                &StakeHistory::default(),
1573                &Config::default(),
1574                &signers,
1575                true,
1576            )
1577            .is_ok());
1578
1579        // verify that delegate() looks right, compare against hand-rolled
1580        let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1581        assert_eq!(
1582            stake,
1583            Stake {
1584                delegation: Delegation {
1585                    voter_pubkey: vote_pubkey,
1586                    stake: stake_carats,
1587                    activation_epoch: clock.epoch,
1588                    deactivation_epoch: std::u64::MAX,
1589                    ..Delegation::default()
1590                },
1591                credits_observed: vote_state_credits,
1592            }
1593        );
1594
1595        clock.epoch += 1;
1596
1597        // verify that delegate fails as stake is active and not deactivating
1598        assert_eq!(
1599            stake_keyed_account.delegate(
1600                &vote_keyed_account,
1601                &clock,
1602                &StakeHistory::default(),
1603                &Config::default(),
1604                &signers,
1605                true
1606            ),
1607            Err(StakeError::TooSoonToRedelegate.into())
1608        );
1609
1610        // deactivate
1611        stake_keyed_account.deactivate(&clock, &signers).unwrap();
1612
1613        // verify that delegate to a different vote account fails
1614        // during deactivation
1615        assert_eq!(
1616            stake_keyed_account.delegate(
1617                &vote_keyed_account_2,
1618                &clock,
1619                &StakeHistory::default(),
1620                &Config::default(),
1621                &signers,
1622                true,
1623            ),
1624            Err(StakeError::TooSoonToRedelegate.into())
1625        );
1626
1627        // verify that delegate succeeds to same vote account
1628        // when stake is deactivating
1629        stake_keyed_account
1630            .delegate(
1631                &vote_keyed_account,
1632                &clock,
1633                &StakeHistory::default(),
1634                &Config::default(),
1635                &signers,
1636                true,
1637            )
1638            .unwrap();
1639
1640        // verify that deactivation has been cleared
1641        let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1642        assert_eq!(stake.delegation.deactivation_epoch, std::u64::MAX);
1643
1644        // verify that delegate to a different vote account fails
1645        // if stake is still active
1646        assert_eq!(
1647            stake_keyed_account.delegate(
1648                &vote_keyed_account_2,
1649                &clock,
1650                &StakeHistory::default(),
1651                &Config::default(),
1652                &signers,
1653                true,
1654            ),
1655            Err(StakeError::TooSoonToRedelegate.into())
1656        );
1657
1658        // deactivate, so we can re-delegate
1659        stake_keyed_account.deactivate(&clock, &signers).unwrap();
1660
1661        // without stake history, cool down is instantaneous
1662        clock.epoch += 1;
1663
1664        // verify that delegate can be called to new vote account, 2nd is redelegate
1665        assert!(stake_keyed_account
1666            .delegate(
1667                &vote_keyed_account_2,
1668                &clock,
1669                &StakeHistory::default(),
1670                &Config::default(),
1671                &signers,
1672                true,
1673            )
1674            .is_ok());
1675
1676        // signed but faked vote account
1677        let faked_vote_account = vote_account_2.clone();
1678        faked_vote_account
1679            .borrow_mut()
1680            .set_owner(gemachain_sdk::pubkey::new_rand());
1681        let faked_vote_keyed_account =
1682            KeyedAccount::new(&vote_pubkey_2, false, &faked_vote_account);
1683        assert_eq!(
1684            stake_keyed_account.delegate(
1685                &faked_vote_keyed_account,
1686                &clock,
1687                &StakeHistory::default(),
1688                &Config::default(),
1689                &signers,
1690                true,
1691            ),
1692            Err(gemachain_sdk::instruction::InstructionError::IncorrectProgramId)
1693        );
1694
1695        // verify that delegate() looks right, compare against hand-rolled
1696        let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1697        assert_eq!(
1698            stake,
1699            Stake {
1700                delegation: Delegation {
1701                    voter_pubkey: vote_pubkey_2,
1702                    stake: stake_carats,
1703                    activation_epoch: clock.epoch,
1704                    deactivation_epoch: std::u64::MAX,
1705                    ..Delegation::default()
1706                },
1707                credits_observed: vote_state_credits_2,
1708            }
1709        );
1710
1711        // verify that non-stakes fail delegate()
1712        let stake_state = StakeState::RewardsPool;
1713
1714        stake_keyed_account.set_state(&stake_state).unwrap();
1715        assert!(stake_keyed_account
1716            .delegate(
1717                &vote_keyed_account,
1718                &clock,
1719                &StakeHistory::default(),
1720                &Config::default(),
1721                &signers,
1722                true,
1723            )
1724            .is_err());
1725    }
1726
1727    fn create_stake_history_from_delegations(
1728        bootstrap: Option<u64>,
1729        epochs: std::ops::Range<Epoch>,
1730        delegations: &[Delegation],
1731    ) -> StakeHistory {
1732        let mut stake_history = StakeHistory::default();
1733
1734        let bootstrap_delegation = if let Some(bootstrap) = bootstrap {
1735            vec![Delegation {
1736                activation_epoch: std::u64::MAX,
1737                stake: bootstrap,
1738                ..Delegation::default()
1739            }]
1740        } else {
1741            vec![]
1742        };
1743
1744        for epoch in epochs {
1745            let entry = new_stake_history_entry(
1746                epoch,
1747                delegations.iter().chain(bootstrap_delegation.iter()),
1748                Some(&stake_history),
1749            );
1750            stake_history.add(epoch, entry);
1751        }
1752
1753        stake_history
1754    }
1755
1756    #[test]
1757    fn test_stake_activating_and_deactivating() {
1758        let stake = Delegation {
1759            stake: 1_000,
1760            activation_epoch: 0, // activating at zero
1761            deactivation_epoch: 5,
1762            ..Delegation::default()
1763        };
1764
1765        // save this off so stake.config.warmup_rate changes don't break this test
1766        let increment = (1_000_f64 * stake.warmup_cooldown_rate) as u64;
1767
1768        let mut stake_history = StakeHistory::default();
1769        // assert that this stake follows step function if there's no history
1770        assert_eq!(
1771            stake.stake_activating_and_deactivating(stake.activation_epoch, Some(&stake_history),),
1772            (0, stake.stake, 0)
1773        );
1774        for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
1775            assert_eq!(
1776                stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
1777                (stake.stake, 0, 0)
1778            );
1779        }
1780        // assert that this stake is full deactivating
1781        assert_eq!(
1782            stake
1783                .stake_activating_and_deactivating(stake.deactivation_epoch, Some(&stake_history),),
1784            (stake.stake, 0, stake.stake)
1785        );
1786        // assert that this stake is fully deactivated if there's no history
1787        assert_eq!(
1788            stake.stake_activating_and_deactivating(
1789                stake.deactivation_epoch + 1,
1790                Some(&stake_history),
1791            ),
1792            (0, 0, 0)
1793        );
1794
1795        stake_history.add(
1796            0u64, // entry for zero doesn't have my activating amount
1797            StakeHistoryEntry {
1798                effective: 1_000,
1799                activating: 0,
1800                ..StakeHistoryEntry::default()
1801            },
1802        );
1803        // assert that this stake is broken, because above setup is broken
1804        assert_eq!(
1805            stake.stake_activating_and_deactivating(1, Some(&stake_history)),
1806            (0, stake.stake, 0)
1807        );
1808
1809        stake_history.add(
1810            0u64, // entry for zero has my activating amount
1811            StakeHistoryEntry {
1812                effective: 1_000,
1813                activating: 1_000,
1814                ..StakeHistoryEntry::default()
1815            },
1816            // no entry for 1, so this stake gets shorted
1817        );
1818        // assert that this stake is broken, because above setup is broken
1819        assert_eq!(
1820            stake.stake_activating_and_deactivating(2, Some(&stake_history)),
1821            (increment, stake.stake - increment, 0)
1822        );
1823
1824        // start over, test deactivation edge cases
1825        let mut stake_history = StakeHistory::default();
1826
1827        stake_history.add(
1828            stake.deactivation_epoch, // entry for zero doesn't have my de-activating amount
1829            StakeHistoryEntry {
1830                effective: 1_000,
1831                activating: 0,
1832                ..StakeHistoryEntry::default()
1833            },
1834        );
1835        // assert that this stake is broken, because above setup is broken
1836        assert_eq!(
1837            stake.stake_activating_and_deactivating(
1838                stake.deactivation_epoch + 1,
1839                Some(&stake_history),
1840            ),
1841            (stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
1842        );
1843
1844        // put in my initial deactivating amount, but don't put in an entry for next
1845        stake_history.add(
1846            stake.deactivation_epoch, // entry for zero has my de-activating amount
1847            StakeHistoryEntry {
1848                effective: 1_000,
1849                deactivating: 1_000,
1850                ..StakeHistoryEntry::default()
1851            },
1852        );
1853        // assert that this stake is broken, because above setup is broken
1854        assert_eq!(
1855            stake.stake_activating_and_deactivating(
1856                stake.deactivation_epoch + 2,
1857                Some(&stake_history),
1858            ),
1859            (stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
1860        );
1861    }
1862
1863    mod same_epoch_activation_then_deactivation {
1864        use super::*;
1865
1866        enum OldDeactivationBehavior {
1867            Stuck,
1868            Slow,
1869        }
1870
1871        fn do_test(old_behavior: OldDeactivationBehavior, expected_stakes: &[(u64, u64, u64)]) {
1872            let cluster_stake = 1_000;
1873            let activating_stake = 10_000;
1874            let some_stake = 700;
1875            let some_epoch = 0;
1876
1877            let stake = Delegation {
1878                stake: some_stake,
1879                activation_epoch: some_epoch,
1880                deactivation_epoch: some_epoch,
1881                ..Delegation::default()
1882            };
1883
1884            let mut stake_history = StakeHistory::default();
1885            let cluster_deactivation_at_stake_modified_epoch = match old_behavior {
1886                OldDeactivationBehavior::Stuck => 0,
1887                OldDeactivationBehavior::Slow => 1000,
1888            };
1889
1890            let stake_history_entries = vec![
1891                (
1892                    cluster_stake,
1893                    activating_stake,
1894                    cluster_deactivation_at_stake_modified_epoch,
1895                ),
1896                (cluster_stake, activating_stake, 1000),
1897                (cluster_stake, activating_stake, 1000),
1898                (cluster_stake, activating_stake, 100),
1899                (cluster_stake, activating_stake, 100),
1900                (cluster_stake, activating_stake, 100),
1901                (cluster_stake, activating_stake, 100),
1902            ];
1903
1904            for (epoch, (effective, activating, deactivating)) in
1905                stake_history_entries.into_iter().enumerate()
1906            {
1907                stake_history.add(
1908                    epoch as Epoch,
1909                    StakeHistoryEntry {
1910                        effective,
1911                        activating,
1912                        deactivating,
1913                    },
1914                );
1915            }
1916
1917            assert_eq!(
1918                expected_stakes,
1919                (0..expected_stakes.len())
1920                    .map(|epoch| stake
1921                        .stake_activating_and_deactivating(epoch as u64, Some(&stake_history),))
1922                    .collect::<Vec<_>>()
1923            );
1924        }
1925
1926        #[test]
1927        fn test_new_behavior_previously_slow() {
1928            // any stake accounts activated and deactivated at the same epoch
1929            // shouldn't been activated (then deactivated) at all!
1930
1931            do_test(
1932                OldDeactivationBehavior::Slow,
1933                &[
1934                    (0, 0, 0),
1935                    (0, 0, 0),
1936                    (0, 0, 0),
1937                    (0, 0, 0),
1938                    (0, 0, 0),
1939                    (0, 0, 0),
1940                    (0, 0, 0),
1941                ],
1942            );
1943        }
1944
1945        #[test]
1946        fn test_new_behavior_previously_stuck() {
1947            // any stake accounts activated and deactivated at the same epoch
1948            // shouldn't been activated (then deactivated) at all!
1949
1950            do_test(
1951                OldDeactivationBehavior::Stuck,
1952                &[
1953                    (0, 0, 0),
1954                    (0, 0, 0),
1955                    (0, 0, 0),
1956                    (0, 0, 0),
1957                    (0, 0, 0),
1958                    (0, 0, 0),
1959                    (0, 0, 0),
1960                ],
1961            );
1962        }
1963    }
1964
1965    #[test]
1966    fn test_inflation_and_slashing_with_activating_and_deactivating_stake() {
1967        // some really boring delegation and stake_history setup
1968        let (delegated_stake, mut stake, stake_history) = {
1969            let cluster_stake = 1_000;
1970            let delegated_stake = 700;
1971
1972            let stake = Delegation {
1973                stake: delegated_stake,
1974                activation_epoch: 0,
1975                deactivation_epoch: 4,
1976                ..Delegation::default()
1977            };
1978
1979            let mut stake_history = StakeHistory::default();
1980            stake_history.add(
1981                0,
1982                StakeHistoryEntry {
1983                    effective: cluster_stake,
1984                    activating: delegated_stake,
1985                    ..StakeHistoryEntry::default()
1986                },
1987            );
1988            let newly_effective_at_epoch1 = (cluster_stake as f64 * 0.25) as u64;
1989            assert_eq!(newly_effective_at_epoch1, 250);
1990            stake_history.add(
1991                1,
1992                StakeHistoryEntry {
1993                    effective: cluster_stake + newly_effective_at_epoch1,
1994                    activating: delegated_stake - newly_effective_at_epoch1,
1995                    ..StakeHistoryEntry::default()
1996                },
1997            );
1998            let newly_effective_at_epoch2 =
1999                ((cluster_stake + newly_effective_at_epoch1) as f64 * 0.25) as u64;
2000            assert_eq!(newly_effective_at_epoch2, 312);
2001            stake_history.add(
2002                2,
2003                StakeHistoryEntry {
2004                    effective: cluster_stake
2005                        + newly_effective_at_epoch1
2006                        + newly_effective_at_epoch2,
2007                    activating: delegated_stake
2008                        - newly_effective_at_epoch1
2009                        - newly_effective_at_epoch2,
2010                    ..StakeHistoryEntry::default()
2011                },
2012            );
2013            stake_history.add(
2014                3,
2015                StakeHistoryEntry {
2016                    effective: cluster_stake + delegated_stake,
2017                    ..StakeHistoryEntry::default()
2018                },
2019            );
2020            stake_history.add(
2021                4,
2022                StakeHistoryEntry {
2023                    effective: cluster_stake + delegated_stake,
2024                    deactivating: delegated_stake,
2025                    ..StakeHistoryEntry::default()
2026                },
2027            );
2028            let newly_not_effective_stake_at_epoch5 =
2029                ((cluster_stake + delegated_stake) as f64 * 0.25) as u64;
2030            assert_eq!(newly_not_effective_stake_at_epoch5, 425);
2031            stake_history.add(
2032                5,
2033                StakeHistoryEntry {
2034                    effective: cluster_stake + delegated_stake
2035                        - newly_not_effective_stake_at_epoch5,
2036                    deactivating: delegated_stake - newly_not_effective_stake_at_epoch5,
2037                    ..StakeHistoryEntry::default()
2038                },
2039            );
2040
2041            (delegated_stake, stake, stake_history)
2042        };
2043
2044        // helper closures
2045        let calculate_each_staking_status = |stake: &Delegation, epoch_count: usize| -> Vec<_> {
2046            (0..epoch_count)
2047                .map(|epoch| {
2048                    stake.stake_activating_and_deactivating(epoch as u64, Some(&stake_history))
2049                })
2050                .collect::<Vec<_>>()
2051        };
2052        let adjust_staking_status = |rate: f64, status: &Vec<_>| {
2053            status
2054                .clone()
2055                .into_iter()
2056                .map(|(a, b, c)| {
2057                    (
2058                        (a as f64 * rate) as u64,
2059                        (b as f64 * rate) as u64,
2060                        (c as f64 * rate) as u64,
2061                    )
2062                })
2063                .collect::<Vec<_>>()
2064        };
2065
2066        let expected_staking_status_transition = vec![
2067            (0, 700, 0),
2068            (250, 450, 0),
2069            (562, 138, 0),
2070            (700, 0, 0),
2071            (700, 0, 700),
2072            (275, 0, 275),
2073            (0, 0, 0),
2074        ];
2075        let expected_staking_status_transition_base = vec![
2076            (0, 700, 0),
2077            (250, 450, 0),
2078            (562, 138 + 1, 0), // +1 is needed for rounding
2079            (700, 0, 0),
2080            (700, 0, 700),
2081            (275 + 1, 0, 275 + 1), // +1 is needed for rounding
2082            (0, 0, 0),
2083        ];
2084
2085        // normal stake activating and deactivating transition test, just in case
2086        assert_eq!(
2087            expected_staking_status_transition,
2088            calculate_each_staking_status(&stake, expected_staking_status_transition.len())
2089        );
2090
2091        // 10% inflation rewards assuming some sizable epochs passed!
2092        let rate = 1.10;
2093        stake.stake = (delegated_stake as f64 * rate) as u64;
2094        let expected_staking_status_transition =
2095            adjust_staking_status(rate, &expected_staking_status_transition_base);
2096
2097        assert_eq!(
2098            expected_staking_status_transition,
2099            calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
2100        );
2101
2102        // 50% slashing!!!
2103        let rate = 0.5;
2104        stake.stake = (delegated_stake as f64 * rate) as u64;
2105        let expected_staking_status_transition =
2106            adjust_staking_status(rate, &expected_staking_status_transition_base);
2107
2108        assert_eq!(
2109            expected_staking_status_transition,
2110            calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
2111        );
2112    }
2113
2114    #[test]
2115    fn test_stop_activating_after_deactivation() {
2116        gemachain_logger::setup();
2117        let stake = Delegation {
2118            stake: 1_000,
2119            activation_epoch: 0,
2120            deactivation_epoch: 3,
2121            ..Delegation::default()
2122        };
2123
2124        let base_stake = 1_000;
2125        let mut stake_history = StakeHistory::default();
2126        let mut effective = base_stake;
2127        let other_activation = 100;
2128        let mut other_activations = vec![0];
2129
2130        // Build a stake history where the test staker always consumes all of the available warm
2131        // up and cool down stake. However, simulate other stakers beginning to activate during
2132        // the test staker's deactivation.
2133        for epoch in 0..=stake.deactivation_epoch + 1 {
2134            let (activating, deactivating) = if epoch < stake.deactivation_epoch {
2135                (stake.stake + base_stake - effective, 0)
2136            } else {
2137                let other_activation_sum: u64 = other_activations.iter().sum();
2138                let deactivating = effective - base_stake - other_activation_sum;
2139                (other_activation, deactivating)
2140            };
2141
2142            stake_history.add(
2143                epoch,
2144                StakeHistoryEntry {
2145                    effective,
2146                    activating,
2147                    deactivating,
2148                },
2149            );
2150
2151            let effective_rate_limited = (effective as f64 * stake.warmup_cooldown_rate) as u64;
2152            if epoch < stake.deactivation_epoch {
2153                effective += effective_rate_limited.min(activating);
2154                other_activations.push(0);
2155            } else {
2156                effective -= effective_rate_limited.min(deactivating);
2157                effective += other_activation;
2158                other_activations.push(other_activation);
2159            }
2160        }
2161
2162        for epoch in 0..=stake.deactivation_epoch + 1 {
2163            let history = stake_history.get(&epoch).unwrap();
2164            let other_activations: u64 = other_activations[..=epoch as usize].iter().sum();
2165            let expected_stake = history.effective - base_stake - other_activations;
2166            let (expected_activating, expected_deactivating) = if epoch < stake.deactivation_epoch {
2167                (history.activating, 0)
2168            } else {
2169                (0, history.deactivating)
2170            };
2171            assert_eq!(
2172                stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
2173                (expected_stake, expected_activating, expected_deactivating)
2174            );
2175        }
2176    }
2177
2178    #[test]
2179    fn test_stake_warmup_cooldown_sub_integer_moves() {
2180        let delegations = [Delegation {
2181            stake: 2,
2182            activation_epoch: 0, // activating at zero
2183            deactivation_epoch: 5,
2184            ..Delegation::default()
2185        }];
2186        // give 2 epochs of cooldown
2187        let epochs = 7;
2188        // make boostrap stake smaller than warmup so warmup/cooldownn
2189        //  increment is always smaller than 1
2190        let bootstrap = (delegations[0].warmup_cooldown_rate * 100.0 / 2.0) as u64;
2191        let stake_history =
2192            create_stake_history_from_delegations(Some(bootstrap), 0..epochs, &delegations);
2193        let mut max_stake = 0;
2194        let mut min_stake = 2;
2195
2196        for epoch in 0..epochs {
2197            let stake = delegations
2198                .iter()
2199                .map(|delegation| delegation.stake(epoch, Some(&stake_history)))
2200                .sum::<u64>();
2201            max_stake = max_stake.max(stake);
2202            min_stake = min_stake.min(stake);
2203        }
2204        assert_eq!(max_stake, 2);
2205        assert_eq!(min_stake, 0);
2206    }
2207
2208    #[test]
2209    fn test_stake_warmup_cooldown() {
2210        let delegations = [
2211            Delegation {
2212                // never deactivates
2213                stake: 1_000,
2214                activation_epoch: std::u64::MAX,
2215                ..Delegation::default()
2216            },
2217            Delegation {
2218                stake: 1_000,
2219                activation_epoch: 0,
2220                deactivation_epoch: 9,
2221                ..Delegation::default()
2222            },
2223            Delegation {
2224                stake: 1_000,
2225                activation_epoch: 1,
2226                deactivation_epoch: 6,
2227                ..Delegation::default()
2228            },
2229            Delegation {
2230                stake: 1_000,
2231                activation_epoch: 2,
2232                deactivation_epoch: 5,
2233                ..Delegation::default()
2234            },
2235            Delegation {
2236                stake: 1_000,
2237                activation_epoch: 2,
2238                deactivation_epoch: 4,
2239                ..Delegation::default()
2240            },
2241            Delegation {
2242                stake: 1_000,
2243                activation_epoch: 4,
2244                deactivation_epoch: 4,
2245                ..Delegation::default()
2246            },
2247        ];
2248        // chosen to ensure that the last activated stake (at 4) finishes
2249        //  warming up and cooling down
2250        //  a stake takes 2.0f64.log(1.0 + STAKE_WARMUP_RATE) epochs to warm up or cool down
2251        //  when all alone, but the above overlap a lot
2252        let epochs = 20;
2253
2254        let stake_history = create_stake_history_from_delegations(None, 0..epochs, &delegations);
2255
2256        let mut prev_total_effective_stake = delegations
2257            .iter()
2258            .map(|delegation| delegation.stake(0, Some(&stake_history)))
2259            .sum::<u64>();
2260
2261        // uncomment and add ! for fun with graphing
2262        // eprintln("\n{:8} {:8} {:8}", "   epoch", "   total", "   delta");
2263        for epoch in 1..epochs {
2264            let total_effective_stake = delegations
2265                .iter()
2266                .map(|delegation| delegation.stake(epoch, Some(&stake_history)))
2267                .sum::<u64>();
2268
2269            let delta = if total_effective_stake > prev_total_effective_stake {
2270                total_effective_stake - prev_total_effective_stake
2271            } else {
2272                prev_total_effective_stake - total_effective_stake
2273            };
2274
2275            // uncomment and add ! for fun with graphing
2276            //eprint("{:8} {:8} {:8} ", epoch, total_effective_stake, delta);
2277            //(0..(total_effective_stake as usize / (stakes.len() * 5))).for_each(|_| eprint("#"));
2278            //eprintln();
2279
2280            assert!(
2281                delta
2282                    <= ((prev_total_effective_stake as f64 * Config::default().warmup_cooldown_rate) as u64)
2283                        .max(1)
2284            );
2285
2286            prev_total_effective_stake = total_effective_stake;
2287        }
2288    }
2289
2290    #[test]
2291    fn test_stake_initialize() {
2292        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2293        let stake_carats = 42;
2294        let stake_account =
2295            AccountSharedData::new_ref(stake_carats, std::mem::size_of::<StakeState>(), &id());
2296
2297        // unsigned keyed account
2298        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2299        let custodian = gemachain_sdk::pubkey::new_rand();
2300
2301        // not enough balance for rent...
2302        assert_eq!(
2303            stake_keyed_account.initialize(
2304                &Authorized::default(),
2305                &Lockup::default(),
2306                &Rent {
2307                    carats_per_byte_year: 42,
2308                    ..Rent::free()
2309                },
2310            ),
2311            Err(InstructionError::InsufficientFunds)
2312        );
2313
2314        // this one works, as is uninit
2315        assert_eq!(
2316            stake_keyed_account.initialize(
2317                &Authorized::auto(&stake_pubkey),
2318                &Lockup {
2319                    epoch: 1,
2320                    unix_timestamp: 0,
2321                    custodian
2322                },
2323                &Rent::free(),
2324            ),
2325            Ok(())
2326        );
2327        // check that we see what we expect
2328        assert_eq!(
2329            from(&stake_keyed_account.account.borrow()).unwrap(),
2330            StakeState::Initialized(Meta {
2331                lockup: Lockup {
2332                    unix_timestamp: 0,
2333                    epoch: 1,
2334                    custodian
2335                },
2336                ..Meta {
2337                    authorized: Authorized::auto(&stake_pubkey),
2338                    ..Meta::default()
2339                }
2340            })
2341        );
2342
2343        // 2nd time fails, can't move it from anything other than uninit->init
2344        assert_eq!(
2345            stake_keyed_account.initialize(
2346                &Authorized::default(),
2347                &Lockup::default(),
2348                &Rent::free()
2349            ),
2350            Err(InstructionError::InvalidAccountData)
2351        );
2352    }
2353
2354    #[test]
2355    fn test_initialize_incorrect_account_sizes() {
2356        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2357        let stake_carats = 42;
2358        let stake_account = AccountSharedData::new_ref(
2359            stake_carats,
2360            std::mem::size_of::<StakeState>() + 1,
2361            &id(),
2362        );
2363        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2364
2365        assert_eq!(
2366            stake_keyed_account.initialize(
2367                &Authorized::default(),
2368                &Lockup::default(),
2369                &Rent {
2370                    carats_per_byte_year: 42,
2371                    ..Rent::free()
2372                },
2373            ),
2374            Err(InstructionError::InvalidAccountData)
2375        );
2376
2377        let stake_account = AccountSharedData::new_ref(
2378            stake_carats,
2379            std::mem::size_of::<StakeState>() - 1,
2380            &id(),
2381        );
2382        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2383
2384        assert_eq!(
2385            stake_keyed_account.initialize(
2386                &Authorized::default(),
2387                &Lockup::default(),
2388                &Rent {
2389                    carats_per_byte_year: 42,
2390                    ..Rent::free()
2391                },
2392            ),
2393            Err(InstructionError::InvalidAccountData)
2394        );
2395    }
2396
2397    #[test]
2398    fn test_deactivate() {
2399        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2400        let stake_carats = 42;
2401        let stake_account = AccountSharedData::new_ref_data_with_space(
2402            stake_carats,
2403            &StakeState::Initialized(Meta::auto(&stake_pubkey)),
2404            std::mem::size_of::<StakeState>(),
2405            &id(),
2406        )
2407        .expect("stake_account");
2408
2409        let clock = Clock {
2410            epoch: 1,
2411            ..Clock::default()
2412        };
2413
2414        // signed keyed account but not staked yet
2415        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2416        let signers = vec![stake_pubkey].into_iter().collect();
2417        assert_eq!(
2418            stake_keyed_account.deactivate(&clock, &signers),
2419            Err(InstructionError::InvalidAccountData)
2420        );
2421
2422        // Staking
2423        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2424        let vote_account = RefCell::new(vote_state::create_account(
2425            &vote_pubkey,
2426            &gemachain_sdk::pubkey::new_rand(),
2427            0,
2428            100,
2429        ));
2430        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2431        vote_keyed_account
2432            .set_state(&VoteStateVersions::new_current(VoteState::default()))
2433            .unwrap();
2434        assert_eq!(
2435            stake_keyed_account.delegate(
2436                &vote_keyed_account,
2437                &clock,
2438                &StakeHistory::default(),
2439                &Config::default(),
2440                &signers,
2441                true,
2442            ),
2443            Ok(())
2444        );
2445
2446        // no signers fails
2447        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2448        assert_eq!(
2449            stake_keyed_account.deactivate(&clock, &HashSet::default()),
2450            Err(InstructionError::MissingRequiredSignature)
2451        );
2452
2453        // Deactivate after staking
2454        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2455        assert_eq!(stake_keyed_account.deactivate(&clock, &signers), Ok(()));
2456
2457        // verify that deactivate() only works once
2458        assert_eq!(
2459            stake_keyed_account.deactivate(&clock, &signers),
2460            Err(StakeError::AlreadyDeactivated.into())
2461        );
2462    }
2463
2464    #[test]
2465    fn test_set_lockup() {
2466        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2467        let stake_carats = 42;
2468        let stake_account = AccountSharedData::new_ref_data_with_space(
2469            stake_carats,
2470            &StakeState::Uninitialized,
2471            std::mem::size_of::<StakeState>(),
2472            &id(),
2473        )
2474        .expect("stake_account");
2475
2476        // wrong state, should fail
2477        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2478        assert_eq!(
2479            stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(), None),
2480            Err(InstructionError::InvalidAccountData)
2481        );
2482
2483        // initalize the stake
2484        let custodian = gemachain_sdk::pubkey::new_rand();
2485        stake_keyed_account
2486            .initialize(
2487                &Authorized::auto(&stake_pubkey),
2488                &Lockup {
2489                    unix_timestamp: 1,
2490                    epoch: 1,
2491                    custodian,
2492                },
2493                &Rent::free(),
2494            )
2495            .unwrap();
2496
2497        assert_eq!(
2498            stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(), None),
2499            Err(InstructionError::MissingRequiredSignature)
2500        );
2501
2502        assert_eq!(
2503            stake_keyed_account.set_lockup(
2504                &LockupArgs {
2505                    unix_timestamp: Some(1),
2506                    epoch: Some(1),
2507                    custodian: Some(custodian),
2508                },
2509                &vec![custodian].into_iter().collect(),
2510                None
2511            ),
2512            Ok(())
2513        );
2514
2515        // delegate stake
2516        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2517        let vote_account = RefCell::new(vote_state::create_account(
2518            &vote_pubkey,
2519            &gemachain_sdk::pubkey::new_rand(),
2520            0,
2521            100,
2522        ));
2523        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2524        vote_keyed_account
2525            .set_state(&VoteStateVersions::new_current(VoteState::default()))
2526            .unwrap();
2527
2528        stake_keyed_account
2529            .delegate(
2530                &vote_keyed_account,
2531                &Clock::default(),
2532                &StakeHistory::default(),
2533                &Config::default(),
2534                &vec![stake_pubkey].into_iter().collect(),
2535                true,
2536            )
2537            .unwrap();
2538
2539        assert_eq!(
2540            stake_keyed_account.set_lockup(
2541                &LockupArgs {
2542                    unix_timestamp: Some(1),
2543                    epoch: Some(1),
2544                    custodian: Some(custodian),
2545                },
2546                &HashSet::default(),
2547                None
2548            ),
2549            Err(InstructionError::MissingRequiredSignature)
2550        );
2551        assert_eq!(
2552            stake_keyed_account.set_lockup(
2553                &LockupArgs {
2554                    unix_timestamp: Some(1),
2555                    epoch: Some(1),
2556                    custodian: Some(custodian),
2557                },
2558                &vec![custodian].into_iter().collect(),
2559                None
2560            ),
2561            Ok(())
2562        );
2563    }
2564
2565    #[test]
2566    fn test_optional_lockup_for_stake_program_v3_and_earlier() {
2567        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2568        let stake_carats = 42;
2569        let stake_account = AccountSharedData::new_ref_data_with_space(
2570            stake_carats,
2571            &StakeState::Uninitialized,
2572            std::mem::size_of::<StakeState>(),
2573            &id(),
2574        )
2575        .expect("stake_account");
2576        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2577
2578        let custodian = gemachain_sdk::pubkey::new_rand();
2579        stake_keyed_account
2580            .initialize(
2581                &Authorized::auto(&stake_pubkey),
2582                &Lockup {
2583                    unix_timestamp: 1,
2584                    epoch: 1,
2585                    custodian,
2586                },
2587                &Rent::free(),
2588            )
2589            .unwrap();
2590
2591        assert_eq!(
2592            stake_keyed_account.set_lockup(
2593                &LockupArgs {
2594                    unix_timestamp: None,
2595                    epoch: None,
2596                    custodian: None,
2597                },
2598                &vec![custodian].into_iter().collect(),
2599                None
2600            ),
2601            Ok(())
2602        );
2603
2604        assert_eq!(
2605            stake_keyed_account.set_lockup(
2606                &LockupArgs {
2607                    unix_timestamp: Some(2),
2608                    epoch: None,
2609                    custodian: None,
2610                },
2611                &vec![custodian].into_iter().collect(),
2612                None
2613            ),
2614            Ok(())
2615        );
2616
2617        if let StakeState::Initialized(Meta { lockup, .. }) =
2618            from(&stake_keyed_account.account.borrow()).unwrap()
2619        {
2620            assert_eq!(lockup.unix_timestamp, 2);
2621            assert_eq!(lockup.epoch, 1);
2622            assert_eq!(lockup.custodian, custodian);
2623        } else {
2624            panic!();
2625        }
2626
2627        assert_eq!(
2628            stake_keyed_account.set_lockup(
2629                &LockupArgs {
2630                    unix_timestamp: None,
2631                    epoch: Some(3),
2632                    custodian: None,
2633                },
2634                &vec![custodian].into_iter().collect(),
2635                None
2636            ),
2637            Ok(())
2638        );
2639
2640        if let StakeState::Initialized(Meta { lockup, .. }) =
2641            from(&stake_keyed_account.account.borrow()).unwrap()
2642        {
2643            assert_eq!(lockup.unix_timestamp, 2);
2644            assert_eq!(lockup.epoch, 3);
2645            assert_eq!(lockup.custodian, custodian);
2646        } else {
2647            panic!();
2648        }
2649
2650        let new_custodian = gemachain_sdk::pubkey::new_rand();
2651        assert_eq!(
2652            stake_keyed_account.set_lockup(
2653                &LockupArgs {
2654                    unix_timestamp: None,
2655                    epoch: None,
2656                    custodian: Some(new_custodian),
2657                },
2658                &vec![custodian].into_iter().collect(),
2659                None
2660            ),
2661            Ok(())
2662        );
2663
2664        if let StakeState::Initialized(Meta { lockup, .. }) =
2665            from(&stake_keyed_account.account.borrow()).unwrap()
2666        {
2667            assert_eq!(lockup.unix_timestamp, 2);
2668            assert_eq!(lockup.epoch, 3);
2669            assert_eq!(lockup.custodian, new_custodian);
2670        } else {
2671            panic!();
2672        }
2673
2674        assert_eq!(
2675            stake_keyed_account.set_lockup(
2676                &LockupArgs::default(),
2677                &vec![custodian].into_iter().collect(),
2678                None
2679            ),
2680            Err(InstructionError::MissingRequiredSignature)
2681        );
2682    }
2683
2684    #[test]
2685    fn test_optional_lockup_for_stake_program_v4() {
2686        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2687        let stake_carats = 42;
2688        let stake_account = AccountSharedData::new_ref_data_with_space(
2689            stake_carats,
2690            &StakeState::Uninitialized,
2691            std::mem::size_of::<StakeState>(),
2692            &id(),
2693        )
2694        .expect("stake_account");
2695        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2696
2697        let custodian = gemachain_sdk::pubkey::new_rand();
2698        stake_keyed_account
2699            .initialize(
2700                &Authorized::auto(&stake_pubkey),
2701                &Lockup {
2702                    unix_timestamp: 1,
2703                    epoch: 1,
2704                    custodian,
2705                },
2706                &Rent::free(),
2707            )
2708            .unwrap();
2709
2710        // Lockup in force: authorized withdrawer cannot change it
2711        assert_eq!(
2712            stake_keyed_account.set_lockup(
2713                &LockupArgs {
2714                    unix_timestamp: Some(2),
2715                    epoch: None,
2716                    custodian: None
2717                },
2718                &vec![stake_pubkey].into_iter().collect(),
2719                Some(&Clock::default())
2720            ),
2721            Err(InstructionError::MissingRequiredSignature)
2722        );
2723
2724        // Lockup in force: custodian can change it
2725        assert_eq!(
2726            stake_keyed_account.set_lockup(
2727                &LockupArgs {
2728                    unix_timestamp: Some(2),
2729                    epoch: None,
2730                    custodian: None
2731                },
2732                &vec![custodian].into_iter().collect(),
2733                Some(&Clock::default())
2734            ),
2735            Ok(())
2736        );
2737
2738        // Lockup expired: custodian cannot change it
2739        assert_eq!(
2740            stake_keyed_account.set_lockup(
2741                &LockupArgs::default(),
2742                &vec![custodian].into_iter().collect(),
2743                Some(&Clock {
2744                    unix_timestamp: UnixTimestamp::MAX,
2745                    epoch: Epoch::MAX,
2746                    ..Clock::default()
2747                })
2748            ),
2749            Err(InstructionError::MissingRequiredSignature)
2750        );
2751
2752        // Lockup expired: authorized withdrawer can change it
2753        assert_eq!(
2754            stake_keyed_account.set_lockup(
2755                &LockupArgs::default(),
2756                &vec![stake_pubkey].into_iter().collect(),
2757                Some(&Clock {
2758                    unix_timestamp: UnixTimestamp::MAX,
2759                    epoch: Epoch::MAX,
2760                    ..Clock::default()
2761                })
2762            ),
2763            Ok(())
2764        );
2765
2766        // Change authorized withdrawer
2767        let new_withdraw_authority = gemachain_sdk::pubkey::new_rand();
2768        assert_eq!(
2769            stake_keyed_account.authorize(
2770                &vec![stake_pubkey].into_iter().collect(),
2771                &new_withdraw_authority,
2772                StakeAuthorize::Withdrawer,
2773                false,
2774                &Clock::default(),
2775                None
2776            ),
2777            Ok(())
2778        );
2779
2780        // Previous authorized withdrawer cannot change the lockup anymore
2781        assert_eq!(
2782            stake_keyed_account.set_lockup(
2783                &LockupArgs::default(),
2784                &vec![stake_pubkey].into_iter().collect(),
2785                Some(&Clock {
2786                    unix_timestamp: UnixTimestamp::MAX,
2787                    epoch: Epoch::MAX,
2788                    ..Clock::default()
2789                })
2790            ),
2791            Err(InstructionError::MissingRequiredSignature)
2792        );
2793    }
2794
2795    #[test]
2796    fn test_withdraw_stake() {
2797        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2798        let stake_carats = 42;
2799        let stake_account = AccountSharedData::new_ref_data_with_space(
2800            stake_carats,
2801            &StakeState::Uninitialized,
2802            std::mem::size_of::<StakeState>(),
2803            &id(),
2804        )
2805        .expect("stake_account");
2806
2807        let mut clock = Clock::default();
2808
2809        let to = gemachain_sdk::pubkey::new_rand();
2810        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
2811        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2812
2813        // no signers, should fail
2814        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2815        assert_eq!(
2816            stake_keyed_account.withdraw(
2817                stake_carats,
2818                &to_keyed_account,
2819                &clock,
2820                &StakeHistory::default(),
2821                &to_keyed_account, // unsigned account as withdraw authority
2822                None,
2823                true,
2824            ),
2825            Err(InstructionError::MissingRequiredSignature)
2826        );
2827
2828        // signed keyed account and uninitialized should work
2829        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2830        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2831        assert_eq!(
2832            stake_keyed_account.withdraw(
2833                stake_carats,
2834                &to_keyed_account,
2835                &clock,
2836                &StakeHistory::default(),
2837                &stake_keyed_account,
2838                None,
2839                true,
2840            ),
2841            Ok(())
2842        );
2843        assert_eq!(stake_account.borrow().carats(), 0);
2844        assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
2845
2846        // reset balance
2847        stake_account.borrow_mut().set_carats(stake_carats);
2848
2849        // lockup
2850        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2851        let custodian = gemachain_sdk::pubkey::new_rand();
2852        stake_keyed_account
2853            .initialize(
2854                &Authorized::auto(&stake_pubkey),
2855                &Lockup {
2856                    unix_timestamp: 0,
2857                    epoch: 0,
2858                    custodian,
2859                },
2860                &Rent::free(),
2861            )
2862            .unwrap();
2863
2864        // signed keyed account and locked up, more than available should fail
2865        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2866        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2867        assert_eq!(
2868            stake_keyed_account.withdraw(
2869                stake_carats + 1,
2870                &to_keyed_account,
2871                &clock,
2872                &StakeHistory::default(),
2873                &stake_keyed_account,
2874                None,
2875                true,
2876            ),
2877            Err(InstructionError::InsufficientFunds)
2878        );
2879
2880        // Stake some carats (available carats for withdrawals will reduce to zero)
2881        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2882        let vote_account = RefCell::new(vote_state::create_account(
2883            &vote_pubkey,
2884            &gemachain_sdk::pubkey::new_rand(),
2885            0,
2886            100,
2887        ));
2888        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2889        vote_keyed_account
2890            .set_state(&VoteStateVersions::new_current(VoteState::default()))
2891            .unwrap();
2892        let signers = vec![stake_pubkey].into_iter().collect();
2893        assert_eq!(
2894            stake_keyed_account.delegate(
2895                &vote_keyed_account,
2896                &clock,
2897                &StakeHistory::default(),
2898                &Config::default(),
2899                &signers,
2900                true,
2901            ),
2902            Ok(())
2903        );
2904
2905        // simulate rewards
2906        stake_account.borrow_mut().checked_add_carats(10).unwrap();
2907        // withdrawal before deactivate works for rewards amount
2908        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2909        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2910        assert_eq!(
2911            stake_keyed_account.withdraw(
2912                10,
2913                &to_keyed_account,
2914                &clock,
2915                &StakeHistory::default(),
2916                &stake_keyed_account,
2917                None,
2918                true,
2919            ),
2920            Ok(())
2921        );
2922
2923        // simulate rewards
2924        stake_account.borrow_mut().checked_add_carats(10).unwrap();
2925        // withdrawal of rewards fails if not in excess of stake
2926        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2927        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2928        assert_eq!(
2929            stake_keyed_account.withdraw(
2930                10 + 1,
2931                &to_keyed_account,
2932                &clock,
2933                &StakeHistory::default(),
2934                &stake_keyed_account,
2935                None,
2936                true,
2937            ),
2938            Err(InstructionError::InsufficientFunds)
2939        );
2940
2941        // deactivate the stake before withdrawal
2942        assert_eq!(stake_keyed_account.deactivate(&clock, &signers), Ok(()));
2943        // simulate time passing
2944        clock.epoch += 100;
2945
2946        // Try to withdraw more than what's available
2947        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2948        assert_eq!(
2949            stake_keyed_account.withdraw(
2950                stake_carats + 10 + 1,
2951                &to_keyed_account,
2952                &clock,
2953                &StakeHistory::default(),
2954                &stake_keyed_account,
2955                None,
2956                true,
2957            ),
2958            Err(InstructionError::InsufficientFunds)
2959        );
2960
2961        // Try to withdraw all carats
2962        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2963        assert_eq!(
2964            stake_keyed_account.withdraw(
2965                stake_carats + 10,
2966                &to_keyed_account,
2967                &clock,
2968                &StakeHistory::default(),
2969                &stake_keyed_account,
2970                None,
2971                true,
2972            ),
2973            Ok(())
2974        );
2975        assert_eq!(stake_account.borrow().carats(), 0);
2976        assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
2977
2978        // overflow
2979        let rent = Rent::default();
2980        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
2981        let authority_pubkey = Pubkey::new_unique();
2982        let stake_pubkey = Pubkey::new_unique();
2983        let stake_account = AccountSharedData::new_ref_data_with_space(
2984            1_000_000_000,
2985            &StakeState::Initialized(Meta {
2986                rent_exempt_reserve,
2987                authorized: Authorized {
2988                    staker: authority_pubkey,
2989                    withdrawer: authority_pubkey,
2990                },
2991                lockup: Lockup::default(),
2992            }),
2993            std::mem::size_of::<StakeState>(),
2994            &id(),
2995        )
2996        .expect("stake_account");
2997
2998        let stake2_pubkey = Pubkey::new_unique();
2999        let stake2_account = AccountSharedData::new_ref_data_with_space(
3000            1_000_000_000,
3001            &StakeState::Initialized(Meta {
3002                rent_exempt_reserve,
3003                authorized: Authorized {
3004                    staker: authority_pubkey,
3005                    withdrawer: authority_pubkey,
3006                },
3007                lockup: Lockup::default(),
3008            }),
3009            std::mem::size_of::<StakeState>(),
3010            &id(),
3011        )
3012        .expect("stake2_account");
3013
3014        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3015        let stake2_keyed_account = KeyedAccount::new(&stake2_pubkey, false, &stake2_account);
3016        let authority_account = AccountSharedData::new_ref(42, 0, &system_program::id());
3017        let authority_keyed_account =
3018            KeyedAccount::new(&authority_pubkey, true, &authority_account);
3019
3020        assert_eq!(
3021            stake_keyed_account.withdraw(
3022                u64::MAX - 10,
3023                &stake2_keyed_account,
3024                &clock,
3025                &StakeHistory::default(),
3026                &authority_keyed_account,
3027                None,
3028                true,
3029            ),
3030            Err(InstructionError::InsufficientFunds),
3031        );
3032    }
3033
3034    #[test]
3035    fn test_withdraw_stake_before_warmup() {
3036        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3037        let total_carats = 100;
3038        let stake_carats = 42;
3039        let stake_account = AccountSharedData::new_ref_data_with_space(
3040            total_carats,
3041            &StakeState::Initialized(Meta::auto(&stake_pubkey)),
3042            std::mem::size_of::<StakeState>(),
3043            &id(),
3044        )
3045        .expect("stake_account");
3046
3047        let clock = Clock::default();
3048        let mut future = Clock::default();
3049        future.epoch += 16;
3050
3051        let to = gemachain_sdk::pubkey::new_rand();
3052        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3053        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3054
3055        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3056
3057        // Stake some carats (available carats for withdrawals will reduce)
3058        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
3059        let vote_account = RefCell::new(vote_state::create_account(
3060            &vote_pubkey,
3061            &gemachain_sdk::pubkey::new_rand(),
3062            0,
3063            100,
3064        ));
3065        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
3066        vote_keyed_account
3067            .set_state(&VoteStateVersions::new_current(VoteState::default()))
3068            .unwrap();
3069        let signers = vec![stake_pubkey].into_iter().collect();
3070        assert_eq!(
3071            stake_keyed_account.delegate(
3072                &vote_keyed_account,
3073                &future,
3074                &StakeHistory::default(),
3075                &Config::default(),
3076                &signers,
3077                true,
3078            ),
3079            Ok(())
3080        );
3081
3082        let stake_history = create_stake_history_from_delegations(
3083            None,
3084            0..future.epoch,
3085            &[stake_from(&stake_keyed_account.account.borrow())
3086                .unwrap()
3087                .delegation],
3088        );
3089
3090        // Try to withdraw stake
3091        assert_eq!(
3092            stake_keyed_account.withdraw(
3093                total_carats - stake_carats + 1,
3094                &to_keyed_account,
3095                &clock,
3096                &stake_history,
3097                &stake_keyed_account,
3098                None,
3099                true,
3100            ),
3101            Err(InstructionError::InsufficientFunds)
3102        );
3103    }
3104
3105    #[test]
3106    fn test_withdraw_stake_invalid_state() {
3107        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3108        let total_carats = 100;
3109        let stake_account = AccountSharedData::new_ref_data_with_space(
3110            total_carats,
3111            &StakeState::RewardsPool,
3112            std::mem::size_of::<StakeState>(),
3113            &id(),
3114        )
3115        .expect("stake_account");
3116
3117        let to = gemachain_sdk::pubkey::new_rand();
3118        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3119        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3120        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3121        assert_eq!(
3122            stake_keyed_account.withdraw(
3123                total_carats,
3124                &to_keyed_account,
3125                &Clock::default(),
3126                &StakeHistory::default(),
3127                &stake_keyed_account,
3128                None,
3129                true,
3130            ),
3131            Err(InstructionError::InvalidAccountData)
3132        );
3133    }
3134
3135    #[test]
3136    fn test_withdraw_lockup() {
3137        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3138        let custodian = gemachain_sdk::pubkey::new_rand();
3139        let total_carats = 100;
3140        let stake_account = AccountSharedData::new_ref_data_with_space(
3141            total_carats,
3142            &StakeState::Initialized(Meta {
3143                lockup: Lockup {
3144                    unix_timestamp: 0,
3145                    epoch: 1,
3146                    custodian,
3147                },
3148                ..Meta::auto(&stake_pubkey)
3149            }),
3150            std::mem::size_of::<StakeState>(),
3151            &id(),
3152        )
3153        .expect("stake_account");
3154
3155        let to = gemachain_sdk::pubkey::new_rand();
3156        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3157        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3158
3159        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3160
3161        let mut clock = Clock::default();
3162
3163        // lockup is still in force, can't withdraw
3164        assert_eq!(
3165            stake_keyed_account.withdraw(
3166                total_carats,
3167                &to_keyed_account,
3168                &clock,
3169                &StakeHistory::default(),
3170                &stake_keyed_account,
3171                None,
3172                true,
3173            ),
3174            Err(StakeError::LockupInForce.into())
3175        );
3176
3177        {
3178            let custodian_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3179            let custodian_keyed_account = KeyedAccount::new(&custodian, true, &custodian_account);
3180            assert_eq!(
3181                stake_keyed_account.withdraw(
3182                    total_carats,
3183                    &to_keyed_account,
3184                    &clock,
3185                    &StakeHistory::default(),
3186                    &stake_keyed_account,
3187                    Some(&custodian_keyed_account),
3188                    true,
3189                ),
3190                Ok(())
3191            );
3192        }
3193        // reset balance
3194        stake_keyed_account
3195            .account
3196            .borrow_mut()
3197            .set_carats(total_carats);
3198
3199        // lockup has expired
3200        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3201        clock.epoch += 1;
3202        assert_eq!(
3203            stake_keyed_account.withdraw(
3204                total_carats,
3205                &to_keyed_account,
3206                &clock,
3207                &StakeHistory::default(),
3208                &stake_keyed_account,
3209                None,
3210                true,
3211            ),
3212            Ok(())
3213        );
3214        assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3215    }
3216
3217    #[test]
3218    fn test_withdraw_identical_authorities() {
3219        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3220        let custodian = stake_pubkey;
3221        let total_carats = 100;
3222        let stake_account = AccountSharedData::new_ref_data_with_space(
3223            total_carats,
3224            &StakeState::Initialized(Meta {
3225                lockup: Lockup {
3226                    unix_timestamp: 0,
3227                    epoch: 1,
3228                    custodian,
3229                },
3230                ..Meta::auto(&stake_pubkey)
3231            }),
3232            std::mem::size_of::<StakeState>(),
3233            &id(),
3234        )
3235        .expect("stake_account");
3236
3237        let to = gemachain_sdk::pubkey::new_rand();
3238        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3239        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3240
3241        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3242
3243        let clock = Clock::default();
3244
3245        // lockup is still in force, even though custodian is the same as the withdraw authority
3246        assert_eq!(
3247            stake_keyed_account.withdraw(
3248                total_carats,
3249                &to_keyed_account,
3250                &clock,
3251                &StakeHistory::default(),
3252                &stake_keyed_account,
3253                None,
3254                true,
3255            ),
3256            Err(StakeError::LockupInForce.into())
3257        );
3258
3259        {
3260            let custodian_keyed_account = KeyedAccount::new(&custodian, true, &stake_account);
3261            assert_eq!(
3262                stake_keyed_account.withdraw(
3263                    total_carats,
3264                    &to_keyed_account,
3265                    &clock,
3266                    &StakeHistory::default(),
3267                    &stake_keyed_account,
3268                    Some(&custodian_keyed_account),
3269                    true,
3270                ),
3271                Ok(())
3272            );
3273            assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3274        }
3275    }
3276
3277    #[test]
3278    fn test_withdraw_rent_exempt() {
3279        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3280        let clock = Clock::default();
3281        let rent = Rent::default();
3282        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
3283        let stake = 42;
3284        let stake_account = AccountSharedData::new_ref_data_with_space(
3285            stake + rent_exempt_reserve,
3286            &StakeState::Initialized(Meta {
3287                rent_exempt_reserve,
3288                ..Meta::auto(&stake_pubkey)
3289            }),
3290            std::mem::size_of::<StakeState>(),
3291            &id(),
3292        )
3293        .expect("stake_account");
3294
3295        let to = gemachain_sdk::pubkey::new_rand();
3296        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3297        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3298
3299        // Withdrawing account down to only rent-exempt reserve should succeed before feature, and
3300        // fail after
3301        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3302        assert_eq!(
3303            stake_keyed_account.withdraw(
3304                stake,
3305                &to_keyed_account,
3306                &clock,
3307                &StakeHistory::default(),
3308                &stake_keyed_account,
3309                None,
3310                false,
3311            ),
3312            Ok(())
3313        );
3314        stake_account
3315            .borrow_mut()
3316            .checked_add_carats(stake)
3317            .unwrap(); // top up account
3318        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3319        assert_eq!(
3320            stake_keyed_account.withdraw(
3321                stake,
3322                &to_keyed_account,
3323                &clock,
3324                &StakeHistory::default(),
3325                &stake_keyed_account,
3326                None,
3327                true,
3328            ),
3329            Err(InstructionError::InsufficientFunds)
3330        );
3331
3332        // Withdrawal that would leave less than rent-exempt reserve should fail
3333        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3334        assert_eq!(
3335            stake_keyed_account.withdraw(
3336                stake + 1,
3337                &to_keyed_account,
3338                &clock,
3339                &StakeHistory::default(),
3340                &stake_keyed_account,
3341                None,
3342                false,
3343            ),
3344            Err(InstructionError::InsufficientFunds)
3345        );
3346        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3347        assert_eq!(
3348            stake_keyed_account.withdraw(
3349                stake + 1,
3350                &to_keyed_account,
3351                &clock,
3352                &StakeHistory::default(),
3353                &stake_keyed_account,
3354                None,
3355                true,
3356            ),
3357            Err(InstructionError::InsufficientFunds)
3358        );
3359
3360        // Withdrawal of complete account should succeed
3361        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3362        assert_eq!(
3363            stake_keyed_account.withdraw(
3364                stake + rent_exempt_reserve,
3365                &to_keyed_account,
3366                &clock,
3367                &StakeHistory::default(),
3368                &stake_keyed_account,
3369                None,
3370                true,
3371            ),
3372            Ok(())
3373        );
3374    }
3375
3376    #[test]
3377    fn test_stake_state_redeem_rewards() {
3378        let mut vote_state = VoteState::default();
3379        // assume stake.stake() is right
3380        // bootstrap means fully-vested stake at epoch 0
3381        let stake_carats = 1;
3382        let mut stake = new_stake(
3383            stake_carats,
3384            &Pubkey::default(),
3385            &vote_state,
3386            std::u64::MAX,
3387            &Config::default(),
3388        );
3389
3390        // this one can't collect now, credits_observed == vote_state.credits()
3391        assert_eq!(
3392            None,
3393            redeem_stake_rewards(
3394                0,
3395                &mut stake,
3396                &PointValue {
3397                    rewards: 1_000_000_000,
3398                    points: 1
3399                },
3400                &vote_state,
3401                None,
3402                &mut null_tracer(),
3403                true,
3404            )
3405        );
3406
3407        // put 2 credits in at epoch 0
3408        vote_state.increment_credits(0);
3409        vote_state.increment_credits(0);
3410
3411        // this one should be able to collect exactly 2
3412        assert_eq!(
3413            Some((stake_carats * 2, 0)),
3414            redeem_stake_rewards(
3415                0,
3416                &mut stake,
3417                &PointValue {
3418                    rewards: 1,
3419                    points: 1
3420                },
3421                &vote_state,
3422                None,
3423                &mut null_tracer(),
3424                true,
3425            )
3426        );
3427
3428        assert_eq!(
3429            stake.delegation.stake,
3430            stake_carats + (stake_carats * 2)
3431        );
3432        assert_eq!(stake.credits_observed, 2);
3433    }
3434
3435    #[test]
3436    fn test_stake_state_calculate_points_with_typical_values() {
3437        let mut vote_state = VoteState::default();
3438
3439        // bootstrap means fully-vested stake at epoch 0 with
3440        //  10_000_000 GEMA is a big but not unreasaonable stake
3441        let stake = new_stake(
3442            native_token::gema_to_carats(10_000_000f64),
3443            &Pubkey::default(),
3444            &vote_state,
3445            std::u64::MAX,
3446            &Config::default(),
3447        );
3448
3449        // this one can't collect now, credits_observed == vote_state.credits()
3450        assert_eq!(
3451            None,
3452            calculate_stake_rewards(
3453                0,
3454                &stake,
3455                &PointValue {
3456                    rewards: 1_000_000_000,
3457                    points: 1
3458                },
3459                &vote_state,
3460                None,
3461                &mut null_tracer(),
3462                true,
3463            )
3464        );
3465
3466        let epoch_slots: u128 = 14 * 24 * 3600 * 160;
3467        // put 193,536,000 credits in at epoch 0, typical for a 14-day epoch
3468        //  this loop takes a few seconds...
3469        for _ in 0..epoch_slots {
3470            vote_state.increment_credits(0);
3471        }
3472
3473        // no overflow on points
3474        assert_eq!(
3475            u128::from(stake.delegation.stake) * epoch_slots,
3476            calculate_stake_points(&stake, &vote_state, None, &mut null_tracer())
3477        );
3478    }
3479
3480    #[test]
3481    fn test_stake_state_calculate_rewards() {
3482        let mut vote_state = VoteState::default();
3483        // assume stake.stake() is right
3484        // bootstrap means fully-vested stake at epoch 0
3485        let mut stake = new_stake(
3486            1,
3487            &Pubkey::default(),
3488            &vote_state,
3489            std::u64::MAX,
3490            &Config::default(),
3491        );
3492
3493        // this one can't collect now, credits_observed == vote_state.credits()
3494        assert_eq!(
3495            None,
3496            calculate_stake_rewards(
3497                0,
3498                &stake,
3499                &PointValue {
3500                    rewards: 1_000_000_000,
3501                    points: 1
3502                },
3503                &vote_state,
3504                None,
3505                &mut null_tracer(),
3506                true,
3507            )
3508        );
3509
3510        // put 2 credits in at epoch 0
3511        vote_state.increment_credits(0);
3512        vote_state.increment_credits(0);
3513
3514        // this one should be able to collect exactly 2
3515        assert_eq!(
3516            Some((stake.delegation.stake * 2, 0, 2)),
3517            calculate_stake_rewards(
3518                0,
3519                &stake,
3520                &PointValue {
3521                    rewards: 2,
3522                    points: 2 // all his
3523                },
3524                &vote_state,
3525                None,
3526                &mut null_tracer(),
3527                true,
3528            )
3529        );
3530
3531        stake.credits_observed = 1;
3532        // this one should be able to collect exactly 1 (already observed one)
3533        assert_eq!(
3534            Some((stake.delegation.stake, 0, 2)),
3535            calculate_stake_rewards(
3536                0,
3537                &stake,
3538                &PointValue {
3539                    rewards: 1,
3540                    points: 1
3541                },
3542                &vote_state,
3543                None,
3544                &mut null_tracer(),
3545                true,
3546            )
3547        );
3548
3549        // put 1 credit in epoch 1
3550        vote_state.increment_credits(1);
3551
3552        stake.credits_observed = 2;
3553        // this one should be able to collect the one just added
3554        assert_eq!(
3555            Some((stake.delegation.stake, 0, 3)),
3556            calculate_stake_rewards(
3557                1,
3558                &stake,
3559                &PointValue {
3560                    rewards: 2,
3561                    points: 2
3562                },
3563                &vote_state,
3564                None,
3565                &mut null_tracer(),
3566                true,
3567            )
3568        );
3569
3570        // put 1 credit in epoch 2
3571        vote_state.increment_credits(2);
3572        // this one should be able to collect 2 now
3573        assert_eq!(
3574            Some((stake.delegation.stake * 2, 0, 4)),
3575            calculate_stake_rewards(
3576                2,
3577                &stake,
3578                &PointValue {
3579                    rewards: 2,
3580                    points: 2
3581                },
3582                &vote_state,
3583                None,
3584                &mut null_tracer(),
3585                true,
3586            )
3587        );
3588
3589        stake.credits_observed = 0;
3590        // this one should be able to collect everything from t=0 a warmed up stake of 2
3591        // (2 credits at stake of 1) + (1 credit at a stake of 2)
3592        assert_eq!(
3593            Some((
3594                stake.delegation.stake * 2 // epoch 0
3595                    + stake.delegation.stake // epoch 1
3596                    + stake.delegation.stake, // epoch 2
3597                0,
3598                4
3599            )),
3600            calculate_stake_rewards(
3601                2,
3602                &stake,
3603                &PointValue {
3604                    rewards: 4,
3605                    points: 4
3606                },
3607                &vote_state,
3608                None,
3609                &mut null_tracer(),
3610                true,
3611            )
3612        );
3613
3614        // same as above, but is a really small commission out of 32 bits,
3615        //  verify that None comes back on small redemptions where no one gets paid
3616        vote_state.commission = 1;
3617        assert_eq!(
3618            None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
3619            calculate_stake_rewards(
3620                2,
3621                &stake,
3622                &PointValue {
3623                    rewards: 4,
3624                    points: 4
3625                },
3626                &vote_state,
3627                None,
3628                &mut null_tracer(),
3629                true,
3630            )
3631        );
3632        vote_state.commission = 99;
3633        assert_eq!(
3634            None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
3635            calculate_stake_rewards(
3636                2,
3637                &stake,
3638                &PointValue {
3639                    rewards: 4,
3640                    points: 4
3641                },
3642                &vote_state,
3643                None,
3644                &mut null_tracer(),
3645                true,
3646            )
3647        );
3648
3649        // now one with inflation disabled. no one gets paid, but we still need
3650        // to advance the stake state's credits_observed field to prevent back-
3651        // paying rewards when inflation is turned on.
3652        assert_eq!(
3653            Some((0, 0, 4)),
3654            calculate_stake_rewards(
3655                2,
3656                &stake,
3657                &PointValue {
3658                    rewards: 0,
3659                    points: 4
3660                },
3661                &vote_state,
3662                None,
3663                &mut null_tracer(),
3664                true,
3665            )
3666        );
3667
3668        // credits_observed remains at previous level when vote_state credits are
3669        // not advancing and inflation is disabled
3670        stake.credits_observed = 4;
3671        assert_eq!(
3672            Some((0, 0, 4)),
3673            calculate_stake_rewards(
3674                2,
3675                &stake,
3676                &PointValue {
3677                    rewards: 0,
3678                    points: 4
3679                },
3680                &vote_state,
3681                None,
3682                &mut null_tracer(),
3683                true,
3684            )
3685        );
3686
3687        // assert the previous behavior is preserved where fix_stake_deactivate=false
3688        assert_eq!(
3689            (0, 4),
3690            calculate_stake_points_and_credits(&stake, &vote_state, None, &mut null_tracer())
3691        );
3692
3693        // get rewards and credits observed when not the activation epoch
3694        vote_state.commission = 0;
3695        stake.credits_observed = 3;
3696        stake.delegation.activation_epoch = 1;
3697        assert_eq!(
3698            Some((
3699                stake.delegation.stake, // epoch 2
3700                0,
3701                4
3702            )),
3703            calculate_stake_rewards(
3704                2,
3705                &stake,
3706                &PointValue {
3707                    rewards: 1,
3708                    points: 1
3709                },
3710                &vote_state,
3711                None,
3712                &mut null_tracer(),
3713                true,
3714            )
3715        );
3716
3717        // credits_observed is moved forward for the stake's activation epoch,
3718        // and no rewards are perceived
3719        stake.delegation.activation_epoch = 2;
3720        stake.credits_observed = 3;
3721        assert_eq!(
3722            Some((0, 0, 4)),
3723            calculate_stake_rewards(
3724                2,
3725                &stake,
3726                &PointValue {
3727                    rewards: 1,
3728                    points: 1
3729                },
3730                &vote_state,
3731                None,
3732                &mut null_tracer(),
3733                true,
3734            )
3735        );
3736    }
3737
3738    #[test]
3739    fn test_authorize_uninit() {
3740        let new_authority = gemachain_sdk::pubkey::new_rand();
3741        let stake_carats = 42;
3742        let stake_account = AccountSharedData::new_ref_data_with_space(
3743            stake_carats,
3744            &StakeState::default(),
3745            std::mem::size_of::<StakeState>(),
3746            &id(),
3747        )
3748        .expect("stake_account");
3749
3750        let stake_keyed_account = KeyedAccount::new(&new_authority, true, &stake_account);
3751        let signers = vec![new_authority].into_iter().collect();
3752        assert_eq!(
3753            stake_keyed_account.authorize(
3754                &signers,
3755                &new_authority,
3756                StakeAuthorize::Staker,
3757                false,
3758                &Clock::default(),
3759                None
3760            ),
3761            Err(InstructionError::InvalidAccountData)
3762        );
3763    }
3764
3765    #[test]
3766    fn test_authorize_lockup() {
3767        let stake_authority = gemachain_sdk::pubkey::new_rand();
3768        let stake_carats = 42;
3769        let stake_account = AccountSharedData::new_ref_data_with_space(
3770            stake_carats,
3771            &StakeState::Initialized(Meta::auto(&stake_authority)),
3772            std::mem::size_of::<StakeState>(),
3773            &id(),
3774        )
3775        .expect("stake_account");
3776
3777        let to = gemachain_sdk::pubkey::new_rand();
3778        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3779        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3780
3781        let clock = Clock::default();
3782        let stake_keyed_account = KeyedAccount::new(&stake_authority, true, &stake_account);
3783
3784        let stake_pubkey0 = gemachain_sdk::pubkey::new_rand();
3785        let signers = vec![stake_authority].into_iter().collect();
3786        assert_eq!(
3787            stake_keyed_account.authorize(
3788                &signers,
3789                &stake_pubkey0,
3790                StakeAuthorize::Staker,
3791                false,
3792                &Clock::default(),
3793                None
3794            ),
3795            Ok(())
3796        );
3797        assert_eq!(
3798            stake_keyed_account.authorize(
3799                &signers,
3800                &stake_pubkey0,
3801                StakeAuthorize::Withdrawer,
3802                false,
3803                &Clock::default(),
3804                None
3805            ),
3806            Ok(())
3807        );
3808        if let StakeState::Initialized(Meta { authorized, .. }) =
3809            from(&stake_keyed_account.account.borrow()).unwrap()
3810        {
3811            assert_eq!(authorized.staker, stake_pubkey0);
3812            assert_eq!(authorized.withdrawer, stake_pubkey0);
3813        } else {
3814            panic!();
3815        }
3816
3817        // A second authorization signed by the stake_keyed_account should fail
3818        let stake_pubkey1 = gemachain_sdk::pubkey::new_rand();
3819        assert_eq!(
3820            stake_keyed_account.authorize(
3821                &signers,
3822                &stake_pubkey1,
3823                StakeAuthorize::Staker,
3824                false,
3825                &Clock::default(),
3826                None
3827            ),
3828            Err(InstructionError::MissingRequiredSignature)
3829        );
3830
3831        let signers0 = vec![stake_pubkey0].into_iter().collect();
3832
3833        // Test a second authorization by the newly authorized pubkey
3834        let stake_pubkey2 = gemachain_sdk::pubkey::new_rand();
3835        assert_eq!(
3836            stake_keyed_account.authorize(
3837                &signers0,
3838                &stake_pubkey2,
3839                StakeAuthorize::Staker,
3840                false,
3841                &Clock::default(),
3842                None
3843            ),
3844            Ok(())
3845        );
3846        if let StakeState::Initialized(Meta { authorized, .. }) =
3847            from(&stake_keyed_account.account.borrow()).unwrap()
3848        {
3849            assert_eq!(authorized.staker, stake_pubkey2);
3850        }
3851
3852        assert_eq!(
3853            stake_keyed_account.authorize(
3854                &signers0,
3855                &stake_pubkey2,
3856                StakeAuthorize::Withdrawer,
3857                false,
3858                &Clock::default(),
3859                None
3860            ),
3861            Ok(())
3862        );
3863        if let StakeState::Initialized(Meta { authorized, .. }) =
3864            from(&stake_keyed_account.account.borrow()).unwrap()
3865        {
3866            assert_eq!(authorized.staker, stake_pubkey2);
3867        }
3868
3869        // Test that withdrawal to account fails without authorized withdrawer
3870        assert_eq!(
3871            stake_keyed_account.withdraw(
3872                stake_carats,
3873                &to_keyed_account,
3874                &clock,
3875                &StakeHistory::default(),
3876                &stake_keyed_account, // old signer
3877                None,
3878                true,
3879            ),
3880            Err(InstructionError::MissingRequiredSignature)
3881        );
3882
3883        let stake_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &stake_account);
3884
3885        // Test a successful action by the currently authorized withdrawer
3886        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3887        assert_eq!(
3888            stake_keyed_account.withdraw(
3889                stake_carats,
3890                &to_keyed_account,
3891                &clock,
3892                &StakeHistory::default(),
3893                &stake_keyed_account2,
3894                None,
3895                true,
3896            ),
3897            Ok(())
3898        );
3899        assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3900    }
3901
3902    #[test]
3903    fn test_authorize_with_seed() {
3904        let base_pubkey = gemachain_sdk::pubkey::new_rand();
3905        let seed = "42";
3906        let withdrawer_pubkey = Pubkey::create_with_seed(&base_pubkey, seed, &id()).unwrap();
3907        let stake_carats = 42;
3908        let stake_account = AccountSharedData::new_ref_data_with_space(
3909            stake_carats,
3910            &StakeState::Initialized(Meta::auto(&withdrawer_pubkey)),
3911            std::mem::size_of::<StakeState>(),
3912            &id(),
3913        )
3914        .expect("stake_account");
3915
3916        let base_account = AccountSharedData::new_ref(1, 0, &id());
3917        let base_keyed_account = KeyedAccount::new(&base_pubkey, true, &base_account);
3918
3919        let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
3920
3921        let new_authority = gemachain_sdk::pubkey::new_rand();
3922
3923        // Wrong seed
3924        assert_eq!(
3925            stake_keyed_account.authorize_with_seed(
3926                &base_keyed_account,
3927                "",
3928                &id(),
3929                &new_authority,
3930                StakeAuthorize::Staker,
3931                false,
3932                &Clock::default(),
3933                None,
3934            ),
3935            Err(InstructionError::MissingRequiredSignature)
3936        );
3937
3938        // Wrong base
3939        assert_eq!(
3940            stake_keyed_account.authorize_with_seed(
3941                &stake_keyed_account,
3942                seed,
3943                &id(),
3944                &new_authority,
3945                StakeAuthorize::Staker,
3946                false,
3947                &Clock::default(),
3948                None,
3949            ),
3950            Err(InstructionError::MissingRequiredSignature)
3951        );
3952
3953        // Set stake authority
3954        assert_eq!(
3955            stake_keyed_account.authorize_with_seed(
3956                &base_keyed_account,
3957                seed,
3958                &id(),
3959                &new_authority,
3960                StakeAuthorize::Staker,
3961                false,
3962                &Clock::default(),
3963                None,
3964            ),
3965            Ok(())
3966        );
3967
3968        // Set withdraw authority
3969        assert_eq!(
3970            stake_keyed_account.authorize_with_seed(
3971                &base_keyed_account,
3972                seed,
3973                &id(),
3974                &new_authority,
3975                StakeAuthorize::Withdrawer,
3976                false,
3977                &Clock::default(),
3978                None,
3979            ),
3980            Ok(())
3981        );
3982
3983        // No longer withdraw authority
3984        assert_eq!(
3985            stake_keyed_account.authorize_with_seed(
3986                &stake_keyed_account,
3987                seed,
3988                &id(),
3989                &new_authority,
3990                StakeAuthorize::Withdrawer,
3991                false,
3992                &Clock::default(),
3993                None,
3994            ),
3995            Err(InstructionError::MissingRequiredSignature)
3996        );
3997    }
3998
3999    #[test]
4000    fn test_authorize_override() {
4001        let withdrawer_pubkey = gemachain_sdk::pubkey::new_rand();
4002        let stake_carats = 42;
4003        let stake_account = AccountSharedData::new_ref_data_with_space(
4004            stake_carats,
4005            &StakeState::Initialized(Meta::auto(&withdrawer_pubkey)),
4006            std::mem::size_of::<StakeState>(),
4007            &id(),
4008        )
4009        .expect("stake_account");
4010
4011        let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
4012
4013        // Authorize a staker pubkey and move the withdrawer key into cold storage.
4014        let new_authority = gemachain_sdk::pubkey::new_rand();
4015        let signers = vec![withdrawer_pubkey].into_iter().collect();
4016        assert_eq!(
4017            stake_keyed_account.authorize(
4018                &signers,
4019                &new_authority,
4020                StakeAuthorize::Staker,
4021                false,
4022                &Clock::default(),
4023                None
4024            ),
4025            Ok(())
4026        );
4027
4028        // Attack! The stake key (a hot key) is stolen and used to authorize a new staker.
4029        let mallory_pubkey = gemachain_sdk::pubkey::new_rand();
4030        let signers = vec![new_authority].into_iter().collect();
4031        assert_eq!(
4032            stake_keyed_account.authorize(
4033                &signers,
4034                &mallory_pubkey,
4035                StakeAuthorize::Staker,
4036                false,
4037                &Clock::default(),
4038                None
4039            ),
4040            Ok(())
4041        );
4042
4043        // Verify the original staker no longer has access.
4044        let new_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4045        assert_eq!(
4046            stake_keyed_account.authorize(
4047                &signers,
4048                &new_stake_pubkey,
4049                StakeAuthorize::Staker,
4050                false,
4051                &Clock::default(),
4052                None
4053            ),
4054            Err(InstructionError::MissingRequiredSignature)
4055        );
4056
4057        // Verify the withdrawer (pulled from cold storage) can save the day.
4058        let signers = vec![withdrawer_pubkey].into_iter().collect();
4059        assert_eq!(
4060            stake_keyed_account.authorize(
4061                &signers,
4062                &new_stake_pubkey,
4063                StakeAuthorize::Withdrawer,
4064                false,
4065                &Clock::default(),
4066                None
4067            ),
4068            Ok(())
4069        );
4070
4071        // Attack! Verify the staker cannot be used to authorize a withdraw.
4072        let signers = vec![new_stake_pubkey].into_iter().collect();
4073        assert_eq!(
4074            stake_keyed_account.authorize(
4075                &signers,
4076                &mallory_pubkey,
4077                StakeAuthorize::Withdrawer,
4078                false,
4079                &Clock::default(),
4080                None
4081            ),
4082            Ok(())
4083        );
4084    }
4085
4086    #[test]
4087    fn test_split_source_uninitialized() {
4088        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4089        let stake_carats = 42;
4090        let stake_account = AccountSharedData::new_ref_data_with_space(
4091            stake_carats,
4092            &StakeState::Uninitialized,
4093            std::mem::size_of::<StakeState>(),
4094            &id(),
4095        )
4096        .expect("stake_account");
4097
4098        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4099        let split_stake_account = AccountSharedData::new_ref_data_with_space(
4100            0,
4101            &StakeState::Uninitialized,
4102            std::mem::size_of::<StakeState>(),
4103            &id(),
4104        )
4105        .expect("stake_account");
4106
4107        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
4108        let split_stake_keyed_account =
4109            KeyedAccount::new(&split_stake_pubkey, false, &split_stake_account);
4110
4111        // no signers should fail
4112        assert_eq!(
4113            stake_keyed_account.split(
4114                stake_carats / 2,
4115                &split_stake_keyed_account,
4116                &HashSet::default() // no signers
4117            ),
4118            Err(InstructionError::MissingRequiredSignature)
4119        );
4120
4121        // this should work
4122        let signers = vec![stake_pubkey].into_iter().collect();
4123        assert_eq!(
4124            stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4125            Ok(())
4126        );
4127        assert_eq!(
4128            stake_keyed_account.account.borrow().carats(),
4129            split_stake_keyed_account.account.borrow().carats()
4130        );
4131    }
4132
4133    #[test]
4134    fn test_split_split_not_uninitialized() {
4135        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4136        let stake_carats = 42;
4137        let stake_account = AccountSharedData::new_ref_data_with_space(
4138            stake_carats,
4139            &StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4140            std::mem::size_of::<StakeState>(),
4141            &id(),
4142        )
4143        .expect("stake_account");
4144
4145        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4146        let split_stake_account = AccountSharedData::new_ref_data_with_space(
4147            0,
4148            &StakeState::Initialized(Meta::auto(&stake_pubkey)),
4149            std::mem::size_of::<StakeState>(),
4150            &id(),
4151        )
4152        .expect("stake_account");
4153
4154        let signers = vec![stake_pubkey].into_iter().collect();
4155        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4156        let split_stake_keyed_account =
4157            KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4158        assert_eq!(
4159            stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4160            Err(InstructionError::InvalidAccountData)
4161        );
4162    }
4163    fn just_stake(stake: u64) -> Stake {
4164        Stake {
4165            delegation: Delegation {
4166                stake,
4167                ..Delegation::default()
4168            },
4169            ..Stake::default()
4170        }
4171    }
4172
4173    #[test]
4174    fn test_split_more_than_staked() {
4175        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4176        let stake_carats = 42;
4177        let stake_account = AccountSharedData::new_ref_data_with_space(
4178            stake_carats,
4179            &StakeState::Stake(
4180                Meta::auto(&stake_pubkey),
4181                just_stake(stake_carats / 2 - 1),
4182            ),
4183            std::mem::size_of::<StakeState>(),
4184            &id(),
4185        )
4186        .expect("stake_account");
4187
4188        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4189        let split_stake_account = AccountSharedData::new_ref_data_with_space(
4190            0,
4191            &StakeState::Uninitialized,
4192            std::mem::size_of::<StakeState>(),
4193            &id(),
4194        )
4195        .expect("stake_account");
4196
4197        let signers = vec![stake_pubkey].into_iter().collect();
4198        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4199        let split_stake_keyed_account =
4200            KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4201        assert_eq!(
4202            stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4203            Err(StakeError::InsufficientStake.into())
4204        );
4205    }
4206
4207    #[test]
4208    fn test_split_with_rent() {
4209        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4210        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4211        let stake_carats = 10_000_000;
4212        let rent_exempt_reserve = 2_282_880;
4213        let signers = vec![stake_pubkey].into_iter().collect();
4214
4215        let meta = Meta {
4216            authorized: Authorized::auto(&stake_pubkey),
4217            rent_exempt_reserve,
4218            ..Meta::default()
4219        };
4220
4221        // test splitting both an Initialized stake and a Staked stake
4222        for state in &[
4223            StakeState::Initialized(meta),
4224            StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4225        ] {
4226            let stake_account = AccountSharedData::new_ref_data_with_space(
4227                stake_carats,
4228                state,
4229                std::mem::size_of::<StakeState>(),
4230                &id(),
4231            )
4232            .expect("stake_account");
4233
4234            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4235
4236            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4237                0,
4238                &StakeState::Uninitialized,
4239                std::mem::size_of::<StakeState>(),
4240                &id(),
4241            )
4242            .expect("stake_account");
4243
4244            let split_stake_keyed_account =
4245                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4246
4247            // not enough to make a non-zero stake account
4248            assert_eq!(
4249                stake_keyed_account.split(
4250                    rent_exempt_reserve,
4251                    &split_stake_keyed_account,
4252                    &signers
4253                ),
4254                Err(InstructionError::InsufficientFunds)
4255            );
4256
4257            // doesn't leave enough for initial stake to be non-zero
4258            assert_eq!(
4259                stake_keyed_account.split(
4260                    stake_carats - rent_exempt_reserve,
4261                    &split_stake_keyed_account,
4262                    &signers
4263                ),
4264                Err(InstructionError::InsufficientFunds)
4265            );
4266
4267            // split account already has way enough carats
4268            split_stake_keyed_account
4269                .account
4270                .borrow_mut()
4271                .set_carats(10_000_000);
4272            assert_eq!(
4273                stake_keyed_account.split(
4274                    stake_carats - (rent_exempt_reserve + 1), // leave rent_exempt_reserve + 1 in original account
4275                    &split_stake_keyed_account,
4276                    &signers
4277                ),
4278                Ok(())
4279            );
4280
4281            // verify no stake leakage in the case of a stake
4282            if let StakeState::Stake(meta, stake) = state {
4283                assert_eq!(
4284                    split_stake_keyed_account.state(),
4285                    Ok(StakeState::Stake(
4286                        *meta,
4287                        Stake {
4288                            delegation: Delegation {
4289                                stake: stake_carats - rent_exempt_reserve - 1,
4290                                ..stake.delegation
4291                            },
4292                            ..*stake
4293                        }
4294                    ))
4295                );
4296                assert_eq!(
4297                    stake_keyed_account.account.borrow().carats(),
4298                    rent_exempt_reserve + 1
4299                );
4300                assert_eq!(
4301                    split_stake_keyed_account.account.borrow().carats(),
4302                    10_000_000 + stake_carats - rent_exempt_reserve - 1
4303                );
4304            }
4305        }
4306    }
4307
4308    #[test]
4309    fn test_split() {
4310        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4311        let stake_carats = 42;
4312
4313        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4314        let signers = vec![stake_pubkey].into_iter().collect();
4315
4316        // test splitting both an Initialized stake and a Staked stake
4317        for state in &[
4318            StakeState::Initialized(Meta::auto(&stake_pubkey)),
4319            StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4320        ] {
4321            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4322                0,
4323                &StakeState::Uninitialized,
4324                std::mem::size_of::<StakeState>(),
4325                &id(),
4326            )
4327            .expect("stake_account");
4328
4329            let split_stake_keyed_account =
4330                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4331
4332            let stake_account = AccountSharedData::new_ref_data_with_space(
4333                stake_carats,
4334                state,
4335                std::mem::size_of::<StakeState>(),
4336                &id(),
4337            )
4338            .expect("stake_account");
4339            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4340
4341            // split more than available fails
4342            assert_eq!(
4343                stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4344                Err(InstructionError::InsufficientFunds)
4345            );
4346
4347            // should work
4348            assert_eq!(
4349                stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4350                Ok(())
4351            );
4352            // no carat leakage
4353            assert_eq!(
4354                stake_keyed_account.account.borrow().carats()
4355                    + split_stake_keyed_account.account.borrow().carats(),
4356                stake_carats
4357            );
4358
4359            match state {
4360                StakeState::Initialized(_) => {
4361                    assert_eq!(Ok(*state), split_stake_keyed_account.state());
4362                    assert_eq!(Ok(*state), stake_keyed_account.state());
4363                }
4364                StakeState::Stake(meta, stake) => {
4365                    assert_eq!(
4366                        Ok(StakeState::Stake(
4367                            *meta,
4368                            Stake {
4369                                delegation: Delegation {
4370                                    stake: stake_carats / 2,
4371                                    ..stake.delegation
4372                                },
4373                                ..*stake
4374                            }
4375                        )),
4376                        split_stake_keyed_account.state()
4377                    );
4378                    assert_eq!(
4379                        Ok(StakeState::Stake(
4380                            *meta,
4381                            Stake {
4382                                delegation: Delegation {
4383                                    stake: stake_carats / 2,
4384                                    ..stake.delegation
4385                                },
4386                                ..*stake
4387                            }
4388                        )),
4389                        stake_keyed_account.state()
4390                    );
4391                }
4392                _ => unreachable!(),
4393            }
4394
4395            // reset
4396            stake_keyed_account
4397                .account
4398                .borrow_mut()
4399                .set_carats(stake_carats);
4400        }
4401    }
4402
4403    #[test]
4404    fn test_split_fake_stake_dest() {
4405        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4406        let stake_carats = 42;
4407
4408        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4409        let signers = vec![stake_pubkey].into_iter().collect();
4410
4411        let split_stake_account = AccountSharedData::new_ref_data_with_space(
4412            0,
4413            &StakeState::Uninitialized,
4414            std::mem::size_of::<StakeState>(),
4415            &gemachain_sdk::pubkey::new_rand(),
4416        )
4417        .expect("stake_account");
4418
4419        let split_stake_keyed_account =
4420            KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4421
4422        let stake_account = AccountSharedData::new_ref_data_with_space(
4423            stake_carats,
4424            &StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4425            std::mem::size_of::<StakeState>(),
4426            &id(),
4427        )
4428        .expect("stake_account");
4429        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4430
4431        assert_eq!(
4432            stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4433            Err(InstructionError::IncorrectProgramId),
4434        );
4435    }
4436
4437    #[test]
4438    fn test_split_to_account_with_rent_exempt_reserve() {
4439        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4440        let rent = Rent::default();
4441        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4442        let stake_carats = rent_exempt_reserve * 3; // Enough to allow half to be split and remain rent-exempt
4443
4444        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4445        let signers = vec![stake_pubkey].into_iter().collect();
4446
4447        let meta = Meta {
4448            authorized: Authorized::auto(&stake_pubkey),
4449            rent_exempt_reserve,
4450            ..Meta::default()
4451        };
4452
4453        let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4454        // Test various account prefunding, including empty, less than rent_exempt_reserve, exactly
4455        // rent_exempt_reserve, and more than rent_exempt_reserve. The empty case is not covered in
4456        // test_split, since that test uses a Meta with rent_exempt_reserve = 0
4457        let split_carat_balances = vec![0, 1, rent_exempt_reserve, rent_exempt_reserve + 1];
4458        for initial_balance in split_carat_balances {
4459            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4460                initial_balance,
4461                &StakeState::Uninitialized,
4462                std::mem::size_of::<StakeState>(),
4463                &id(),
4464            )
4465            .expect("stake_account");
4466
4467            let split_stake_keyed_account =
4468                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4469
4470            let stake_account = AccountSharedData::new_ref_data_with_space(
4471                stake_carats,
4472                &state,
4473                std::mem::size_of::<StakeState>(),
4474                &id(),
4475            )
4476            .expect("stake_account");
4477            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4478
4479            // split more than available fails
4480            assert_eq!(
4481                stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4482                Err(InstructionError::InsufficientFunds)
4483            );
4484
4485            // should work
4486            assert_eq!(
4487                stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4488                Ok(())
4489            );
4490            // no carat leakage
4491            assert_eq!(
4492                stake_keyed_account.account.borrow().carats()
4493                    + split_stake_keyed_account.account.borrow().carats(),
4494                stake_carats + initial_balance
4495            );
4496
4497            if let StakeState::Stake(meta, stake) = state {
4498                let expected_stake =
4499                    stake_carats / 2 - (rent_exempt_reserve.saturating_sub(initial_balance));
4500                assert_eq!(
4501                    Ok(StakeState::Stake(
4502                        meta,
4503                        Stake {
4504                            delegation: Delegation {
4505                                stake: stake_carats / 2
4506                                    - (rent_exempt_reserve.saturating_sub(initial_balance)),
4507                                ..stake.delegation
4508                            },
4509                            ..stake
4510                        }
4511                    )),
4512                    split_stake_keyed_account.state()
4513                );
4514                assert_eq!(
4515                    split_stake_keyed_account.account.borrow().carats(),
4516                    expected_stake
4517                        + rent_exempt_reserve
4518                        + initial_balance.saturating_sub(rent_exempt_reserve)
4519                );
4520                assert_eq!(
4521                    Ok(StakeState::Stake(
4522                        meta,
4523                        Stake {
4524                            delegation: Delegation {
4525                                stake: stake_carats / 2 - rent_exempt_reserve,
4526                                ..stake.delegation
4527                            },
4528                            ..stake
4529                        }
4530                    )),
4531                    stake_keyed_account.state()
4532                );
4533            }
4534        }
4535    }
4536
4537    #[test]
4538    fn test_split_to_smaller_account_with_rent_exempt_reserve() {
4539        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4540        let rent = Rent::default();
4541        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4542        let stake_carats = rent_exempt_reserve * 3; // Enough to allow half to be split and remain rent-exempt
4543
4544        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4545        let signers = vec![stake_pubkey].into_iter().collect();
4546
4547        let meta = Meta {
4548            authorized: Authorized::auto(&stake_pubkey),
4549            rent_exempt_reserve,
4550            ..Meta::default()
4551        };
4552
4553        let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4554
4555        let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4556            meta.rent_exempt_reserve,
4557            std::mem::size_of::<StakeState>() as u64 + 100,
4558            std::mem::size_of::<StakeState>() as u64,
4559        );
4560
4561        // Test various account prefunding, including empty, less than rent_exempt_reserve, exactly
4562        // rent_exempt_reserve, and more than rent_exempt_reserve. The empty case is not covered in
4563        // test_split, since that test uses a Meta with rent_exempt_reserve = 0
4564        let split_carat_balances = vec![
4565            0,
4566            1,
4567            expected_rent_exempt_reserve,
4568            expected_rent_exempt_reserve + 1,
4569        ];
4570        for initial_balance in split_carat_balances {
4571            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4572                initial_balance,
4573                &StakeState::Uninitialized,
4574                std::mem::size_of::<StakeState>(),
4575                &id(),
4576            )
4577            .expect("stake_account");
4578
4579            let split_stake_keyed_account =
4580                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4581
4582            let stake_account = AccountSharedData::new_ref_data_with_space(
4583                stake_carats,
4584                &state,
4585                std::mem::size_of::<StakeState>() + 100,
4586                &id(),
4587            )
4588            .expect("stake_account");
4589            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4590
4591            // split more than available fails
4592            assert_eq!(
4593                stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4594                Err(InstructionError::InsufficientFunds)
4595            );
4596
4597            // should work
4598            assert_eq!(
4599                stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4600                Ok(())
4601            );
4602            // no carat leakage
4603            assert_eq!(
4604                stake_keyed_account.account.borrow().carats()
4605                    + split_stake_keyed_account.account.borrow().carats(),
4606                stake_carats + initial_balance
4607            );
4608
4609            if let StakeState::Stake(meta, stake) = state {
4610                let expected_split_meta = Meta {
4611                    authorized: Authorized::auto(&stake_pubkey),
4612                    rent_exempt_reserve: expected_rent_exempt_reserve,
4613                    ..Meta::default()
4614                };
4615                let expected_stake = stake_carats / 2
4616                    - (expected_rent_exempt_reserve.saturating_sub(initial_balance));
4617
4618                assert_eq!(
4619                    Ok(StakeState::Stake(
4620                        expected_split_meta,
4621                        Stake {
4622                            delegation: Delegation {
4623                                stake: expected_stake,
4624                                ..stake.delegation
4625                            },
4626                            ..stake
4627                        }
4628                    )),
4629                    split_stake_keyed_account.state()
4630                );
4631                assert_eq!(
4632                    split_stake_keyed_account.account.borrow().carats(),
4633                    expected_stake
4634                        + expected_rent_exempt_reserve
4635                        + initial_balance.saturating_sub(expected_rent_exempt_reserve)
4636                );
4637                assert_eq!(
4638                    Ok(StakeState::Stake(
4639                        meta,
4640                        Stake {
4641                            delegation: Delegation {
4642                                stake: stake_carats / 2 - rent_exempt_reserve,
4643                                ..stake.delegation
4644                            },
4645                            ..stake
4646                        }
4647                    )),
4648                    stake_keyed_account.state()
4649                );
4650            }
4651        }
4652    }
4653
4654    #[test]
4655    fn test_split_to_larger_account() {
4656        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4657        let rent = Rent::default();
4658        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4659
4660        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4661        let signers = vec![stake_pubkey].into_iter().collect();
4662
4663        let meta = Meta {
4664            authorized: Authorized::auto(&stake_pubkey),
4665            rent_exempt_reserve,
4666            ..Meta::default()
4667        };
4668
4669        let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4670            meta.rent_exempt_reserve,
4671            std::mem::size_of::<StakeState>() as u64,
4672            std::mem::size_of::<StakeState>() as u64 + 100,
4673        );
4674        let stake_carats = expected_rent_exempt_reserve + 1;
4675        let split_amount = stake_carats - (rent_exempt_reserve + 1); // Enough so that split stake is > 0
4676
4677        let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4678
4679        let split_carat_balances = vec![
4680            0,
4681            1,
4682            expected_rent_exempt_reserve,
4683            expected_rent_exempt_reserve + 1,
4684        ];
4685        for initial_balance in split_carat_balances {
4686            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4687                initial_balance,
4688                &StakeState::Uninitialized,
4689                std::mem::size_of::<StakeState>() + 100,
4690                &id(),
4691            )
4692            .expect("stake_account");
4693
4694            let split_stake_keyed_account =
4695                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4696
4697            let stake_account = AccountSharedData::new_ref_data_with_space(
4698                stake_carats,
4699                &state,
4700                std::mem::size_of::<StakeState>(),
4701                &id(),
4702            )
4703            .expect("stake_account");
4704            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4705
4706            // should always return error when splitting to larger account
4707            let split_result =
4708                stake_keyed_account.split(split_amount, &split_stake_keyed_account, &signers);
4709            assert_eq!(split_result, Err(InstructionError::InvalidAccountData));
4710
4711            // Splitting 100% of source should not make a difference
4712            let split_result =
4713                stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers);
4714            assert_eq!(split_result, Err(InstructionError::InvalidAccountData));
4715        }
4716    }
4717
4718    #[test]
4719    fn test_split_100_percent_of_source() {
4720        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4721        let rent = Rent::default();
4722        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4723        let stake_carats = rent_exempt_reserve * 3; // Arbitrary amount over rent_exempt_reserve
4724
4725        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4726        let signers = vec![stake_pubkey].into_iter().collect();
4727
4728        let meta = Meta {
4729            authorized: Authorized::auto(&stake_pubkey),
4730            rent_exempt_reserve,
4731            ..Meta::default()
4732        };
4733
4734        // test splitting both an Initialized stake and a Staked stake
4735        for state in &[
4736            StakeState::Initialized(meta),
4737            StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4738        ] {
4739            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4740                0,
4741                &StakeState::Uninitialized,
4742                std::mem::size_of::<StakeState>(),
4743                &id(),
4744            )
4745            .expect("stake_account");
4746
4747            let split_stake_keyed_account =
4748                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4749
4750            let stake_account = AccountSharedData::new_ref_data_with_space(
4751                stake_carats,
4752                state,
4753                std::mem::size_of::<StakeState>(),
4754                &id(),
4755            )
4756            .expect("stake_account");
4757            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4758
4759            // split 100% over to dest
4760            assert_eq!(
4761                stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4762                Ok(())
4763            );
4764
4765            // no carat leakage
4766            assert_eq!(
4767                stake_keyed_account.account.borrow().carats()
4768                    + split_stake_keyed_account.account.borrow().carats(),
4769                stake_carats
4770            );
4771
4772            match state {
4773                StakeState::Initialized(_) => {
4774                    assert_eq!(Ok(*state), split_stake_keyed_account.state());
4775                    assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4776                }
4777                StakeState::Stake(meta, stake) => {
4778                    assert_eq!(
4779                        Ok(StakeState::Stake(
4780                            *meta,
4781                            Stake {
4782                                delegation: Delegation {
4783                                    stake: stake_carats - rent_exempt_reserve,
4784                                    ..stake.delegation
4785                                },
4786                                ..*stake
4787                            }
4788                        )),
4789                        split_stake_keyed_account.state()
4790                    );
4791                    assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4792                }
4793                _ => unreachable!(),
4794            }
4795
4796            // reset
4797            stake_keyed_account
4798                .account
4799                .borrow_mut()
4800                .set_carats(stake_carats);
4801        }
4802    }
4803
4804    #[test]
4805    fn test_split_100_percent_of_source_to_account_with_carats() {
4806        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4807        let rent = Rent::default();
4808        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4809        let stake_carats = rent_exempt_reserve * 3; // Arbitrary amount over rent_exempt_reserve
4810
4811        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4812        let signers = vec![stake_pubkey].into_iter().collect();
4813
4814        let meta = Meta {
4815            authorized: Authorized::auto(&stake_pubkey),
4816            rent_exempt_reserve,
4817            ..Meta::default()
4818        };
4819
4820        let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4821        // Test various account prefunding, including empty, less than rent_exempt_reserve, exactly
4822        // rent_exempt_reserve, and more than rent_exempt_reserve. Technically, the empty case is
4823        // covered in test_split_100_percent_of_source, but included here as well for readability
4824        let split_carat_balances = vec![0, 1, rent_exempt_reserve, rent_exempt_reserve + 1];
4825        for initial_balance in split_carat_balances {
4826            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4827                initial_balance,
4828                &StakeState::Uninitialized,
4829                std::mem::size_of::<StakeState>(),
4830                &id(),
4831            )
4832            .expect("stake_account");
4833
4834            let split_stake_keyed_account =
4835                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4836
4837            let stake_account = AccountSharedData::new_ref_data_with_space(
4838                stake_carats,
4839                &state,
4840                std::mem::size_of::<StakeState>(),
4841                &id(),
4842            )
4843            .expect("stake_account");
4844            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4845
4846            // split 100% over to dest
4847            assert_eq!(
4848                stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4849                Ok(())
4850            );
4851
4852            // no carat leakage
4853            assert_eq!(
4854                stake_keyed_account.account.borrow().carats()
4855                    + split_stake_keyed_account.account.borrow().carats(),
4856                stake_carats + initial_balance
4857            );
4858
4859            if let StakeState::Stake(meta, stake) = state {
4860                assert_eq!(
4861                    Ok(StakeState::Stake(
4862                        meta,
4863                        Stake {
4864                            delegation: Delegation {
4865                                stake: stake_carats - rent_exempt_reserve,
4866                                ..stake.delegation
4867                            },
4868                            ..stake
4869                        }
4870                    )),
4871                    split_stake_keyed_account.state()
4872                );
4873                assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4874            }
4875        }
4876    }
4877
4878    #[test]
4879    fn test_split_rent_exemptness() {
4880        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4881        let rent = Rent::default();
4882        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4883        let stake_carats = rent_exempt_reserve + 1;
4884
4885        let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4886        let signers = vec![stake_pubkey].into_iter().collect();
4887
4888        let meta = Meta {
4889            authorized: Authorized::auto(&stake_pubkey),
4890            rent_exempt_reserve,
4891            ..Meta::default()
4892        };
4893
4894        for state in &[
4895            StakeState::Initialized(meta),
4896            StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4897        ] {
4898            // Test that splitting to a larger account fails
4899            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4900                0,
4901                &StakeState::Uninitialized,
4902                std::mem::size_of::<StakeState>() + 10000,
4903                &id(),
4904            )
4905            .expect("stake_account");
4906            let split_stake_keyed_account =
4907                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4908
4909            let stake_account = AccountSharedData::new_ref_data_with_space(
4910                stake_carats,
4911                &state,
4912                std::mem::size_of::<StakeState>(),
4913                &id(),
4914            )
4915            .expect("stake_account");
4916            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4917
4918            assert_eq!(
4919                stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4920                Err(InstructionError::InvalidAccountData)
4921            );
4922
4923            // Test that splitting from a larger account to a smaller one works.
4924            // Split amount should not matter, assuming other fund criteria are met
4925            let split_stake_account = AccountSharedData::new_ref_data_with_space(
4926                0,
4927                &StakeState::Uninitialized,
4928                std::mem::size_of::<StakeState>(),
4929                &id(),
4930            )
4931            .expect("stake_account");
4932            let split_stake_keyed_account =
4933                KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4934
4935            let stake_account = AccountSharedData::new_ref_data_with_space(
4936                stake_carats,
4937                &state,
4938                std::mem::size_of::<StakeState>() + 100,
4939                &id(),
4940            )
4941            .expect("stake_account");
4942            let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4943
4944            assert_eq!(
4945                stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4946                Ok(())
4947            );
4948
4949            assert_eq!(
4950                split_stake_keyed_account.account.borrow().carats(),
4951                stake_carats
4952            );
4953
4954            let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4955                meta.rent_exempt_reserve,
4956                std::mem::size_of::<StakeState>() as u64 + 100,
4957                std::mem::size_of::<StakeState>() as u64,
4958            );
4959            let expected_split_meta = Meta {
4960                authorized: Authorized::auto(&stake_pubkey),
4961                rent_exempt_reserve: expected_rent_exempt_reserve,
4962                ..Meta::default()
4963            };
4964
4965            match state {
4966                StakeState::Initialized(_) => {
4967                    assert_eq!(
4968                        Ok(StakeState::Initialized(expected_split_meta)),
4969                        split_stake_keyed_account.state()
4970                    );
4971                    assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4972                }
4973                StakeState::Stake(_meta, stake) => {
4974                    // Expected stake should reflect original stake amount so that extra carats
4975                    // from the rent_exempt_reserve inequality do not magically activate
4976                    let expected_stake = stake_carats - rent_exempt_reserve;
4977
4978                    assert_eq!(
4979                        Ok(StakeState::Stake(
4980                            expected_split_meta,
4981                            Stake {
4982                                delegation: Delegation {
4983                                    stake: expected_stake,
4984                                    ..stake.delegation
4985                                },
4986                                ..*stake
4987                            }
4988                        )),
4989                        split_stake_keyed_account.state()
4990                    );
4991                    assert_eq!(
4992                        split_stake_keyed_account.account.borrow().carats(),
4993                        expected_stake
4994                            + expected_rent_exempt_reserve
4995                            + (rent_exempt_reserve - expected_rent_exempt_reserve)
4996                    );
4997                    assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4998                }
4999                _ => unreachable!(),
5000            }
5001        }
5002    }
5003
5004    #[test]
5005    fn test_merge() {
5006        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5007        let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5008        let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5009        let stake_carats = 42;
5010
5011        let signers = vec![authorized_pubkey].into_iter().collect();
5012        let invoke_context = MockInvokeContext::new(vec![]);
5013
5014        for state in &[
5015            StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5016            StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5017        ] {
5018            for source_state in &[
5019                StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5020                StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5021            ] {
5022                let stake_account = AccountSharedData::new_ref_data_with_space(
5023                    stake_carats,
5024                    state,
5025                    std::mem::size_of::<StakeState>(),
5026                    &id(),
5027                )
5028                .expect("stake_account");
5029                let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5030
5031                let source_stake_account = AccountSharedData::new_ref_data_with_space(
5032                    stake_carats,
5033                    source_state,
5034                    std::mem::size_of::<StakeState>(),
5035                    &id(),
5036                )
5037                .expect("source_stake_account");
5038                let source_stake_keyed_account =
5039                    KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5040
5041                // Authorized staker signature required...
5042                assert_eq!(
5043                    stake_keyed_account.merge(
5044                        &invoke_context,
5045                        &source_stake_keyed_account,
5046                        &Clock::default(),
5047                        &StakeHistory::default(),
5048                        &HashSet::new(),
5049                        false,
5050                    ),
5051                    Err(InstructionError::MissingRequiredSignature)
5052                );
5053
5054                assert_eq!(
5055                    stake_keyed_account.merge(
5056                        &invoke_context,
5057                        &source_stake_keyed_account,
5058                        &Clock::default(),
5059                        &StakeHistory::default(),
5060                        &signers,
5061                        false,
5062                    ),
5063                    Ok(())
5064                );
5065
5066                // check carats
5067                assert_eq!(
5068                    stake_keyed_account.account.borrow().carats(),
5069                    stake_carats * 2
5070                );
5071                assert_eq!(source_stake_keyed_account.account.borrow().carats(), 0);
5072
5073                // check state
5074                match state {
5075                    StakeState::Initialized(meta) => {
5076                        assert_eq!(
5077                            stake_keyed_account.state(),
5078                            Ok(StakeState::Initialized(*meta)),
5079                        );
5080                    }
5081                    StakeState::Stake(meta, stake) => {
5082                        let expected_stake = stake.delegation.stake
5083                            + source_state
5084                                .stake()
5085                                .map(|stake| stake.delegation.stake)
5086                                .unwrap_or_else(|| {
5087                                    stake_carats
5088                                        - source_state.meta().unwrap().rent_exempt_reserve
5089                                });
5090                        assert_eq!(
5091                            stake_keyed_account.state(),
5092                            Ok(StakeState::Stake(
5093                                *meta,
5094                                Stake {
5095                                    delegation: Delegation {
5096                                        stake: expected_stake,
5097                                        ..stake.delegation
5098                                    },
5099                                    ..*stake
5100                                }
5101                            )),
5102                        );
5103                    }
5104                    _ => unreachable!(),
5105                }
5106                assert_eq!(
5107                    source_stake_keyed_account.state(),
5108                    Ok(StakeState::Uninitialized)
5109                );
5110            }
5111        }
5112    }
5113
5114    #[test]
5115    fn test_merge_self_fails() {
5116        let invoke_context = MockInvokeContext::new(vec![]);
5117        let stake_address = Pubkey::new_unique();
5118        let authority_pubkey = Pubkey::new_unique();
5119        let signers = HashSet::from_iter(vec![authority_pubkey]);
5120        let rent = Rent::default();
5121        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5122        let stake_amount = 4242424242;
5123        let stake_carats = rent_exempt_reserve + stake_amount;
5124
5125        let meta = Meta {
5126            rent_exempt_reserve,
5127            ..Meta::auto(&authority_pubkey)
5128        };
5129        let stake = Stake {
5130            delegation: Delegation {
5131                stake: stake_amount,
5132                activation_epoch: 0,
5133                ..Delegation::default()
5134            },
5135            ..Stake::default()
5136        };
5137        let stake_account = AccountSharedData::new_ref_data_with_space(
5138            stake_carats,
5139            &StakeState::Stake(meta, stake),
5140            std::mem::size_of::<StakeState>(),
5141            &id(),
5142        )
5143        .expect("stake_account");
5144        let stake_keyed_account = KeyedAccount::new(&stake_address, true, &stake_account);
5145
5146        assert_eq!(
5147            stake_keyed_account.merge(
5148                &invoke_context,
5149                &stake_keyed_account,
5150                &Clock::default(),
5151                &StakeHistory::default(),
5152                &signers,
5153                false,
5154            ),
5155            Err(InstructionError::InvalidArgument),
5156        );
5157    }
5158
5159    #[test]
5160    fn test_merge_incorrect_authorized_staker() {
5161        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5162        let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5163        let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5164        let wrong_authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5165        let stake_carats = 42;
5166
5167        let signers = vec![authorized_pubkey].into_iter().collect();
5168        let wrong_signers = vec![wrong_authorized_pubkey].into_iter().collect();
5169        let invoke_context = MockInvokeContext::new(vec![]);
5170
5171        for state in &[
5172            StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5173            StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5174        ] {
5175            for source_state in &[
5176                StakeState::Initialized(Meta::auto(&wrong_authorized_pubkey)),
5177                StakeState::Stake(
5178                    Meta::auto(&wrong_authorized_pubkey),
5179                    just_stake(stake_carats),
5180                ),
5181            ] {
5182                let stake_account = AccountSharedData::new_ref_data_with_space(
5183                    stake_carats,
5184                    state,
5185                    std::mem::size_of::<StakeState>(),
5186                    &id(),
5187                )
5188                .expect("stake_account");
5189                let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5190
5191                let source_stake_account = AccountSharedData::new_ref_data_with_space(
5192                    stake_carats,
5193                    source_state,
5194                    std::mem::size_of::<StakeState>(),
5195                    &id(),
5196                )
5197                .expect("source_stake_account");
5198                let source_stake_keyed_account =
5199                    KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5200
5201                assert_eq!(
5202                    stake_keyed_account.merge(
5203                        &invoke_context,
5204                        &source_stake_keyed_account,
5205                        &Clock::default(),
5206                        &StakeHistory::default(),
5207                        &wrong_signers,
5208                        false,
5209                    ),
5210                    Err(InstructionError::MissingRequiredSignature)
5211                );
5212
5213                assert_eq!(
5214                    stake_keyed_account.merge(
5215                        &invoke_context,
5216                        &source_stake_keyed_account,
5217                        &Clock::default(),
5218                        &StakeHistory::default(),
5219                        &signers,
5220                        false,
5221                    ),
5222                    Err(StakeError::MergeMismatch.into())
5223                );
5224            }
5225        }
5226    }
5227
5228    #[test]
5229    fn test_merge_invalid_account_data() {
5230        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5231        let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5232        let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5233        let stake_carats = 42;
5234        let signers = vec![authorized_pubkey].into_iter().collect();
5235        let invoke_context = MockInvokeContext::new(vec![]);
5236
5237        for state in &[
5238            StakeState::Uninitialized,
5239            StakeState::RewardsPool,
5240            StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5241            StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5242        ] {
5243            for source_state in &[StakeState::Uninitialized, StakeState::RewardsPool] {
5244                let stake_account = AccountSharedData::new_ref_data_with_space(
5245                    stake_carats,
5246                    state,
5247                    std::mem::size_of::<StakeState>(),
5248                    &id(),
5249                )
5250                .expect("stake_account");
5251                let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5252
5253                let source_stake_account = AccountSharedData::new_ref_data_with_space(
5254                    stake_carats,
5255                    source_state,
5256                    std::mem::size_of::<StakeState>(),
5257                    &id(),
5258                )
5259                .expect("source_stake_account");
5260                let source_stake_keyed_account =
5261                    KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5262
5263                assert_eq!(
5264                    stake_keyed_account.merge(
5265                        &invoke_context,
5266                        &source_stake_keyed_account,
5267                        &Clock::default(),
5268                        &StakeHistory::default(),
5269                        &signers,
5270                        false,
5271                    ),
5272                    Err(InstructionError::InvalidAccountData)
5273                );
5274            }
5275        }
5276    }
5277
5278    #[test]
5279    fn test_merge_fake_stake_source() {
5280        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5281        let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5282        let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5283        let stake_carats = 42;
5284
5285        let signers = vec![authorized_pubkey].into_iter().collect();
5286
5287        let stake_account = AccountSharedData::new_ref_data_with_space(
5288            stake_carats,
5289            &StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5290            std::mem::size_of::<StakeState>(),
5291            &id(),
5292        )
5293        .expect("stake_account");
5294        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5295
5296        let source_stake_account = AccountSharedData::new_ref_data_with_space(
5297            stake_carats,
5298            &StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5299            std::mem::size_of::<StakeState>(),
5300            &gemachain_sdk::pubkey::new_rand(),
5301        )
5302        .expect("source_stake_account");
5303        let source_stake_keyed_account =
5304            KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5305        let invoke_context = MockInvokeContext::new(vec![]);
5306
5307        assert_eq!(
5308            stake_keyed_account.merge(
5309                &invoke_context,
5310                &source_stake_keyed_account,
5311                &Clock::default(),
5312                &StakeHistory::default(),
5313                &signers,
5314                false,
5315            ),
5316            Err(InstructionError::IncorrectProgramId)
5317        );
5318    }
5319
5320    #[test]
5321    fn test_merge_active_stake() {
5322        let base_carats = 4242424242;
5323        let stake_address = Pubkey::new_unique();
5324        let source_address = Pubkey::new_unique();
5325        let authority_pubkey = Pubkey::new_unique();
5326        let signers = HashSet::from_iter(vec![authority_pubkey]);
5327        let rent = Rent::default();
5328        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5329        let stake_amount = base_carats;
5330        let stake_carats = rent_exempt_reserve + stake_amount;
5331        let source_amount = base_carats;
5332        let source_carats = rent_exempt_reserve + source_amount;
5333
5334        let meta = Meta {
5335            rent_exempt_reserve,
5336            ..Meta::auto(&authority_pubkey)
5337        };
5338        let mut stake = Stake {
5339            delegation: Delegation {
5340                stake: stake_amount,
5341                activation_epoch: 0,
5342                ..Delegation::default()
5343            },
5344            ..Stake::default()
5345        };
5346        let stake_account = AccountSharedData::new_ref_data_with_space(
5347            stake_carats,
5348            &StakeState::Stake(meta, stake),
5349            std::mem::size_of::<StakeState>(),
5350            &id(),
5351        )
5352        .expect("stake_account");
5353        let stake_keyed_account = KeyedAccount::new(&stake_address, true, &stake_account);
5354
5355        let source_activation_epoch = 2;
5356        let mut source_stake = Stake {
5357            delegation: Delegation {
5358                stake: source_amount,
5359                activation_epoch: source_activation_epoch,
5360                ..stake.delegation
5361            },
5362            ..stake
5363        };
5364        let source_account = AccountSharedData::new_ref_data_with_space(
5365            source_carats,
5366            &StakeState::Stake(meta, source_stake),
5367            std::mem::size_of::<StakeState>(),
5368            &id(),
5369        )
5370        .expect("source_account");
5371        let source_keyed_account = KeyedAccount::new(&source_address, true, &source_account);
5372
5373        let mut clock = Clock::default();
5374        let mut stake_history = StakeHistory::default();
5375        let invoke_context = MockInvokeContext::new(vec![]);
5376
5377        clock.epoch = 0;
5378        let mut effective = base_carats;
5379        let mut activating = stake_amount;
5380        let mut deactivating = 0;
5381        stake_history.add(
5382            clock.epoch,
5383            StakeHistoryEntry {
5384                effective,
5385                activating,
5386                deactivating,
5387            },
5388        );
5389
5390        fn try_merge(
5391            invoke_context: &dyn InvokeContext,
5392            stake_account: &KeyedAccount,
5393            source_account: &KeyedAccount,
5394            clock: &Clock,
5395            stake_history: &StakeHistory,
5396            signers: &HashSet<Pubkey>,
5397        ) -> Result<(), InstructionError> {
5398            let test_stake_account = stake_account.account.clone();
5399            let test_stake_keyed =
5400                KeyedAccount::new(stake_account.unsigned_key(), true, &test_stake_account);
5401            let test_source_account = source_account.account.clone();
5402            let test_source_keyed =
5403                KeyedAccount::new(source_account.unsigned_key(), true, &test_source_account);
5404
5405            let result = test_stake_keyed.merge(
5406                invoke_context,
5407                &test_source_keyed,
5408                clock,
5409                stake_history,
5410                signers,
5411                false,
5412            );
5413            if result.is_ok() {
5414                assert_eq!(test_source_keyed.state(), Ok(StakeState::Uninitialized),);
5415            }
5416            result
5417        }
5418
5419        // stake activation epoch, source initialized succeeds
5420        assert!(try_merge(
5421            &invoke_context,
5422            &stake_keyed_account,
5423            &source_keyed_account,
5424            &clock,
5425            &stake_history,
5426            &signers
5427        )
5428        .is_ok(),);
5429        assert!(try_merge(
5430            &invoke_context,
5431            &source_keyed_account,
5432            &stake_keyed_account,
5433            &clock,
5434            &stake_history,
5435            &signers
5436        )
5437        .is_ok(),);
5438
5439        // both activating fails
5440        loop {
5441            clock.epoch += 1;
5442            if clock.epoch == source_activation_epoch {
5443                activating += source_amount;
5444            }
5445            let delta =
5446                activating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
5447            effective += delta;
5448            activating -= delta;
5449            stake_history.add(
5450                clock.epoch,
5451                StakeHistoryEntry {
5452                    effective,
5453                    activating,
5454                    deactivating,
5455                },
5456            );
5457            if stake_amount == stake.stake(clock.epoch, Some(&stake_history))
5458                && source_amount == source_stake.stake(clock.epoch, Some(&stake_history))
5459            {
5460                break;
5461            }
5462            assert_eq!(
5463                try_merge(
5464                    &invoke_context,
5465                    &stake_keyed_account,
5466                    &source_keyed_account,
5467                    &clock,
5468                    &stake_history,
5469                    &signers
5470                )
5471                .unwrap_err(),
5472                InstructionError::from(StakeError::MergeTransientStake),
5473            );
5474            assert_eq!(
5475                try_merge(
5476                    &invoke_context,
5477                    &source_keyed_account,
5478                    &stake_keyed_account,
5479                    &clock,
5480                    &stake_history,
5481                    &signers
5482                )
5483                .unwrap_err(),
5484                InstructionError::from(StakeError::MergeTransientStake),
5485            );
5486        }
5487        // Both fully activated works
5488        assert!(try_merge(
5489            &invoke_context,
5490            &stake_keyed_account,
5491            &source_keyed_account,
5492            &clock,
5493            &stake_history,
5494            &signers
5495        )
5496        .is_ok(),);
5497
5498        // deactivate setup for deactivation
5499        let source_deactivation_epoch = clock.epoch + 1;
5500        let stake_deactivation_epoch = clock.epoch + 2;
5501
5502        // active/deactivating and deactivating/inactive mismatches fail
5503        loop {
5504            clock.epoch += 1;
5505            let delta =
5506                deactivating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
5507            effective -= delta;
5508            deactivating -= delta;
5509            if clock.epoch == stake_deactivation_epoch {
5510                deactivating += stake_amount;
5511                stake = Stake {
5512                    delegation: Delegation {
5513                        deactivation_epoch: stake_deactivation_epoch,
5514                        ..stake.delegation
5515                    },
5516                    ..stake
5517                };
5518                stake_keyed_account
5519                    .set_state(&StakeState::Stake(meta, stake))
5520                    .unwrap();
5521            }
5522            if clock.epoch == source_deactivation_epoch {
5523                deactivating += source_amount;
5524                source_stake = Stake {
5525                    delegation: Delegation {
5526                        deactivation_epoch: source_deactivation_epoch,
5527                        ..source_stake.delegation
5528                    },
5529                    ..source_stake
5530                };
5531                source_keyed_account
5532                    .set_state(&StakeState::Stake(meta, source_stake))
5533                    .unwrap();
5534            }
5535            stake_history.add(
5536                clock.epoch,
5537                StakeHistoryEntry {
5538                    effective,
5539                    activating,
5540                    deactivating,
5541                },
5542            );
5543            if 0 == stake.stake(clock.epoch, Some(&stake_history))
5544                && 0 == source_stake.stake(clock.epoch, Some(&stake_history))
5545            {
5546                break;
5547            }
5548            assert_eq!(
5549                try_merge(
5550                    &invoke_context,
5551                    &stake_keyed_account,
5552                    &source_keyed_account,
5553                    &clock,
5554                    &stake_history,
5555                    &signers
5556                )
5557                .unwrap_err(),
5558                InstructionError::from(StakeError::MergeTransientStake),
5559            );
5560            assert_eq!(
5561                try_merge(
5562                    &invoke_context,
5563                    &source_keyed_account,
5564                    &stake_keyed_account,
5565                    &clock,
5566                    &stake_history,
5567                    &signers
5568                )
5569                .unwrap_err(),
5570                InstructionError::from(StakeError::MergeTransientStake),
5571            );
5572        }
5573
5574        // Both fully deactivated works
5575        assert!(try_merge(
5576            &invoke_context,
5577            &stake_keyed_account,
5578            &source_keyed_account,
5579            &clock,
5580            &stake_history,
5581            &signers
5582        )
5583        .is_ok(),);
5584    }
5585
5586    #[test]
5587    fn test_lockup_is_expired() {
5588        let custodian = gemachain_sdk::pubkey::new_rand();
5589        let lockup = Lockup {
5590            epoch: 1,
5591            unix_timestamp: 1,
5592            custodian,
5593        };
5594        // neither time
5595        assert!(lockup.is_in_force(
5596            &Clock {
5597                epoch: 0,
5598                unix_timestamp: 0,
5599                ..Clock::default()
5600            },
5601            None
5602        ));
5603        // not timestamp
5604        assert!(lockup.is_in_force(
5605            &Clock {
5606                epoch: 2,
5607                unix_timestamp: 0,
5608                ..Clock::default()
5609            },
5610            None
5611        ));
5612        // not epoch
5613        assert!(lockup.is_in_force(
5614            &Clock {
5615                epoch: 0,
5616                unix_timestamp: 2,
5617                ..Clock::default()
5618            },
5619            None
5620        ));
5621        // both, no custodian
5622        assert!(!lockup.is_in_force(
5623            &Clock {
5624                epoch: 1,
5625                unix_timestamp: 1,
5626                ..Clock::default()
5627            },
5628            None
5629        ));
5630        // neither, but custodian
5631        assert!(!lockup.is_in_force(
5632            &Clock {
5633                epoch: 0,
5634                unix_timestamp: 0,
5635                ..Clock::default()
5636            },
5637            Some(&custodian),
5638        ));
5639    }
5640
5641    #[test]
5642    #[ignore]
5643    #[should_panic]
5644    fn test_dbg_stake_minimum_balance() {
5645        let minimum_balance = Rent::default().minimum_balance(std::mem::size_of::<StakeState>());
5646        panic!(
5647            "stake minimum_balance: {} carats, {} GEMA",
5648            minimum_balance,
5649            minimum_balance as f64 / gemachain_sdk::native_token::CARATS_PER_GEMA as f64
5650        );
5651    }
5652
5653    #[test]
5654    fn test_authorize_delegated_stake() {
5655        let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5656        let stake_carats = 42;
5657        let stake_account = AccountSharedData::new_ref_data_with_space(
5658            stake_carats,
5659            &StakeState::Initialized(Meta::auto(&stake_pubkey)),
5660            std::mem::size_of::<StakeState>(),
5661            &id(),
5662        )
5663        .expect("stake_account");
5664
5665        let clock = Clock::default();
5666
5667        let vote_pubkey = gemachain_sdk::pubkey::new_rand();
5668        let vote_account = RefCell::new(vote_state::create_account(
5669            &vote_pubkey,
5670            &gemachain_sdk::pubkey::new_rand(),
5671            0,
5672            100,
5673        ));
5674        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
5675
5676        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5677        let signers = vec![stake_pubkey].into_iter().collect();
5678        stake_keyed_account
5679            .delegate(
5680                &vote_keyed_account,
5681                &clock,
5682                &StakeHistory::default(),
5683                &Config::default(),
5684                &signers,
5685                true,
5686            )
5687            .unwrap();
5688
5689        // deactivate, so we can re-delegate
5690        stake_keyed_account.deactivate(&clock, &signers).unwrap();
5691
5692        let new_staker_pubkey = gemachain_sdk::pubkey::new_rand();
5693        assert_eq!(
5694            stake_keyed_account.authorize(
5695                &signers,
5696                &new_staker_pubkey,
5697                StakeAuthorize::Staker,
5698                false,
5699                &Clock::default(),
5700                None
5701            ),
5702            Ok(())
5703        );
5704        let authorized = authorized_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap();
5705        assert_eq!(authorized.staker, new_staker_pubkey);
5706
5707        let other_pubkey = gemachain_sdk::pubkey::new_rand();
5708        let other_signers = vec![other_pubkey].into_iter().collect();
5709
5710        // Use unsigned stake_keyed_account to test other signers
5711        let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
5712
5713        let new_voter_pubkey = gemachain_sdk::pubkey::new_rand();
5714        let vote_state = VoteState::default();
5715        let new_vote_account = RefCell::new(vote_state::create_account(
5716            &new_voter_pubkey,
5717            &gemachain_sdk::pubkey::new_rand(),
5718            0,
5719            100,
5720        ));
5721        let new_vote_keyed_account = KeyedAccount::new(&new_voter_pubkey, false, &new_vote_account);
5722        new_vote_keyed_account.set_state(&vote_state).unwrap();
5723
5724        // Random other account should fail
5725        assert_eq!(
5726            stake_keyed_account.delegate(
5727                &new_vote_keyed_account,
5728                &clock,
5729                &StakeHistory::default(),
5730                &Config::default(),
5731                &other_signers,
5732                true,
5733            ),
5734            Err(InstructionError::MissingRequiredSignature)
5735        );
5736
5737        let new_signers = vec![new_staker_pubkey].into_iter().collect();
5738        // Authorized staker should succeed
5739        assert_eq!(
5740            stake_keyed_account.delegate(
5741                &new_vote_keyed_account,
5742                &clock,
5743                &StakeHistory::default(),
5744                &Config::default(),
5745                &new_signers,
5746                true,
5747            ),
5748            Ok(())
5749        );
5750        let stake = stake_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap();
5751        assert_eq!(stake.delegation.voter_pubkey, new_voter_pubkey);
5752
5753        // Test another staking action
5754        assert_eq!(stake_keyed_account.deactivate(&clock, &new_signers), Ok(()));
5755    }
5756
5757    #[test]
5758    fn test_redelegate_consider_balance_changes() {
5759        let initial_carats = 4242424242;
5760        let rent = Rent::default();
5761        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5762        let withdrawer_pubkey = Pubkey::new_unique();
5763        let stake_carats = rent_exempt_reserve + initial_carats;
5764
5765        let meta = Meta {
5766            rent_exempt_reserve,
5767            ..Meta::auto(&withdrawer_pubkey)
5768        };
5769        let stake_account = AccountSharedData::new_ref_data_with_space(
5770            stake_carats,
5771            &StakeState::Initialized(meta),
5772            std::mem::size_of::<StakeState>(),
5773            &id(),
5774        )
5775        .expect("stake_account");
5776        let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
5777
5778        let vote_pubkey = Pubkey::new_unique();
5779        let vote_account = RefCell::new(vote_state::create_account(
5780            &vote_pubkey,
5781            &Pubkey::new_unique(),
5782            0,
5783            100,
5784        ));
5785        let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
5786
5787        let signers = HashSet::from_iter(vec![withdrawer_pubkey]);
5788        let config = Config::default();
5789        let stake_history = StakeHistory::default();
5790        let mut clock = Clock::default();
5791        stake_keyed_account
5792            .delegate(
5793                &vote_keyed_account,
5794                &clock,
5795                &stake_history,
5796                &config,
5797                &signers,
5798                true,
5799            )
5800            .unwrap();
5801
5802        clock.epoch += 1;
5803        stake_keyed_account.deactivate(&clock, &signers).unwrap();
5804
5805        clock.epoch += 1;
5806        // Once deactivated, we withdraw stake to new keyed account
5807        let to = Pubkey::new_unique();
5808        let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
5809        let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
5810        let withdraw_carats = initial_carats / 2;
5811        stake_keyed_account
5812            .withdraw(
5813                withdraw_carats,
5814                &to_keyed_account,
5815                &clock,
5816                &stake_history,
5817                &stake_keyed_account,
5818                None,
5819                true,
5820            )
5821            .unwrap();
5822        let expected_balance = rent_exempt_reserve + initial_carats - withdraw_carats;
5823        assert_eq!(stake_keyed_account.carats().unwrap(), expected_balance);
5824
5825        clock.epoch += 1;
5826        stake_keyed_account
5827            .delegate(
5828                &vote_keyed_account,
5829                &clock,
5830                &stake_history,
5831                &config,
5832                &signers,
5833                true,
5834            )
5835            .unwrap();
5836        let stake = stake_from(&stake_account.borrow()).unwrap();
5837        assert_eq!(
5838            stake.delegation.stake,
5839            stake_keyed_account.carats().unwrap() - rent_exempt_reserve,
5840        );
5841
5842        clock.epoch += 1;
5843        stake_keyed_account.deactivate(&clock, &signers).unwrap();
5844
5845        // Out of band deposit
5846        stake_keyed_account
5847            .try_account_ref_mut()
5848            .unwrap()
5849            .checked_add_carats(withdraw_carats)
5850            .unwrap();
5851
5852        clock.epoch += 1;
5853        stake_keyed_account
5854            .delegate(
5855                &vote_keyed_account,
5856                &clock,
5857                &stake_history,
5858                &config,
5859                &signers,
5860                true,
5861            )
5862            .unwrap();
5863        let stake = stake_from(&stake_account.borrow()).unwrap();
5864        assert_eq!(
5865            stake.delegation.stake,
5866            stake_keyed_account.carats().unwrap() - rent_exempt_reserve,
5867        );
5868    }
5869
5870    #[test]
5871    fn test_meta_rewrite_rent_exempt_reserve() {
5872        let right_data_len = std::mem::size_of::<StakeState>() as u64;
5873        let rent = Rent::default();
5874        let expected_rent_exempt_reserve = rent.minimum_balance(right_data_len as usize);
5875
5876        let test_cases = [
5877            (
5878                right_data_len + 100,
5879                Some((
5880                    rent.minimum_balance(right_data_len as usize + 100),
5881                    expected_rent_exempt_reserve,
5882                )),
5883            ), // large data_len, too small rent exempt
5884            (right_data_len, None), // correct
5885            (
5886                right_data_len - 100,
5887                Some((
5888                    rent.minimum_balance(right_data_len as usize - 100),
5889                    expected_rent_exempt_reserve,
5890                )),
5891            ), // small data_len, too large rent exempt
5892        ];
5893        for (data_len, expected_rewrite) in &test_cases {
5894            let rent_exempt_reserve = rent.minimum_balance(*data_len as usize);
5895            let mut meta = Meta {
5896                rent_exempt_reserve,
5897                ..Meta::default()
5898            };
5899            let actual_rewrite = meta.rewrite_rent_exempt_reserve(&rent, right_data_len as usize);
5900            assert_eq!(actual_rewrite, *expected_rewrite);
5901            assert_eq!(meta.rent_exempt_reserve, expected_rent_exempt_reserve);
5902        }
5903    }
5904
5905    #[test]
5906    fn test_calculate_carats_per_byte_year() {
5907        let rent = Rent::default();
5908        let data_len = 200u64;
5909        let rent_exempt_reserve = rent.minimum_balance(data_len as usize);
5910        assert_eq!(
5911            calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, data_len),
5912            rent_exempt_reserve
5913        );
5914
5915        let larger_data = 4008u64;
5916        let larger_rent_exempt_reserve = rent.minimum_balance(larger_data as usize);
5917        assert_eq!(
5918            calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, larger_data),
5919            larger_rent_exempt_reserve
5920        );
5921        assert_eq!(
5922            calculate_split_rent_exempt_reserve(larger_rent_exempt_reserve, larger_data, data_len),
5923            rent_exempt_reserve
5924        );
5925
5926        let even_larger_data = gemachain_sdk::system_instruction::MAX_PERMITTED_DATA_LENGTH;
5927        let even_larger_rent_exempt_reserve = rent.minimum_balance(even_larger_data as usize);
5928        assert_eq!(
5929            calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, even_larger_data),
5930            even_larger_rent_exempt_reserve
5931        );
5932        assert_eq!(
5933            calculate_split_rent_exempt_reserve(
5934                even_larger_rent_exempt_reserve,
5935                even_larger_data,
5936                data_len
5937            ),
5938            rent_exempt_reserve
5939        );
5940    }
5941
5942    #[test]
5943    fn test_things_can_merge() {
5944        let good_stake = Stake {
5945            credits_observed: 4242,
5946            delegation: Delegation {
5947                voter_pubkey: Pubkey::new_unique(),
5948                stake: 424242424242,
5949                activation_epoch: 42,
5950                ..Delegation::default()
5951            },
5952        };
5953        let invoke_context = MockInvokeContext::new(vec![]);
5954
5955        let identical = good_stake;
5956        assert!(
5957            MergeKind::active_stakes_can_merge(&invoke_context, &good_stake, &identical).is_ok()
5958        );
5959
5960        let bad_credits_observed = Stake {
5961            credits_observed: good_stake.credits_observed + 1,
5962            ..good_stake
5963        };
5964        assert!(MergeKind::active_stakes_can_merge(
5965            &invoke_context,
5966            &good_stake,
5967            &bad_credits_observed
5968        )
5969        .is_err());
5970
5971        let good_delegation = good_stake.delegation;
5972        let different_stake_ok = Delegation {
5973            stake: good_delegation.stake + 1,
5974            ..good_delegation
5975        };
5976        assert!(MergeKind::active_delegations_can_merge(
5977            &invoke_context,
5978            &good_delegation,
5979            &different_stake_ok
5980        )
5981        .is_ok());
5982
5983        let different_activation_epoch_ok = Delegation {
5984            activation_epoch: good_delegation.activation_epoch + 1,
5985            ..good_delegation
5986        };
5987        assert!(MergeKind::active_delegations_can_merge(
5988            &invoke_context,
5989            &good_delegation,
5990            &different_activation_epoch_ok
5991        )
5992        .is_ok());
5993
5994        let bad_voter = Delegation {
5995            voter_pubkey: Pubkey::new_unique(),
5996            ..good_delegation
5997        };
5998        assert!(MergeKind::active_delegations_can_merge(
5999            &invoke_context,
6000            &good_delegation,
6001            &bad_voter
6002        )
6003        .is_err());
6004
6005        let bad_warmup_cooldown_rate = Delegation {
6006            warmup_cooldown_rate: good_delegation.warmup_cooldown_rate + f64::EPSILON,
6007            ..good_delegation
6008        };
6009        assert!(MergeKind::active_delegations_can_merge(
6010            &invoke_context,
6011            &good_delegation,
6012            &bad_warmup_cooldown_rate
6013        )
6014        .is_err());
6015        assert!(MergeKind::active_delegations_can_merge(
6016            &invoke_context,
6017            &bad_warmup_cooldown_rate,
6018            &good_delegation
6019        )
6020        .is_err());
6021
6022        let bad_deactivation_epoch = Delegation {
6023            deactivation_epoch: 43,
6024            ..good_delegation
6025        };
6026        assert!(MergeKind::active_delegations_can_merge(
6027            &invoke_context,
6028            &good_delegation,
6029            &bad_deactivation_epoch
6030        )
6031        .is_err());
6032        assert!(MergeKind::active_delegations_can_merge(
6033            &invoke_context,
6034            &bad_deactivation_epoch,
6035            &good_delegation
6036        )
6037        .is_err());
6038    }
6039
6040    #[test]
6041    fn test_metas_can_merge_pre_v4() {
6042        let invoke_context = MockInvokeContext::new(vec![]);
6043        // Identical Metas can merge
6044        assert!(MergeKind::metas_can_merge(
6045            &invoke_context,
6046            &Meta::default(),
6047            &Meta::default(),
6048            None,
6049        )
6050        .is_ok());
6051
6052        let mismatched_rent_exempt_reserve_ok = Meta {
6053            rent_exempt_reserve: 42,
6054            ..Meta::default()
6055        };
6056        assert_ne!(
6057            mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
6058            Meta::default().rent_exempt_reserve
6059        );
6060        assert!(MergeKind::metas_can_merge(
6061            &invoke_context,
6062            &Meta::default(),
6063            &mismatched_rent_exempt_reserve_ok,
6064            None,
6065        )
6066        .is_ok());
6067        assert!(MergeKind::metas_can_merge(
6068            &invoke_context,
6069            &mismatched_rent_exempt_reserve_ok,
6070            &Meta::default(),
6071            None,
6072        )
6073        .is_ok());
6074
6075        let mismatched_authorized_fails = Meta {
6076            authorized: Authorized {
6077                staker: Pubkey::new_unique(),
6078                withdrawer: Pubkey::new_unique(),
6079            },
6080            ..Meta::default()
6081        };
6082        assert_ne!(
6083            mismatched_authorized_fails.authorized,
6084            Meta::default().authorized
6085        );
6086        assert!(MergeKind::metas_can_merge(
6087            &invoke_context,
6088            &Meta::default(),
6089            &mismatched_authorized_fails,
6090            None,
6091        )
6092        .is_err());
6093        assert!(MergeKind::metas_can_merge(
6094            &invoke_context,
6095            &mismatched_authorized_fails,
6096            &Meta::default(),
6097            None,
6098        )
6099        .is_err());
6100
6101        let mismatched_lockup_fails = Meta {
6102            lockup: Lockup {
6103                unix_timestamp: 424242424,
6104                epoch: 42,
6105                custodian: Pubkey::new_unique(),
6106            },
6107            ..Meta::default()
6108        };
6109        assert_ne!(mismatched_lockup_fails.lockup, Meta::default().lockup);
6110        assert!(MergeKind::metas_can_merge(
6111            &invoke_context,
6112            &Meta::default(),
6113            &mismatched_lockup_fails,
6114            None,
6115        )
6116        .is_err());
6117        assert!(MergeKind::metas_can_merge(
6118            &invoke_context,
6119            &mismatched_lockup_fails,
6120            &Meta::default(),
6121            None,
6122        )
6123        .is_err());
6124    }
6125
6126    #[test]
6127    fn test_metas_can_merge_v4() {
6128        let invoke_context = MockInvokeContext::new(vec![]);
6129        // Identical Metas can merge
6130        assert!(MergeKind::metas_can_merge(
6131            &invoke_context,
6132            &Meta::default(),
6133            &Meta::default(),
6134            Some(&Clock::default())
6135        )
6136        .is_ok());
6137
6138        let mismatched_rent_exempt_reserve_ok = Meta {
6139            rent_exempt_reserve: 42,
6140            ..Meta::default()
6141        };
6142        assert_ne!(
6143            mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
6144            Meta::default().rent_exempt_reserve,
6145        );
6146        assert!(MergeKind::metas_can_merge(
6147            &invoke_context,
6148            &Meta::default(),
6149            &mismatched_rent_exempt_reserve_ok,
6150            Some(&Clock::default())
6151        )
6152        .is_ok());
6153        assert!(MergeKind::metas_can_merge(
6154            &invoke_context,
6155            &mismatched_rent_exempt_reserve_ok,
6156            &Meta::default(),
6157            Some(&Clock::default())
6158        )
6159        .is_ok());
6160
6161        let mismatched_authorized_fails = Meta {
6162            authorized: Authorized {
6163                staker: Pubkey::new_unique(),
6164                withdrawer: Pubkey::new_unique(),
6165            },
6166            ..Meta::default()
6167        };
6168        assert_ne!(
6169            mismatched_authorized_fails.authorized,
6170            Meta::default().authorized,
6171        );
6172        assert!(MergeKind::metas_can_merge(
6173            &invoke_context,
6174            &Meta::default(),
6175            &mismatched_authorized_fails,
6176            Some(&Clock::default())
6177        )
6178        .is_err());
6179        assert!(MergeKind::metas_can_merge(
6180            &invoke_context,
6181            &mismatched_authorized_fails,
6182            &Meta::default(),
6183            Some(&Clock::default())
6184        )
6185        .is_err());
6186
6187        let lockup1_timestamp = 42;
6188        let lockup2_timestamp = 4242;
6189        let lockup1_epoch = 4;
6190        let lockup2_epoch = 42;
6191        let metas_with_lockup1 = Meta {
6192            lockup: Lockup {
6193                unix_timestamp: lockup1_timestamp,
6194                epoch: lockup1_epoch,
6195                custodian: Pubkey::new_unique(),
6196            },
6197            ..Meta::default()
6198        };
6199        let metas_with_lockup2 = Meta {
6200            lockup: Lockup {
6201                unix_timestamp: lockup2_timestamp,
6202                epoch: lockup2_epoch,
6203                custodian: Pubkey::new_unique(),
6204            },
6205            ..Meta::default()
6206        };
6207
6208        // Mismatched lockups fail when both in force
6209        assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6210        assert!(MergeKind::metas_can_merge(
6211            &invoke_context,
6212            &metas_with_lockup1,
6213            &metas_with_lockup2,
6214            Some(&Clock::default())
6215        )
6216        .is_err());
6217        assert!(MergeKind::metas_can_merge(
6218            &invoke_context,
6219            &metas_with_lockup2,
6220            &metas_with_lockup1,
6221            Some(&Clock::default())
6222        )
6223        .is_err());
6224
6225        let clock = Clock {
6226            epoch: lockup1_epoch + 1,
6227            unix_timestamp: lockup1_timestamp + 1,
6228            ..Clock::default()
6229        };
6230
6231        // Mismatched lockups fail when either in force
6232        assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6233        assert!(MergeKind::metas_can_merge(
6234            &invoke_context,
6235            &metas_with_lockup1,
6236            &metas_with_lockup2,
6237            Some(&clock)
6238        )
6239        .is_err());
6240        assert!(MergeKind::metas_can_merge(
6241            &invoke_context,
6242            &metas_with_lockup2,
6243            &metas_with_lockup1,
6244            Some(&clock)
6245        )
6246        .is_err());
6247
6248        let clock = Clock {
6249            epoch: lockup2_epoch + 1,
6250            unix_timestamp: lockup2_timestamp + 1,
6251            ..Clock::default()
6252        };
6253
6254        // Mismatched lockups succeed when both expired
6255        assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6256        assert!(MergeKind::metas_can_merge(
6257            &invoke_context,
6258            &metas_with_lockup1,
6259            &metas_with_lockup2,
6260            Some(&clock)
6261        )
6262        .is_ok());
6263        assert!(MergeKind::metas_can_merge(
6264            &invoke_context,
6265            &metas_with_lockup2,
6266            &metas_with_lockup1,
6267            Some(&clock)
6268        )
6269        .is_ok());
6270    }
6271
6272    #[test]
6273    fn test_merge_kind_get_if_mergeable() {
6274        let authority_pubkey = Pubkey::new_unique();
6275        let initial_carats = 4242424242;
6276        let rent = Rent::default();
6277        let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
6278        let stake_carats = rent_exempt_reserve + initial_carats;
6279
6280        let meta = Meta {
6281            rent_exempt_reserve,
6282            ..Meta::auto(&authority_pubkey)
6283        };
6284        let stake_account = AccountSharedData::new_ref_data_with_space(
6285            stake_carats,
6286            &StakeState::Uninitialized,
6287            std::mem::size_of::<StakeState>(),
6288            &id(),
6289        )
6290        .expect("stake_account");
6291        let stake_keyed_account = KeyedAccount::new(&authority_pubkey, true, &stake_account);
6292        let mut clock = Clock::default();
6293        let mut stake_history = StakeHistory::default();
6294        let invoke_context = MockInvokeContext::new(vec![]);
6295
6296        // Uninitialized state fails
6297        assert_eq!(
6298            MergeKind::get_if_mergeable(
6299                &invoke_context,
6300                &stake_keyed_account,
6301                &clock,
6302                &stake_history
6303            )
6304            .unwrap_err(),
6305            InstructionError::InvalidAccountData
6306        );
6307
6308        // RewardsPool state fails
6309        stake_keyed_account
6310            .set_state(&StakeState::RewardsPool)
6311            .unwrap();
6312        assert_eq!(
6313            MergeKind::get_if_mergeable(
6314                &invoke_context,
6315                &stake_keyed_account,
6316                &clock,
6317                &stake_history
6318            )
6319            .unwrap_err(),
6320            InstructionError::InvalidAccountData
6321        );
6322
6323        // Initialized state succeeds
6324        stake_keyed_account
6325            .set_state(&StakeState::Initialized(meta))
6326            .unwrap();
6327        assert_eq!(
6328            MergeKind::get_if_mergeable(
6329                &invoke_context,
6330                &stake_keyed_account,
6331                &clock,
6332                &stake_history
6333            )
6334            .unwrap(),
6335            MergeKind::Inactive(meta, stake_carats)
6336        );
6337
6338        clock.epoch = 0;
6339        let mut effective = 2 * initial_carats;
6340        let mut activating = 0;
6341        let mut deactivating = 0;
6342        stake_history.add(
6343            clock.epoch,
6344            StakeHistoryEntry {
6345                effective,
6346                activating,
6347                deactivating,
6348            },
6349        );
6350
6351        clock.epoch += 1;
6352        activating = initial_carats;
6353        stake_history.add(
6354            clock.epoch,
6355            StakeHistoryEntry {
6356                effective,
6357                activating,
6358                deactivating,
6359            },
6360        );
6361
6362        let stake = Stake {
6363            delegation: Delegation {
6364                stake: initial_carats,
6365                activation_epoch: 1,
6366                deactivation_epoch: 5,
6367                ..Delegation::default()
6368            },
6369            ..Stake::default()
6370        };
6371        stake_keyed_account
6372            .set_state(&StakeState::Stake(meta, stake))
6373            .unwrap();
6374        // activation_epoch succeeds
6375        assert_eq!(
6376            MergeKind::get_if_mergeable(
6377                &invoke_context,
6378                &stake_keyed_account,
6379                &clock,
6380                &stake_history
6381            )
6382            .unwrap(),
6383            MergeKind::ActivationEpoch(meta, stake),
6384        );
6385
6386        // all paritially activated, transient epochs fail
6387        loop {
6388            clock.epoch += 1;
6389            let delta =
6390                activating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
6391            effective += delta;
6392            activating -= delta;
6393            stake_history.add(
6394                clock.epoch,
6395                StakeHistoryEntry {
6396                    effective,
6397                    activating,
6398                    deactivating,
6399                },
6400            );
6401            if activating == 0 {
6402                break;
6403            }
6404            assert_eq!(
6405                MergeKind::get_if_mergeable(
6406                    &invoke_context,
6407                    &stake_keyed_account,
6408                    &clock,
6409                    &stake_history
6410                )
6411                .unwrap_err(),
6412                InstructionError::from(StakeError::MergeTransientStake),
6413            );
6414        }
6415
6416        // all epochs for which we're fully active succeed
6417        while clock.epoch < stake.delegation.deactivation_epoch - 1 {
6418            clock.epoch += 1;
6419            stake_history.add(
6420                clock.epoch,
6421                StakeHistoryEntry {
6422                    effective,
6423                    activating,
6424                    deactivating,
6425                },
6426            );
6427            assert_eq!(
6428                MergeKind::get_if_mergeable(
6429                    &invoke_context,
6430                    &stake_keyed_account,
6431                    &clock,
6432                    &stake_history
6433                )
6434                .unwrap(),
6435                MergeKind::FullyActive(meta, stake),
6436            );
6437        }
6438
6439        clock.epoch += 1;
6440        deactivating = stake.delegation.stake;
6441        stake_history.add(
6442            clock.epoch,
6443            StakeHistoryEntry {
6444                effective,
6445                activating,
6446                deactivating,
6447            },
6448        );
6449        // deactivation epoch fails, fully transient/deactivating
6450        assert_eq!(
6451            MergeKind::get_if_mergeable(
6452                &invoke_context,
6453                &stake_keyed_account,
6454                &clock,
6455                &stake_history
6456            )
6457            .unwrap_err(),
6458            InstructionError::from(StakeError::MergeTransientStake),
6459        );
6460
6461        // all transient, deactivating epochs fail
6462        loop {
6463            clock.epoch += 1;
6464            let delta =
6465                deactivating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
6466            effective -= delta;
6467            deactivating -= delta;
6468            stake_history.add(
6469                clock.epoch,
6470                StakeHistoryEntry {
6471                    effective,
6472                    activating,
6473                    deactivating,
6474                },
6475            );
6476            if deactivating == 0 {
6477                break;
6478            }
6479            assert_eq!(
6480                MergeKind::get_if_mergeable(
6481                    &invoke_context,
6482                    &stake_keyed_account,
6483                    &clock,
6484                    &stake_history
6485                )
6486                .unwrap_err(),
6487                InstructionError::from(StakeError::MergeTransientStake),
6488            );
6489        }
6490
6491        // first fully-deactivated epoch succeeds
6492        assert_eq!(
6493            MergeKind::get_if_mergeable(
6494                &invoke_context,
6495                &stake_keyed_account,
6496                &clock,
6497                &stake_history
6498            )
6499            .unwrap(),
6500            MergeKind::Inactive(meta, stake_carats),
6501        );
6502    }
6503
6504    #[test]
6505    fn test_merge_kind_merge() {
6506        let carats = 424242;
6507        let meta = Meta {
6508            rent_exempt_reserve: 42,
6509            ..Meta::default()
6510        };
6511        let stake = Stake {
6512            delegation: Delegation {
6513                stake: 4242,
6514                ..Delegation::default()
6515            },
6516            ..Stake::default()
6517        };
6518        let inactive = MergeKind::Inactive(Meta::default(), carats);
6519        let activation_epoch = MergeKind::ActivationEpoch(meta, stake);
6520        let fully_active = MergeKind::FullyActive(meta, stake);
6521        let invoke_context = MockInvokeContext::new(vec![]);
6522
6523        assert_eq!(
6524            inactive
6525                .clone()
6526                .merge(&invoke_context, inactive.clone(), None)
6527                .unwrap(),
6528            None
6529        );
6530        assert_eq!(
6531            inactive
6532                .clone()
6533                .merge(&invoke_context, activation_epoch.clone(), None)
6534                .unwrap(),
6535            None
6536        );
6537        assert!(inactive
6538            .clone()
6539            .merge(&invoke_context, fully_active.clone(), None)
6540            .is_err());
6541        assert!(activation_epoch
6542            .clone()
6543            .merge(&invoke_context, fully_active.clone(), None)
6544            .is_err());
6545        assert!(fully_active
6546            .clone()
6547            .merge(&invoke_context, inactive.clone(), None)
6548            .is_err());
6549        assert!(fully_active
6550            .clone()
6551            .merge(&invoke_context, activation_epoch.clone(), None)
6552            .is_err());
6553
6554        let new_state = activation_epoch
6555            .clone()
6556            .merge(&invoke_context, inactive, None)
6557            .unwrap()
6558            .unwrap();
6559        let delegation = new_state.delegation().unwrap();
6560        assert_eq!(delegation.stake, stake.delegation.stake + carats);
6561
6562        let new_state = activation_epoch
6563            .clone()
6564            .merge(&invoke_context, activation_epoch, None)
6565            .unwrap()
6566            .unwrap();
6567        let delegation = new_state.delegation().unwrap();
6568        assert_eq!(
6569            delegation.stake,
6570            2 * stake.delegation.stake + meta.rent_exempt_reserve
6571        );
6572
6573        let new_state = fully_active
6574            .clone()
6575            .merge(&invoke_context, fully_active, None)
6576            .unwrap()
6577            .unwrap();
6578        let delegation = new_state.delegation().unwrap();
6579        assert_eq!(delegation.stake, 2 * stake.delegation.stake);
6580    }
6581
6582    #[test]
6583    fn test_active_stake_merge() {
6584        let delegation_a = 4_242_424_242u64;
6585        let delegation_b = 6_200_000_000u64;
6586        let credits_a = 124_521_000u64;
6587        let rent_exempt_reserve = 227_000_000u64;
6588        let meta = Meta {
6589            rent_exempt_reserve,
6590            ..Meta::default()
6591        };
6592        let stake_a = Stake {
6593            delegation: Delegation {
6594                stake: delegation_a,
6595                ..Delegation::default()
6596            },
6597            credits_observed: credits_a,
6598        };
6599        let stake_b = Stake {
6600            delegation: Delegation {
6601                stake: delegation_b,
6602                ..Delegation::default()
6603            },
6604            credits_observed: credits_a,
6605        };
6606
6607        let invoke_context = MockInvokeContext::new(vec![]);
6608
6609        // activating stake merge, match credits observed
6610        let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a);
6611        let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b);
6612        let new_stake = activation_epoch_a
6613            .merge(&invoke_context, activation_epoch_b, None)
6614            .unwrap()
6615            .unwrap()
6616            .stake()
6617            .unwrap();
6618        assert_eq!(new_stake.credits_observed, credits_a);
6619        assert_eq!(
6620            new_stake.delegation.stake,
6621            delegation_a + delegation_b + rent_exempt_reserve
6622        );
6623
6624        // active stake merge, match credits observed
6625        let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6626        let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6627        let new_stake = fully_active_a
6628            .merge(&invoke_context, fully_active_b, None)
6629            .unwrap()
6630            .unwrap()
6631            .stake()
6632            .unwrap();
6633        assert_eq!(new_stake.credits_observed, credits_a);
6634        assert_eq!(new_stake.delegation.stake, delegation_a + delegation_b);
6635
6636        // activating stake merge, unmatched credits observed
6637        let credits_b = 125_124_521u64;
6638        let stake_b = Stake {
6639            delegation: Delegation {
6640                stake: delegation_b,
6641                ..Delegation::default()
6642            },
6643            credits_observed: credits_b,
6644        };
6645        let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a);
6646        let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b);
6647        let new_stake = activation_epoch_a
6648            .merge(&invoke_context, activation_epoch_b, None)
6649            .unwrap()
6650            .unwrap()
6651            .stake()
6652            .unwrap();
6653        assert_eq!(
6654            new_stake.credits_observed,
6655            (credits_a * delegation_a + credits_b * (delegation_b + rent_exempt_reserve))
6656                / (delegation_a + delegation_b + rent_exempt_reserve)
6657                + 1
6658        );
6659        assert_eq!(
6660            new_stake.delegation.stake,
6661            delegation_a + delegation_b + rent_exempt_reserve
6662        );
6663
6664        // active stake merge, unmatched credits observed
6665        let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6666        let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6667        let new_stake = fully_active_a
6668            .merge(&invoke_context, fully_active_b, None)
6669            .unwrap()
6670            .unwrap()
6671            .stake()
6672            .unwrap();
6673        assert_eq!(
6674            new_stake.credits_observed,
6675            (credits_a * delegation_a + credits_b * delegation_b) / (delegation_a + delegation_b)
6676                + 1
6677        );
6678        assert_eq!(new_stake.delegation.stake, delegation_a + delegation_b);
6679
6680        // active stake merge, unmatched credits observed, no need to ceiling the calculation
6681        let delegation = 1_000_000u64;
6682        let credits_a = 200_000_000u64;
6683        let credits_b = 100_000_000u64;
6684        let rent_exempt_reserve = 227_000_000u64;
6685        let meta = Meta {
6686            rent_exempt_reserve,
6687            ..Meta::default()
6688        };
6689        let stake_a = Stake {
6690            delegation: Delegation {
6691                stake: delegation,
6692                ..Delegation::default()
6693            },
6694            credits_observed: credits_a,
6695        };
6696        let stake_b = Stake {
6697            delegation: Delegation {
6698                stake: delegation,
6699                ..Delegation::default()
6700            },
6701            credits_observed: credits_b,
6702        };
6703        let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6704        let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6705        let new_stake = fully_active_a
6706            .merge(&invoke_context, fully_active_b, None)
6707            .unwrap()
6708            .unwrap()
6709            .stake()
6710            .unwrap();
6711        assert_eq!(
6712            new_stake.credits_observed,
6713            (credits_a * delegation + credits_b * delegation) / (delegation + delegation)
6714        );
6715        assert_eq!(new_stake.delegation.stake, delegation * 2);
6716    }
6717
6718    prop_compose! {
6719        pub fn sum_within(max: u64)(total in 1..max)
6720            (intermediate in 1..total, total in Just(total))
6721            -> (u64, u64) {
6722                (intermediate, total - intermediate)
6723        }
6724    }
6725
6726    proptest! {
6727        #[test]
6728        fn test_stake_weighted_credits_observed(
6729            (credits_a, credits_b) in sum_within(u64::MAX),
6730            (delegation_a, delegation_b) in sum_within(u64::MAX),
6731        ) {
6732            let stake = Stake {
6733                delegation: Delegation {
6734                    stake: delegation_a,
6735                    ..Delegation::default()
6736                },
6737                credits_observed: credits_a
6738            };
6739            let credits_observed = stake_weighted_credits_observed(
6740                &stake,
6741                delegation_b,
6742                credits_b,
6743            ).unwrap();
6744
6745            // calculated credits observed should always be between the credits of a and b
6746            if credits_a < credits_b {
6747                assert!(credits_a < credits_observed);
6748                assert!(credits_observed <= credits_b);
6749            } else {
6750                assert!(credits_b <= credits_observed);
6751                assert!(credits_observed <= credits_a);
6752            }
6753
6754            // the difference of the combined weighted credits and the separate weighted credits
6755            // should be 1 or 0
6756            let weighted_credits_total = credits_observed as u128 * (delegation_a + delegation_b) as u128;
6757            let weighted_credits_a = credits_a as u128 * delegation_a as u128;
6758            let weighted_credits_b = credits_b as u128 * delegation_b as u128;
6759            let raw_diff = weighted_credits_total - (weighted_credits_a + weighted_credits_b);
6760            let credits_observed_diff = raw_diff / (delegation_a + delegation_b) as u128;
6761            assert!(credits_observed_diff <= 1);
6762        }
6763    }
6764}