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