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