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