solana_stake_program/
processor.rs

1use {
2    crate::{helpers::*, id, PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH},
3    solana_account_info::{next_account_info, AccountInfo},
4    solana_clock::Clock,
5    solana_cpi::set_return_data,
6    solana_msg::msg,
7    solana_program_error::{ProgramError, ProgramResult},
8    solana_pubkey::Pubkey,
9    solana_rent::Rent,
10    solana_stake_interface::{
11        error::StakeError,
12        instruction::{
13            AuthorizeCheckedWithSeedArgs, AuthorizeWithSeedArgs, LockupArgs, LockupCheckedArgs,
14            StakeInstruction,
15        },
16        stake_flags::StakeFlags,
17        state::{Authorized, Lockup, Meta, StakeAuthorize, StakeStateV2},
18        sysvar::stake_history::StakeHistorySysvar,
19        tools::{acceptable_reference_epoch_credits, eligible_for_deactivate_delinquent},
20    },
21    solana_sysvar::{epoch_rewards::EpochRewards, Sysvar, SysvarSerialize},
22    solana_vote_interface::{program as solana_vote_program, state::VoteStateV4},
23    std::{collections::HashSet, mem::MaybeUninit},
24};
25
26fn get_vote_state(vote_account_info: &AccountInfo) -> Result<Box<VoteStateV4>, ProgramError> {
27    if *vote_account_info.owner != solana_vote_program::id() {
28        return Err(ProgramError::IncorrectProgramId);
29    }
30
31    let mut vote_state = Box::new(MaybeUninit::uninit());
32    VoteStateV4::deserialize_into_uninit(
33        &vote_account_info.try_borrow_data()?,
34        vote_state.as_mut(),
35        vote_account_info.key,
36    )
37    .map_err(|_| ProgramError::InvalidAccountData)?;
38    let vote_state = unsafe { vote_state.assume_init() };
39
40    Ok(vote_state)
41}
42
43fn get_stake_state(stake_account_info: &AccountInfo) -> Result<StakeStateV2, ProgramError> {
44    if *stake_account_info.owner != id() {
45        return Err(ProgramError::InvalidAccountOwner);
46    }
47
48    stake_account_info
49        .deserialize_data()
50        .map_err(|_| ProgramError::InvalidAccountData)
51}
52
53fn set_stake_state(stake_account_info: &AccountInfo, new_state: &StakeStateV2) -> ProgramResult {
54    let serialized_size =
55        bincode::serialized_size(new_state).map_err(|_| ProgramError::InvalidAccountData)?;
56    if serialized_size > stake_account_info.data_len() as u64 {
57        return Err(ProgramError::AccountDataTooSmall);
58    }
59
60    bincode::serialize_into(&mut stake_account_info.data.borrow_mut()[..], new_state)
61        .map_err(|_| ProgramError::InvalidAccountData)
62}
63
64// dont call this "move" because we have an instruction MoveLamports
65fn relocate_lamports(
66    source_account_info: &AccountInfo,
67    destination_account_info: &AccountInfo,
68    lamports: u64,
69) -> ProgramResult {
70    {
71        let mut source_lamports = source_account_info.try_borrow_mut_lamports()?;
72        **source_lamports = source_lamports
73            .checked_sub(lamports)
74            .ok_or(ProgramError::InsufficientFunds)?;
75    }
76
77    {
78        let mut destination_lamports = destination_account_info.try_borrow_mut_lamports()?;
79        **destination_lamports = destination_lamports
80            .checked_add(lamports)
81            .ok_or(ProgramError::ArithmeticOverflow)?;
82    }
83
84    Ok(())
85}
86
87// almost all native stake program processors accumulate every account signer
88// they then defer all signer validation to functions on Meta or Authorized
89// this results in an instruction interface that is much looser than the one documented
90// to avoid breaking backwards compatibility, we do the same here
91// in the future, we may decide to tighten the interface and break badly formed transactions
92fn collect_signers(accounts: &[AccountInfo]) -> HashSet<Pubkey> {
93    let mut signers = HashSet::new();
94
95    for account in accounts {
96        if account.is_signer {
97            signers.insert(*account.key);
98        }
99    }
100
101    signers
102}
103
104// MoveStake, MoveLamports, Withdraw, and AuthorizeWithSeed assemble signers explicitly
105fn collect_signers_checked<'a>(
106    authority_info: Option<&'a AccountInfo>,
107    custodian_info: Option<&'a AccountInfo>,
108) -> Result<(HashSet<Pubkey>, Option<&'a Pubkey>), ProgramError> {
109    let mut signers = HashSet::new();
110
111    if let Some(authority_info) = authority_info {
112        if authority_info.is_signer {
113            signers.insert(*authority_info.key);
114        } else {
115            return Err(ProgramError::MissingRequiredSignature);
116        }
117    }
118
119    let custodian = if let Some(custodian_info) = custodian_info {
120        if custodian_info.is_signer {
121            signers.insert(*custodian_info.key);
122            Some(custodian_info.key)
123        } else {
124            return Err(ProgramError::MissingRequiredSignature);
125        }
126    } else {
127        None
128    };
129
130    Ok((signers, custodian))
131}
132
133fn do_initialize(
134    stake_account_info: &AccountInfo,
135    authorized: Authorized,
136    lockup: Lockup,
137    rent: &Rent,
138) -> ProgramResult {
139    if stake_account_info.data_len() != StakeStateV2::size_of() {
140        return Err(ProgramError::InvalidAccountData);
141    }
142
143    if let StakeStateV2::Uninitialized = get_stake_state(stake_account_info)? {
144        let rent_exempt_reserve = rent.minimum_balance(stake_account_info.data_len());
145        if stake_account_info.lamports() >= rent_exempt_reserve {
146            let stake_state = StakeStateV2::Initialized(Meta {
147                rent_exempt_reserve,
148                authorized,
149                lockup,
150            });
151
152            set_stake_state(stake_account_info, &stake_state)
153        } else {
154            Err(ProgramError::InsufficientFunds)
155        }
156    } else {
157        Err(ProgramError::InvalidAccountData)
158    }
159}
160
161fn do_authorize(
162    stake_account_info: &AccountInfo,
163    signers: &HashSet<Pubkey>,
164    new_authority: &Pubkey,
165    authority_type: StakeAuthorize,
166    custodian: Option<&Pubkey>,
167    clock: &Clock,
168) -> ProgramResult {
169    match get_stake_state(stake_account_info)? {
170        StakeStateV2::Initialized(mut meta) => {
171            meta.authorized
172                .authorize(
173                    signers,
174                    new_authority,
175                    authority_type,
176                    Some((&meta.lockup, clock, custodian)),
177                )
178                .map_err(to_program_error)?;
179
180            set_stake_state(stake_account_info, &StakeStateV2::Initialized(meta))
181        }
182        StakeStateV2::Stake(mut meta, stake, stake_flags) => {
183            meta.authorized
184                .authorize(
185                    signers,
186                    new_authority,
187                    authority_type,
188                    Some((&meta.lockup, clock, custodian)),
189                )
190                .map_err(to_program_error)?;
191
192            set_stake_state(
193                stake_account_info,
194                &StakeStateV2::Stake(meta, stake, stake_flags),
195            )
196        }
197        _ => Err(ProgramError::InvalidAccountData),
198    }
199}
200
201fn do_set_lockup(
202    stake_account_info: &AccountInfo,
203    signers: &HashSet<Pubkey>,
204    lockup: &LockupArgs,
205    clock: &Clock,
206) -> ProgramResult {
207    match get_stake_state(stake_account_info)? {
208        StakeStateV2::Initialized(mut meta) => {
209            meta.set_lockup(lockup, signers, clock)
210                .map_err(to_program_error)?;
211
212            set_stake_state(stake_account_info, &StakeStateV2::Initialized(meta))
213        }
214        StakeStateV2::Stake(mut meta, stake, stake_flags) => {
215            meta.set_lockup(lockup, signers, clock)
216                .map_err(to_program_error)?;
217
218            set_stake_state(
219                stake_account_info,
220                &StakeStateV2::Stake(meta, stake, stake_flags),
221            )
222        }
223        _ => Err(ProgramError::InvalidAccountData),
224    }
225}
226
227fn move_stake_or_lamports_shared_checks(
228    source_stake_account_info: &AccountInfo,
229    lamports: u64,
230    destination_stake_account_info: &AccountInfo,
231    stake_authority_info: &AccountInfo,
232) -> Result<(MergeKind, MergeKind), ProgramError> {
233    // authority must sign
234    let (signers, _) = collect_signers_checked(Some(stake_authority_info), None)?;
235
236    // confirm not the same account
237    if *source_stake_account_info.key == *destination_stake_account_info.key {
238        return Err(ProgramError::InvalidInstructionData);
239    }
240
241    // source and destination must be writable
242    // runtime guards against unowned writes, but MoveStake and MoveLamports are defined by SIMD
243    // we check explicitly to avoid any possibility of a successful no-op that never attempts to write
244    if !source_stake_account_info.is_writable || !destination_stake_account_info.is_writable {
245        return Err(ProgramError::InvalidInstructionData);
246    }
247
248    // must move something
249    if lamports == 0 {
250        return Err(ProgramError::InvalidArgument);
251    }
252
253    let clock = Clock::get()?;
254    let stake_history = StakeHistorySysvar(clock.epoch);
255
256    // get_if_mergeable ensures accounts are not partly activated or in any form of deactivating
257    // we still need to exclude activating state ourselves
258    let source_merge_kind = MergeKind::get_if_mergeable(
259        &get_stake_state(source_stake_account_info)?,
260        source_stake_account_info.lamports(),
261        &clock,
262        &stake_history,
263    )?;
264
265    // Authorized staker is allowed to move stake
266    source_merge_kind
267        .meta()
268        .authorized
269        .check(&signers, StakeAuthorize::Staker)
270        .map_err(to_program_error)?;
271
272    // same transient assurance as with source
273    let destination_merge_kind = MergeKind::get_if_mergeable(
274        &get_stake_state(destination_stake_account_info)?,
275        destination_stake_account_info.lamports(),
276        &clock,
277        &stake_history,
278    )?;
279
280    // ensure all authorities match and lockups match if lockup is in force
281    MergeKind::metas_can_merge(
282        source_merge_kind.meta(),
283        destination_merge_kind.meta(),
284        &clock,
285    )?;
286
287    Ok((source_merge_kind, destination_merge_kind))
288}
289
290// NOTE our usage of the accounts iter is idiosyncratic, in imitation of the native stake program
291// native stake typically accumulates all signers from the accounts array indiscriminately
292// each instruction processor also asserts a required number of instruction accounts
293// but this is extremely ad hoc, essentially allowing any account to act as a signing authority
294// when lengths are asserted in setup, accounts are retrieved via hardcoded index from InstructionContext
295// but after control is passed to main processing functions, they are pulled from the TransactionContext
296//
297// we aim to implement this behavior exactly, such that both programs are consensus compatible:
298// * all transactions that would fail on one program also fail on the other
299// * all transactions that would succeed on one program also succeed on the other
300// * for successful transactions, all account state transitions are identical
301// error codes and log output may differ
302//
303// this is not strictly necessary, since the switchover will be feature-gated. so this is not a security issue
304// mostly its so no one can blame the bpf switchover for breaking their usecase, even pathological ones
305//
306// in service to this end, all accounts iters are commented with how the native program uses them
307// for accounts that should always, or almost always, exist, which the native program does not assert...
308// ...we leave a commented-out `next_account_info()` call, to aid in a future refactor
309// after the bpf switchover ships, we may update to strictly assert these accounts exist
310pub struct Processor {}
311impl Processor {
312    fn process_initialize(
313        accounts: &[AccountInfo],
314        authorized: Authorized,
315        lockup: Lockup,
316    ) -> ProgramResult {
317        let account_info_iter = &mut accounts.iter();
318
319        // native asserts: 2 accounts (1 sysvar)
320        let stake_account_info = next_account_info(account_info_iter)?;
321        let rent_info = next_account_info(account_info_iter)?;
322
323        let rent = &Rent::from_account_info(rent_info)?;
324
325        // `get_stake_state()` is called unconditionally, which checks owner
326        do_initialize(stake_account_info, authorized, lockup, rent)?;
327
328        Ok(())
329    }
330
331    fn process_authorize(
332        accounts: &[AccountInfo],
333        new_authority: Pubkey,
334        authority_type: StakeAuthorize,
335    ) -> ProgramResult {
336        let signers = collect_signers(accounts);
337        let account_info_iter = &mut accounts.iter();
338
339        // native asserts: 3 accounts (1 sysvar)
340        let stake_account_info = next_account_info(account_info_iter)?;
341        let clock_info = next_account_info(account_info_iter)?;
342        let _stake_or_withdraw_authority_info = next_account_info(account_info_iter)?;
343
344        // other accounts
345        let option_lockup_authority_info = next_account_info(account_info_iter).ok();
346
347        let clock = &Clock::from_account_info(clock_info)?;
348
349        let custodian = option_lockup_authority_info
350            .filter(|a| a.is_signer)
351            .map(|a| a.key);
352
353        // `get_stake_state()` is called unconditionally, which checks owner
354        do_authorize(
355            stake_account_info,
356            &signers,
357            &new_authority,
358            authority_type,
359            custodian,
360            clock,
361        )?;
362
363        Ok(())
364    }
365
366    fn process_delegate(accounts: &[AccountInfo]) -> ProgramResult {
367        let signers = collect_signers(accounts);
368        let account_info_iter = &mut accounts.iter();
369
370        // native asserts: 5 accounts (2 sysvars + stake config)
371        let stake_account_info = next_account_info(account_info_iter)?;
372        let vote_account_info = next_account_info(account_info_iter)?;
373        let clock_info = next_account_info(account_info_iter)?;
374        let _stake_history_info = next_account_info(account_info_iter)?;
375        let _stake_config_info = next_account_info(account_info_iter)?;
376
377        // other accounts
378        // let _stake_authority_info = next_account_info(account_info_iter);
379
380        let clock = &Clock::from_account_info(clock_info)?;
381        let stake_history = &StakeHistorySysvar(clock.epoch);
382
383        let vote_state = get_vote_state(vote_account_info)?;
384
385        match get_stake_state(stake_account_info)? {
386            StakeStateV2::Initialized(meta) => {
387                meta.authorized
388                    .check(&signers, StakeAuthorize::Staker)
389                    .map_err(to_program_error)?;
390
391                let ValidatedDelegatedInfo { stake_amount } =
392                    validate_delegated_amount(stake_account_info, &meta)?;
393
394                let stake = new_stake(
395                    stake_amount,
396                    vote_account_info.key,
397                    vote_state.credits(),
398                    clock.epoch,
399                );
400
401                set_stake_state(
402                    stake_account_info,
403                    &StakeStateV2::Stake(meta, stake, StakeFlags::empty()),
404                )
405            }
406            StakeStateV2::Stake(meta, mut stake, flags) => {
407                // Only the staker may (re)delegate
408                meta.authorized
409                    .check(&signers, StakeAuthorize::Staker)
410                    .map_err(to_program_error)?;
411
412                // Compute the maximum stake allowed to (re)delegate
413                let ValidatedDelegatedInfo { stake_amount } =
414                    validate_delegated_amount(stake_account_info, &meta)?;
415
416                // Get current activation status at this epoch
417                let effective_stake = stake.delegation.stake(
418                    clock.epoch,
419                    stake_history,
420                    PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
421                );
422
423                if effective_stake == 0 {
424                    // The stake has no effective voting power this epoch. This means it is either:
425                    //   1. Inactive (fully cooled down after a previous deactivation)
426                    //   2. Still activating (was delegated for the first time this epoch)
427                    stake = new_stake(
428                        stake_amount,
429                        vote_account_info.key,
430                        vote_state.credits(),
431                        clock.epoch,
432                    );
433                } else if clock.epoch == stake.delegation.deactivation_epoch
434                    && stake.delegation.voter_pubkey == *vote_account_info.key
435                {
436                    if stake_amount < stake.delegation.stake {
437                        return Err(StakeError::InsufficientDelegation.into());
438                    }
439                    stake.delegation.deactivation_epoch = u64::MAX;
440                } else {
441                    // Not a valid state for redelegation
442                    return Err(StakeError::TooSoonToRedelegate.into());
443                }
444
445                // Persist the updated stake state back to the account.
446                set_stake_state(stake_account_info, &StakeStateV2::Stake(meta, stake, flags))
447            }
448            _ => Err(ProgramError::InvalidAccountData),
449        }?;
450
451        Ok(())
452    }
453
454    fn process_split(accounts: &[AccountInfo], split_lamports: u64) -> ProgramResult {
455        let signers = collect_signers(accounts);
456        let account_info_iter = &mut accounts.iter();
457
458        // native asserts: 2 accounts
459        let source_stake_account_info = next_account_info(account_info_iter)?;
460        let destination_stake_account_info = next_account_info(account_info_iter)?;
461
462        // other accounts
463        // let _stake_authority_info = next_account_info(account_info_iter);
464
465        let clock = Clock::get()?;
466        let stake_history = &StakeHistorySysvar(clock.epoch);
467
468        let destination_data_len = destination_stake_account_info.data_len();
469        if destination_data_len != StakeStateV2::size_of() {
470            return Err(ProgramError::InvalidAccountData);
471        }
472
473        if let StakeStateV2::Uninitialized = get_stake_state(destination_stake_account_info)? {
474            // we can split into this
475        } else {
476            return Err(ProgramError::InvalidAccountData);
477        }
478
479        let source_lamport_balance = source_stake_account_info.lamports();
480        let destination_lamport_balance = destination_stake_account_info.lamports();
481
482        if split_lamports > source_lamport_balance {
483            return Err(ProgramError::InsufficientFunds);
484        }
485
486        match get_stake_state(source_stake_account_info)? {
487            StakeStateV2::Stake(source_meta, mut source_stake, stake_flags) => {
488                source_meta
489                    .authorized
490                    .check(&signers, StakeAuthorize::Staker)
491                    .map_err(to_program_error)?;
492
493                let minimum_delegation = crate::get_minimum_delegation();
494
495                let status = source_stake.delegation.stake_activating_and_deactivating(
496                    clock.epoch,
497                    stake_history,
498                    PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
499                );
500
501                let is_active = status.effective > 0;
502
503                // NOTE this function also internally summons Rent via syscall
504                let validated_split_info = validate_split_amount(
505                    source_lamport_balance,
506                    destination_lamport_balance,
507                    split_lamports,
508                    &source_meta,
509                    destination_data_len,
510                    minimum_delegation,
511                    is_active,
512                )?;
513
514                // split the stake, subtract rent_exempt_balance unless
515                // the destination account already has those lamports
516                // in place.
517                // this means that the new stake account will have a stake equivalent to
518                // lamports minus rent_exempt_reserve if it starts out with a zero balance
519                let (remaining_stake_delta, split_stake_amount) =
520                    if validated_split_info.source_remaining_balance == 0 {
521                        // If split amount equals the full source stake (as implied by 0
522                        // source_remaining_balance), the new split stake must equal the same
523                        // amount, regardless of any current lamport balance in the split account.
524                        // Since split accounts retain the state of their source account, this
525                        // prevents any magic activation of stake by prefunding the split account.
526                        //
527                        // The new split stake also needs to ignore any positive delta between the
528                        // original rent_exempt_reserve and the split_rent_exempt_reserve, in order
529                        // to prevent magic activation of stake by splitting between accounts of
530                        // different sizes.
531                        let remaining_stake_delta =
532                            split_lamports.saturating_sub(source_meta.rent_exempt_reserve);
533                        (remaining_stake_delta, remaining_stake_delta)
534                    } else {
535                        // Otherwise, the new split stake should reflect the entire split
536                        // requested, less any lamports needed to cover the
537                        // split_rent_exempt_reserve.
538                        if source_stake.delegation.stake.saturating_sub(split_lamports)
539                            < minimum_delegation
540                        {
541                            return Err(StakeError::InsufficientDelegation.into());
542                        }
543
544                        (
545                            split_lamports,
546                            split_lamports.saturating_sub(
547                                validated_split_info
548                                    .destination_rent_exempt_reserve
549                                    .saturating_sub(destination_lamport_balance),
550                            ),
551                        )
552                    };
553
554                if split_stake_amount < minimum_delegation {
555                    return Err(StakeError::InsufficientDelegation.into());
556                }
557
558                let destination_stake =
559                    source_stake.split(remaining_stake_delta, split_stake_amount)?;
560
561                let mut destination_meta = source_meta;
562                destination_meta.rent_exempt_reserve =
563                    validated_split_info.destination_rent_exempt_reserve;
564
565                set_stake_state(
566                    source_stake_account_info,
567                    &StakeStateV2::Stake(source_meta, source_stake, stake_flags),
568                )?;
569
570                set_stake_state(
571                    destination_stake_account_info,
572                    &StakeStateV2::Stake(destination_meta, destination_stake, stake_flags),
573                )?;
574            }
575            StakeStateV2::Initialized(source_meta) => {
576                source_meta
577                    .authorized
578                    .check(&signers, StakeAuthorize::Staker)
579                    .map_err(to_program_error)?;
580
581                // NOTE this function also internally summons Rent via syscall
582                let validated_split_info = validate_split_amount(
583                    source_lamport_balance,
584                    destination_lamport_balance,
585                    split_lamports,
586                    &source_meta,
587                    destination_data_len,
588                    0,     // additional_required_lamports
589                    false, // is_active
590                )?;
591
592                let mut destination_meta = source_meta;
593                destination_meta.rent_exempt_reserve =
594                    validated_split_info.destination_rent_exempt_reserve;
595
596                set_stake_state(
597                    destination_stake_account_info,
598                    &StakeStateV2::Initialized(destination_meta),
599                )?;
600            }
601            StakeStateV2::Uninitialized => {
602                if !source_stake_account_info.is_signer {
603                    return Err(ProgramError::MissingRequiredSignature);
604                }
605            }
606            _ => return Err(ProgramError::InvalidAccountData),
607        }
608
609        // Truncate state upon zero balance
610        if source_stake_account_info.key != destination_stake_account_info.key
611            && split_lamports == source_lamport_balance
612        {
613            source_stake_account_info.resize(0)?;
614        }
615
616        relocate_lamports(
617            source_stake_account_info,
618            destination_stake_account_info,
619            split_lamports,
620        )?;
621
622        Ok(())
623    }
624
625    fn process_withdraw(accounts: &[AccountInfo], withdraw_lamports: u64) -> ProgramResult {
626        let account_info_iter = &mut accounts.iter();
627
628        // native asserts: 5 accounts (2 sysvars)
629        let source_stake_account_info = next_account_info(account_info_iter)?;
630        let destination_info = next_account_info(account_info_iter)?;
631        let clock_info = next_account_info(account_info_iter)?;
632        let _stake_history_info = next_account_info(account_info_iter)?;
633        let withdraw_authority_info = next_account_info(account_info_iter)?;
634
635        // other accounts
636        let option_lockup_authority_info = next_account_info(account_info_iter).ok();
637
638        let clock = &Clock::from_account_info(clock_info)?;
639        let stake_history = &StakeHistorySysvar(clock.epoch);
640
641        if source_stake_account_info.key == destination_info.key {
642            return Err(ProgramError::InvalidArgument);
643        }
644
645        // this is somewhat subtle. for Initialized and Stake, there is a real authority
646        // but for Uninitialized, the source account is passed twice, and signed for
647        let (signers, custodian) =
648            collect_signers_checked(Some(withdraw_authority_info), option_lockup_authority_info)?;
649
650        let (lockup, reserve, is_staked) = match get_stake_state(source_stake_account_info) {
651            Ok(StakeStateV2::Stake(meta, stake, _stake_flag)) => {
652                meta.authorized
653                    .check(&signers, StakeAuthorize::Withdrawer)
654                    .map_err(to_program_error)?;
655                // if we have a deactivation epoch and we're in cooldown
656                let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
657                    stake.delegation.stake(
658                        clock.epoch,
659                        stake_history,
660                        PERPETUAL_NEW_WARMUP_COOLDOWN_RATE_EPOCH,
661                    )
662                } else {
663                    // Assume full stake if the stake account hasn't been
664                    //  de-activated, because in the future the exposed stake
665                    //  might be higher than stake.stake() due to warmup
666                    stake.delegation.stake
667                };
668
669                let staked_and_reserve = checked_add(staked, meta.rent_exempt_reserve)?;
670                (meta.lockup, staked_and_reserve, staked != 0)
671            }
672            Ok(StakeStateV2::Initialized(meta)) => {
673                meta.authorized
674                    .check(&signers, StakeAuthorize::Withdrawer)
675                    .map_err(to_program_error)?;
676                // stake accounts must have a balance >= rent_exempt_reserve
677                (meta.lockup, meta.rent_exempt_reserve, false)
678            }
679            Ok(StakeStateV2::Uninitialized) => {
680                if !signers.contains(source_stake_account_info.key) {
681                    return Err(ProgramError::MissingRequiredSignature);
682                }
683                (Lockup::default(), 0, false) // no lockup, no restrictions
684            }
685            Err(e)
686                if e == ProgramError::InvalidAccountData
687                    && source_stake_account_info.data_len() == 0 =>
688            {
689                if !signers.contains(source_stake_account_info.key) {
690                    return Err(ProgramError::MissingRequiredSignature);
691                }
692                (Lockup::default(), 0, false) // no lockup, no restrictions
693            }
694            Ok(StakeStateV2::RewardsPool) => return Err(ProgramError::InvalidAccountData),
695            Err(e) => return Err(e),
696        };
697
698        // verify that lockup has expired or that the withdrawal is signed by the
699        // custodian both epoch and unix_timestamp must have passed
700        if lockup.is_in_force(clock, custodian) {
701            return Err(StakeError::LockupInForce.into());
702        }
703
704        let stake_account_lamports = source_stake_account_info.lamports();
705        if withdraw_lamports == stake_account_lamports {
706            // if the stake is active, we mustn't allow the account to go away
707            if is_staked {
708                return Err(ProgramError::InsufficientFunds);
709            }
710
711            // Truncate state upon zero balance
712            source_stake_account_info.resize(0)?;
713        } else {
714            // a partial withdrawal must not deplete the reserve
715            let withdraw_lamports_and_reserve = checked_add(withdraw_lamports, reserve)?;
716            if withdraw_lamports_and_reserve > stake_account_lamports {
717                return Err(ProgramError::InsufficientFunds);
718            }
719        }
720
721        relocate_lamports(
722            source_stake_account_info,
723            destination_info,
724            withdraw_lamports,
725        )?;
726
727        Ok(())
728    }
729
730    fn process_deactivate(accounts: &[AccountInfo]) -> ProgramResult {
731        let signers = collect_signers(accounts);
732        let account_info_iter = &mut accounts.iter();
733
734        // native asserts: 2 accounts (1 sysvar)
735        let stake_account_info = next_account_info(account_info_iter)?;
736        let clock_info = next_account_info(account_info_iter)?;
737
738        // other accounts
739        // let _stake_authority_info = next_account_info(account_info_iter);
740
741        let clock = &Clock::from_account_info(clock_info)?;
742
743        match get_stake_state(stake_account_info)? {
744            StakeStateV2::Stake(meta, mut stake, stake_flags) => {
745                meta.authorized
746                    .check(&signers, StakeAuthorize::Staker)
747                    .map_err(to_program_error)?;
748
749                stake.deactivate(clock.epoch)?;
750
751                set_stake_state(
752                    stake_account_info,
753                    &StakeStateV2::Stake(meta, stake, stake_flags),
754                )
755            }
756            _ => Err(ProgramError::InvalidAccountData),
757        }?;
758
759        Ok(())
760    }
761
762    fn process_set_lockup(accounts: &[AccountInfo], lockup: LockupArgs) -> ProgramResult {
763        let signers = collect_signers(accounts);
764        let account_info_iter = &mut accounts.iter();
765
766        // native asserts: 1 account
767        let stake_account_info = next_account_info(account_info_iter)?;
768
769        // other accounts
770        // let _old_withdraw_or_lockup_authority_info = next_account_info(account_info_iter);
771
772        let clock = Clock::get()?;
773
774        // `get_stake_state()` is called unconditionally, which checks owner
775        do_set_lockup(stake_account_info, &signers, &lockup, &clock)?;
776
777        Ok(())
778    }
779
780    fn process_merge(accounts: &[AccountInfo]) -> ProgramResult {
781        let signers = collect_signers(accounts);
782        let account_info_iter = &mut accounts.iter();
783
784        // native asserts: 4 accounts (2 sysvars)
785        let destination_stake_account_info = next_account_info(account_info_iter)?;
786        let source_stake_account_info = next_account_info(account_info_iter)?;
787        let clock_info = next_account_info(account_info_iter)?;
788        let _stake_history_info = next_account_info(account_info_iter)?;
789
790        // other accounts
791        // let _stake_authority_info = next_account_info(account_info_iter);
792
793        let clock = &Clock::from_account_info(clock_info)?;
794        let stake_history = &StakeHistorySysvar(clock.epoch);
795
796        if source_stake_account_info.key == destination_stake_account_info.key {
797            return Err(ProgramError::InvalidArgument);
798        }
799
800        msg!("Checking if destination stake is mergeable");
801        let destination_merge_kind = MergeKind::get_if_mergeable(
802            &get_stake_state(destination_stake_account_info)?,
803            destination_stake_account_info.lamports(),
804            clock,
805            stake_history,
806        )?;
807
808        // Authorized staker is allowed to split/merge accounts
809        destination_merge_kind
810            .meta()
811            .authorized
812            .check(&signers, StakeAuthorize::Staker)
813            .map_err(|_| ProgramError::MissingRequiredSignature)?;
814
815        msg!("Checking if source stake is mergeable");
816        let source_merge_kind = MergeKind::get_if_mergeable(
817            &get_stake_state(source_stake_account_info)?,
818            source_stake_account_info.lamports(),
819            clock,
820            stake_history,
821        )?;
822
823        msg!("Merging stake accounts");
824        if let Some(merged_state) = destination_merge_kind.merge(source_merge_kind, clock)? {
825            set_stake_state(destination_stake_account_info, &merged_state)?;
826        }
827
828        // Source is about to be drained, truncate its state
829        source_stake_account_info.resize(0)?;
830
831        // Drain the source stake account
832        relocate_lamports(
833            source_stake_account_info,
834            destination_stake_account_info,
835            source_stake_account_info.lamports(),
836        )?;
837
838        Ok(())
839    }
840
841    fn process_authorize_with_seed(
842        accounts: &[AccountInfo],
843        authorize_args: AuthorizeWithSeedArgs,
844    ) -> ProgramResult {
845        let account_info_iter = &mut accounts.iter();
846
847        // native asserts: 3 accounts (1 sysvar)
848        let stake_account_info = next_account_info(account_info_iter)?;
849        let stake_or_withdraw_authority_base_info = next_account_info(account_info_iter)?;
850        let clock_info = next_account_info(account_info_iter)?;
851
852        // other accounts
853        let option_lockup_authority_info = next_account_info(account_info_iter).ok();
854
855        let clock = &Clock::from_account_info(clock_info)?;
856
857        let (mut signers, custodian) = collect_signers_checked(None, option_lockup_authority_info)?;
858
859        if stake_or_withdraw_authority_base_info.is_signer {
860            signers.insert(Pubkey::create_with_seed(
861                stake_or_withdraw_authority_base_info.key,
862                &authorize_args.authority_seed,
863                &authorize_args.authority_owner,
864            )?);
865        }
866
867        // `get_stake_state()` is called unconditionally, which checks owner
868        do_authorize(
869            stake_account_info,
870            &signers,
871            &authorize_args.new_authorized_pubkey,
872            authorize_args.stake_authorize,
873            custodian,
874            clock,
875        )?;
876
877        Ok(())
878    }
879
880    fn process_initialize_checked(accounts: &[AccountInfo]) -> ProgramResult {
881        let account_info_iter = &mut accounts.iter();
882
883        // native asserts: 4 accounts (1 sysvar)
884        let stake_account_info = next_account_info(account_info_iter)?;
885        let rent_info = next_account_info(account_info_iter)?;
886        let stake_authority_info = next_account_info(account_info_iter)?;
887        let withdraw_authority_info = next_account_info(account_info_iter)?;
888
889        let rent = &Rent::from_account_info(rent_info)?;
890
891        if !withdraw_authority_info.is_signer {
892            return Err(ProgramError::MissingRequiredSignature);
893        }
894
895        let authorized = Authorized {
896            staker: *stake_authority_info.key,
897            withdrawer: *withdraw_authority_info.key,
898        };
899
900        // `get_stake_state()` is called unconditionally, which checks owner
901        do_initialize(stake_account_info, authorized, Lockup::default(), rent)?;
902
903        Ok(())
904    }
905
906    fn process_authorize_checked(
907        accounts: &[AccountInfo],
908        authority_type: StakeAuthorize,
909    ) -> ProgramResult {
910        let signers = collect_signers(accounts);
911        let account_info_iter = &mut accounts.iter();
912
913        // native asserts: 4 accounts (1 sysvar)
914        let stake_account_info = next_account_info(account_info_iter)?;
915        let clock_info = next_account_info(account_info_iter)?;
916        let _old_stake_or_withdraw_authority_info = next_account_info(account_info_iter)?;
917        let new_stake_or_withdraw_authority_info = next_account_info(account_info_iter)?;
918
919        // other accounts
920        let option_lockup_authority_info = next_account_info(account_info_iter).ok();
921
922        let clock = &Clock::from_account_info(clock_info)?;
923
924        if !new_stake_or_withdraw_authority_info.is_signer {
925            return Err(ProgramError::MissingRequiredSignature);
926        }
927
928        let custodian = option_lockup_authority_info
929            .filter(|a| a.is_signer)
930            .map(|a| a.key);
931
932        // `get_stake_state()` is called unconditionally, which checks owner
933        do_authorize(
934            stake_account_info,
935            &signers,
936            new_stake_or_withdraw_authority_info.key,
937            authority_type,
938            custodian,
939            clock,
940        )?;
941
942        Ok(())
943    }
944
945    fn process_authorize_checked_with_seed(
946        accounts: &[AccountInfo],
947        authorize_args: AuthorizeCheckedWithSeedArgs,
948    ) -> ProgramResult {
949        let account_info_iter = &mut accounts.iter();
950
951        // native asserts: 4 accounts (1 sysvar)
952        let stake_account_info = next_account_info(account_info_iter)?;
953        let old_stake_or_withdraw_authority_base_info = next_account_info(account_info_iter)?;
954        let clock_info = next_account_info(account_info_iter)?;
955        let new_stake_or_withdraw_authority_info = next_account_info(account_info_iter)?;
956
957        // other accounts
958        let option_lockup_authority_info = next_account_info(account_info_iter).ok();
959
960        let clock = &Clock::from_account_info(clock_info)?;
961
962        let (mut signers, custodian) = collect_signers_checked(
963            Some(new_stake_or_withdraw_authority_info),
964            option_lockup_authority_info,
965        )?;
966
967        if old_stake_or_withdraw_authority_base_info.is_signer {
968            signers.insert(Pubkey::create_with_seed(
969                old_stake_or_withdraw_authority_base_info.key,
970                &authorize_args.authority_seed,
971                &authorize_args.authority_owner,
972            )?);
973        }
974
975        // `get_stake_state()` is called unconditionally, which checks owner
976        do_authorize(
977            stake_account_info,
978            &signers,
979            new_stake_or_withdraw_authority_info.key,
980            authorize_args.stake_authorize,
981            custodian,
982            clock,
983        )?;
984
985        Ok(())
986    }
987
988    fn process_set_lockup_checked(
989        accounts: &[AccountInfo],
990        lockup_checked: LockupCheckedArgs,
991    ) -> ProgramResult {
992        let signers = collect_signers(accounts);
993        let account_info_iter = &mut accounts.iter();
994
995        // native asserts: 1 account
996        let stake_account_info = next_account_info(account_info_iter)?;
997
998        // other accounts
999        let _old_withdraw_or_lockup_authority_info = next_account_info(account_info_iter);
1000        let option_new_lockup_authority_info = next_account_info(account_info_iter).ok();
1001
1002        let clock = Clock::get()?;
1003
1004        let custodian = match option_new_lockup_authority_info {
1005            Some(new_lockup_authority_info) if new_lockup_authority_info.is_signer => {
1006                Some(new_lockup_authority_info.key)
1007            }
1008            Some(_) => return Err(ProgramError::MissingRequiredSignature),
1009            None => None,
1010        };
1011
1012        let lockup = LockupArgs {
1013            unix_timestamp: lockup_checked.unix_timestamp,
1014            epoch: lockup_checked.epoch,
1015            custodian: custodian.copied(),
1016        };
1017
1018        // `get_stake_state()` is called unconditionally, which checks owner
1019        do_set_lockup(stake_account_info, &signers, &lockup, &clock)?;
1020
1021        Ok(())
1022    }
1023
1024    fn process_deactivate_delinquent(accounts: &[AccountInfo]) -> ProgramResult {
1025        let account_info_iter = &mut accounts.iter();
1026
1027        // native asserts: 3 accounts
1028        let stake_account_info = next_account_info(account_info_iter)?;
1029        let delinquent_vote_account_info = next_account_info(account_info_iter)?;
1030        let reference_vote_account_info = next_account_info(account_info_iter)?;
1031
1032        let clock = Clock::get()?;
1033
1034        let delinquent_vote_state = get_vote_state(delinquent_vote_account_info)?;
1035        let reference_vote_state = get_vote_state(reference_vote_account_info)?;
1036
1037        if !acceptable_reference_epoch_credits(&reference_vote_state.epoch_credits, clock.epoch) {
1038            return Err(StakeError::InsufficientReferenceVotes.into());
1039        }
1040
1041        if let StakeStateV2::Stake(meta, mut stake, stake_flags) =
1042            get_stake_state(stake_account_info)?
1043        {
1044            if stake.delegation.voter_pubkey != *delinquent_vote_account_info.key {
1045                return Err(StakeError::VoteAddressMismatch.into());
1046            }
1047
1048            // Deactivate the stake account if its delegated vote account has never voted or
1049            // has not voted in the last
1050            // `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION`
1051            if eligible_for_deactivate_delinquent(&delinquent_vote_state.epoch_credits, clock.epoch)
1052            {
1053                stake.deactivate(clock.epoch)?;
1054
1055                set_stake_state(
1056                    stake_account_info,
1057                    &StakeStateV2::Stake(meta, stake, stake_flags),
1058                )
1059            } else {
1060                Err(StakeError::MinimumDelinquentEpochsForDeactivationNotMet.into())
1061            }
1062        } else {
1063            Err(ProgramError::InvalidAccountData)
1064        }?;
1065
1066        Ok(())
1067    }
1068
1069    fn process_move_stake(accounts: &[AccountInfo], lamports: u64) -> ProgramResult {
1070        let account_info_iter = &mut accounts.iter();
1071
1072        // native asserts: 3 accounts
1073        let source_stake_account_info = next_account_info(account_info_iter)?;
1074        let destination_stake_account_info = next_account_info(account_info_iter)?;
1075        let stake_authority_info = next_account_info(account_info_iter)?;
1076
1077        let (source_merge_kind, destination_merge_kind) = move_stake_or_lamports_shared_checks(
1078            source_stake_account_info,
1079            lamports,
1080            destination_stake_account_info,
1081            stake_authority_info,
1082        )?;
1083
1084        // ensure source and destination are the right size for the current version of
1085        // StakeState this a safeguard in case there is a new version of the
1086        // struct that cannot fit into an old account
1087        if source_stake_account_info.data_len() != StakeStateV2::size_of()
1088            || destination_stake_account_info.data_len() != StakeStateV2::size_of()
1089        {
1090            return Err(ProgramError::InvalidAccountData);
1091        }
1092
1093        // source must be fully active
1094        let MergeKind::FullyActive(source_meta, mut source_stake) = source_merge_kind else {
1095            return Err(ProgramError::InvalidAccountData);
1096        };
1097
1098        let minimum_delegation = crate::get_minimum_delegation();
1099        let source_effective_stake = source_stake.delegation.stake;
1100
1101        // source cannot move more stake than it has, regardless of how many lamports it
1102        // has
1103        let source_final_stake = source_effective_stake
1104            .checked_sub(lamports)
1105            .ok_or(ProgramError::InvalidArgument)?;
1106
1107        // unless all stake is being moved, source must retain at least the minimum
1108        // delegation
1109        if source_final_stake != 0 && source_final_stake < minimum_delegation {
1110            return Err(ProgramError::InvalidArgument);
1111        }
1112
1113        // destination must be fully active or fully inactive
1114        let destination_meta = match destination_merge_kind {
1115            MergeKind::FullyActive(destination_meta, mut destination_stake) => {
1116                // if active, destination must be delegated to the same vote account as source
1117                if source_stake.delegation.voter_pubkey != destination_stake.delegation.voter_pubkey
1118                {
1119                    return Err(StakeError::VoteAddressMismatch.into());
1120                }
1121
1122                let destination_effective_stake = destination_stake.delegation.stake;
1123                let destination_final_stake = destination_effective_stake
1124                    .checked_add(lamports)
1125                    .ok_or(ProgramError::ArithmeticOverflow)?;
1126
1127                // ensure destination meets miniumum delegation
1128                // since it is already active, this only really applies if the minimum is raised
1129                if destination_final_stake < minimum_delegation {
1130                    return Err(ProgramError::InvalidArgument);
1131                }
1132
1133                merge_delegation_stake_and_credits_observed(
1134                    &mut destination_stake,
1135                    lamports,
1136                    source_stake.credits_observed,
1137                )?;
1138
1139                // StakeFlags::empty() is valid here because the only existing stake flag,
1140                // MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, does not apply to
1141                // active stakes
1142                set_stake_state(
1143                    destination_stake_account_info,
1144                    &StakeStateV2::Stake(destination_meta, destination_stake, StakeFlags::empty()),
1145                )?;
1146
1147                destination_meta
1148            }
1149            MergeKind::Inactive(destination_meta, _, _) => {
1150                // if destination is inactive, it must be given at least the minimum delegation
1151                if lamports < minimum_delegation {
1152                    return Err(ProgramError::InvalidArgument);
1153                }
1154
1155                let mut destination_stake = source_stake;
1156                destination_stake.delegation.stake = lamports;
1157
1158                // StakeFlags::empty() is valid here because the only existing stake flag,
1159                // MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, is cleared when a stake
1160                // is activated
1161                set_stake_state(
1162                    destination_stake_account_info,
1163                    &StakeStateV2::Stake(destination_meta, destination_stake, StakeFlags::empty()),
1164                )?;
1165
1166                destination_meta
1167            }
1168            _ => return Err(ProgramError::InvalidAccountData),
1169        };
1170
1171        if source_final_stake == 0 {
1172            set_stake_state(
1173                source_stake_account_info,
1174                &StakeStateV2::Initialized(source_meta),
1175            )?;
1176        } else {
1177            source_stake.delegation.stake = source_final_stake;
1178
1179            // StakeFlags::empty() is valid here because the only existing stake flag,
1180            // MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED, does not apply to
1181            // active stakes
1182            set_stake_state(
1183                source_stake_account_info,
1184                &StakeStateV2::Stake(source_meta, source_stake, StakeFlags::empty()),
1185            )?;
1186        }
1187
1188        relocate_lamports(
1189            source_stake_account_info,
1190            destination_stake_account_info,
1191            lamports,
1192        )?;
1193
1194        // this should be impossible, but because we do all our math with delegations,
1195        // best to guard it
1196        if source_stake_account_info.lamports() < source_meta.rent_exempt_reserve
1197            || destination_stake_account_info.lamports() < destination_meta.rent_exempt_reserve
1198        {
1199            msg!("Delegation calculations violated lamport balance assumptions");
1200            return Err(ProgramError::InvalidArgument);
1201        }
1202
1203        Ok(())
1204    }
1205
1206    fn process_move_lamports(accounts: &[AccountInfo], lamports: u64) -> ProgramResult {
1207        let account_info_iter = &mut accounts.iter();
1208
1209        // native asserts: 3 accounts
1210        let source_stake_account_info = next_account_info(account_info_iter)?;
1211        let destination_stake_account_info = next_account_info(account_info_iter)?;
1212        let stake_authority_info = next_account_info(account_info_iter)?;
1213
1214        let (source_merge_kind, _) = move_stake_or_lamports_shared_checks(
1215            source_stake_account_info,
1216            lamports,
1217            destination_stake_account_info,
1218            stake_authority_info,
1219        )?;
1220
1221        let source_free_lamports = match source_merge_kind {
1222            MergeKind::FullyActive(source_meta, source_stake) => source_stake_account_info
1223                .lamports()
1224                .saturating_sub(source_stake.delegation.stake)
1225                .saturating_sub(source_meta.rent_exempt_reserve),
1226            MergeKind::Inactive(source_meta, source_lamports, _) => {
1227                source_lamports.saturating_sub(source_meta.rent_exempt_reserve)
1228            }
1229            _ => return Err(ProgramError::InvalidAccountData),
1230        };
1231
1232        if lamports > source_free_lamports {
1233            return Err(ProgramError::InvalidArgument);
1234        }
1235
1236        relocate_lamports(
1237            source_stake_account_info,
1238            destination_stake_account_info,
1239            lamports,
1240        )?;
1241
1242        Ok(())
1243    }
1244
1245    /// Processes [Instruction](enum.Instruction.html).
1246    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
1247        // convenience so we can safely use id() everywhere
1248        if *program_id != id() {
1249            return Err(ProgramError::IncorrectProgramId);
1250        }
1251
1252        let epoch_rewards_active = EpochRewards::get()
1253            .map(|epoch_rewards| epoch_rewards.active)
1254            .unwrap_or(false);
1255
1256        let instruction =
1257            bincode::deserialize(data).map_err(|_| ProgramError::InvalidInstructionData)?;
1258
1259        if epoch_rewards_active && !matches!(instruction, StakeInstruction::GetMinimumDelegation) {
1260            return Err(StakeError::EpochRewardsActive.into());
1261        }
1262
1263        match instruction {
1264            StakeInstruction::Initialize(authorize, lockup) => {
1265                msg!("Instruction: Initialize");
1266                Self::process_initialize(accounts, authorize, lockup)
1267            }
1268            StakeInstruction::Authorize(new_authority, authority_type) => {
1269                msg!("Instruction: Authorize");
1270                Self::process_authorize(accounts, new_authority, authority_type)
1271            }
1272            StakeInstruction::DelegateStake => {
1273                msg!("Instruction: DelegateStake");
1274                Self::process_delegate(accounts)
1275            }
1276            StakeInstruction::Split(lamports) => {
1277                msg!("Instruction: Split");
1278                Self::process_split(accounts, lamports)
1279            }
1280            StakeInstruction::Withdraw(lamports) => {
1281                msg!("Instruction: Withdraw");
1282                Self::process_withdraw(accounts, lamports)
1283            }
1284            StakeInstruction::Deactivate => {
1285                msg!("Instruction: Deactivate");
1286                Self::process_deactivate(accounts)
1287            }
1288            StakeInstruction::SetLockup(lockup) => {
1289                msg!("Instruction: SetLockup");
1290                Self::process_set_lockup(accounts, lockup)
1291            }
1292            StakeInstruction::Merge => {
1293                msg!("Instruction: Merge");
1294                Self::process_merge(accounts)
1295            }
1296            StakeInstruction::AuthorizeWithSeed(args) => {
1297                msg!("Instruction: AuthorizeWithSeed");
1298                Self::process_authorize_with_seed(accounts, args)
1299            }
1300            StakeInstruction::InitializeChecked => {
1301                msg!("Instruction: InitializeChecked");
1302                Self::process_initialize_checked(accounts)
1303            }
1304            StakeInstruction::AuthorizeChecked(authority_type) => {
1305                msg!("Instruction: AuthorizeChecked");
1306                Self::process_authorize_checked(accounts, authority_type)
1307            }
1308            StakeInstruction::AuthorizeCheckedWithSeed(args) => {
1309                msg!("Instruction: AuthorizeCheckedWithSeed");
1310                Self::process_authorize_checked_with_seed(accounts, args)
1311            }
1312            StakeInstruction::SetLockupChecked(lockup_checked) => {
1313                msg!("Instruction: SetLockupChecked");
1314                Self::process_set_lockup_checked(accounts, lockup_checked)
1315            }
1316            StakeInstruction::GetMinimumDelegation => {
1317                msg!("Instruction: GetMinimumDelegation");
1318                let minimum_delegation = crate::get_minimum_delegation();
1319                set_return_data(&minimum_delegation.to_le_bytes());
1320                Ok(())
1321            }
1322            StakeInstruction::DeactivateDelinquent => {
1323                msg!("Instruction: DeactivateDelinquent");
1324                Self::process_deactivate_delinquent(accounts)
1325            }
1326            #[allow(deprecated)]
1327            StakeInstruction::Redelegate => Err(ProgramError::InvalidInstructionData),
1328            // NOTE we assume the program is going live after `move_stake_and_move_lamports_ixs` is
1329            // activated
1330            StakeInstruction::MoveStake(lamports) => {
1331                msg!("Instruction: MoveStake");
1332                Self::process_move_stake(accounts, lamports)
1333            }
1334            StakeInstruction::MoveLamports(lamports) => {
1335                msg!("Instruction: MoveLamports");
1336                Self::process_move_lamports(accounts, lamports)
1337            }
1338        }
1339    }
1340}