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