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