solana_stake_program/
stake_instruction.rs

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