solana_stake_program/
stake_instruction.rs

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