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