solana_stake_program/
stake_instruction.rs

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