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