1use {
7 gemachain_sdk::{
8 account::{AccountSharedData, ReadableAccount, WritableAccount},
9 account_utils::{State, StateMut},
10 clock::{Clock, Epoch},
11 feature_set::stake_merge_with_unmatched_credits_observed,
12 ic_msg,
13 instruction::{checked_add, InstructionError},
14 keyed_account::KeyedAccount,
15 process_instruction::InvokeContext,
16 pubkey::Pubkey,
17 rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
18 stake::{
19 config::Config,
20 instruction::{LockupArgs, StakeError},
21 program::id,
22 },
23 stake_history::{StakeHistory, StakeHistoryEntry},
24 },
25 gemachain_vote_program::vote_state::{VoteState, VoteStateVersions},
26 std::{collections::HashSet, convert::TryFrom},
27};
28
29#[deprecated(
30 since = "1.8.0",
31 note = "Please use `gemachain_sdk::stake::state` or `gemachain_program::stake::state` instead"
32)]
33pub use gemachain_sdk::stake::state::*;
34
35#[derive(Debug)]
36pub enum SkippedReason {
37 ZeroPoints,
38 ZeroPointValue,
39 ZeroReward,
40 ZeroCreditsAndReturnZero,
41 ZeroCreditsAndReturnCurrent,
42}
43
44impl From<SkippedReason> for InflationPointCalculationEvent {
45 fn from(reason: SkippedReason) -> Self {
46 InflationPointCalculationEvent::Skipped(reason)
47 }
48}
49
50#[derive(Debug)]
51pub enum InflationPointCalculationEvent {
52 CalculatedPoints(u64, u128, u128, u128),
53 SplitRewards(u64, u64, u64, PointValue),
54 EffectiveStakeAtRewardedEpoch(u64),
55 RentExemptReserve(u64),
56 Delegation(Delegation, Pubkey),
57 Commission(u8),
58 CreditsObserved(u64, Option<u64>),
59 Skipped(SkippedReason),
60}
61
62pub(crate) fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
63 None::<fn(&_)>
64}
65
66pub fn from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<StakeState> {
68 account.state().ok()
69}
70
71pub fn stake_from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<Stake> {
72 from(account).and_then(|state: StakeState| state.stake())
73}
74
75pub fn delegation_from(account: &AccountSharedData) -> Option<Delegation> {
76 from(account).and_then(|state: StakeState| state.delegation())
77}
78
79pub fn authorized_from(account: &AccountSharedData) -> Option<Authorized> {
80 from(account).and_then(|state: StakeState| state.authorized())
81}
82
83pub fn lockup_from<T: ReadableAccount + StateMut<StakeState>>(account: &T) -> Option<Lockup> {
84 from(account).and_then(|state: StakeState| state.lockup())
85}
86
87pub fn meta_from(account: &AccountSharedData) -> Option<Meta> {
88 from(account).and_then(|state: StakeState| state.meta())
89}
90
91fn redelegate(
92 stake: &mut Stake,
93 stake_carats: u64,
94 voter_pubkey: &Pubkey,
95 vote_state: &VoteState,
96 clock: &Clock,
97 stake_history: &StakeHistory,
98 config: &Config,
99 can_reverse_deactivation: bool,
100) -> Result<(), StakeError> {
101 if stake.stake(clock.epoch, Some(stake_history)) != 0 {
103 if stake.delegation.voter_pubkey == *voter_pubkey
107 && clock.epoch == stake.delegation.deactivation_epoch
108 && can_reverse_deactivation
109 {
110 stake.delegation.deactivation_epoch = std::u64::MAX;
111 return Ok(());
112 } else {
113 return Err(StakeError::TooSoonToRedelegate);
115 }
116 }
117 stake.delegation.stake = stake_carats;
122 stake.delegation.activation_epoch = clock.epoch;
123 stake.delegation.deactivation_epoch = std::u64::MAX;
124 stake.delegation.voter_pubkey = *voter_pubkey;
125 stake.delegation.warmup_cooldown_rate = config.warmup_cooldown_rate;
126 stake.credits_observed = vote_state.credits();
127 Ok(())
128}
129
130fn new_stake(
131 stake: u64,
132 voter_pubkey: &Pubkey,
133 vote_state: &VoteState,
134 activation_epoch: Epoch,
135 config: &Config,
136) -> Stake {
137 Stake {
138 delegation: Delegation::new(
139 voter_pubkey,
140 stake,
141 activation_epoch,
142 config.warmup_cooldown_rate,
143 ),
144 credits_observed: vote_state.credits(),
145 }
146}
147
148#[derive(Clone, Debug, PartialEq)]
153pub struct PointValue {
154 pub rewards: u64, pub points: u128, }
157
158fn redeem_stake_rewards(
159 rewarded_epoch: Epoch,
160 stake: &mut Stake,
161 point_value: &PointValue,
162 vote_state: &VoteState,
163 stake_history: Option<&StakeHistory>,
164 inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
165 fix_activating_credits_observed: bool,
166) -> Option<(u64, u64)> {
167 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
168 inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
169 stake.credits_observed,
170 None,
171 ));
172 }
173 calculate_stake_rewards(
174 rewarded_epoch,
175 stake,
176 point_value,
177 vote_state,
178 stake_history,
179 inflation_point_calc_tracer,
180 fix_activating_credits_observed,
181 )
182 .map(|(stakers_reward, voters_reward, credits_observed)| {
183 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
184 inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
185 stake.credits_observed,
186 Some(credits_observed),
187 ));
188 }
189 stake.credits_observed = credits_observed;
190 stake.delegation.stake += stakers_reward;
191 (stakers_reward, voters_reward)
192 })
193}
194
195fn calculate_stake_points(
196 stake: &Stake,
197 vote_state: &VoteState,
198 stake_history: Option<&StakeHistory>,
199 inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
200) -> u128 {
201 calculate_stake_points_and_credits(
202 stake,
203 vote_state,
204 stake_history,
205 inflation_point_calc_tracer,
206 )
207 .0
208}
209
210fn calculate_stake_points_and_credits(
214 stake: &Stake,
215 new_vote_state: &VoteState,
216 stake_history: Option<&StakeHistory>,
217 inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
218) -> (u128, u64) {
219 if new_vote_state.credits() <= stake.credits_observed {
221 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
222 inflation_point_calc_tracer(&SkippedReason::ZeroCreditsAndReturnCurrent.into());
223 }
224 return (0, stake.credits_observed);
225 }
226
227 let mut points = 0;
228 let mut new_credits_observed = stake.credits_observed;
229
230 for (epoch, final_epoch_credits, initial_epoch_credits) in
231 new_vote_state.epoch_credits().iter().copied()
232 {
233 let stake_amount = u128::from(stake.delegation.stake(epoch, stake_history));
234
235 let earned_credits = if stake.credits_observed < initial_epoch_credits {
238 final_epoch_credits - initial_epoch_credits
240 } else if stake.credits_observed < final_epoch_credits {
241 final_epoch_credits - new_credits_observed
243 } else {
244 0
247 };
248 let earned_credits = u128::from(earned_credits);
249
250 new_credits_observed = new_credits_observed.max(final_epoch_credits);
252
253 let earned_points = stake_amount * earned_credits;
255 points += earned_points;
256
257 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
258 inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints(
259 epoch,
260 stake_amount,
261 earned_credits,
262 earned_points,
263 ));
264 }
265 }
266
267 (points, new_credits_observed)
268}
269
270fn calculate_stake_rewards(
277 rewarded_epoch: Epoch,
278 stake: &Stake,
279 point_value: &PointValue,
280 vote_state: &VoteState,
281 stake_history: Option<&StakeHistory>,
282 inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
283 fix_activating_credits_observed: bool,
284) -> Option<(u64, u64, u64)> {
285 let (points, credits_observed) = calculate_stake_points_and_credits(
286 stake,
287 vote_state,
288 stake_history,
289 inflation_point_calc_tracer,
290 );
291
292 if point_value.rewards == 0
295 || (fix_activating_credits_observed && stake.delegation.activation_epoch == rewarded_epoch)
296 {
297 return Some((0, 0, credits_observed));
298 }
299
300 if points == 0 {
301 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
302 inflation_point_calc_tracer(&SkippedReason::ZeroPoints.into());
303 }
304 return None;
305 }
306 if point_value.points == 0 {
307 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
308 inflation_point_calc_tracer(&SkippedReason::ZeroPointValue.into());
309 }
310 return None;
311 }
312
313 let rewards = points
314 .checked_mul(u128::from(point_value.rewards))
315 .unwrap()
316 .checked_div(point_value.points)
317 .unwrap();
318
319 let rewards = u64::try_from(rewards).unwrap();
320
321 if rewards == 0 {
323 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
324 inflation_point_calc_tracer(&SkippedReason::ZeroReward.into());
325 }
326 return None;
327 }
328 let (voter_rewards, staker_rewards, is_split) = vote_state.commission_split(rewards);
329 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
330 inflation_point_calc_tracer(&InflationPointCalculationEvent::SplitRewards(
331 rewards,
332 voter_rewards,
333 staker_rewards,
334 (*point_value).clone(),
335 ));
336 }
337
338 if (voter_rewards == 0 || staker_rewards == 0) && is_split {
339 return None;
343 }
344
345 Some((staker_rewards, voter_rewards, credits_observed))
346}
347
348pub trait StakeAccount {
349 fn initialize(
350 &self,
351 authorized: &Authorized,
352 lockup: &Lockup,
353 rent: &Rent,
354 ) -> Result<(), InstructionError>;
355 fn authorize(
356 &self,
357 signers: &HashSet<Pubkey>,
358 new_authority: &Pubkey,
359 stake_authorize: StakeAuthorize,
360 require_custodian_for_locked_stake_authorize: bool,
361 clock: &Clock,
362 custodian: Option<&Pubkey>,
363 ) -> Result<(), InstructionError>;
364 fn authorize_with_seed(
365 &self,
366 authority_base: &KeyedAccount,
367 authority_seed: &str,
368 authority_owner: &Pubkey,
369 new_authority: &Pubkey,
370 stake_authorize: StakeAuthorize,
371 require_custodian_for_locked_stake_authorize: bool,
372 clock: &Clock,
373 custodian: Option<&Pubkey>,
374 ) -> Result<(), InstructionError>;
375 fn delegate(
376 &self,
377 vote_account: &KeyedAccount,
378 clock: &Clock,
379 stake_history: &StakeHistory,
380 config: &Config,
381 signers: &HashSet<Pubkey>,
382 can_reverse_deactivation: bool,
383 ) -> Result<(), InstructionError>;
384 fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError>;
385 fn set_lockup(
386 &self,
387 lockup: &LockupArgs,
388 signers: &HashSet<Pubkey>,
389 clock: Option<&Clock>,
390 ) -> Result<(), InstructionError>;
391 fn split(
392 &self,
393 carats: u64,
394 split_stake: &KeyedAccount,
395 signers: &HashSet<Pubkey>,
396 ) -> Result<(), InstructionError>;
397 fn merge(
398 &self,
399 invoke_context: &dyn InvokeContext,
400 source_stake: &KeyedAccount,
401 clock: &Clock,
402 stake_history: &StakeHistory,
403 signers: &HashSet<Pubkey>,
404 can_merge_expired_lockups: bool,
405 ) -> Result<(), InstructionError>;
406 fn withdraw(
407 &self,
408 carats: u64,
409 to: &KeyedAccount,
410 clock: &Clock,
411 stake_history: &StakeHistory,
412 withdraw_authority: &KeyedAccount,
413 custodian: Option<&KeyedAccount>,
414 prevent_withdraw_to_zero: bool,
415 ) -> Result<(), InstructionError>;
416}
417
418impl<'a> StakeAccount for KeyedAccount<'a> {
419 fn initialize(
420 &self,
421 authorized: &Authorized,
422 lockup: &Lockup,
423 rent: &Rent,
424 ) -> Result<(), InstructionError> {
425 if self.data_len()? != std::mem::size_of::<StakeState>() {
426 return Err(InstructionError::InvalidAccountData);
427 }
428 if let StakeState::Uninitialized = self.state()? {
429 let rent_exempt_reserve = rent.minimum_balance(self.data_len()?);
430
431 if rent_exempt_reserve < self.carats()? {
432 self.set_state(&StakeState::Initialized(Meta {
433 rent_exempt_reserve,
434 authorized: *authorized,
435 lockup: *lockup,
436 }))
437 } else {
438 Err(InstructionError::InsufficientFunds)
439 }
440 } else {
441 Err(InstructionError::InvalidAccountData)
442 }
443 }
444
445 fn authorize(
449 &self,
450 signers: &HashSet<Pubkey>,
451 new_authority: &Pubkey,
452 stake_authorize: StakeAuthorize,
453 require_custodian_for_locked_stake_authorize: bool,
454 clock: &Clock,
455 custodian: Option<&Pubkey>,
456 ) -> Result<(), InstructionError> {
457 match self.state()? {
458 StakeState::Stake(mut meta, stake) => {
459 meta.authorized.authorize(
460 signers,
461 new_authority,
462 stake_authorize,
463 if require_custodian_for_locked_stake_authorize {
464 Some((&meta.lockup, clock, custodian))
465 } else {
466 None
467 },
468 )?;
469 self.set_state(&StakeState::Stake(meta, stake))
470 }
471 StakeState::Initialized(mut meta) => {
472 meta.authorized.authorize(
473 signers,
474 new_authority,
475 stake_authorize,
476 if require_custodian_for_locked_stake_authorize {
477 Some((&meta.lockup, clock, custodian))
478 } else {
479 None
480 },
481 )?;
482 self.set_state(&StakeState::Initialized(meta))
483 }
484 _ => Err(InstructionError::InvalidAccountData),
485 }
486 }
487 fn authorize_with_seed(
488 &self,
489 authority_base: &KeyedAccount,
490 authority_seed: &str,
491 authority_owner: &Pubkey,
492 new_authority: &Pubkey,
493 stake_authorize: StakeAuthorize,
494 require_custodian_for_locked_stake_authorize: bool,
495 clock: &Clock,
496 custodian: Option<&Pubkey>,
497 ) -> Result<(), InstructionError> {
498 let mut signers = HashSet::default();
499 if let Some(base_pubkey) = authority_base.signer_key() {
500 signers.insert(Pubkey::create_with_seed(
501 base_pubkey,
502 authority_seed,
503 authority_owner,
504 )?);
505 }
506 self.authorize(
507 &signers,
508 new_authority,
509 stake_authorize,
510 require_custodian_for_locked_stake_authorize,
511 clock,
512 custodian,
513 )
514 }
515 fn delegate(
516 &self,
517 vote_account: &KeyedAccount,
518 clock: &Clock,
519 stake_history: &StakeHistory,
520 config: &Config,
521 signers: &HashSet<Pubkey>,
522 can_reverse_deactivation: bool,
523 ) -> Result<(), InstructionError> {
524 if vote_account.owner()? != gemachain_vote_program::id() {
525 return Err(InstructionError::IncorrectProgramId);
526 }
527
528 match self.state()? {
529 StakeState::Initialized(meta) => {
530 meta.authorized.check(signers, StakeAuthorize::Staker)?;
531 let stake = new_stake(
532 self.carats()?.saturating_sub(meta.rent_exempt_reserve), vote_account.unsigned_key(),
534 &State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
535 clock.epoch,
536 config,
537 );
538 self.set_state(&StakeState::Stake(meta, stake))
539 }
540 StakeState::Stake(meta, mut stake) => {
541 meta.authorized.check(signers, StakeAuthorize::Staker)?;
542 redelegate(
543 &mut stake,
544 self.carats()?.saturating_sub(meta.rent_exempt_reserve), vote_account.unsigned_key(),
546 &State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
547 clock,
548 stake_history,
549 config,
550 can_reverse_deactivation,
551 )?;
552 self.set_state(&StakeState::Stake(meta, stake))
553 }
554 _ => Err(InstructionError::InvalidAccountData),
555 }
556 }
557 fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError> {
558 if let StakeState::Stake(meta, mut stake) = self.state()? {
559 meta.authorized.check(signers, StakeAuthorize::Staker)?;
560 stake.deactivate(clock.epoch)?;
561
562 self.set_state(&StakeState::Stake(meta, stake))
563 } else {
564 Err(InstructionError::InvalidAccountData)
565 }
566 }
567 fn set_lockup(
568 &self,
569 lockup: &LockupArgs,
570 signers: &HashSet<Pubkey>,
571 clock: Option<&Clock>,
572 ) -> Result<(), InstructionError> {
573 match self.state()? {
574 StakeState::Initialized(mut meta) => {
575 meta.set_lockup(lockup, signers, clock)?;
576 self.set_state(&StakeState::Initialized(meta))
577 }
578 StakeState::Stake(mut meta, stake) => {
579 meta.set_lockup(lockup, signers, clock)?;
580 self.set_state(&StakeState::Stake(meta, stake))
581 }
582 _ => Err(InstructionError::InvalidAccountData),
583 }
584 }
585
586 fn split(
587 &self,
588 carats: u64,
589 split: &KeyedAccount,
590 signers: &HashSet<Pubkey>,
591 ) -> Result<(), InstructionError> {
592 if split.owner()? != id() {
593 return Err(InstructionError::IncorrectProgramId);
594 }
595 if split.data_len()? != std::mem::size_of::<StakeState>() {
596 return Err(InstructionError::InvalidAccountData);
597 }
598
599 if let StakeState::Uninitialized = split.state()? {
600 if carats > self.carats()? {
602 return Err(InstructionError::InsufficientFunds);
603 }
604
605 match self.state()? {
606 StakeState::Stake(meta, mut stake) => {
607 meta.authorized.check(signers, StakeAuthorize::Staker)?;
608 let split_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
609 meta.rent_exempt_reserve,
610 self.data_len()? as u64,
611 split.data_len()? as u64,
612 );
613
614 if carats <= split_rent_exempt_reserve.saturating_sub(split.carats()?)
616 || (carats != self.carats()?
618 && checked_add(carats, meta.rent_exempt_reserve)? >= self.carats()?)
620 {
621 return Err(InstructionError::InsufficientFunds);
622 }
623 let (remaining_stake_delta, split_stake_amount) = if carats
629 == self.carats()?
630 {
631 let remaining_stake_delta =
641 carats.saturating_sub(meta.rent_exempt_reserve);
642 (remaining_stake_delta, remaining_stake_delta)
643 } else {
644 (
647 carats,
648 carats - split_rent_exempt_reserve.saturating_sub(split.carats()?),
649 )
650 };
651 let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?;
652 let mut split_meta = meta;
653 split_meta.rent_exempt_reserve = split_rent_exempt_reserve;
654
655 self.set_state(&StakeState::Stake(meta, stake))?;
656 split.set_state(&StakeState::Stake(split_meta, split_stake))?;
657 }
658 StakeState::Initialized(meta) => {
659 meta.authorized.check(signers, StakeAuthorize::Staker)?;
660 let split_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
661 meta.rent_exempt_reserve,
662 self.data_len()? as u64,
663 split.data_len()? as u64,
664 );
665
666 if carats <= split_rent_exempt_reserve.saturating_sub(split.carats()?)
668 || (carats != self.carats()?
670 && checked_add(carats, meta.rent_exempt_reserve)? >= self.carats()?)
672 {
673 return Err(InstructionError::InsufficientFunds);
674 }
675
676 let mut split_meta = meta;
677 split_meta.rent_exempt_reserve = split_rent_exempt_reserve;
678 split.set_state(&StakeState::Initialized(split_meta))?;
679 }
680 StakeState::Uninitialized => {
681 if !signers.contains(self.unsigned_key()) {
682 return Err(InstructionError::MissingRequiredSignature);
683 }
684 }
685 _ => return Err(InstructionError::InvalidAccountData),
686 }
687
688 if carats == self.carats()? {
690 self.set_state(&StakeState::Uninitialized)?;
691 }
692
693 split
694 .try_account_ref_mut()?
695 .checked_add_carats(carats)?;
696 self.try_account_ref_mut()?.checked_sub_carats(carats)?;
697 Ok(())
698 } else {
699 Err(InstructionError::InvalidAccountData)
700 }
701 }
702
703 fn merge(
704 &self,
705 invoke_context: &dyn InvokeContext,
706 source_account: &KeyedAccount,
707 clock: &Clock,
708 stake_history: &StakeHistory,
709 signers: &HashSet<Pubkey>,
710 can_merge_expired_lockups: bool,
711 ) -> Result<(), InstructionError> {
712 if source_account.owner()? != id() {
714 return Err(InstructionError::IncorrectProgramId);
715 }
716 if source_account.unsigned_key() == self.unsigned_key() {
718 return Err(InstructionError::InvalidArgument);
719 }
720
721 ic_msg!(invoke_context, "Checking if destination stake is mergeable");
722 let stake_merge_kind =
723 MergeKind::get_if_mergeable(invoke_context, self, clock, stake_history)?;
724 let meta = stake_merge_kind.meta();
725
726 meta.authorized.check(signers, StakeAuthorize::Staker)?;
728
729 ic_msg!(invoke_context, "Checking if source stake is mergeable");
730 let source_merge_kind =
731 MergeKind::get_if_mergeable(invoke_context, source_account, clock, stake_history)?;
732
733 let clock = if can_merge_expired_lockups {
734 Some(clock)
735 } else {
736 None
737 };
738
739 ic_msg!(invoke_context, "Merging stake accounts");
740 if let Some(merged_state) =
741 stake_merge_kind.merge(invoke_context, source_merge_kind, clock)?
742 {
743 self.set_state(&merged_state)?;
744 }
745
746 source_account.set_state(&StakeState::Uninitialized)?;
748
749 let carats = source_account.carats()?;
751 source_account
752 .try_account_ref_mut()?
753 .checked_sub_carats(carats)?;
754 self.try_account_ref_mut()?.checked_add_carats(carats)?;
755 Ok(())
756 }
757
758 fn withdraw(
759 &self,
760 carats: u64,
761 to: &KeyedAccount,
762 clock: &Clock,
763 stake_history: &StakeHistory,
764 withdraw_authority: &KeyedAccount,
765 custodian: Option<&KeyedAccount>,
766 prevent_withdraw_to_zero: bool,
767 ) -> Result<(), InstructionError> {
768 let mut signers = HashSet::new();
769 let withdraw_authority_pubkey = withdraw_authority
770 .signer_key()
771 .ok_or(InstructionError::MissingRequiredSignature)?;
772 signers.insert(*withdraw_authority_pubkey);
773
774 let (lockup, reserve, is_staked) = match self.state()? {
775 StakeState::Stake(meta, stake) => {
776 meta.authorized
777 .check(&signers, StakeAuthorize::Withdrawer)?;
778 let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
780 stake.delegation.stake(clock.epoch, Some(stake_history))
781 } else {
782 stake.delegation.stake
786 };
787
788 let staked_and_reserve = checked_add(staked, meta.rent_exempt_reserve)?;
789 (meta.lockup, staked_and_reserve, staked != 0)
790 }
791 StakeState::Initialized(meta) => {
792 meta.authorized
793 .check(&signers, StakeAuthorize::Withdrawer)?;
794 let reserve = if prevent_withdraw_to_zero {
795 checked_add(meta.rent_exempt_reserve, 1)? } else {
797 meta.rent_exempt_reserve
798 };
799
800 (meta.lockup, reserve, false)
801 }
802 StakeState::Uninitialized => {
803 if !signers.contains(self.unsigned_key()) {
804 return Err(InstructionError::MissingRequiredSignature);
805 }
806 (Lockup::default(), 0, false) }
808 _ => return Err(InstructionError::InvalidAccountData),
809 };
810
811 let custodian_pubkey = custodian.and_then(|keyed_account| keyed_account.signer_key());
814 if lockup.is_in_force(clock, custodian_pubkey) {
815 return Err(StakeError::LockupInForce.into());
816 }
817
818 let carats_and_reserve = checked_add(carats, reserve)?;
819 if is_staked && carats_and_reserve > self.carats()?
822 {
823 return Err(InstructionError::InsufficientFunds);
824 }
825
826 if carats != self.carats()? && carats_and_reserve > self.carats()?
828 {
829 assert!(!is_staked);
830 return Err(InstructionError::InsufficientFunds);
831 }
832
833 if carats == self.carats()? {
835 self.set_state(&StakeState::Uninitialized)?;
836 }
837
838 self.try_account_ref_mut()?.checked_sub_carats(carats)?;
839 to.try_account_ref_mut()?.checked_add_carats(carats)?;
840 Ok(())
841 }
842}
843
844#[derive(Clone, Debug, PartialEq)]
845enum MergeKind {
846 Inactive(Meta, u64),
847 ActivationEpoch(Meta, Stake),
848 FullyActive(Meta, Stake),
849}
850
851impl MergeKind {
852 fn meta(&self) -> &Meta {
853 match self {
854 Self::Inactive(meta, _) => meta,
855 Self::ActivationEpoch(meta, _) => meta,
856 Self::FullyActive(meta, _) => meta,
857 }
858 }
859
860 fn active_stake(&self) -> Option<&Stake> {
861 match self {
862 Self::Inactive(_, _) => None,
863 Self::ActivationEpoch(_, stake) => Some(stake),
864 Self::FullyActive(_, stake) => Some(stake),
865 }
866 }
867
868 fn get_if_mergeable(
869 invoke_context: &dyn InvokeContext,
870 stake_keyed_account: &KeyedAccount,
871 clock: &Clock,
872 stake_history: &StakeHistory,
873 ) -> Result<Self, InstructionError> {
874 match stake_keyed_account.state()? {
875 StakeState::Stake(meta, stake) => {
876 match stake
879 .delegation
880 .stake_activating_and_deactivating(clock.epoch, Some(stake_history))
881 {
882 (0, 0, 0) => Ok(Self::Inactive(meta, stake_keyed_account.carats()?)),
885 (0, _, _) => Ok(Self::ActivationEpoch(meta, stake)),
886 (_, 0, 0) => Ok(Self::FullyActive(meta, stake)),
887 _ => {
888 let err = StakeError::MergeTransientStake;
889 ic_msg!(invoke_context, "{}", err);
890 Err(err.into())
891 }
892 }
893 }
894 StakeState::Initialized(meta) => {
895 Ok(Self::Inactive(meta, stake_keyed_account.carats()?))
896 }
897 _ => Err(InstructionError::InvalidAccountData),
898 }
899 }
900
901 fn metas_can_merge(
902 invoke_context: &dyn InvokeContext,
903 stake: &Meta,
904 source: &Meta,
905 clock: Option<&Clock>,
906 ) -> Result<(), InstructionError> {
907 let can_merge_lockups = match clock {
908 None => stake.lockup == source.lockup,
910 Some(clock) => {
912 stake.lockup == source.lockup
913 || (!stake.lockup.is_in_force(clock, None)
914 && !source.lockup.is_in_force(clock, None))
915 }
916 };
917 if stake.authorized == source.authorized && can_merge_lockups {
923 Ok(())
924 } else {
925 ic_msg!(invoke_context, "Unable to merge due to metadata mismatch");
926 Err(StakeError::MergeMismatch.into())
927 }
928 }
929
930 fn active_delegations_can_merge(
931 invoke_context: &dyn InvokeContext,
932 stake: &Delegation,
933 source: &Delegation,
934 ) -> Result<(), InstructionError> {
935 if stake.voter_pubkey != source.voter_pubkey {
936 ic_msg!(invoke_context, "Unable to merge due to voter mismatch");
937 Err(StakeError::MergeMismatch.into())
938 } else if (stake.warmup_cooldown_rate - source.warmup_cooldown_rate).abs() < f64::EPSILON
939 && stake.deactivation_epoch == Epoch::MAX
940 && source.deactivation_epoch == Epoch::MAX
941 {
942 Ok(())
943 } else {
944 ic_msg!(invoke_context, "Unable to merge due to stake deactivation");
945 Err(StakeError::MergeMismatch.into())
946 }
947 }
948
949 fn active_stakes_can_merge(
951 invoke_context: &dyn InvokeContext,
952 stake: &Stake,
953 source: &Stake,
954 ) -> Result<(), InstructionError> {
955 Self::active_delegations_can_merge(invoke_context, &stake.delegation, &source.delegation)?;
956 if stake.credits_observed == source.credits_observed {
962 Ok(())
963 } else {
964 ic_msg!(
965 invoke_context,
966 "Unable to merge due to credits observed mismatch"
967 );
968 Err(StakeError::MergeMismatch.into())
969 }
970 }
971
972 fn merge(
973 self,
974 invoke_context: &dyn InvokeContext,
975 source: Self,
976 clock: Option<&Clock>,
977 ) -> Result<Option<StakeState>, InstructionError> {
978 Self::metas_can_merge(invoke_context, self.meta(), source.meta(), clock)?;
979 self.active_stake()
980 .zip(source.active_stake())
981 .map(|(stake, source)| {
982 if invoke_context
983 .is_feature_active(&stake_merge_with_unmatched_credits_observed::id())
984 {
985 Self::active_delegations_can_merge(
986 invoke_context,
987 &stake.delegation,
988 &source.delegation,
989 )
990 } else {
991 Self::active_stakes_can_merge(invoke_context, stake, source)
992 }
993 })
994 .unwrap_or(Ok(()))?;
995 let merged_state = match (self, source) {
996 (Self::Inactive(_, _), Self::Inactive(_, _)) => None,
997 (Self::Inactive(_, _), Self::ActivationEpoch(_, _)) => None,
998 (Self::ActivationEpoch(meta, mut stake), Self::Inactive(_, source_carats)) => {
999 stake.delegation.stake = checked_add(stake.delegation.stake, source_carats)?;
1000 Some(StakeState::Stake(meta, stake))
1001 }
1002 (
1003 Self::ActivationEpoch(meta, mut stake),
1004 Self::ActivationEpoch(source_meta, source_stake),
1005 ) => {
1006 let source_carats = checked_add(
1007 source_meta.rent_exempt_reserve,
1008 source_stake.delegation.stake,
1009 )?;
1010 merge_delegation_stake_and_credits_observed(
1011 invoke_context,
1012 &mut stake,
1013 source_carats,
1014 source_stake.credits_observed,
1015 )?;
1016 Some(StakeState::Stake(meta, stake))
1017 }
1018 (Self::FullyActive(meta, mut stake), Self::FullyActive(_, source_stake)) => {
1019 merge_delegation_stake_and_credits_observed(
1024 invoke_context,
1025 &mut stake,
1026 source_stake.delegation.stake,
1027 source_stake.credits_observed,
1028 )?;
1029 Some(StakeState::Stake(meta, stake))
1030 }
1031 _ => return Err(StakeError::MergeMismatch.into()),
1032 };
1033 Ok(merged_state)
1034 }
1035}
1036
1037fn merge_delegation_stake_and_credits_observed(
1038 invoke_context: &dyn InvokeContext,
1039 stake: &mut Stake,
1040 absorbed_carats: u64,
1041 absorbed_credits_observed: u64,
1042) -> Result<(), InstructionError> {
1043 if invoke_context.is_feature_active(&stake_merge_with_unmatched_credits_observed::id()) {
1044 stake.credits_observed =
1045 stake_weighted_credits_observed(stake, absorbed_carats, absorbed_credits_observed)
1046 .ok_or(InstructionError::ArithmeticOverflow)?;
1047 }
1048 stake.delegation.stake = checked_add(stake.delegation.stake, absorbed_carats)?;
1049 Ok(())
1050}
1051
1052fn stake_weighted_credits_observed(
1078 stake: &Stake,
1079 absorbed_carats: u64,
1080 absorbed_credits_observed: u64,
1081) -> Option<u64> {
1082 if stake.credits_observed == absorbed_credits_observed {
1083 Some(stake.credits_observed)
1084 } else {
1085 let total_stake = u128::from(stake.delegation.stake.checked_add(absorbed_carats)?);
1086 let stake_weighted_credits =
1087 u128::from(stake.credits_observed).checked_mul(u128::from(stake.delegation.stake))?;
1088 let absorbed_weighted_credits =
1089 u128::from(absorbed_credits_observed).checked_mul(u128::from(absorbed_carats))?;
1090 let total_weighted_credits = stake_weighted_credits
1093 .checked_add(absorbed_weighted_credits)?
1094 .checked_add(total_stake)?
1095 .checked_sub(1)?;
1096 u64::try_from(total_weighted_credits.checked_div(total_stake)?).ok()
1097 }
1098}
1099
1100pub fn redeem_rewards(
1103 rewarded_epoch: Epoch,
1104 stake_account: &mut AccountSharedData,
1105 vote_account: &mut AccountSharedData,
1106 vote_state: &VoteState,
1107 point_value: &PointValue,
1108 stake_history: Option<&StakeHistory>,
1109 inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
1110 fix_activating_credits_observed: bool,
1111) -> Result<(u64, u64), InstructionError> {
1112 if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
1113 if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
1114 inflation_point_calc_tracer(
1115 &InflationPointCalculationEvent::EffectiveStakeAtRewardedEpoch(
1116 stake.stake(rewarded_epoch, stake_history),
1117 ),
1118 );
1119 inflation_point_calc_tracer(&InflationPointCalculationEvent::RentExemptReserve(
1120 meta.rent_exempt_reserve,
1121 ));
1122 inflation_point_calc_tracer(&InflationPointCalculationEvent::Commission(
1123 vote_state.commission,
1124 ));
1125 }
1126
1127 if let Some((stakers_reward, voters_reward)) = redeem_stake_rewards(
1128 rewarded_epoch,
1129 &mut stake,
1130 point_value,
1131 vote_state,
1132 stake_history,
1133 inflation_point_calc_tracer,
1134 fix_activating_credits_observed,
1135 ) {
1136 stake_account.checked_add_carats(stakers_reward)?;
1137 vote_account.checked_add_carats(voters_reward)?;
1138
1139 stake_account.set_state(&StakeState::Stake(meta, stake))?;
1140
1141 Ok((stakers_reward, voters_reward))
1142 } else {
1143 Err(StakeError::NoCreditsToRedeem.into())
1144 }
1145 } else {
1146 Err(InstructionError::InvalidAccountData)
1147 }
1148}
1149
1150pub fn calculate_points(
1152 stake_account: &AccountSharedData,
1153 vote_account: &AccountSharedData,
1154 stake_history: Option<&StakeHistory>,
1155) -> Result<u128, InstructionError> {
1156 if let StakeState::Stake(_meta, stake) = stake_account.state()? {
1157 let vote_state: VoteState =
1158 StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
1159
1160 Ok(calculate_stake_points(
1161 &stake,
1162 &vote_state,
1163 stake_history,
1164 &mut null_tracer(),
1165 ))
1166 } else {
1167 Err(InstructionError::InvalidAccountData)
1168 }
1169}
1170
1171fn calculate_split_rent_exempt_reserve(
1175 source_rent_exempt_reserve: u64,
1176 source_data_len: u64,
1177 split_data_len: u64,
1178) -> u64 {
1179 let carats_per_byte_year =
1180 source_rent_exempt_reserve / (source_data_len + ACCOUNT_STORAGE_OVERHEAD);
1181 carats_per_byte_year * (split_data_len + ACCOUNT_STORAGE_OVERHEAD)
1182}
1183
1184pub type RewriteStakeStatus = (&'static str, (u64, u64), (u64, u64));
1185
1186pub fn new_stake_history_entry<'a, I>(
1188 epoch: Epoch,
1189 stakes: I,
1190 history: Option<&StakeHistory>,
1191) -> StakeHistoryEntry
1192where
1193 I: Iterator<Item = &'a Delegation>,
1194{
1195 fn add(a: (u64, u64, u64), b: (u64, u64, u64)) -> (u64, u64, u64) {
1198 (a.0 + b.0, a.1 + b.1, a.2 + b.2)
1199 }
1200 let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
1201 add(sum, stake.stake_activating_and_deactivating(epoch, history))
1202 });
1203
1204 StakeHistoryEntry {
1205 effective,
1206 activating,
1207 deactivating,
1208 }
1209}
1210
1211pub fn create_lockup_stake_account(
1213 authorized: &Authorized,
1214 lockup: &Lockup,
1215 rent: &Rent,
1216 carats: u64,
1217) -> AccountSharedData {
1218 let mut stake_account =
1219 AccountSharedData::new(carats, std::mem::size_of::<StakeState>(), &id());
1220
1221 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
1222 assert!(
1223 carats >= rent_exempt_reserve,
1224 "carats: {} is less than rent_exempt_reserve {}",
1225 carats,
1226 rent_exempt_reserve
1227 );
1228
1229 stake_account
1230 .set_state(&StakeState::Initialized(Meta {
1231 authorized: *authorized,
1232 lockup: *lockup,
1233 rent_exempt_reserve,
1234 }))
1235 .expect("set_state");
1236
1237 stake_account
1238}
1239
1240pub fn create_account(
1242 authorized: &Pubkey,
1243 voter_pubkey: &Pubkey,
1244 vote_account: &AccountSharedData,
1245 rent: &Rent,
1246 carats: u64,
1247) -> AccountSharedData {
1248 do_create_account(
1249 authorized,
1250 voter_pubkey,
1251 vote_account,
1252 rent,
1253 carats,
1254 Epoch::MAX,
1255 )
1256}
1257
1258pub fn create_account_with_activation_epoch(
1260 authorized: &Pubkey,
1261 voter_pubkey: &Pubkey,
1262 vote_account: &AccountSharedData,
1263 rent: &Rent,
1264 carats: u64,
1265 activation_epoch: Epoch,
1266) -> AccountSharedData {
1267 do_create_account(
1268 authorized,
1269 voter_pubkey,
1270 vote_account,
1271 rent,
1272 carats,
1273 activation_epoch,
1274 )
1275}
1276
1277fn do_create_account(
1278 authorized: &Pubkey,
1279 voter_pubkey: &Pubkey,
1280 vote_account: &AccountSharedData,
1281 rent: &Rent,
1282 carats: u64,
1283 activation_epoch: Epoch,
1284) -> AccountSharedData {
1285 let mut stake_account =
1286 AccountSharedData::new(carats, std::mem::size_of::<StakeState>(), &id());
1287
1288 let vote_state = VoteState::from(vote_account).expect("vote_state");
1289
1290 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
1291
1292 stake_account
1293 .set_state(&StakeState::Stake(
1294 Meta {
1295 authorized: Authorized::auto(authorized),
1296 rent_exempt_reserve,
1297 ..Meta::default()
1298 },
1299 new_stake(
1300 carats - rent_exempt_reserve, voter_pubkey,
1302 &vote_state,
1303 activation_epoch,
1304 &Config::default(),
1305 ),
1306 ))
1307 .expect("set_state");
1308
1309 stake_account
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314 use super::*;
1315 use proptest::prelude::*;
1316 use gemachain_sdk::{
1317 account::{AccountSharedData, WritableAccount},
1318 clock::UnixTimestamp,
1319 native_token,
1320 process_instruction::MockInvokeContext,
1321 pubkey::Pubkey,
1322 system_program,
1323 };
1324 use gemachain_vote_program::vote_state;
1325 use std::{cell::RefCell, iter::FromIterator};
1326
1327 #[test]
1328 fn test_authorized_authorize() {
1329 let staker = gemachain_sdk::pubkey::new_rand();
1330 let mut authorized = Authorized::auto(&staker);
1331 let mut signers = HashSet::new();
1332 assert_eq!(
1333 authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
1334 Err(InstructionError::MissingRequiredSignature)
1335 );
1336 signers.insert(staker);
1337 assert_eq!(
1338 authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
1339 Ok(())
1340 );
1341 }
1342
1343 #[test]
1344 fn test_authorized_authorize_with_custodian() {
1345 let staker = gemachain_sdk::pubkey::new_rand();
1346 let custodian = gemachain_sdk::pubkey::new_rand();
1347 let invalid_custodian = gemachain_sdk::pubkey::new_rand();
1348 let mut authorized = Authorized::auto(&staker);
1349 let mut signers = HashSet::new();
1350 signers.insert(staker);
1351
1352 let lockup = Lockup {
1353 epoch: 1,
1354 unix_timestamp: 1,
1355 custodian,
1356 };
1357 let clock = Clock {
1358 epoch: 0,
1359 unix_timestamp: 0,
1360 ..Clock::default()
1361 };
1362
1363 assert_eq!(
1366 authorized.authorize(&signers, &staker, StakeAuthorize::Withdrawer, None),
1367 Ok(())
1368 );
1369
1370 assert_eq!(
1372 authorized.authorize(
1373 &signers,
1374 &staker,
1375 StakeAuthorize::Withdrawer,
1376 Some((&Lockup::default(), &clock, None))
1377 ),
1378 Ok(())
1379 );
1380
1381 assert_eq!(
1383 authorized.authorize(
1384 &signers,
1385 &staker,
1386 StakeAuthorize::Withdrawer,
1387 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1388 ),
1389 Ok(()) );
1391
1392 assert_eq!(
1394 authorized.authorize(
1395 &signers,
1396 &staker,
1397 StakeAuthorize::Withdrawer,
1398 Some((&lockup, &clock, Some(&invalid_custodian)))
1399 ),
1400 Err(StakeError::CustodianSignatureMissing.into()),
1401 );
1402
1403 signers.insert(invalid_custodian);
1404
1405 assert_eq!(
1407 authorized.authorize(
1408 &signers,
1409 &staker,
1410 StakeAuthorize::Withdrawer,
1411 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1412 ),
1413 Ok(()) );
1415
1416 signers.insert(invalid_custodian);
1418 assert_eq!(
1419 authorized.authorize(
1420 &signers,
1421 &staker,
1422 StakeAuthorize::Withdrawer,
1423 Some((&lockup, &clock, Some(&invalid_custodian)))
1424 ),
1425 Err(StakeError::LockupInForce.into()), );
1427
1428 signers.remove(&invalid_custodian);
1429
1430 assert_eq!(
1432 authorized.authorize(
1433 &signers,
1434 &staker,
1435 StakeAuthorize::Withdrawer,
1436 Some((&lockup, &clock, None))
1437 ),
1438 Err(StakeError::CustodianMissing.into()),
1439 );
1440
1441 assert_eq!(
1443 authorized.authorize(
1444 &signers,
1445 &staker,
1446 StakeAuthorize::Withdrawer,
1447 Some((&lockup, &clock, Some(&custodian)))
1448 ),
1449 Err(StakeError::CustodianSignatureMissing.into()),
1450 );
1451
1452 signers.insert(custodian);
1454 assert_eq!(
1455 authorized.authorize(
1456 &signers,
1457 &staker,
1458 StakeAuthorize::Withdrawer,
1459 Some((&lockup, &clock, Some(&custodian)))
1460 ),
1461 Ok(())
1462 );
1463 }
1464
1465 #[test]
1466 fn test_stake_state_stake_from_fail() {
1467 let mut stake_account = AccountSharedData::new(0, std::mem::size_of::<StakeState>(), &id());
1468
1469 stake_account
1470 .set_state(&StakeState::default())
1471 .expect("set_state");
1472
1473 assert_eq!(stake_from(&stake_account), None);
1474 }
1475
1476 #[test]
1477 fn test_stake_is_bootstrap() {
1478 assert!(Delegation {
1479 activation_epoch: std::u64::MAX,
1480 ..Delegation::default()
1481 }
1482 .is_bootstrap());
1483 assert!(!Delegation {
1484 activation_epoch: 0,
1485 ..Delegation::default()
1486 }
1487 .is_bootstrap());
1488 }
1489
1490 #[test]
1491 fn test_stake_delegate() {
1492 let mut clock = Clock {
1493 epoch: 1,
1494 ..Clock::default()
1495 };
1496
1497 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
1498 let vote_pubkey_2 = gemachain_sdk::pubkey::new_rand();
1499
1500 let mut vote_state = VoteState::default();
1501 for i in 0..1000 {
1502 vote_state.process_slot_vote_unchecked(i);
1503 }
1504 let mut vote_state_2 = VoteState::default();
1505 for i in 0..1000 {
1506 vote_state_2.process_slot_vote_unchecked(i);
1507 }
1508
1509 let vote_account = RefCell::new(vote_state::create_account(
1510 &vote_pubkey,
1511 &gemachain_sdk::pubkey::new_rand(),
1512 0,
1513 100,
1514 ));
1515 let vote_account_2 = RefCell::new(vote_state::create_account(
1516 &vote_pubkey_2,
1517 &gemachain_sdk::pubkey::new_rand(),
1518 0,
1519 100,
1520 ));
1521 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
1522 let vote_keyed_account_2 = KeyedAccount::new(&vote_pubkey_2, false, &vote_account_2);
1523
1524 let vote_state_credits = vote_state.credits();
1525 vote_keyed_account
1526 .set_state(&VoteStateVersions::new_current(vote_state))
1527 .unwrap();
1528 let vote_state_credits_2 = vote_state_2.credits();
1529 vote_keyed_account_2
1530 .set_state(&VoteStateVersions::new_current(vote_state_2))
1531 .unwrap();
1532
1533 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
1534 let stake_carats = 42;
1535 let stake_account = AccountSharedData::new_ref_data_with_space(
1536 stake_carats,
1537 &StakeState::Initialized(Meta {
1538 authorized: Authorized {
1539 staker: stake_pubkey,
1540 withdrawer: stake_pubkey,
1541 },
1542 ..Meta::default()
1543 }),
1544 std::mem::size_of::<StakeState>(),
1545 &id(),
1546 )
1547 .expect("stake_account");
1548
1549 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
1551
1552 let mut signers = HashSet::default();
1553 assert_eq!(
1554 stake_keyed_account.delegate(
1555 &vote_keyed_account,
1556 &clock,
1557 &StakeHistory::default(),
1558 &Config::default(),
1559 &signers,
1560 true,
1561 ),
1562 Err(InstructionError::MissingRequiredSignature)
1563 );
1564
1565 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
1567 signers.insert(stake_pubkey);
1568 assert!(stake_keyed_account
1569 .delegate(
1570 &vote_keyed_account,
1571 &clock,
1572 &StakeHistory::default(),
1573 &Config::default(),
1574 &signers,
1575 true,
1576 )
1577 .is_ok());
1578
1579 let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1581 assert_eq!(
1582 stake,
1583 Stake {
1584 delegation: Delegation {
1585 voter_pubkey: vote_pubkey,
1586 stake: stake_carats,
1587 activation_epoch: clock.epoch,
1588 deactivation_epoch: std::u64::MAX,
1589 ..Delegation::default()
1590 },
1591 credits_observed: vote_state_credits,
1592 }
1593 );
1594
1595 clock.epoch += 1;
1596
1597 assert_eq!(
1599 stake_keyed_account.delegate(
1600 &vote_keyed_account,
1601 &clock,
1602 &StakeHistory::default(),
1603 &Config::default(),
1604 &signers,
1605 true
1606 ),
1607 Err(StakeError::TooSoonToRedelegate.into())
1608 );
1609
1610 stake_keyed_account.deactivate(&clock, &signers).unwrap();
1612
1613 assert_eq!(
1616 stake_keyed_account.delegate(
1617 &vote_keyed_account_2,
1618 &clock,
1619 &StakeHistory::default(),
1620 &Config::default(),
1621 &signers,
1622 true,
1623 ),
1624 Err(StakeError::TooSoonToRedelegate.into())
1625 );
1626
1627 stake_keyed_account
1630 .delegate(
1631 &vote_keyed_account,
1632 &clock,
1633 &StakeHistory::default(),
1634 &Config::default(),
1635 &signers,
1636 true,
1637 )
1638 .unwrap();
1639
1640 let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1642 assert_eq!(stake.delegation.deactivation_epoch, std::u64::MAX);
1643
1644 assert_eq!(
1647 stake_keyed_account.delegate(
1648 &vote_keyed_account_2,
1649 &clock,
1650 &StakeHistory::default(),
1651 &Config::default(),
1652 &signers,
1653 true,
1654 ),
1655 Err(StakeError::TooSoonToRedelegate.into())
1656 );
1657
1658 stake_keyed_account.deactivate(&clock, &signers).unwrap();
1660
1661 clock.epoch += 1;
1663
1664 assert!(stake_keyed_account
1666 .delegate(
1667 &vote_keyed_account_2,
1668 &clock,
1669 &StakeHistory::default(),
1670 &Config::default(),
1671 &signers,
1672 true,
1673 )
1674 .is_ok());
1675
1676 let faked_vote_account = vote_account_2.clone();
1678 faked_vote_account
1679 .borrow_mut()
1680 .set_owner(gemachain_sdk::pubkey::new_rand());
1681 let faked_vote_keyed_account =
1682 KeyedAccount::new(&vote_pubkey_2, false, &faked_vote_account);
1683 assert_eq!(
1684 stake_keyed_account.delegate(
1685 &faked_vote_keyed_account,
1686 &clock,
1687 &StakeHistory::default(),
1688 &Config::default(),
1689 &signers,
1690 true,
1691 ),
1692 Err(gemachain_sdk::instruction::InstructionError::IncorrectProgramId)
1693 );
1694
1695 let stake = stake_from(&stake_keyed_account.account.borrow()).unwrap();
1697 assert_eq!(
1698 stake,
1699 Stake {
1700 delegation: Delegation {
1701 voter_pubkey: vote_pubkey_2,
1702 stake: stake_carats,
1703 activation_epoch: clock.epoch,
1704 deactivation_epoch: std::u64::MAX,
1705 ..Delegation::default()
1706 },
1707 credits_observed: vote_state_credits_2,
1708 }
1709 );
1710
1711 let stake_state = StakeState::RewardsPool;
1713
1714 stake_keyed_account.set_state(&stake_state).unwrap();
1715 assert!(stake_keyed_account
1716 .delegate(
1717 &vote_keyed_account,
1718 &clock,
1719 &StakeHistory::default(),
1720 &Config::default(),
1721 &signers,
1722 true,
1723 )
1724 .is_err());
1725 }
1726
1727 fn create_stake_history_from_delegations(
1728 bootstrap: Option<u64>,
1729 epochs: std::ops::Range<Epoch>,
1730 delegations: &[Delegation],
1731 ) -> StakeHistory {
1732 let mut stake_history = StakeHistory::default();
1733
1734 let bootstrap_delegation = if let Some(bootstrap) = bootstrap {
1735 vec![Delegation {
1736 activation_epoch: std::u64::MAX,
1737 stake: bootstrap,
1738 ..Delegation::default()
1739 }]
1740 } else {
1741 vec![]
1742 };
1743
1744 for epoch in epochs {
1745 let entry = new_stake_history_entry(
1746 epoch,
1747 delegations.iter().chain(bootstrap_delegation.iter()),
1748 Some(&stake_history),
1749 );
1750 stake_history.add(epoch, entry);
1751 }
1752
1753 stake_history
1754 }
1755
1756 #[test]
1757 fn test_stake_activating_and_deactivating() {
1758 let stake = Delegation {
1759 stake: 1_000,
1760 activation_epoch: 0, deactivation_epoch: 5,
1762 ..Delegation::default()
1763 };
1764
1765 let increment = (1_000_f64 * stake.warmup_cooldown_rate) as u64;
1767
1768 let mut stake_history = StakeHistory::default();
1769 assert_eq!(
1771 stake.stake_activating_and_deactivating(stake.activation_epoch, Some(&stake_history),),
1772 (0, stake.stake, 0)
1773 );
1774 for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
1775 assert_eq!(
1776 stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
1777 (stake.stake, 0, 0)
1778 );
1779 }
1780 assert_eq!(
1782 stake
1783 .stake_activating_and_deactivating(stake.deactivation_epoch, Some(&stake_history),),
1784 (stake.stake, 0, stake.stake)
1785 );
1786 assert_eq!(
1788 stake.stake_activating_and_deactivating(
1789 stake.deactivation_epoch + 1,
1790 Some(&stake_history),
1791 ),
1792 (0, 0, 0)
1793 );
1794
1795 stake_history.add(
1796 0u64, StakeHistoryEntry {
1798 effective: 1_000,
1799 activating: 0,
1800 ..StakeHistoryEntry::default()
1801 },
1802 );
1803 assert_eq!(
1805 stake.stake_activating_and_deactivating(1, Some(&stake_history)),
1806 (0, stake.stake, 0)
1807 );
1808
1809 stake_history.add(
1810 0u64, StakeHistoryEntry {
1812 effective: 1_000,
1813 activating: 1_000,
1814 ..StakeHistoryEntry::default()
1815 },
1816 );
1818 assert_eq!(
1820 stake.stake_activating_and_deactivating(2, Some(&stake_history)),
1821 (increment, stake.stake - increment, 0)
1822 );
1823
1824 let mut stake_history = StakeHistory::default();
1826
1827 stake_history.add(
1828 stake.deactivation_epoch, StakeHistoryEntry {
1830 effective: 1_000,
1831 activating: 0,
1832 ..StakeHistoryEntry::default()
1833 },
1834 );
1835 assert_eq!(
1837 stake.stake_activating_and_deactivating(
1838 stake.deactivation_epoch + 1,
1839 Some(&stake_history),
1840 ),
1841 (stake.stake, 0, stake.stake) );
1843
1844 stake_history.add(
1846 stake.deactivation_epoch, StakeHistoryEntry {
1848 effective: 1_000,
1849 deactivating: 1_000,
1850 ..StakeHistoryEntry::default()
1851 },
1852 );
1853 assert_eq!(
1855 stake.stake_activating_and_deactivating(
1856 stake.deactivation_epoch + 2,
1857 Some(&stake_history),
1858 ),
1859 (stake.stake - increment, 0, stake.stake - increment) );
1861 }
1862
1863 mod same_epoch_activation_then_deactivation {
1864 use super::*;
1865
1866 enum OldDeactivationBehavior {
1867 Stuck,
1868 Slow,
1869 }
1870
1871 fn do_test(old_behavior: OldDeactivationBehavior, expected_stakes: &[(u64, u64, u64)]) {
1872 let cluster_stake = 1_000;
1873 let activating_stake = 10_000;
1874 let some_stake = 700;
1875 let some_epoch = 0;
1876
1877 let stake = Delegation {
1878 stake: some_stake,
1879 activation_epoch: some_epoch,
1880 deactivation_epoch: some_epoch,
1881 ..Delegation::default()
1882 };
1883
1884 let mut stake_history = StakeHistory::default();
1885 let cluster_deactivation_at_stake_modified_epoch = match old_behavior {
1886 OldDeactivationBehavior::Stuck => 0,
1887 OldDeactivationBehavior::Slow => 1000,
1888 };
1889
1890 let stake_history_entries = vec![
1891 (
1892 cluster_stake,
1893 activating_stake,
1894 cluster_deactivation_at_stake_modified_epoch,
1895 ),
1896 (cluster_stake, activating_stake, 1000),
1897 (cluster_stake, activating_stake, 1000),
1898 (cluster_stake, activating_stake, 100),
1899 (cluster_stake, activating_stake, 100),
1900 (cluster_stake, activating_stake, 100),
1901 (cluster_stake, activating_stake, 100),
1902 ];
1903
1904 for (epoch, (effective, activating, deactivating)) in
1905 stake_history_entries.into_iter().enumerate()
1906 {
1907 stake_history.add(
1908 epoch as Epoch,
1909 StakeHistoryEntry {
1910 effective,
1911 activating,
1912 deactivating,
1913 },
1914 );
1915 }
1916
1917 assert_eq!(
1918 expected_stakes,
1919 (0..expected_stakes.len())
1920 .map(|epoch| stake
1921 .stake_activating_and_deactivating(epoch as u64, Some(&stake_history),))
1922 .collect::<Vec<_>>()
1923 );
1924 }
1925
1926 #[test]
1927 fn test_new_behavior_previously_slow() {
1928 do_test(
1932 OldDeactivationBehavior::Slow,
1933 &[
1934 (0, 0, 0),
1935 (0, 0, 0),
1936 (0, 0, 0),
1937 (0, 0, 0),
1938 (0, 0, 0),
1939 (0, 0, 0),
1940 (0, 0, 0),
1941 ],
1942 );
1943 }
1944
1945 #[test]
1946 fn test_new_behavior_previously_stuck() {
1947 do_test(
1951 OldDeactivationBehavior::Stuck,
1952 &[
1953 (0, 0, 0),
1954 (0, 0, 0),
1955 (0, 0, 0),
1956 (0, 0, 0),
1957 (0, 0, 0),
1958 (0, 0, 0),
1959 (0, 0, 0),
1960 ],
1961 );
1962 }
1963 }
1964
1965 #[test]
1966 fn test_inflation_and_slashing_with_activating_and_deactivating_stake() {
1967 let (delegated_stake, mut stake, stake_history) = {
1969 let cluster_stake = 1_000;
1970 let delegated_stake = 700;
1971
1972 let stake = Delegation {
1973 stake: delegated_stake,
1974 activation_epoch: 0,
1975 deactivation_epoch: 4,
1976 ..Delegation::default()
1977 };
1978
1979 let mut stake_history = StakeHistory::default();
1980 stake_history.add(
1981 0,
1982 StakeHistoryEntry {
1983 effective: cluster_stake,
1984 activating: delegated_stake,
1985 ..StakeHistoryEntry::default()
1986 },
1987 );
1988 let newly_effective_at_epoch1 = (cluster_stake as f64 * 0.25) as u64;
1989 assert_eq!(newly_effective_at_epoch1, 250);
1990 stake_history.add(
1991 1,
1992 StakeHistoryEntry {
1993 effective: cluster_stake + newly_effective_at_epoch1,
1994 activating: delegated_stake - newly_effective_at_epoch1,
1995 ..StakeHistoryEntry::default()
1996 },
1997 );
1998 let newly_effective_at_epoch2 =
1999 ((cluster_stake + newly_effective_at_epoch1) as f64 * 0.25) as u64;
2000 assert_eq!(newly_effective_at_epoch2, 312);
2001 stake_history.add(
2002 2,
2003 StakeHistoryEntry {
2004 effective: cluster_stake
2005 + newly_effective_at_epoch1
2006 + newly_effective_at_epoch2,
2007 activating: delegated_stake
2008 - newly_effective_at_epoch1
2009 - newly_effective_at_epoch2,
2010 ..StakeHistoryEntry::default()
2011 },
2012 );
2013 stake_history.add(
2014 3,
2015 StakeHistoryEntry {
2016 effective: cluster_stake + delegated_stake,
2017 ..StakeHistoryEntry::default()
2018 },
2019 );
2020 stake_history.add(
2021 4,
2022 StakeHistoryEntry {
2023 effective: cluster_stake + delegated_stake,
2024 deactivating: delegated_stake,
2025 ..StakeHistoryEntry::default()
2026 },
2027 );
2028 let newly_not_effective_stake_at_epoch5 =
2029 ((cluster_stake + delegated_stake) as f64 * 0.25) as u64;
2030 assert_eq!(newly_not_effective_stake_at_epoch5, 425);
2031 stake_history.add(
2032 5,
2033 StakeHistoryEntry {
2034 effective: cluster_stake + delegated_stake
2035 - newly_not_effective_stake_at_epoch5,
2036 deactivating: delegated_stake - newly_not_effective_stake_at_epoch5,
2037 ..StakeHistoryEntry::default()
2038 },
2039 );
2040
2041 (delegated_stake, stake, stake_history)
2042 };
2043
2044 let calculate_each_staking_status = |stake: &Delegation, epoch_count: usize| -> Vec<_> {
2046 (0..epoch_count)
2047 .map(|epoch| {
2048 stake.stake_activating_and_deactivating(epoch as u64, Some(&stake_history))
2049 })
2050 .collect::<Vec<_>>()
2051 };
2052 let adjust_staking_status = |rate: f64, status: &Vec<_>| {
2053 status
2054 .clone()
2055 .into_iter()
2056 .map(|(a, b, c)| {
2057 (
2058 (a as f64 * rate) as u64,
2059 (b as f64 * rate) as u64,
2060 (c as f64 * rate) as u64,
2061 )
2062 })
2063 .collect::<Vec<_>>()
2064 };
2065
2066 let expected_staking_status_transition = vec![
2067 (0, 700, 0),
2068 (250, 450, 0),
2069 (562, 138, 0),
2070 (700, 0, 0),
2071 (700, 0, 700),
2072 (275, 0, 275),
2073 (0, 0, 0),
2074 ];
2075 let expected_staking_status_transition_base = vec![
2076 (0, 700, 0),
2077 (250, 450, 0),
2078 (562, 138 + 1, 0), (700, 0, 0),
2080 (700, 0, 700),
2081 (275 + 1, 0, 275 + 1), (0, 0, 0),
2083 ];
2084
2085 assert_eq!(
2087 expected_staking_status_transition,
2088 calculate_each_staking_status(&stake, expected_staking_status_transition.len())
2089 );
2090
2091 let rate = 1.10;
2093 stake.stake = (delegated_stake as f64 * rate) as u64;
2094 let expected_staking_status_transition =
2095 adjust_staking_status(rate, &expected_staking_status_transition_base);
2096
2097 assert_eq!(
2098 expected_staking_status_transition,
2099 calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
2100 );
2101
2102 let rate = 0.5;
2104 stake.stake = (delegated_stake as f64 * rate) as u64;
2105 let expected_staking_status_transition =
2106 adjust_staking_status(rate, &expected_staking_status_transition_base);
2107
2108 assert_eq!(
2109 expected_staking_status_transition,
2110 calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
2111 );
2112 }
2113
2114 #[test]
2115 fn test_stop_activating_after_deactivation() {
2116 gemachain_logger::setup();
2117 let stake = Delegation {
2118 stake: 1_000,
2119 activation_epoch: 0,
2120 deactivation_epoch: 3,
2121 ..Delegation::default()
2122 };
2123
2124 let base_stake = 1_000;
2125 let mut stake_history = StakeHistory::default();
2126 let mut effective = base_stake;
2127 let other_activation = 100;
2128 let mut other_activations = vec![0];
2129
2130 for epoch in 0..=stake.deactivation_epoch + 1 {
2134 let (activating, deactivating) = if epoch < stake.deactivation_epoch {
2135 (stake.stake + base_stake - effective, 0)
2136 } else {
2137 let other_activation_sum: u64 = other_activations.iter().sum();
2138 let deactivating = effective - base_stake - other_activation_sum;
2139 (other_activation, deactivating)
2140 };
2141
2142 stake_history.add(
2143 epoch,
2144 StakeHistoryEntry {
2145 effective,
2146 activating,
2147 deactivating,
2148 },
2149 );
2150
2151 let effective_rate_limited = (effective as f64 * stake.warmup_cooldown_rate) as u64;
2152 if epoch < stake.deactivation_epoch {
2153 effective += effective_rate_limited.min(activating);
2154 other_activations.push(0);
2155 } else {
2156 effective -= effective_rate_limited.min(deactivating);
2157 effective += other_activation;
2158 other_activations.push(other_activation);
2159 }
2160 }
2161
2162 for epoch in 0..=stake.deactivation_epoch + 1 {
2163 let history = stake_history.get(&epoch).unwrap();
2164 let other_activations: u64 = other_activations[..=epoch as usize].iter().sum();
2165 let expected_stake = history.effective - base_stake - other_activations;
2166 let (expected_activating, expected_deactivating) = if epoch < stake.deactivation_epoch {
2167 (history.activating, 0)
2168 } else {
2169 (0, history.deactivating)
2170 };
2171 assert_eq!(
2172 stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
2173 (expected_stake, expected_activating, expected_deactivating)
2174 );
2175 }
2176 }
2177
2178 #[test]
2179 fn test_stake_warmup_cooldown_sub_integer_moves() {
2180 let delegations = [Delegation {
2181 stake: 2,
2182 activation_epoch: 0, deactivation_epoch: 5,
2184 ..Delegation::default()
2185 }];
2186 let epochs = 7;
2188 let bootstrap = (delegations[0].warmup_cooldown_rate * 100.0 / 2.0) as u64;
2191 let stake_history =
2192 create_stake_history_from_delegations(Some(bootstrap), 0..epochs, &delegations);
2193 let mut max_stake = 0;
2194 let mut min_stake = 2;
2195
2196 for epoch in 0..epochs {
2197 let stake = delegations
2198 .iter()
2199 .map(|delegation| delegation.stake(epoch, Some(&stake_history)))
2200 .sum::<u64>();
2201 max_stake = max_stake.max(stake);
2202 min_stake = min_stake.min(stake);
2203 }
2204 assert_eq!(max_stake, 2);
2205 assert_eq!(min_stake, 0);
2206 }
2207
2208 #[test]
2209 fn test_stake_warmup_cooldown() {
2210 let delegations = [
2211 Delegation {
2212 stake: 1_000,
2214 activation_epoch: std::u64::MAX,
2215 ..Delegation::default()
2216 },
2217 Delegation {
2218 stake: 1_000,
2219 activation_epoch: 0,
2220 deactivation_epoch: 9,
2221 ..Delegation::default()
2222 },
2223 Delegation {
2224 stake: 1_000,
2225 activation_epoch: 1,
2226 deactivation_epoch: 6,
2227 ..Delegation::default()
2228 },
2229 Delegation {
2230 stake: 1_000,
2231 activation_epoch: 2,
2232 deactivation_epoch: 5,
2233 ..Delegation::default()
2234 },
2235 Delegation {
2236 stake: 1_000,
2237 activation_epoch: 2,
2238 deactivation_epoch: 4,
2239 ..Delegation::default()
2240 },
2241 Delegation {
2242 stake: 1_000,
2243 activation_epoch: 4,
2244 deactivation_epoch: 4,
2245 ..Delegation::default()
2246 },
2247 ];
2248 let epochs = 20;
2253
2254 let stake_history = create_stake_history_from_delegations(None, 0..epochs, &delegations);
2255
2256 let mut prev_total_effective_stake = delegations
2257 .iter()
2258 .map(|delegation| delegation.stake(0, Some(&stake_history)))
2259 .sum::<u64>();
2260
2261 for epoch in 1..epochs {
2264 let total_effective_stake = delegations
2265 .iter()
2266 .map(|delegation| delegation.stake(epoch, Some(&stake_history)))
2267 .sum::<u64>();
2268
2269 let delta = if total_effective_stake > prev_total_effective_stake {
2270 total_effective_stake - prev_total_effective_stake
2271 } else {
2272 prev_total_effective_stake - total_effective_stake
2273 };
2274
2275 assert!(
2281 delta
2282 <= ((prev_total_effective_stake as f64 * Config::default().warmup_cooldown_rate) as u64)
2283 .max(1)
2284 );
2285
2286 prev_total_effective_stake = total_effective_stake;
2287 }
2288 }
2289
2290 #[test]
2291 fn test_stake_initialize() {
2292 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2293 let stake_carats = 42;
2294 let stake_account =
2295 AccountSharedData::new_ref(stake_carats, std::mem::size_of::<StakeState>(), &id());
2296
2297 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2299 let custodian = gemachain_sdk::pubkey::new_rand();
2300
2301 assert_eq!(
2303 stake_keyed_account.initialize(
2304 &Authorized::default(),
2305 &Lockup::default(),
2306 &Rent {
2307 carats_per_byte_year: 42,
2308 ..Rent::free()
2309 },
2310 ),
2311 Err(InstructionError::InsufficientFunds)
2312 );
2313
2314 assert_eq!(
2316 stake_keyed_account.initialize(
2317 &Authorized::auto(&stake_pubkey),
2318 &Lockup {
2319 epoch: 1,
2320 unix_timestamp: 0,
2321 custodian
2322 },
2323 &Rent::free(),
2324 ),
2325 Ok(())
2326 );
2327 assert_eq!(
2329 from(&stake_keyed_account.account.borrow()).unwrap(),
2330 StakeState::Initialized(Meta {
2331 lockup: Lockup {
2332 unix_timestamp: 0,
2333 epoch: 1,
2334 custodian
2335 },
2336 ..Meta {
2337 authorized: Authorized::auto(&stake_pubkey),
2338 ..Meta::default()
2339 }
2340 })
2341 );
2342
2343 assert_eq!(
2345 stake_keyed_account.initialize(
2346 &Authorized::default(),
2347 &Lockup::default(),
2348 &Rent::free()
2349 ),
2350 Err(InstructionError::InvalidAccountData)
2351 );
2352 }
2353
2354 #[test]
2355 fn test_initialize_incorrect_account_sizes() {
2356 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2357 let stake_carats = 42;
2358 let stake_account = AccountSharedData::new_ref(
2359 stake_carats,
2360 std::mem::size_of::<StakeState>() + 1,
2361 &id(),
2362 );
2363 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2364
2365 assert_eq!(
2366 stake_keyed_account.initialize(
2367 &Authorized::default(),
2368 &Lockup::default(),
2369 &Rent {
2370 carats_per_byte_year: 42,
2371 ..Rent::free()
2372 },
2373 ),
2374 Err(InstructionError::InvalidAccountData)
2375 );
2376
2377 let stake_account = AccountSharedData::new_ref(
2378 stake_carats,
2379 std::mem::size_of::<StakeState>() - 1,
2380 &id(),
2381 );
2382 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2383
2384 assert_eq!(
2385 stake_keyed_account.initialize(
2386 &Authorized::default(),
2387 &Lockup::default(),
2388 &Rent {
2389 carats_per_byte_year: 42,
2390 ..Rent::free()
2391 },
2392 ),
2393 Err(InstructionError::InvalidAccountData)
2394 );
2395 }
2396
2397 #[test]
2398 fn test_deactivate() {
2399 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2400 let stake_carats = 42;
2401 let stake_account = AccountSharedData::new_ref_data_with_space(
2402 stake_carats,
2403 &StakeState::Initialized(Meta::auto(&stake_pubkey)),
2404 std::mem::size_of::<StakeState>(),
2405 &id(),
2406 )
2407 .expect("stake_account");
2408
2409 let clock = Clock {
2410 epoch: 1,
2411 ..Clock::default()
2412 };
2413
2414 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2416 let signers = vec![stake_pubkey].into_iter().collect();
2417 assert_eq!(
2418 stake_keyed_account.deactivate(&clock, &signers),
2419 Err(InstructionError::InvalidAccountData)
2420 );
2421
2422 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2424 let vote_account = RefCell::new(vote_state::create_account(
2425 &vote_pubkey,
2426 &gemachain_sdk::pubkey::new_rand(),
2427 0,
2428 100,
2429 ));
2430 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2431 vote_keyed_account
2432 .set_state(&VoteStateVersions::new_current(VoteState::default()))
2433 .unwrap();
2434 assert_eq!(
2435 stake_keyed_account.delegate(
2436 &vote_keyed_account,
2437 &clock,
2438 &StakeHistory::default(),
2439 &Config::default(),
2440 &signers,
2441 true,
2442 ),
2443 Ok(())
2444 );
2445
2446 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2448 assert_eq!(
2449 stake_keyed_account.deactivate(&clock, &HashSet::default()),
2450 Err(InstructionError::MissingRequiredSignature)
2451 );
2452
2453 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2455 assert_eq!(stake_keyed_account.deactivate(&clock, &signers), Ok(()));
2456
2457 assert_eq!(
2459 stake_keyed_account.deactivate(&clock, &signers),
2460 Err(StakeError::AlreadyDeactivated.into())
2461 );
2462 }
2463
2464 #[test]
2465 fn test_set_lockup() {
2466 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2467 let stake_carats = 42;
2468 let stake_account = AccountSharedData::new_ref_data_with_space(
2469 stake_carats,
2470 &StakeState::Uninitialized,
2471 std::mem::size_of::<StakeState>(),
2472 &id(),
2473 )
2474 .expect("stake_account");
2475
2476 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2478 assert_eq!(
2479 stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(), None),
2480 Err(InstructionError::InvalidAccountData)
2481 );
2482
2483 let custodian = gemachain_sdk::pubkey::new_rand();
2485 stake_keyed_account
2486 .initialize(
2487 &Authorized::auto(&stake_pubkey),
2488 &Lockup {
2489 unix_timestamp: 1,
2490 epoch: 1,
2491 custodian,
2492 },
2493 &Rent::free(),
2494 )
2495 .unwrap();
2496
2497 assert_eq!(
2498 stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(), None),
2499 Err(InstructionError::MissingRequiredSignature)
2500 );
2501
2502 assert_eq!(
2503 stake_keyed_account.set_lockup(
2504 &LockupArgs {
2505 unix_timestamp: Some(1),
2506 epoch: Some(1),
2507 custodian: Some(custodian),
2508 },
2509 &vec![custodian].into_iter().collect(),
2510 None
2511 ),
2512 Ok(())
2513 );
2514
2515 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2517 let vote_account = RefCell::new(vote_state::create_account(
2518 &vote_pubkey,
2519 &gemachain_sdk::pubkey::new_rand(),
2520 0,
2521 100,
2522 ));
2523 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2524 vote_keyed_account
2525 .set_state(&VoteStateVersions::new_current(VoteState::default()))
2526 .unwrap();
2527
2528 stake_keyed_account
2529 .delegate(
2530 &vote_keyed_account,
2531 &Clock::default(),
2532 &StakeHistory::default(),
2533 &Config::default(),
2534 &vec![stake_pubkey].into_iter().collect(),
2535 true,
2536 )
2537 .unwrap();
2538
2539 assert_eq!(
2540 stake_keyed_account.set_lockup(
2541 &LockupArgs {
2542 unix_timestamp: Some(1),
2543 epoch: Some(1),
2544 custodian: Some(custodian),
2545 },
2546 &HashSet::default(),
2547 None
2548 ),
2549 Err(InstructionError::MissingRequiredSignature)
2550 );
2551 assert_eq!(
2552 stake_keyed_account.set_lockup(
2553 &LockupArgs {
2554 unix_timestamp: Some(1),
2555 epoch: Some(1),
2556 custodian: Some(custodian),
2557 },
2558 &vec![custodian].into_iter().collect(),
2559 None
2560 ),
2561 Ok(())
2562 );
2563 }
2564
2565 #[test]
2566 fn test_optional_lockup_for_stake_program_v3_and_earlier() {
2567 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2568 let stake_carats = 42;
2569 let stake_account = AccountSharedData::new_ref_data_with_space(
2570 stake_carats,
2571 &StakeState::Uninitialized,
2572 std::mem::size_of::<StakeState>(),
2573 &id(),
2574 )
2575 .expect("stake_account");
2576 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2577
2578 let custodian = gemachain_sdk::pubkey::new_rand();
2579 stake_keyed_account
2580 .initialize(
2581 &Authorized::auto(&stake_pubkey),
2582 &Lockup {
2583 unix_timestamp: 1,
2584 epoch: 1,
2585 custodian,
2586 },
2587 &Rent::free(),
2588 )
2589 .unwrap();
2590
2591 assert_eq!(
2592 stake_keyed_account.set_lockup(
2593 &LockupArgs {
2594 unix_timestamp: None,
2595 epoch: None,
2596 custodian: None,
2597 },
2598 &vec![custodian].into_iter().collect(),
2599 None
2600 ),
2601 Ok(())
2602 );
2603
2604 assert_eq!(
2605 stake_keyed_account.set_lockup(
2606 &LockupArgs {
2607 unix_timestamp: Some(2),
2608 epoch: None,
2609 custodian: None,
2610 },
2611 &vec![custodian].into_iter().collect(),
2612 None
2613 ),
2614 Ok(())
2615 );
2616
2617 if let StakeState::Initialized(Meta { lockup, .. }) =
2618 from(&stake_keyed_account.account.borrow()).unwrap()
2619 {
2620 assert_eq!(lockup.unix_timestamp, 2);
2621 assert_eq!(lockup.epoch, 1);
2622 assert_eq!(lockup.custodian, custodian);
2623 } else {
2624 panic!();
2625 }
2626
2627 assert_eq!(
2628 stake_keyed_account.set_lockup(
2629 &LockupArgs {
2630 unix_timestamp: None,
2631 epoch: Some(3),
2632 custodian: None,
2633 },
2634 &vec![custodian].into_iter().collect(),
2635 None
2636 ),
2637 Ok(())
2638 );
2639
2640 if let StakeState::Initialized(Meta { lockup, .. }) =
2641 from(&stake_keyed_account.account.borrow()).unwrap()
2642 {
2643 assert_eq!(lockup.unix_timestamp, 2);
2644 assert_eq!(lockup.epoch, 3);
2645 assert_eq!(lockup.custodian, custodian);
2646 } else {
2647 panic!();
2648 }
2649
2650 let new_custodian = gemachain_sdk::pubkey::new_rand();
2651 assert_eq!(
2652 stake_keyed_account.set_lockup(
2653 &LockupArgs {
2654 unix_timestamp: None,
2655 epoch: None,
2656 custodian: Some(new_custodian),
2657 },
2658 &vec![custodian].into_iter().collect(),
2659 None
2660 ),
2661 Ok(())
2662 );
2663
2664 if let StakeState::Initialized(Meta { lockup, .. }) =
2665 from(&stake_keyed_account.account.borrow()).unwrap()
2666 {
2667 assert_eq!(lockup.unix_timestamp, 2);
2668 assert_eq!(lockup.epoch, 3);
2669 assert_eq!(lockup.custodian, new_custodian);
2670 } else {
2671 panic!();
2672 }
2673
2674 assert_eq!(
2675 stake_keyed_account.set_lockup(
2676 &LockupArgs::default(),
2677 &vec![custodian].into_iter().collect(),
2678 None
2679 ),
2680 Err(InstructionError::MissingRequiredSignature)
2681 );
2682 }
2683
2684 #[test]
2685 fn test_optional_lockup_for_stake_program_v4() {
2686 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2687 let stake_carats = 42;
2688 let stake_account = AccountSharedData::new_ref_data_with_space(
2689 stake_carats,
2690 &StakeState::Uninitialized,
2691 std::mem::size_of::<StakeState>(),
2692 &id(),
2693 )
2694 .expect("stake_account");
2695 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2696
2697 let custodian = gemachain_sdk::pubkey::new_rand();
2698 stake_keyed_account
2699 .initialize(
2700 &Authorized::auto(&stake_pubkey),
2701 &Lockup {
2702 unix_timestamp: 1,
2703 epoch: 1,
2704 custodian,
2705 },
2706 &Rent::free(),
2707 )
2708 .unwrap();
2709
2710 assert_eq!(
2712 stake_keyed_account.set_lockup(
2713 &LockupArgs {
2714 unix_timestamp: Some(2),
2715 epoch: None,
2716 custodian: None
2717 },
2718 &vec![stake_pubkey].into_iter().collect(),
2719 Some(&Clock::default())
2720 ),
2721 Err(InstructionError::MissingRequiredSignature)
2722 );
2723
2724 assert_eq!(
2726 stake_keyed_account.set_lockup(
2727 &LockupArgs {
2728 unix_timestamp: Some(2),
2729 epoch: None,
2730 custodian: None
2731 },
2732 &vec![custodian].into_iter().collect(),
2733 Some(&Clock::default())
2734 ),
2735 Ok(())
2736 );
2737
2738 assert_eq!(
2740 stake_keyed_account.set_lockup(
2741 &LockupArgs::default(),
2742 &vec![custodian].into_iter().collect(),
2743 Some(&Clock {
2744 unix_timestamp: UnixTimestamp::MAX,
2745 epoch: Epoch::MAX,
2746 ..Clock::default()
2747 })
2748 ),
2749 Err(InstructionError::MissingRequiredSignature)
2750 );
2751
2752 assert_eq!(
2754 stake_keyed_account.set_lockup(
2755 &LockupArgs::default(),
2756 &vec![stake_pubkey].into_iter().collect(),
2757 Some(&Clock {
2758 unix_timestamp: UnixTimestamp::MAX,
2759 epoch: Epoch::MAX,
2760 ..Clock::default()
2761 })
2762 ),
2763 Ok(())
2764 );
2765
2766 let new_withdraw_authority = gemachain_sdk::pubkey::new_rand();
2768 assert_eq!(
2769 stake_keyed_account.authorize(
2770 &vec![stake_pubkey].into_iter().collect(),
2771 &new_withdraw_authority,
2772 StakeAuthorize::Withdrawer,
2773 false,
2774 &Clock::default(),
2775 None
2776 ),
2777 Ok(())
2778 );
2779
2780 assert_eq!(
2782 stake_keyed_account.set_lockup(
2783 &LockupArgs::default(),
2784 &vec![stake_pubkey].into_iter().collect(),
2785 Some(&Clock {
2786 unix_timestamp: UnixTimestamp::MAX,
2787 epoch: Epoch::MAX,
2788 ..Clock::default()
2789 })
2790 ),
2791 Err(InstructionError::MissingRequiredSignature)
2792 );
2793 }
2794
2795 #[test]
2796 fn test_withdraw_stake() {
2797 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
2798 let stake_carats = 42;
2799 let stake_account = AccountSharedData::new_ref_data_with_space(
2800 stake_carats,
2801 &StakeState::Uninitialized,
2802 std::mem::size_of::<StakeState>(),
2803 &id(),
2804 )
2805 .expect("stake_account");
2806
2807 let mut clock = Clock::default();
2808
2809 let to = gemachain_sdk::pubkey::new_rand();
2810 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
2811 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2812
2813 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
2815 assert_eq!(
2816 stake_keyed_account.withdraw(
2817 stake_carats,
2818 &to_keyed_account,
2819 &clock,
2820 &StakeHistory::default(),
2821 &to_keyed_account, None,
2823 true,
2824 ),
2825 Err(InstructionError::MissingRequiredSignature)
2826 );
2827
2828 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2830 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2831 assert_eq!(
2832 stake_keyed_account.withdraw(
2833 stake_carats,
2834 &to_keyed_account,
2835 &clock,
2836 &StakeHistory::default(),
2837 &stake_keyed_account,
2838 None,
2839 true,
2840 ),
2841 Ok(())
2842 );
2843 assert_eq!(stake_account.borrow().carats(), 0);
2844 assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
2845
2846 stake_account.borrow_mut().set_carats(stake_carats);
2848
2849 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2851 let custodian = gemachain_sdk::pubkey::new_rand();
2852 stake_keyed_account
2853 .initialize(
2854 &Authorized::auto(&stake_pubkey),
2855 &Lockup {
2856 unix_timestamp: 0,
2857 epoch: 0,
2858 custodian,
2859 },
2860 &Rent::free(),
2861 )
2862 .unwrap();
2863
2864 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2866 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2867 assert_eq!(
2868 stake_keyed_account.withdraw(
2869 stake_carats + 1,
2870 &to_keyed_account,
2871 &clock,
2872 &StakeHistory::default(),
2873 &stake_keyed_account,
2874 None,
2875 true,
2876 ),
2877 Err(InstructionError::InsufficientFunds)
2878 );
2879
2880 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
2882 let vote_account = RefCell::new(vote_state::create_account(
2883 &vote_pubkey,
2884 &gemachain_sdk::pubkey::new_rand(),
2885 0,
2886 100,
2887 ));
2888 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
2889 vote_keyed_account
2890 .set_state(&VoteStateVersions::new_current(VoteState::default()))
2891 .unwrap();
2892 let signers = vec![stake_pubkey].into_iter().collect();
2893 assert_eq!(
2894 stake_keyed_account.delegate(
2895 &vote_keyed_account,
2896 &clock,
2897 &StakeHistory::default(),
2898 &Config::default(),
2899 &signers,
2900 true,
2901 ),
2902 Ok(())
2903 );
2904
2905 stake_account.borrow_mut().checked_add_carats(10).unwrap();
2907 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2909 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2910 assert_eq!(
2911 stake_keyed_account.withdraw(
2912 10,
2913 &to_keyed_account,
2914 &clock,
2915 &StakeHistory::default(),
2916 &stake_keyed_account,
2917 None,
2918 true,
2919 ),
2920 Ok(())
2921 );
2922
2923 stake_account.borrow_mut().checked_add_carats(10).unwrap();
2925 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
2927 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2928 assert_eq!(
2929 stake_keyed_account.withdraw(
2930 10 + 1,
2931 &to_keyed_account,
2932 &clock,
2933 &StakeHistory::default(),
2934 &stake_keyed_account,
2935 None,
2936 true,
2937 ),
2938 Err(InstructionError::InsufficientFunds)
2939 );
2940
2941 assert_eq!(stake_keyed_account.deactivate(&clock, &signers), Ok(()));
2943 clock.epoch += 100;
2945
2946 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2948 assert_eq!(
2949 stake_keyed_account.withdraw(
2950 stake_carats + 10 + 1,
2951 &to_keyed_account,
2952 &clock,
2953 &StakeHistory::default(),
2954 &stake_keyed_account,
2955 None,
2956 true,
2957 ),
2958 Err(InstructionError::InsufficientFunds)
2959 );
2960
2961 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
2963 assert_eq!(
2964 stake_keyed_account.withdraw(
2965 stake_carats + 10,
2966 &to_keyed_account,
2967 &clock,
2968 &StakeHistory::default(),
2969 &stake_keyed_account,
2970 None,
2971 true,
2972 ),
2973 Ok(())
2974 );
2975 assert_eq!(stake_account.borrow().carats(), 0);
2976 assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
2977
2978 let rent = Rent::default();
2980 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
2981 let authority_pubkey = Pubkey::new_unique();
2982 let stake_pubkey = Pubkey::new_unique();
2983 let stake_account = AccountSharedData::new_ref_data_with_space(
2984 1_000_000_000,
2985 &StakeState::Initialized(Meta {
2986 rent_exempt_reserve,
2987 authorized: Authorized {
2988 staker: authority_pubkey,
2989 withdrawer: authority_pubkey,
2990 },
2991 lockup: Lockup::default(),
2992 }),
2993 std::mem::size_of::<StakeState>(),
2994 &id(),
2995 )
2996 .expect("stake_account");
2997
2998 let stake2_pubkey = Pubkey::new_unique();
2999 let stake2_account = AccountSharedData::new_ref_data_with_space(
3000 1_000_000_000,
3001 &StakeState::Initialized(Meta {
3002 rent_exempt_reserve,
3003 authorized: Authorized {
3004 staker: authority_pubkey,
3005 withdrawer: authority_pubkey,
3006 },
3007 lockup: Lockup::default(),
3008 }),
3009 std::mem::size_of::<StakeState>(),
3010 &id(),
3011 )
3012 .expect("stake2_account");
3013
3014 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3015 let stake2_keyed_account = KeyedAccount::new(&stake2_pubkey, false, &stake2_account);
3016 let authority_account = AccountSharedData::new_ref(42, 0, &system_program::id());
3017 let authority_keyed_account =
3018 KeyedAccount::new(&authority_pubkey, true, &authority_account);
3019
3020 assert_eq!(
3021 stake_keyed_account.withdraw(
3022 u64::MAX - 10,
3023 &stake2_keyed_account,
3024 &clock,
3025 &StakeHistory::default(),
3026 &authority_keyed_account,
3027 None,
3028 true,
3029 ),
3030 Err(InstructionError::InsufficientFunds),
3031 );
3032 }
3033
3034 #[test]
3035 fn test_withdraw_stake_before_warmup() {
3036 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3037 let total_carats = 100;
3038 let stake_carats = 42;
3039 let stake_account = AccountSharedData::new_ref_data_with_space(
3040 total_carats,
3041 &StakeState::Initialized(Meta::auto(&stake_pubkey)),
3042 std::mem::size_of::<StakeState>(),
3043 &id(),
3044 )
3045 .expect("stake_account");
3046
3047 let clock = Clock::default();
3048 let mut future = Clock::default();
3049 future.epoch += 16;
3050
3051 let to = gemachain_sdk::pubkey::new_rand();
3052 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3053 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3054
3055 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3056
3057 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
3059 let vote_account = RefCell::new(vote_state::create_account(
3060 &vote_pubkey,
3061 &gemachain_sdk::pubkey::new_rand(),
3062 0,
3063 100,
3064 ));
3065 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
3066 vote_keyed_account
3067 .set_state(&VoteStateVersions::new_current(VoteState::default()))
3068 .unwrap();
3069 let signers = vec![stake_pubkey].into_iter().collect();
3070 assert_eq!(
3071 stake_keyed_account.delegate(
3072 &vote_keyed_account,
3073 &future,
3074 &StakeHistory::default(),
3075 &Config::default(),
3076 &signers,
3077 true,
3078 ),
3079 Ok(())
3080 );
3081
3082 let stake_history = create_stake_history_from_delegations(
3083 None,
3084 0..future.epoch,
3085 &[stake_from(&stake_keyed_account.account.borrow())
3086 .unwrap()
3087 .delegation],
3088 );
3089
3090 assert_eq!(
3092 stake_keyed_account.withdraw(
3093 total_carats - stake_carats + 1,
3094 &to_keyed_account,
3095 &clock,
3096 &stake_history,
3097 &stake_keyed_account,
3098 None,
3099 true,
3100 ),
3101 Err(InstructionError::InsufficientFunds)
3102 );
3103 }
3104
3105 #[test]
3106 fn test_withdraw_stake_invalid_state() {
3107 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3108 let total_carats = 100;
3109 let stake_account = AccountSharedData::new_ref_data_with_space(
3110 total_carats,
3111 &StakeState::RewardsPool,
3112 std::mem::size_of::<StakeState>(),
3113 &id(),
3114 )
3115 .expect("stake_account");
3116
3117 let to = gemachain_sdk::pubkey::new_rand();
3118 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3119 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3120 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3121 assert_eq!(
3122 stake_keyed_account.withdraw(
3123 total_carats,
3124 &to_keyed_account,
3125 &Clock::default(),
3126 &StakeHistory::default(),
3127 &stake_keyed_account,
3128 None,
3129 true,
3130 ),
3131 Err(InstructionError::InvalidAccountData)
3132 );
3133 }
3134
3135 #[test]
3136 fn test_withdraw_lockup() {
3137 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3138 let custodian = gemachain_sdk::pubkey::new_rand();
3139 let total_carats = 100;
3140 let stake_account = AccountSharedData::new_ref_data_with_space(
3141 total_carats,
3142 &StakeState::Initialized(Meta {
3143 lockup: Lockup {
3144 unix_timestamp: 0,
3145 epoch: 1,
3146 custodian,
3147 },
3148 ..Meta::auto(&stake_pubkey)
3149 }),
3150 std::mem::size_of::<StakeState>(),
3151 &id(),
3152 )
3153 .expect("stake_account");
3154
3155 let to = gemachain_sdk::pubkey::new_rand();
3156 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3157 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3158
3159 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3160
3161 let mut clock = Clock::default();
3162
3163 assert_eq!(
3165 stake_keyed_account.withdraw(
3166 total_carats,
3167 &to_keyed_account,
3168 &clock,
3169 &StakeHistory::default(),
3170 &stake_keyed_account,
3171 None,
3172 true,
3173 ),
3174 Err(StakeError::LockupInForce.into())
3175 );
3176
3177 {
3178 let custodian_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3179 let custodian_keyed_account = KeyedAccount::new(&custodian, true, &custodian_account);
3180 assert_eq!(
3181 stake_keyed_account.withdraw(
3182 total_carats,
3183 &to_keyed_account,
3184 &clock,
3185 &StakeHistory::default(),
3186 &stake_keyed_account,
3187 Some(&custodian_keyed_account),
3188 true,
3189 ),
3190 Ok(())
3191 );
3192 }
3193 stake_keyed_account
3195 .account
3196 .borrow_mut()
3197 .set_carats(total_carats);
3198
3199 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3201 clock.epoch += 1;
3202 assert_eq!(
3203 stake_keyed_account.withdraw(
3204 total_carats,
3205 &to_keyed_account,
3206 &clock,
3207 &StakeHistory::default(),
3208 &stake_keyed_account,
3209 None,
3210 true,
3211 ),
3212 Ok(())
3213 );
3214 assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3215 }
3216
3217 #[test]
3218 fn test_withdraw_identical_authorities() {
3219 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3220 let custodian = stake_pubkey;
3221 let total_carats = 100;
3222 let stake_account = AccountSharedData::new_ref_data_with_space(
3223 total_carats,
3224 &StakeState::Initialized(Meta {
3225 lockup: Lockup {
3226 unix_timestamp: 0,
3227 epoch: 1,
3228 custodian,
3229 },
3230 ..Meta::auto(&stake_pubkey)
3231 }),
3232 std::mem::size_of::<StakeState>(),
3233 &id(),
3234 )
3235 .expect("stake_account");
3236
3237 let to = gemachain_sdk::pubkey::new_rand();
3238 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3239 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3240
3241 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3242
3243 let clock = Clock::default();
3244
3245 assert_eq!(
3247 stake_keyed_account.withdraw(
3248 total_carats,
3249 &to_keyed_account,
3250 &clock,
3251 &StakeHistory::default(),
3252 &stake_keyed_account,
3253 None,
3254 true,
3255 ),
3256 Err(StakeError::LockupInForce.into())
3257 );
3258
3259 {
3260 let custodian_keyed_account = KeyedAccount::new(&custodian, true, &stake_account);
3261 assert_eq!(
3262 stake_keyed_account.withdraw(
3263 total_carats,
3264 &to_keyed_account,
3265 &clock,
3266 &StakeHistory::default(),
3267 &stake_keyed_account,
3268 Some(&custodian_keyed_account),
3269 true,
3270 ),
3271 Ok(())
3272 );
3273 assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3274 }
3275 }
3276
3277 #[test]
3278 fn test_withdraw_rent_exempt() {
3279 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
3280 let clock = Clock::default();
3281 let rent = Rent::default();
3282 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
3283 let stake = 42;
3284 let stake_account = AccountSharedData::new_ref_data_with_space(
3285 stake + rent_exempt_reserve,
3286 &StakeState::Initialized(Meta {
3287 rent_exempt_reserve,
3288 ..Meta::auto(&stake_pubkey)
3289 }),
3290 std::mem::size_of::<StakeState>(),
3291 &id(),
3292 )
3293 .expect("stake_account");
3294
3295 let to = gemachain_sdk::pubkey::new_rand();
3296 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3297 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3298
3299 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3302 assert_eq!(
3303 stake_keyed_account.withdraw(
3304 stake,
3305 &to_keyed_account,
3306 &clock,
3307 &StakeHistory::default(),
3308 &stake_keyed_account,
3309 None,
3310 false,
3311 ),
3312 Ok(())
3313 );
3314 stake_account
3315 .borrow_mut()
3316 .checked_add_carats(stake)
3317 .unwrap(); let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3319 assert_eq!(
3320 stake_keyed_account.withdraw(
3321 stake,
3322 &to_keyed_account,
3323 &clock,
3324 &StakeHistory::default(),
3325 &stake_keyed_account,
3326 None,
3327 true,
3328 ),
3329 Err(InstructionError::InsufficientFunds)
3330 );
3331
3332 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3334 assert_eq!(
3335 stake_keyed_account.withdraw(
3336 stake + 1,
3337 &to_keyed_account,
3338 &clock,
3339 &StakeHistory::default(),
3340 &stake_keyed_account,
3341 None,
3342 false,
3343 ),
3344 Err(InstructionError::InsufficientFunds)
3345 );
3346 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3347 assert_eq!(
3348 stake_keyed_account.withdraw(
3349 stake + 1,
3350 &to_keyed_account,
3351 &clock,
3352 &StakeHistory::default(),
3353 &stake_keyed_account,
3354 None,
3355 true,
3356 ),
3357 Err(InstructionError::InsufficientFunds)
3358 );
3359
3360 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
3362 assert_eq!(
3363 stake_keyed_account.withdraw(
3364 stake + rent_exempt_reserve,
3365 &to_keyed_account,
3366 &clock,
3367 &StakeHistory::default(),
3368 &stake_keyed_account,
3369 None,
3370 true,
3371 ),
3372 Ok(())
3373 );
3374 }
3375
3376 #[test]
3377 fn test_stake_state_redeem_rewards() {
3378 let mut vote_state = VoteState::default();
3379 let stake_carats = 1;
3382 let mut stake = new_stake(
3383 stake_carats,
3384 &Pubkey::default(),
3385 &vote_state,
3386 std::u64::MAX,
3387 &Config::default(),
3388 );
3389
3390 assert_eq!(
3392 None,
3393 redeem_stake_rewards(
3394 0,
3395 &mut stake,
3396 &PointValue {
3397 rewards: 1_000_000_000,
3398 points: 1
3399 },
3400 &vote_state,
3401 None,
3402 &mut null_tracer(),
3403 true,
3404 )
3405 );
3406
3407 vote_state.increment_credits(0);
3409 vote_state.increment_credits(0);
3410
3411 assert_eq!(
3413 Some((stake_carats * 2, 0)),
3414 redeem_stake_rewards(
3415 0,
3416 &mut stake,
3417 &PointValue {
3418 rewards: 1,
3419 points: 1
3420 },
3421 &vote_state,
3422 None,
3423 &mut null_tracer(),
3424 true,
3425 )
3426 );
3427
3428 assert_eq!(
3429 stake.delegation.stake,
3430 stake_carats + (stake_carats * 2)
3431 );
3432 assert_eq!(stake.credits_observed, 2);
3433 }
3434
3435 #[test]
3436 fn test_stake_state_calculate_points_with_typical_values() {
3437 let mut vote_state = VoteState::default();
3438
3439 let stake = new_stake(
3442 native_token::gema_to_carats(10_000_000f64),
3443 &Pubkey::default(),
3444 &vote_state,
3445 std::u64::MAX,
3446 &Config::default(),
3447 );
3448
3449 assert_eq!(
3451 None,
3452 calculate_stake_rewards(
3453 0,
3454 &stake,
3455 &PointValue {
3456 rewards: 1_000_000_000,
3457 points: 1
3458 },
3459 &vote_state,
3460 None,
3461 &mut null_tracer(),
3462 true,
3463 )
3464 );
3465
3466 let epoch_slots: u128 = 14 * 24 * 3600 * 160;
3467 for _ in 0..epoch_slots {
3470 vote_state.increment_credits(0);
3471 }
3472
3473 assert_eq!(
3475 u128::from(stake.delegation.stake) * epoch_slots,
3476 calculate_stake_points(&stake, &vote_state, None, &mut null_tracer())
3477 );
3478 }
3479
3480 #[test]
3481 fn test_stake_state_calculate_rewards() {
3482 let mut vote_state = VoteState::default();
3483 let mut stake = new_stake(
3486 1,
3487 &Pubkey::default(),
3488 &vote_state,
3489 std::u64::MAX,
3490 &Config::default(),
3491 );
3492
3493 assert_eq!(
3495 None,
3496 calculate_stake_rewards(
3497 0,
3498 &stake,
3499 &PointValue {
3500 rewards: 1_000_000_000,
3501 points: 1
3502 },
3503 &vote_state,
3504 None,
3505 &mut null_tracer(),
3506 true,
3507 )
3508 );
3509
3510 vote_state.increment_credits(0);
3512 vote_state.increment_credits(0);
3513
3514 assert_eq!(
3516 Some((stake.delegation.stake * 2, 0, 2)),
3517 calculate_stake_rewards(
3518 0,
3519 &stake,
3520 &PointValue {
3521 rewards: 2,
3522 points: 2 },
3524 &vote_state,
3525 None,
3526 &mut null_tracer(),
3527 true,
3528 )
3529 );
3530
3531 stake.credits_observed = 1;
3532 assert_eq!(
3534 Some((stake.delegation.stake, 0, 2)),
3535 calculate_stake_rewards(
3536 0,
3537 &stake,
3538 &PointValue {
3539 rewards: 1,
3540 points: 1
3541 },
3542 &vote_state,
3543 None,
3544 &mut null_tracer(),
3545 true,
3546 )
3547 );
3548
3549 vote_state.increment_credits(1);
3551
3552 stake.credits_observed = 2;
3553 assert_eq!(
3555 Some((stake.delegation.stake, 0, 3)),
3556 calculate_stake_rewards(
3557 1,
3558 &stake,
3559 &PointValue {
3560 rewards: 2,
3561 points: 2
3562 },
3563 &vote_state,
3564 None,
3565 &mut null_tracer(),
3566 true,
3567 )
3568 );
3569
3570 vote_state.increment_credits(2);
3572 assert_eq!(
3574 Some((stake.delegation.stake * 2, 0, 4)),
3575 calculate_stake_rewards(
3576 2,
3577 &stake,
3578 &PointValue {
3579 rewards: 2,
3580 points: 2
3581 },
3582 &vote_state,
3583 None,
3584 &mut null_tracer(),
3585 true,
3586 )
3587 );
3588
3589 stake.credits_observed = 0;
3590 assert_eq!(
3593 Some((
3594 stake.delegation.stake * 2 + stake.delegation.stake + stake.delegation.stake, 0,
3598 4
3599 )),
3600 calculate_stake_rewards(
3601 2,
3602 &stake,
3603 &PointValue {
3604 rewards: 4,
3605 points: 4
3606 },
3607 &vote_state,
3608 None,
3609 &mut null_tracer(),
3610 true,
3611 )
3612 );
3613
3614 vote_state.commission = 1;
3617 assert_eq!(
3618 None, calculate_stake_rewards(
3620 2,
3621 &stake,
3622 &PointValue {
3623 rewards: 4,
3624 points: 4
3625 },
3626 &vote_state,
3627 None,
3628 &mut null_tracer(),
3629 true,
3630 )
3631 );
3632 vote_state.commission = 99;
3633 assert_eq!(
3634 None, calculate_stake_rewards(
3636 2,
3637 &stake,
3638 &PointValue {
3639 rewards: 4,
3640 points: 4
3641 },
3642 &vote_state,
3643 None,
3644 &mut null_tracer(),
3645 true,
3646 )
3647 );
3648
3649 assert_eq!(
3653 Some((0, 0, 4)),
3654 calculate_stake_rewards(
3655 2,
3656 &stake,
3657 &PointValue {
3658 rewards: 0,
3659 points: 4
3660 },
3661 &vote_state,
3662 None,
3663 &mut null_tracer(),
3664 true,
3665 )
3666 );
3667
3668 stake.credits_observed = 4;
3671 assert_eq!(
3672 Some((0, 0, 4)),
3673 calculate_stake_rewards(
3674 2,
3675 &stake,
3676 &PointValue {
3677 rewards: 0,
3678 points: 4
3679 },
3680 &vote_state,
3681 None,
3682 &mut null_tracer(),
3683 true,
3684 )
3685 );
3686
3687 assert_eq!(
3689 (0, 4),
3690 calculate_stake_points_and_credits(&stake, &vote_state, None, &mut null_tracer())
3691 );
3692
3693 vote_state.commission = 0;
3695 stake.credits_observed = 3;
3696 stake.delegation.activation_epoch = 1;
3697 assert_eq!(
3698 Some((
3699 stake.delegation.stake, 0,
3701 4
3702 )),
3703 calculate_stake_rewards(
3704 2,
3705 &stake,
3706 &PointValue {
3707 rewards: 1,
3708 points: 1
3709 },
3710 &vote_state,
3711 None,
3712 &mut null_tracer(),
3713 true,
3714 )
3715 );
3716
3717 stake.delegation.activation_epoch = 2;
3720 stake.credits_observed = 3;
3721 assert_eq!(
3722 Some((0, 0, 4)),
3723 calculate_stake_rewards(
3724 2,
3725 &stake,
3726 &PointValue {
3727 rewards: 1,
3728 points: 1
3729 },
3730 &vote_state,
3731 None,
3732 &mut null_tracer(),
3733 true,
3734 )
3735 );
3736 }
3737
3738 #[test]
3739 fn test_authorize_uninit() {
3740 let new_authority = gemachain_sdk::pubkey::new_rand();
3741 let stake_carats = 42;
3742 let stake_account = AccountSharedData::new_ref_data_with_space(
3743 stake_carats,
3744 &StakeState::default(),
3745 std::mem::size_of::<StakeState>(),
3746 &id(),
3747 )
3748 .expect("stake_account");
3749
3750 let stake_keyed_account = KeyedAccount::new(&new_authority, true, &stake_account);
3751 let signers = vec![new_authority].into_iter().collect();
3752 assert_eq!(
3753 stake_keyed_account.authorize(
3754 &signers,
3755 &new_authority,
3756 StakeAuthorize::Staker,
3757 false,
3758 &Clock::default(),
3759 None
3760 ),
3761 Err(InstructionError::InvalidAccountData)
3762 );
3763 }
3764
3765 #[test]
3766 fn test_authorize_lockup() {
3767 let stake_authority = gemachain_sdk::pubkey::new_rand();
3768 let stake_carats = 42;
3769 let stake_account = AccountSharedData::new_ref_data_with_space(
3770 stake_carats,
3771 &StakeState::Initialized(Meta::auto(&stake_authority)),
3772 std::mem::size_of::<StakeState>(),
3773 &id(),
3774 )
3775 .expect("stake_account");
3776
3777 let to = gemachain_sdk::pubkey::new_rand();
3778 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
3779 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3780
3781 let clock = Clock::default();
3782 let stake_keyed_account = KeyedAccount::new(&stake_authority, true, &stake_account);
3783
3784 let stake_pubkey0 = gemachain_sdk::pubkey::new_rand();
3785 let signers = vec![stake_authority].into_iter().collect();
3786 assert_eq!(
3787 stake_keyed_account.authorize(
3788 &signers,
3789 &stake_pubkey0,
3790 StakeAuthorize::Staker,
3791 false,
3792 &Clock::default(),
3793 None
3794 ),
3795 Ok(())
3796 );
3797 assert_eq!(
3798 stake_keyed_account.authorize(
3799 &signers,
3800 &stake_pubkey0,
3801 StakeAuthorize::Withdrawer,
3802 false,
3803 &Clock::default(),
3804 None
3805 ),
3806 Ok(())
3807 );
3808 if let StakeState::Initialized(Meta { authorized, .. }) =
3809 from(&stake_keyed_account.account.borrow()).unwrap()
3810 {
3811 assert_eq!(authorized.staker, stake_pubkey0);
3812 assert_eq!(authorized.withdrawer, stake_pubkey0);
3813 } else {
3814 panic!();
3815 }
3816
3817 let stake_pubkey1 = gemachain_sdk::pubkey::new_rand();
3819 assert_eq!(
3820 stake_keyed_account.authorize(
3821 &signers,
3822 &stake_pubkey1,
3823 StakeAuthorize::Staker,
3824 false,
3825 &Clock::default(),
3826 None
3827 ),
3828 Err(InstructionError::MissingRequiredSignature)
3829 );
3830
3831 let signers0 = vec![stake_pubkey0].into_iter().collect();
3832
3833 let stake_pubkey2 = gemachain_sdk::pubkey::new_rand();
3835 assert_eq!(
3836 stake_keyed_account.authorize(
3837 &signers0,
3838 &stake_pubkey2,
3839 StakeAuthorize::Staker,
3840 false,
3841 &Clock::default(),
3842 None
3843 ),
3844 Ok(())
3845 );
3846 if let StakeState::Initialized(Meta { authorized, .. }) =
3847 from(&stake_keyed_account.account.borrow()).unwrap()
3848 {
3849 assert_eq!(authorized.staker, stake_pubkey2);
3850 }
3851
3852 assert_eq!(
3853 stake_keyed_account.authorize(
3854 &signers0,
3855 &stake_pubkey2,
3856 StakeAuthorize::Withdrawer,
3857 false,
3858 &Clock::default(),
3859 None
3860 ),
3861 Ok(())
3862 );
3863 if let StakeState::Initialized(Meta { authorized, .. }) =
3864 from(&stake_keyed_account.account.borrow()).unwrap()
3865 {
3866 assert_eq!(authorized.staker, stake_pubkey2);
3867 }
3868
3869 assert_eq!(
3871 stake_keyed_account.withdraw(
3872 stake_carats,
3873 &to_keyed_account,
3874 &clock,
3875 &StakeHistory::default(),
3876 &stake_keyed_account, None,
3878 true,
3879 ),
3880 Err(InstructionError::MissingRequiredSignature)
3881 );
3882
3883 let stake_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &stake_account);
3884
3885 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
3887 assert_eq!(
3888 stake_keyed_account.withdraw(
3889 stake_carats,
3890 &to_keyed_account,
3891 &clock,
3892 &StakeHistory::default(),
3893 &stake_keyed_account2,
3894 None,
3895 true,
3896 ),
3897 Ok(())
3898 );
3899 assert_eq!(stake_keyed_account.state(), Ok(StakeState::Uninitialized));
3900 }
3901
3902 #[test]
3903 fn test_authorize_with_seed() {
3904 let base_pubkey = gemachain_sdk::pubkey::new_rand();
3905 let seed = "42";
3906 let withdrawer_pubkey = Pubkey::create_with_seed(&base_pubkey, seed, &id()).unwrap();
3907 let stake_carats = 42;
3908 let stake_account = AccountSharedData::new_ref_data_with_space(
3909 stake_carats,
3910 &StakeState::Initialized(Meta::auto(&withdrawer_pubkey)),
3911 std::mem::size_of::<StakeState>(),
3912 &id(),
3913 )
3914 .expect("stake_account");
3915
3916 let base_account = AccountSharedData::new_ref(1, 0, &id());
3917 let base_keyed_account = KeyedAccount::new(&base_pubkey, true, &base_account);
3918
3919 let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
3920
3921 let new_authority = gemachain_sdk::pubkey::new_rand();
3922
3923 assert_eq!(
3925 stake_keyed_account.authorize_with_seed(
3926 &base_keyed_account,
3927 "",
3928 &id(),
3929 &new_authority,
3930 StakeAuthorize::Staker,
3931 false,
3932 &Clock::default(),
3933 None,
3934 ),
3935 Err(InstructionError::MissingRequiredSignature)
3936 );
3937
3938 assert_eq!(
3940 stake_keyed_account.authorize_with_seed(
3941 &stake_keyed_account,
3942 seed,
3943 &id(),
3944 &new_authority,
3945 StakeAuthorize::Staker,
3946 false,
3947 &Clock::default(),
3948 None,
3949 ),
3950 Err(InstructionError::MissingRequiredSignature)
3951 );
3952
3953 assert_eq!(
3955 stake_keyed_account.authorize_with_seed(
3956 &base_keyed_account,
3957 seed,
3958 &id(),
3959 &new_authority,
3960 StakeAuthorize::Staker,
3961 false,
3962 &Clock::default(),
3963 None,
3964 ),
3965 Ok(())
3966 );
3967
3968 assert_eq!(
3970 stake_keyed_account.authorize_with_seed(
3971 &base_keyed_account,
3972 seed,
3973 &id(),
3974 &new_authority,
3975 StakeAuthorize::Withdrawer,
3976 false,
3977 &Clock::default(),
3978 None,
3979 ),
3980 Ok(())
3981 );
3982
3983 assert_eq!(
3985 stake_keyed_account.authorize_with_seed(
3986 &stake_keyed_account,
3987 seed,
3988 &id(),
3989 &new_authority,
3990 StakeAuthorize::Withdrawer,
3991 false,
3992 &Clock::default(),
3993 None,
3994 ),
3995 Err(InstructionError::MissingRequiredSignature)
3996 );
3997 }
3998
3999 #[test]
4000 fn test_authorize_override() {
4001 let withdrawer_pubkey = gemachain_sdk::pubkey::new_rand();
4002 let stake_carats = 42;
4003 let stake_account = AccountSharedData::new_ref_data_with_space(
4004 stake_carats,
4005 &StakeState::Initialized(Meta::auto(&withdrawer_pubkey)),
4006 std::mem::size_of::<StakeState>(),
4007 &id(),
4008 )
4009 .expect("stake_account");
4010
4011 let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
4012
4013 let new_authority = gemachain_sdk::pubkey::new_rand();
4015 let signers = vec![withdrawer_pubkey].into_iter().collect();
4016 assert_eq!(
4017 stake_keyed_account.authorize(
4018 &signers,
4019 &new_authority,
4020 StakeAuthorize::Staker,
4021 false,
4022 &Clock::default(),
4023 None
4024 ),
4025 Ok(())
4026 );
4027
4028 let mallory_pubkey = gemachain_sdk::pubkey::new_rand();
4030 let signers = vec![new_authority].into_iter().collect();
4031 assert_eq!(
4032 stake_keyed_account.authorize(
4033 &signers,
4034 &mallory_pubkey,
4035 StakeAuthorize::Staker,
4036 false,
4037 &Clock::default(),
4038 None
4039 ),
4040 Ok(())
4041 );
4042
4043 let new_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4045 assert_eq!(
4046 stake_keyed_account.authorize(
4047 &signers,
4048 &new_stake_pubkey,
4049 StakeAuthorize::Staker,
4050 false,
4051 &Clock::default(),
4052 None
4053 ),
4054 Err(InstructionError::MissingRequiredSignature)
4055 );
4056
4057 let signers = vec![withdrawer_pubkey].into_iter().collect();
4059 assert_eq!(
4060 stake_keyed_account.authorize(
4061 &signers,
4062 &new_stake_pubkey,
4063 StakeAuthorize::Withdrawer,
4064 false,
4065 &Clock::default(),
4066 None
4067 ),
4068 Ok(())
4069 );
4070
4071 let signers = vec![new_stake_pubkey].into_iter().collect();
4073 assert_eq!(
4074 stake_keyed_account.authorize(
4075 &signers,
4076 &mallory_pubkey,
4077 StakeAuthorize::Withdrawer,
4078 false,
4079 &Clock::default(),
4080 None
4081 ),
4082 Ok(())
4083 );
4084 }
4085
4086 #[test]
4087 fn test_split_source_uninitialized() {
4088 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4089 let stake_carats = 42;
4090 let stake_account = AccountSharedData::new_ref_data_with_space(
4091 stake_carats,
4092 &StakeState::Uninitialized,
4093 std::mem::size_of::<StakeState>(),
4094 &id(),
4095 )
4096 .expect("stake_account");
4097
4098 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4099 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4100 0,
4101 &StakeState::Uninitialized,
4102 std::mem::size_of::<StakeState>(),
4103 &id(),
4104 )
4105 .expect("stake_account");
4106
4107 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
4108 let split_stake_keyed_account =
4109 KeyedAccount::new(&split_stake_pubkey, false, &split_stake_account);
4110
4111 assert_eq!(
4113 stake_keyed_account.split(
4114 stake_carats / 2,
4115 &split_stake_keyed_account,
4116 &HashSet::default() ),
4118 Err(InstructionError::MissingRequiredSignature)
4119 );
4120
4121 let signers = vec![stake_pubkey].into_iter().collect();
4123 assert_eq!(
4124 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4125 Ok(())
4126 );
4127 assert_eq!(
4128 stake_keyed_account.account.borrow().carats(),
4129 split_stake_keyed_account.account.borrow().carats()
4130 );
4131 }
4132
4133 #[test]
4134 fn test_split_split_not_uninitialized() {
4135 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4136 let stake_carats = 42;
4137 let stake_account = AccountSharedData::new_ref_data_with_space(
4138 stake_carats,
4139 &StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4140 std::mem::size_of::<StakeState>(),
4141 &id(),
4142 )
4143 .expect("stake_account");
4144
4145 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4146 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4147 0,
4148 &StakeState::Initialized(Meta::auto(&stake_pubkey)),
4149 std::mem::size_of::<StakeState>(),
4150 &id(),
4151 )
4152 .expect("stake_account");
4153
4154 let signers = vec![stake_pubkey].into_iter().collect();
4155 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4156 let split_stake_keyed_account =
4157 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4158 assert_eq!(
4159 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4160 Err(InstructionError::InvalidAccountData)
4161 );
4162 }
4163 fn just_stake(stake: u64) -> Stake {
4164 Stake {
4165 delegation: Delegation {
4166 stake,
4167 ..Delegation::default()
4168 },
4169 ..Stake::default()
4170 }
4171 }
4172
4173 #[test]
4174 fn test_split_more_than_staked() {
4175 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4176 let stake_carats = 42;
4177 let stake_account = AccountSharedData::new_ref_data_with_space(
4178 stake_carats,
4179 &StakeState::Stake(
4180 Meta::auto(&stake_pubkey),
4181 just_stake(stake_carats / 2 - 1),
4182 ),
4183 std::mem::size_of::<StakeState>(),
4184 &id(),
4185 )
4186 .expect("stake_account");
4187
4188 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4189 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4190 0,
4191 &StakeState::Uninitialized,
4192 std::mem::size_of::<StakeState>(),
4193 &id(),
4194 )
4195 .expect("stake_account");
4196
4197 let signers = vec![stake_pubkey].into_iter().collect();
4198 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4199 let split_stake_keyed_account =
4200 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4201 assert_eq!(
4202 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4203 Err(StakeError::InsufficientStake.into())
4204 );
4205 }
4206
4207 #[test]
4208 fn test_split_with_rent() {
4209 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4210 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4211 let stake_carats = 10_000_000;
4212 let rent_exempt_reserve = 2_282_880;
4213 let signers = vec![stake_pubkey].into_iter().collect();
4214
4215 let meta = Meta {
4216 authorized: Authorized::auto(&stake_pubkey),
4217 rent_exempt_reserve,
4218 ..Meta::default()
4219 };
4220
4221 for state in &[
4223 StakeState::Initialized(meta),
4224 StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4225 ] {
4226 let stake_account = AccountSharedData::new_ref_data_with_space(
4227 stake_carats,
4228 state,
4229 std::mem::size_of::<StakeState>(),
4230 &id(),
4231 )
4232 .expect("stake_account");
4233
4234 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4235
4236 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4237 0,
4238 &StakeState::Uninitialized,
4239 std::mem::size_of::<StakeState>(),
4240 &id(),
4241 )
4242 .expect("stake_account");
4243
4244 let split_stake_keyed_account =
4245 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4246
4247 assert_eq!(
4249 stake_keyed_account.split(
4250 rent_exempt_reserve,
4251 &split_stake_keyed_account,
4252 &signers
4253 ),
4254 Err(InstructionError::InsufficientFunds)
4255 );
4256
4257 assert_eq!(
4259 stake_keyed_account.split(
4260 stake_carats - rent_exempt_reserve,
4261 &split_stake_keyed_account,
4262 &signers
4263 ),
4264 Err(InstructionError::InsufficientFunds)
4265 );
4266
4267 split_stake_keyed_account
4269 .account
4270 .borrow_mut()
4271 .set_carats(10_000_000);
4272 assert_eq!(
4273 stake_keyed_account.split(
4274 stake_carats - (rent_exempt_reserve + 1), &split_stake_keyed_account,
4276 &signers
4277 ),
4278 Ok(())
4279 );
4280
4281 if let StakeState::Stake(meta, stake) = state {
4283 assert_eq!(
4284 split_stake_keyed_account.state(),
4285 Ok(StakeState::Stake(
4286 *meta,
4287 Stake {
4288 delegation: Delegation {
4289 stake: stake_carats - rent_exempt_reserve - 1,
4290 ..stake.delegation
4291 },
4292 ..*stake
4293 }
4294 ))
4295 );
4296 assert_eq!(
4297 stake_keyed_account.account.borrow().carats(),
4298 rent_exempt_reserve + 1
4299 );
4300 assert_eq!(
4301 split_stake_keyed_account.account.borrow().carats(),
4302 10_000_000 + stake_carats - rent_exempt_reserve - 1
4303 );
4304 }
4305 }
4306 }
4307
4308 #[test]
4309 fn test_split() {
4310 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4311 let stake_carats = 42;
4312
4313 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4314 let signers = vec![stake_pubkey].into_iter().collect();
4315
4316 for state in &[
4318 StakeState::Initialized(Meta::auto(&stake_pubkey)),
4319 StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4320 ] {
4321 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4322 0,
4323 &StakeState::Uninitialized,
4324 std::mem::size_of::<StakeState>(),
4325 &id(),
4326 )
4327 .expect("stake_account");
4328
4329 let split_stake_keyed_account =
4330 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4331
4332 let stake_account = AccountSharedData::new_ref_data_with_space(
4333 stake_carats,
4334 state,
4335 std::mem::size_of::<StakeState>(),
4336 &id(),
4337 )
4338 .expect("stake_account");
4339 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4340
4341 assert_eq!(
4343 stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4344 Err(InstructionError::InsufficientFunds)
4345 );
4346
4347 assert_eq!(
4349 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4350 Ok(())
4351 );
4352 assert_eq!(
4354 stake_keyed_account.account.borrow().carats()
4355 + split_stake_keyed_account.account.borrow().carats(),
4356 stake_carats
4357 );
4358
4359 match state {
4360 StakeState::Initialized(_) => {
4361 assert_eq!(Ok(*state), split_stake_keyed_account.state());
4362 assert_eq!(Ok(*state), stake_keyed_account.state());
4363 }
4364 StakeState::Stake(meta, stake) => {
4365 assert_eq!(
4366 Ok(StakeState::Stake(
4367 *meta,
4368 Stake {
4369 delegation: Delegation {
4370 stake: stake_carats / 2,
4371 ..stake.delegation
4372 },
4373 ..*stake
4374 }
4375 )),
4376 split_stake_keyed_account.state()
4377 );
4378 assert_eq!(
4379 Ok(StakeState::Stake(
4380 *meta,
4381 Stake {
4382 delegation: Delegation {
4383 stake: stake_carats / 2,
4384 ..stake.delegation
4385 },
4386 ..*stake
4387 }
4388 )),
4389 stake_keyed_account.state()
4390 );
4391 }
4392 _ => unreachable!(),
4393 }
4394
4395 stake_keyed_account
4397 .account
4398 .borrow_mut()
4399 .set_carats(stake_carats);
4400 }
4401 }
4402
4403 #[test]
4404 fn test_split_fake_stake_dest() {
4405 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4406 let stake_carats = 42;
4407
4408 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4409 let signers = vec![stake_pubkey].into_iter().collect();
4410
4411 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4412 0,
4413 &StakeState::Uninitialized,
4414 std::mem::size_of::<StakeState>(),
4415 &gemachain_sdk::pubkey::new_rand(),
4416 )
4417 .expect("stake_account");
4418
4419 let split_stake_keyed_account =
4420 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4421
4422 let stake_account = AccountSharedData::new_ref_data_with_space(
4423 stake_carats,
4424 &StakeState::Stake(Meta::auto(&stake_pubkey), just_stake(stake_carats)),
4425 std::mem::size_of::<StakeState>(),
4426 &id(),
4427 )
4428 .expect("stake_account");
4429 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4430
4431 assert_eq!(
4432 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4433 Err(InstructionError::IncorrectProgramId),
4434 );
4435 }
4436
4437 #[test]
4438 fn test_split_to_account_with_rent_exempt_reserve() {
4439 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4440 let rent = Rent::default();
4441 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4442 let stake_carats = rent_exempt_reserve * 3; let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4445 let signers = vec![stake_pubkey].into_iter().collect();
4446
4447 let meta = Meta {
4448 authorized: Authorized::auto(&stake_pubkey),
4449 rent_exempt_reserve,
4450 ..Meta::default()
4451 };
4452
4453 let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4454 let split_carat_balances = vec![0, 1, rent_exempt_reserve, rent_exempt_reserve + 1];
4458 for initial_balance in split_carat_balances {
4459 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4460 initial_balance,
4461 &StakeState::Uninitialized,
4462 std::mem::size_of::<StakeState>(),
4463 &id(),
4464 )
4465 .expect("stake_account");
4466
4467 let split_stake_keyed_account =
4468 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4469
4470 let stake_account = AccountSharedData::new_ref_data_with_space(
4471 stake_carats,
4472 &state,
4473 std::mem::size_of::<StakeState>(),
4474 &id(),
4475 )
4476 .expect("stake_account");
4477 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4478
4479 assert_eq!(
4481 stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4482 Err(InstructionError::InsufficientFunds)
4483 );
4484
4485 assert_eq!(
4487 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4488 Ok(())
4489 );
4490 assert_eq!(
4492 stake_keyed_account.account.borrow().carats()
4493 + split_stake_keyed_account.account.borrow().carats(),
4494 stake_carats + initial_balance
4495 );
4496
4497 if let StakeState::Stake(meta, stake) = state {
4498 let expected_stake =
4499 stake_carats / 2 - (rent_exempt_reserve.saturating_sub(initial_balance));
4500 assert_eq!(
4501 Ok(StakeState::Stake(
4502 meta,
4503 Stake {
4504 delegation: Delegation {
4505 stake: stake_carats / 2
4506 - (rent_exempt_reserve.saturating_sub(initial_balance)),
4507 ..stake.delegation
4508 },
4509 ..stake
4510 }
4511 )),
4512 split_stake_keyed_account.state()
4513 );
4514 assert_eq!(
4515 split_stake_keyed_account.account.borrow().carats(),
4516 expected_stake
4517 + rent_exempt_reserve
4518 + initial_balance.saturating_sub(rent_exempt_reserve)
4519 );
4520 assert_eq!(
4521 Ok(StakeState::Stake(
4522 meta,
4523 Stake {
4524 delegation: Delegation {
4525 stake: stake_carats / 2 - rent_exempt_reserve,
4526 ..stake.delegation
4527 },
4528 ..stake
4529 }
4530 )),
4531 stake_keyed_account.state()
4532 );
4533 }
4534 }
4535 }
4536
4537 #[test]
4538 fn test_split_to_smaller_account_with_rent_exempt_reserve() {
4539 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4540 let rent = Rent::default();
4541 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4542 let stake_carats = rent_exempt_reserve * 3; let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4545 let signers = vec![stake_pubkey].into_iter().collect();
4546
4547 let meta = Meta {
4548 authorized: Authorized::auto(&stake_pubkey),
4549 rent_exempt_reserve,
4550 ..Meta::default()
4551 };
4552
4553 let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4554
4555 let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4556 meta.rent_exempt_reserve,
4557 std::mem::size_of::<StakeState>() as u64 + 100,
4558 std::mem::size_of::<StakeState>() as u64,
4559 );
4560
4561 let split_carat_balances = vec![
4565 0,
4566 1,
4567 expected_rent_exempt_reserve,
4568 expected_rent_exempt_reserve + 1,
4569 ];
4570 for initial_balance in split_carat_balances {
4571 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4572 initial_balance,
4573 &StakeState::Uninitialized,
4574 std::mem::size_of::<StakeState>(),
4575 &id(),
4576 )
4577 .expect("stake_account");
4578
4579 let split_stake_keyed_account =
4580 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4581
4582 let stake_account = AccountSharedData::new_ref_data_with_space(
4583 stake_carats,
4584 &state,
4585 std::mem::size_of::<StakeState>() + 100,
4586 &id(),
4587 )
4588 .expect("stake_account");
4589 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4590
4591 assert_eq!(
4593 stake_keyed_account.split(stake_carats + 1, &split_stake_keyed_account, &signers),
4594 Err(InstructionError::InsufficientFunds)
4595 );
4596
4597 assert_eq!(
4599 stake_keyed_account.split(stake_carats / 2, &split_stake_keyed_account, &signers),
4600 Ok(())
4601 );
4602 assert_eq!(
4604 stake_keyed_account.account.borrow().carats()
4605 + split_stake_keyed_account.account.borrow().carats(),
4606 stake_carats + initial_balance
4607 );
4608
4609 if let StakeState::Stake(meta, stake) = state {
4610 let expected_split_meta = Meta {
4611 authorized: Authorized::auto(&stake_pubkey),
4612 rent_exempt_reserve: expected_rent_exempt_reserve,
4613 ..Meta::default()
4614 };
4615 let expected_stake = stake_carats / 2
4616 - (expected_rent_exempt_reserve.saturating_sub(initial_balance));
4617
4618 assert_eq!(
4619 Ok(StakeState::Stake(
4620 expected_split_meta,
4621 Stake {
4622 delegation: Delegation {
4623 stake: expected_stake,
4624 ..stake.delegation
4625 },
4626 ..stake
4627 }
4628 )),
4629 split_stake_keyed_account.state()
4630 );
4631 assert_eq!(
4632 split_stake_keyed_account.account.borrow().carats(),
4633 expected_stake
4634 + expected_rent_exempt_reserve
4635 + initial_balance.saturating_sub(expected_rent_exempt_reserve)
4636 );
4637 assert_eq!(
4638 Ok(StakeState::Stake(
4639 meta,
4640 Stake {
4641 delegation: Delegation {
4642 stake: stake_carats / 2 - rent_exempt_reserve,
4643 ..stake.delegation
4644 },
4645 ..stake
4646 }
4647 )),
4648 stake_keyed_account.state()
4649 );
4650 }
4651 }
4652 }
4653
4654 #[test]
4655 fn test_split_to_larger_account() {
4656 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4657 let rent = Rent::default();
4658 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4659
4660 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4661 let signers = vec![stake_pubkey].into_iter().collect();
4662
4663 let meta = Meta {
4664 authorized: Authorized::auto(&stake_pubkey),
4665 rent_exempt_reserve,
4666 ..Meta::default()
4667 };
4668
4669 let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4670 meta.rent_exempt_reserve,
4671 std::mem::size_of::<StakeState>() as u64,
4672 std::mem::size_of::<StakeState>() as u64 + 100,
4673 );
4674 let stake_carats = expected_rent_exempt_reserve + 1;
4675 let split_amount = stake_carats - (rent_exempt_reserve + 1); let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4678
4679 let split_carat_balances = vec![
4680 0,
4681 1,
4682 expected_rent_exempt_reserve,
4683 expected_rent_exempt_reserve + 1,
4684 ];
4685 for initial_balance in split_carat_balances {
4686 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4687 initial_balance,
4688 &StakeState::Uninitialized,
4689 std::mem::size_of::<StakeState>() + 100,
4690 &id(),
4691 )
4692 .expect("stake_account");
4693
4694 let split_stake_keyed_account =
4695 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4696
4697 let stake_account = AccountSharedData::new_ref_data_with_space(
4698 stake_carats,
4699 &state,
4700 std::mem::size_of::<StakeState>(),
4701 &id(),
4702 )
4703 .expect("stake_account");
4704 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4705
4706 let split_result =
4708 stake_keyed_account.split(split_amount, &split_stake_keyed_account, &signers);
4709 assert_eq!(split_result, Err(InstructionError::InvalidAccountData));
4710
4711 let split_result =
4713 stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers);
4714 assert_eq!(split_result, Err(InstructionError::InvalidAccountData));
4715 }
4716 }
4717
4718 #[test]
4719 fn test_split_100_percent_of_source() {
4720 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4721 let rent = Rent::default();
4722 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4723 let stake_carats = rent_exempt_reserve * 3; let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4726 let signers = vec![stake_pubkey].into_iter().collect();
4727
4728 let meta = Meta {
4729 authorized: Authorized::auto(&stake_pubkey),
4730 rent_exempt_reserve,
4731 ..Meta::default()
4732 };
4733
4734 for state in &[
4736 StakeState::Initialized(meta),
4737 StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4738 ] {
4739 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4740 0,
4741 &StakeState::Uninitialized,
4742 std::mem::size_of::<StakeState>(),
4743 &id(),
4744 )
4745 .expect("stake_account");
4746
4747 let split_stake_keyed_account =
4748 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4749
4750 let stake_account = AccountSharedData::new_ref_data_with_space(
4751 stake_carats,
4752 state,
4753 std::mem::size_of::<StakeState>(),
4754 &id(),
4755 )
4756 .expect("stake_account");
4757 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4758
4759 assert_eq!(
4761 stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4762 Ok(())
4763 );
4764
4765 assert_eq!(
4767 stake_keyed_account.account.borrow().carats()
4768 + split_stake_keyed_account.account.borrow().carats(),
4769 stake_carats
4770 );
4771
4772 match state {
4773 StakeState::Initialized(_) => {
4774 assert_eq!(Ok(*state), split_stake_keyed_account.state());
4775 assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4776 }
4777 StakeState::Stake(meta, stake) => {
4778 assert_eq!(
4779 Ok(StakeState::Stake(
4780 *meta,
4781 Stake {
4782 delegation: Delegation {
4783 stake: stake_carats - rent_exempt_reserve,
4784 ..stake.delegation
4785 },
4786 ..*stake
4787 }
4788 )),
4789 split_stake_keyed_account.state()
4790 );
4791 assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4792 }
4793 _ => unreachable!(),
4794 }
4795
4796 stake_keyed_account
4798 .account
4799 .borrow_mut()
4800 .set_carats(stake_carats);
4801 }
4802 }
4803
4804 #[test]
4805 fn test_split_100_percent_of_source_to_account_with_carats() {
4806 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4807 let rent = Rent::default();
4808 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4809 let stake_carats = rent_exempt_reserve * 3; let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4812 let signers = vec![stake_pubkey].into_iter().collect();
4813
4814 let meta = Meta {
4815 authorized: Authorized::auto(&stake_pubkey),
4816 rent_exempt_reserve,
4817 ..Meta::default()
4818 };
4819
4820 let state = StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve));
4821 let split_carat_balances = vec![0, 1, rent_exempt_reserve, rent_exempt_reserve + 1];
4825 for initial_balance in split_carat_balances {
4826 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4827 initial_balance,
4828 &StakeState::Uninitialized,
4829 std::mem::size_of::<StakeState>(),
4830 &id(),
4831 )
4832 .expect("stake_account");
4833
4834 let split_stake_keyed_account =
4835 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4836
4837 let stake_account = AccountSharedData::new_ref_data_with_space(
4838 stake_carats,
4839 &state,
4840 std::mem::size_of::<StakeState>(),
4841 &id(),
4842 )
4843 .expect("stake_account");
4844 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4845
4846 assert_eq!(
4848 stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4849 Ok(())
4850 );
4851
4852 assert_eq!(
4854 stake_keyed_account.account.borrow().carats()
4855 + split_stake_keyed_account.account.borrow().carats(),
4856 stake_carats + initial_balance
4857 );
4858
4859 if let StakeState::Stake(meta, stake) = state {
4860 assert_eq!(
4861 Ok(StakeState::Stake(
4862 meta,
4863 Stake {
4864 delegation: Delegation {
4865 stake: stake_carats - rent_exempt_reserve,
4866 ..stake.delegation
4867 },
4868 ..stake
4869 }
4870 )),
4871 split_stake_keyed_account.state()
4872 );
4873 assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4874 }
4875 }
4876 }
4877
4878 #[test]
4879 fn test_split_rent_exemptness() {
4880 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
4881 let rent = Rent::default();
4882 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
4883 let stake_carats = rent_exempt_reserve + 1;
4884
4885 let split_stake_pubkey = gemachain_sdk::pubkey::new_rand();
4886 let signers = vec![stake_pubkey].into_iter().collect();
4887
4888 let meta = Meta {
4889 authorized: Authorized::auto(&stake_pubkey),
4890 rent_exempt_reserve,
4891 ..Meta::default()
4892 };
4893
4894 for state in &[
4895 StakeState::Initialized(meta),
4896 StakeState::Stake(meta, just_stake(stake_carats - rent_exempt_reserve)),
4897 ] {
4898 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4900 0,
4901 &StakeState::Uninitialized,
4902 std::mem::size_of::<StakeState>() + 10000,
4903 &id(),
4904 )
4905 .expect("stake_account");
4906 let split_stake_keyed_account =
4907 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4908
4909 let stake_account = AccountSharedData::new_ref_data_with_space(
4910 stake_carats,
4911 &state,
4912 std::mem::size_of::<StakeState>(),
4913 &id(),
4914 )
4915 .expect("stake_account");
4916 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4917
4918 assert_eq!(
4919 stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4920 Err(InstructionError::InvalidAccountData)
4921 );
4922
4923 let split_stake_account = AccountSharedData::new_ref_data_with_space(
4926 0,
4927 &StakeState::Uninitialized,
4928 std::mem::size_of::<StakeState>(),
4929 &id(),
4930 )
4931 .expect("stake_account");
4932 let split_stake_keyed_account =
4933 KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account);
4934
4935 let stake_account = AccountSharedData::new_ref_data_with_space(
4936 stake_carats,
4937 &state,
4938 std::mem::size_of::<StakeState>() + 100,
4939 &id(),
4940 )
4941 .expect("stake_account");
4942 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
4943
4944 assert_eq!(
4945 stake_keyed_account.split(stake_carats, &split_stake_keyed_account, &signers),
4946 Ok(())
4947 );
4948
4949 assert_eq!(
4950 split_stake_keyed_account.account.borrow().carats(),
4951 stake_carats
4952 );
4953
4954 let expected_rent_exempt_reserve = calculate_split_rent_exempt_reserve(
4955 meta.rent_exempt_reserve,
4956 std::mem::size_of::<StakeState>() as u64 + 100,
4957 std::mem::size_of::<StakeState>() as u64,
4958 );
4959 let expected_split_meta = Meta {
4960 authorized: Authorized::auto(&stake_pubkey),
4961 rent_exempt_reserve: expected_rent_exempt_reserve,
4962 ..Meta::default()
4963 };
4964
4965 match state {
4966 StakeState::Initialized(_) => {
4967 assert_eq!(
4968 Ok(StakeState::Initialized(expected_split_meta)),
4969 split_stake_keyed_account.state()
4970 );
4971 assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4972 }
4973 StakeState::Stake(_meta, stake) => {
4974 let expected_stake = stake_carats - rent_exempt_reserve;
4977
4978 assert_eq!(
4979 Ok(StakeState::Stake(
4980 expected_split_meta,
4981 Stake {
4982 delegation: Delegation {
4983 stake: expected_stake,
4984 ..stake.delegation
4985 },
4986 ..*stake
4987 }
4988 )),
4989 split_stake_keyed_account.state()
4990 );
4991 assert_eq!(
4992 split_stake_keyed_account.account.borrow().carats(),
4993 expected_stake
4994 + expected_rent_exempt_reserve
4995 + (rent_exempt_reserve - expected_rent_exempt_reserve)
4996 );
4997 assert_eq!(Ok(StakeState::Uninitialized), stake_keyed_account.state());
4998 }
4999 _ => unreachable!(),
5000 }
5001 }
5002 }
5003
5004 #[test]
5005 fn test_merge() {
5006 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5007 let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5008 let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5009 let stake_carats = 42;
5010
5011 let signers = vec![authorized_pubkey].into_iter().collect();
5012 let invoke_context = MockInvokeContext::new(vec![]);
5013
5014 for state in &[
5015 StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5016 StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5017 ] {
5018 for source_state in &[
5019 StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5020 StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5021 ] {
5022 let stake_account = AccountSharedData::new_ref_data_with_space(
5023 stake_carats,
5024 state,
5025 std::mem::size_of::<StakeState>(),
5026 &id(),
5027 )
5028 .expect("stake_account");
5029 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5030
5031 let source_stake_account = AccountSharedData::new_ref_data_with_space(
5032 stake_carats,
5033 source_state,
5034 std::mem::size_of::<StakeState>(),
5035 &id(),
5036 )
5037 .expect("source_stake_account");
5038 let source_stake_keyed_account =
5039 KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5040
5041 assert_eq!(
5043 stake_keyed_account.merge(
5044 &invoke_context,
5045 &source_stake_keyed_account,
5046 &Clock::default(),
5047 &StakeHistory::default(),
5048 &HashSet::new(),
5049 false,
5050 ),
5051 Err(InstructionError::MissingRequiredSignature)
5052 );
5053
5054 assert_eq!(
5055 stake_keyed_account.merge(
5056 &invoke_context,
5057 &source_stake_keyed_account,
5058 &Clock::default(),
5059 &StakeHistory::default(),
5060 &signers,
5061 false,
5062 ),
5063 Ok(())
5064 );
5065
5066 assert_eq!(
5068 stake_keyed_account.account.borrow().carats(),
5069 stake_carats * 2
5070 );
5071 assert_eq!(source_stake_keyed_account.account.borrow().carats(), 0);
5072
5073 match state {
5075 StakeState::Initialized(meta) => {
5076 assert_eq!(
5077 stake_keyed_account.state(),
5078 Ok(StakeState::Initialized(*meta)),
5079 );
5080 }
5081 StakeState::Stake(meta, stake) => {
5082 let expected_stake = stake.delegation.stake
5083 + source_state
5084 .stake()
5085 .map(|stake| stake.delegation.stake)
5086 .unwrap_or_else(|| {
5087 stake_carats
5088 - source_state.meta().unwrap().rent_exempt_reserve
5089 });
5090 assert_eq!(
5091 stake_keyed_account.state(),
5092 Ok(StakeState::Stake(
5093 *meta,
5094 Stake {
5095 delegation: Delegation {
5096 stake: expected_stake,
5097 ..stake.delegation
5098 },
5099 ..*stake
5100 }
5101 )),
5102 );
5103 }
5104 _ => unreachable!(),
5105 }
5106 assert_eq!(
5107 source_stake_keyed_account.state(),
5108 Ok(StakeState::Uninitialized)
5109 );
5110 }
5111 }
5112 }
5113
5114 #[test]
5115 fn test_merge_self_fails() {
5116 let invoke_context = MockInvokeContext::new(vec![]);
5117 let stake_address = Pubkey::new_unique();
5118 let authority_pubkey = Pubkey::new_unique();
5119 let signers = HashSet::from_iter(vec![authority_pubkey]);
5120 let rent = Rent::default();
5121 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5122 let stake_amount = 4242424242;
5123 let stake_carats = rent_exempt_reserve + stake_amount;
5124
5125 let meta = Meta {
5126 rent_exempt_reserve,
5127 ..Meta::auto(&authority_pubkey)
5128 };
5129 let stake = Stake {
5130 delegation: Delegation {
5131 stake: stake_amount,
5132 activation_epoch: 0,
5133 ..Delegation::default()
5134 },
5135 ..Stake::default()
5136 };
5137 let stake_account = AccountSharedData::new_ref_data_with_space(
5138 stake_carats,
5139 &StakeState::Stake(meta, stake),
5140 std::mem::size_of::<StakeState>(),
5141 &id(),
5142 )
5143 .expect("stake_account");
5144 let stake_keyed_account = KeyedAccount::new(&stake_address, true, &stake_account);
5145
5146 assert_eq!(
5147 stake_keyed_account.merge(
5148 &invoke_context,
5149 &stake_keyed_account,
5150 &Clock::default(),
5151 &StakeHistory::default(),
5152 &signers,
5153 false,
5154 ),
5155 Err(InstructionError::InvalidArgument),
5156 );
5157 }
5158
5159 #[test]
5160 fn test_merge_incorrect_authorized_staker() {
5161 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5162 let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5163 let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5164 let wrong_authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5165 let stake_carats = 42;
5166
5167 let signers = vec![authorized_pubkey].into_iter().collect();
5168 let wrong_signers = vec![wrong_authorized_pubkey].into_iter().collect();
5169 let invoke_context = MockInvokeContext::new(vec![]);
5170
5171 for state in &[
5172 StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5173 StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5174 ] {
5175 for source_state in &[
5176 StakeState::Initialized(Meta::auto(&wrong_authorized_pubkey)),
5177 StakeState::Stake(
5178 Meta::auto(&wrong_authorized_pubkey),
5179 just_stake(stake_carats),
5180 ),
5181 ] {
5182 let stake_account = AccountSharedData::new_ref_data_with_space(
5183 stake_carats,
5184 state,
5185 std::mem::size_of::<StakeState>(),
5186 &id(),
5187 )
5188 .expect("stake_account");
5189 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5190
5191 let source_stake_account = AccountSharedData::new_ref_data_with_space(
5192 stake_carats,
5193 source_state,
5194 std::mem::size_of::<StakeState>(),
5195 &id(),
5196 )
5197 .expect("source_stake_account");
5198 let source_stake_keyed_account =
5199 KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5200
5201 assert_eq!(
5202 stake_keyed_account.merge(
5203 &invoke_context,
5204 &source_stake_keyed_account,
5205 &Clock::default(),
5206 &StakeHistory::default(),
5207 &wrong_signers,
5208 false,
5209 ),
5210 Err(InstructionError::MissingRequiredSignature)
5211 );
5212
5213 assert_eq!(
5214 stake_keyed_account.merge(
5215 &invoke_context,
5216 &source_stake_keyed_account,
5217 &Clock::default(),
5218 &StakeHistory::default(),
5219 &signers,
5220 false,
5221 ),
5222 Err(StakeError::MergeMismatch.into())
5223 );
5224 }
5225 }
5226 }
5227
5228 #[test]
5229 fn test_merge_invalid_account_data() {
5230 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5231 let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5232 let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5233 let stake_carats = 42;
5234 let signers = vec![authorized_pubkey].into_iter().collect();
5235 let invoke_context = MockInvokeContext::new(vec![]);
5236
5237 for state in &[
5238 StakeState::Uninitialized,
5239 StakeState::RewardsPool,
5240 StakeState::Initialized(Meta::auto(&authorized_pubkey)),
5241 StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5242 ] {
5243 for source_state in &[StakeState::Uninitialized, StakeState::RewardsPool] {
5244 let stake_account = AccountSharedData::new_ref_data_with_space(
5245 stake_carats,
5246 state,
5247 std::mem::size_of::<StakeState>(),
5248 &id(),
5249 )
5250 .expect("stake_account");
5251 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5252
5253 let source_stake_account = AccountSharedData::new_ref_data_with_space(
5254 stake_carats,
5255 source_state,
5256 std::mem::size_of::<StakeState>(),
5257 &id(),
5258 )
5259 .expect("source_stake_account");
5260 let source_stake_keyed_account =
5261 KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5262
5263 assert_eq!(
5264 stake_keyed_account.merge(
5265 &invoke_context,
5266 &source_stake_keyed_account,
5267 &Clock::default(),
5268 &StakeHistory::default(),
5269 &signers,
5270 false,
5271 ),
5272 Err(InstructionError::InvalidAccountData)
5273 );
5274 }
5275 }
5276 }
5277
5278 #[test]
5279 fn test_merge_fake_stake_source() {
5280 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5281 let source_stake_pubkey = gemachain_sdk::pubkey::new_rand();
5282 let authorized_pubkey = gemachain_sdk::pubkey::new_rand();
5283 let stake_carats = 42;
5284
5285 let signers = vec![authorized_pubkey].into_iter().collect();
5286
5287 let stake_account = AccountSharedData::new_ref_data_with_space(
5288 stake_carats,
5289 &StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5290 std::mem::size_of::<StakeState>(),
5291 &id(),
5292 )
5293 .expect("stake_account");
5294 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5295
5296 let source_stake_account = AccountSharedData::new_ref_data_with_space(
5297 stake_carats,
5298 &StakeState::Stake(Meta::auto(&authorized_pubkey), just_stake(stake_carats)),
5299 std::mem::size_of::<StakeState>(),
5300 &gemachain_sdk::pubkey::new_rand(),
5301 )
5302 .expect("source_stake_account");
5303 let source_stake_keyed_account =
5304 KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account);
5305 let invoke_context = MockInvokeContext::new(vec![]);
5306
5307 assert_eq!(
5308 stake_keyed_account.merge(
5309 &invoke_context,
5310 &source_stake_keyed_account,
5311 &Clock::default(),
5312 &StakeHistory::default(),
5313 &signers,
5314 false,
5315 ),
5316 Err(InstructionError::IncorrectProgramId)
5317 );
5318 }
5319
5320 #[test]
5321 fn test_merge_active_stake() {
5322 let base_carats = 4242424242;
5323 let stake_address = Pubkey::new_unique();
5324 let source_address = Pubkey::new_unique();
5325 let authority_pubkey = Pubkey::new_unique();
5326 let signers = HashSet::from_iter(vec![authority_pubkey]);
5327 let rent = Rent::default();
5328 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5329 let stake_amount = base_carats;
5330 let stake_carats = rent_exempt_reserve + stake_amount;
5331 let source_amount = base_carats;
5332 let source_carats = rent_exempt_reserve + source_amount;
5333
5334 let meta = Meta {
5335 rent_exempt_reserve,
5336 ..Meta::auto(&authority_pubkey)
5337 };
5338 let mut stake = Stake {
5339 delegation: Delegation {
5340 stake: stake_amount,
5341 activation_epoch: 0,
5342 ..Delegation::default()
5343 },
5344 ..Stake::default()
5345 };
5346 let stake_account = AccountSharedData::new_ref_data_with_space(
5347 stake_carats,
5348 &StakeState::Stake(meta, stake),
5349 std::mem::size_of::<StakeState>(),
5350 &id(),
5351 )
5352 .expect("stake_account");
5353 let stake_keyed_account = KeyedAccount::new(&stake_address, true, &stake_account);
5354
5355 let source_activation_epoch = 2;
5356 let mut source_stake = Stake {
5357 delegation: Delegation {
5358 stake: source_amount,
5359 activation_epoch: source_activation_epoch,
5360 ..stake.delegation
5361 },
5362 ..stake
5363 };
5364 let source_account = AccountSharedData::new_ref_data_with_space(
5365 source_carats,
5366 &StakeState::Stake(meta, source_stake),
5367 std::mem::size_of::<StakeState>(),
5368 &id(),
5369 )
5370 .expect("source_account");
5371 let source_keyed_account = KeyedAccount::new(&source_address, true, &source_account);
5372
5373 let mut clock = Clock::default();
5374 let mut stake_history = StakeHistory::default();
5375 let invoke_context = MockInvokeContext::new(vec![]);
5376
5377 clock.epoch = 0;
5378 let mut effective = base_carats;
5379 let mut activating = stake_amount;
5380 let mut deactivating = 0;
5381 stake_history.add(
5382 clock.epoch,
5383 StakeHistoryEntry {
5384 effective,
5385 activating,
5386 deactivating,
5387 },
5388 );
5389
5390 fn try_merge(
5391 invoke_context: &dyn InvokeContext,
5392 stake_account: &KeyedAccount,
5393 source_account: &KeyedAccount,
5394 clock: &Clock,
5395 stake_history: &StakeHistory,
5396 signers: &HashSet<Pubkey>,
5397 ) -> Result<(), InstructionError> {
5398 let test_stake_account = stake_account.account.clone();
5399 let test_stake_keyed =
5400 KeyedAccount::new(stake_account.unsigned_key(), true, &test_stake_account);
5401 let test_source_account = source_account.account.clone();
5402 let test_source_keyed =
5403 KeyedAccount::new(source_account.unsigned_key(), true, &test_source_account);
5404
5405 let result = test_stake_keyed.merge(
5406 invoke_context,
5407 &test_source_keyed,
5408 clock,
5409 stake_history,
5410 signers,
5411 false,
5412 );
5413 if result.is_ok() {
5414 assert_eq!(test_source_keyed.state(), Ok(StakeState::Uninitialized),);
5415 }
5416 result
5417 }
5418
5419 assert!(try_merge(
5421 &invoke_context,
5422 &stake_keyed_account,
5423 &source_keyed_account,
5424 &clock,
5425 &stake_history,
5426 &signers
5427 )
5428 .is_ok(),);
5429 assert!(try_merge(
5430 &invoke_context,
5431 &source_keyed_account,
5432 &stake_keyed_account,
5433 &clock,
5434 &stake_history,
5435 &signers
5436 )
5437 .is_ok(),);
5438
5439 loop {
5441 clock.epoch += 1;
5442 if clock.epoch == source_activation_epoch {
5443 activating += source_amount;
5444 }
5445 let delta =
5446 activating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
5447 effective += delta;
5448 activating -= delta;
5449 stake_history.add(
5450 clock.epoch,
5451 StakeHistoryEntry {
5452 effective,
5453 activating,
5454 deactivating,
5455 },
5456 );
5457 if stake_amount == stake.stake(clock.epoch, Some(&stake_history))
5458 && source_amount == source_stake.stake(clock.epoch, Some(&stake_history))
5459 {
5460 break;
5461 }
5462 assert_eq!(
5463 try_merge(
5464 &invoke_context,
5465 &stake_keyed_account,
5466 &source_keyed_account,
5467 &clock,
5468 &stake_history,
5469 &signers
5470 )
5471 .unwrap_err(),
5472 InstructionError::from(StakeError::MergeTransientStake),
5473 );
5474 assert_eq!(
5475 try_merge(
5476 &invoke_context,
5477 &source_keyed_account,
5478 &stake_keyed_account,
5479 &clock,
5480 &stake_history,
5481 &signers
5482 )
5483 .unwrap_err(),
5484 InstructionError::from(StakeError::MergeTransientStake),
5485 );
5486 }
5487 assert!(try_merge(
5489 &invoke_context,
5490 &stake_keyed_account,
5491 &source_keyed_account,
5492 &clock,
5493 &stake_history,
5494 &signers
5495 )
5496 .is_ok(),);
5497
5498 let source_deactivation_epoch = clock.epoch + 1;
5500 let stake_deactivation_epoch = clock.epoch + 2;
5501
5502 loop {
5504 clock.epoch += 1;
5505 let delta =
5506 deactivating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
5507 effective -= delta;
5508 deactivating -= delta;
5509 if clock.epoch == stake_deactivation_epoch {
5510 deactivating += stake_amount;
5511 stake = Stake {
5512 delegation: Delegation {
5513 deactivation_epoch: stake_deactivation_epoch,
5514 ..stake.delegation
5515 },
5516 ..stake
5517 };
5518 stake_keyed_account
5519 .set_state(&StakeState::Stake(meta, stake))
5520 .unwrap();
5521 }
5522 if clock.epoch == source_deactivation_epoch {
5523 deactivating += source_amount;
5524 source_stake = Stake {
5525 delegation: Delegation {
5526 deactivation_epoch: source_deactivation_epoch,
5527 ..source_stake.delegation
5528 },
5529 ..source_stake
5530 };
5531 source_keyed_account
5532 .set_state(&StakeState::Stake(meta, source_stake))
5533 .unwrap();
5534 }
5535 stake_history.add(
5536 clock.epoch,
5537 StakeHistoryEntry {
5538 effective,
5539 activating,
5540 deactivating,
5541 },
5542 );
5543 if 0 == stake.stake(clock.epoch, Some(&stake_history))
5544 && 0 == source_stake.stake(clock.epoch, Some(&stake_history))
5545 {
5546 break;
5547 }
5548 assert_eq!(
5549 try_merge(
5550 &invoke_context,
5551 &stake_keyed_account,
5552 &source_keyed_account,
5553 &clock,
5554 &stake_history,
5555 &signers
5556 )
5557 .unwrap_err(),
5558 InstructionError::from(StakeError::MergeTransientStake),
5559 );
5560 assert_eq!(
5561 try_merge(
5562 &invoke_context,
5563 &source_keyed_account,
5564 &stake_keyed_account,
5565 &clock,
5566 &stake_history,
5567 &signers
5568 )
5569 .unwrap_err(),
5570 InstructionError::from(StakeError::MergeTransientStake),
5571 );
5572 }
5573
5574 assert!(try_merge(
5576 &invoke_context,
5577 &stake_keyed_account,
5578 &source_keyed_account,
5579 &clock,
5580 &stake_history,
5581 &signers
5582 )
5583 .is_ok(),);
5584 }
5585
5586 #[test]
5587 fn test_lockup_is_expired() {
5588 let custodian = gemachain_sdk::pubkey::new_rand();
5589 let lockup = Lockup {
5590 epoch: 1,
5591 unix_timestamp: 1,
5592 custodian,
5593 };
5594 assert!(lockup.is_in_force(
5596 &Clock {
5597 epoch: 0,
5598 unix_timestamp: 0,
5599 ..Clock::default()
5600 },
5601 None
5602 ));
5603 assert!(lockup.is_in_force(
5605 &Clock {
5606 epoch: 2,
5607 unix_timestamp: 0,
5608 ..Clock::default()
5609 },
5610 None
5611 ));
5612 assert!(lockup.is_in_force(
5614 &Clock {
5615 epoch: 0,
5616 unix_timestamp: 2,
5617 ..Clock::default()
5618 },
5619 None
5620 ));
5621 assert!(!lockup.is_in_force(
5623 &Clock {
5624 epoch: 1,
5625 unix_timestamp: 1,
5626 ..Clock::default()
5627 },
5628 None
5629 ));
5630 assert!(!lockup.is_in_force(
5632 &Clock {
5633 epoch: 0,
5634 unix_timestamp: 0,
5635 ..Clock::default()
5636 },
5637 Some(&custodian),
5638 ));
5639 }
5640
5641 #[test]
5642 #[ignore]
5643 #[should_panic]
5644 fn test_dbg_stake_minimum_balance() {
5645 let minimum_balance = Rent::default().minimum_balance(std::mem::size_of::<StakeState>());
5646 panic!(
5647 "stake minimum_balance: {} carats, {} GEMA",
5648 minimum_balance,
5649 minimum_balance as f64 / gemachain_sdk::native_token::CARATS_PER_GEMA as f64
5650 );
5651 }
5652
5653 #[test]
5654 fn test_authorize_delegated_stake() {
5655 let stake_pubkey = gemachain_sdk::pubkey::new_rand();
5656 let stake_carats = 42;
5657 let stake_account = AccountSharedData::new_ref_data_with_space(
5658 stake_carats,
5659 &StakeState::Initialized(Meta::auto(&stake_pubkey)),
5660 std::mem::size_of::<StakeState>(),
5661 &id(),
5662 )
5663 .expect("stake_account");
5664
5665 let clock = Clock::default();
5666
5667 let vote_pubkey = gemachain_sdk::pubkey::new_rand();
5668 let vote_account = RefCell::new(vote_state::create_account(
5669 &vote_pubkey,
5670 &gemachain_sdk::pubkey::new_rand(),
5671 0,
5672 100,
5673 ));
5674 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
5675
5676 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
5677 let signers = vec![stake_pubkey].into_iter().collect();
5678 stake_keyed_account
5679 .delegate(
5680 &vote_keyed_account,
5681 &clock,
5682 &StakeHistory::default(),
5683 &Config::default(),
5684 &signers,
5685 true,
5686 )
5687 .unwrap();
5688
5689 stake_keyed_account.deactivate(&clock, &signers).unwrap();
5691
5692 let new_staker_pubkey = gemachain_sdk::pubkey::new_rand();
5693 assert_eq!(
5694 stake_keyed_account.authorize(
5695 &signers,
5696 &new_staker_pubkey,
5697 StakeAuthorize::Staker,
5698 false,
5699 &Clock::default(),
5700 None
5701 ),
5702 Ok(())
5703 );
5704 let authorized = authorized_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap();
5705 assert_eq!(authorized.staker, new_staker_pubkey);
5706
5707 let other_pubkey = gemachain_sdk::pubkey::new_rand();
5708 let other_signers = vec![other_pubkey].into_iter().collect();
5709
5710 let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
5712
5713 let new_voter_pubkey = gemachain_sdk::pubkey::new_rand();
5714 let vote_state = VoteState::default();
5715 let new_vote_account = RefCell::new(vote_state::create_account(
5716 &new_voter_pubkey,
5717 &gemachain_sdk::pubkey::new_rand(),
5718 0,
5719 100,
5720 ));
5721 let new_vote_keyed_account = KeyedAccount::new(&new_voter_pubkey, false, &new_vote_account);
5722 new_vote_keyed_account.set_state(&vote_state).unwrap();
5723
5724 assert_eq!(
5726 stake_keyed_account.delegate(
5727 &new_vote_keyed_account,
5728 &clock,
5729 &StakeHistory::default(),
5730 &Config::default(),
5731 &other_signers,
5732 true,
5733 ),
5734 Err(InstructionError::MissingRequiredSignature)
5735 );
5736
5737 let new_signers = vec![new_staker_pubkey].into_iter().collect();
5738 assert_eq!(
5740 stake_keyed_account.delegate(
5741 &new_vote_keyed_account,
5742 &clock,
5743 &StakeHistory::default(),
5744 &Config::default(),
5745 &new_signers,
5746 true,
5747 ),
5748 Ok(())
5749 );
5750 let stake = stake_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap();
5751 assert_eq!(stake.delegation.voter_pubkey, new_voter_pubkey);
5752
5753 assert_eq!(stake_keyed_account.deactivate(&clock, &new_signers), Ok(()));
5755 }
5756
5757 #[test]
5758 fn test_redelegate_consider_balance_changes() {
5759 let initial_carats = 4242424242;
5760 let rent = Rent::default();
5761 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
5762 let withdrawer_pubkey = Pubkey::new_unique();
5763 let stake_carats = rent_exempt_reserve + initial_carats;
5764
5765 let meta = Meta {
5766 rent_exempt_reserve,
5767 ..Meta::auto(&withdrawer_pubkey)
5768 };
5769 let stake_account = AccountSharedData::new_ref_data_with_space(
5770 stake_carats,
5771 &StakeState::Initialized(meta),
5772 std::mem::size_of::<StakeState>(),
5773 &id(),
5774 )
5775 .expect("stake_account");
5776 let stake_keyed_account = KeyedAccount::new(&withdrawer_pubkey, true, &stake_account);
5777
5778 let vote_pubkey = Pubkey::new_unique();
5779 let vote_account = RefCell::new(vote_state::create_account(
5780 &vote_pubkey,
5781 &Pubkey::new_unique(),
5782 0,
5783 100,
5784 ));
5785 let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
5786
5787 let signers = HashSet::from_iter(vec![withdrawer_pubkey]);
5788 let config = Config::default();
5789 let stake_history = StakeHistory::default();
5790 let mut clock = Clock::default();
5791 stake_keyed_account
5792 .delegate(
5793 &vote_keyed_account,
5794 &clock,
5795 &stake_history,
5796 &config,
5797 &signers,
5798 true,
5799 )
5800 .unwrap();
5801
5802 clock.epoch += 1;
5803 stake_keyed_account.deactivate(&clock, &signers).unwrap();
5804
5805 clock.epoch += 1;
5806 let to = Pubkey::new_unique();
5808 let to_account = AccountSharedData::new_ref(1, 0, &system_program::id());
5809 let to_keyed_account = KeyedAccount::new(&to, false, &to_account);
5810 let withdraw_carats = initial_carats / 2;
5811 stake_keyed_account
5812 .withdraw(
5813 withdraw_carats,
5814 &to_keyed_account,
5815 &clock,
5816 &stake_history,
5817 &stake_keyed_account,
5818 None,
5819 true,
5820 )
5821 .unwrap();
5822 let expected_balance = rent_exempt_reserve + initial_carats - withdraw_carats;
5823 assert_eq!(stake_keyed_account.carats().unwrap(), expected_balance);
5824
5825 clock.epoch += 1;
5826 stake_keyed_account
5827 .delegate(
5828 &vote_keyed_account,
5829 &clock,
5830 &stake_history,
5831 &config,
5832 &signers,
5833 true,
5834 )
5835 .unwrap();
5836 let stake = stake_from(&stake_account.borrow()).unwrap();
5837 assert_eq!(
5838 stake.delegation.stake,
5839 stake_keyed_account.carats().unwrap() - rent_exempt_reserve,
5840 );
5841
5842 clock.epoch += 1;
5843 stake_keyed_account.deactivate(&clock, &signers).unwrap();
5844
5845 stake_keyed_account
5847 .try_account_ref_mut()
5848 .unwrap()
5849 .checked_add_carats(withdraw_carats)
5850 .unwrap();
5851
5852 clock.epoch += 1;
5853 stake_keyed_account
5854 .delegate(
5855 &vote_keyed_account,
5856 &clock,
5857 &stake_history,
5858 &config,
5859 &signers,
5860 true,
5861 )
5862 .unwrap();
5863 let stake = stake_from(&stake_account.borrow()).unwrap();
5864 assert_eq!(
5865 stake.delegation.stake,
5866 stake_keyed_account.carats().unwrap() - rent_exempt_reserve,
5867 );
5868 }
5869
5870 #[test]
5871 fn test_meta_rewrite_rent_exempt_reserve() {
5872 let right_data_len = std::mem::size_of::<StakeState>() as u64;
5873 let rent = Rent::default();
5874 let expected_rent_exempt_reserve = rent.minimum_balance(right_data_len as usize);
5875
5876 let test_cases = [
5877 (
5878 right_data_len + 100,
5879 Some((
5880 rent.minimum_balance(right_data_len as usize + 100),
5881 expected_rent_exempt_reserve,
5882 )),
5883 ), (right_data_len, None), (
5886 right_data_len - 100,
5887 Some((
5888 rent.minimum_balance(right_data_len as usize - 100),
5889 expected_rent_exempt_reserve,
5890 )),
5891 ), ];
5893 for (data_len, expected_rewrite) in &test_cases {
5894 let rent_exempt_reserve = rent.minimum_balance(*data_len as usize);
5895 let mut meta = Meta {
5896 rent_exempt_reserve,
5897 ..Meta::default()
5898 };
5899 let actual_rewrite = meta.rewrite_rent_exempt_reserve(&rent, right_data_len as usize);
5900 assert_eq!(actual_rewrite, *expected_rewrite);
5901 assert_eq!(meta.rent_exempt_reserve, expected_rent_exempt_reserve);
5902 }
5903 }
5904
5905 #[test]
5906 fn test_calculate_carats_per_byte_year() {
5907 let rent = Rent::default();
5908 let data_len = 200u64;
5909 let rent_exempt_reserve = rent.minimum_balance(data_len as usize);
5910 assert_eq!(
5911 calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, data_len),
5912 rent_exempt_reserve
5913 );
5914
5915 let larger_data = 4008u64;
5916 let larger_rent_exempt_reserve = rent.minimum_balance(larger_data as usize);
5917 assert_eq!(
5918 calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, larger_data),
5919 larger_rent_exempt_reserve
5920 );
5921 assert_eq!(
5922 calculate_split_rent_exempt_reserve(larger_rent_exempt_reserve, larger_data, data_len),
5923 rent_exempt_reserve
5924 );
5925
5926 let even_larger_data = gemachain_sdk::system_instruction::MAX_PERMITTED_DATA_LENGTH;
5927 let even_larger_rent_exempt_reserve = rent.minimum_balance(even_larger_data as usize);
5928 assert_eq!(
5929 calculate_split_rent_exempt_reserve(rent_exempt_reserve, data_len, even_larger_data),
5930 even_larger_rent_exempt_reserve
5931 );
5932 assert_eq!(
5933 calculate_split_rent_exempt_reserve(
5934 even_larger_rent_exempt_reserve,
5935 even_larger_data,
5936 data_len
5937 ),
5938 rent_exempt_reserve
5939 );
5940 }
5941
5942 #[test]
5943 fn test_things_can_merge() {
5944 let good_stake = Stake {
5945 credits_observed: 4242,
5946 delegation: Delegation {
5947 voter_pubkey: Pubkey::new_unique(),
5948 stake: 424242424242,
5949 activation_epoch: 42,
5950 ..Delegation::default()
5951 },
5952 };
5953 let invoke_context = MockInvokeContext::new(vec![]);
5954
5955 let identical = good_stake;
5956 assert!(
5957 MergeKind::active_stakes_can_merge(&invoke_context, &good_stake, &identical).is_ok()
5958 );
5959
5960 let bad_credits_observed = Stake {
5961 credits_observed: good_stake.credits_observed + 1,
5962 ..good_stake
5963 };
5964 assert!(MergeKind::active_stakes_can_merge(
5965 &invoke_context,
5966 &good_stake,
5967 &bad_credits_observed
5968 )
5969 .is_err());
5970
5971 let good_delegation = good_stake.delegation;
5972 let different_stake_ok = Delegation {
5973 stake: good_delegation.stake + 1,
5974 ..good_delegation
5975 };
5976 assert!(MergeKind::active_delegations_can_merge(
5977 &invoke_context,
5978 &good_delegation,
5979 &different_stake_ok
5980 )
5981 .is_ok());
5982
5983 let different_activation_epoch_ok = Delegation {
5984 activation_epoch: good_delegation.activation_epoch + 1,
5985 ..good_delegation
5986 };
5987 assert!(MergeKind::active_delegations_can_merge(
5988 &invoke_context,
5989 &good_delegation,
5990 &different_activation_epoch_ok
5991 )
5992 .is_ok());
5993
5994 let bad_voter = Delegation {
5995 voter_pubkey: Pubkey::new_unique(),
5996 ..good_delegation
5997 };
5998 assert!(MergeKind::active_delegations_can_merge(
5999 &invoke_context,
6000 &good_delegation,
6001 &bad_voter
6002 )
6003 .is_err());
6004
6005 let bad_warmup_cooldown_rate = Delegation {
6006 warmup_cooldown_rate: good_delegation.warmup_cooldown_rate + f64::EPSILON,
6007 ..good_delegation
6008 };
6009 assert!(MergeKind::active_delegations_can_merge(
6010 &invoke_context,
6011 &good_delegation,
6012 &bad_warmup_cooldown_rate
6013 )
6014 .is_err());
6015 assert!(MergeKind::active_delegations_can_merge(
6016 &invoke_context,
6017 &bad_warmup_cooldown_rate,
6018 &good_delegation
6019 )
6020 .is_err());
6021
6022 let bad_deactivation_epoch = Delegation {
6023 deactivation_epoch: 43,
6024 ..good_delegation
6025 };
6026 assert!(MergeKind::active_delegations_can_merge(
6027 &invoke_context,
6028 &good_delegation,
6029 &bad_deactivation_epoch
6030 )
6031 .is_err());
6032 assert!(MergeKind::active_delegations_can_merge(
6033 &invoke_context,
6034 &bad_deactivation_epoch,
6035 &good_delegation
6036 )
6037 .is_err());
6038 }
6039
6040 #[test]
6041 fn test_metas_can_merge_pre_v4() {
6042 let invoke_context = MockInvokeContext::new(vec![]);
6043 assert!(MergeKind::metas_can_merge(
6045 &invoke_context,
6046 &Meta::default(),
6047 &Meta::default(),
6048 None,
6049 )
6050 .is_ok());
6051
6052 let mismatched_rent_exempt_reserve_ok = Meta {
6053 rent_exempt_reserve: 42,
6054 ..Meta::default()
6055 };
6056 assert_ne!(
6057 mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
6058 Meta::default().rent_exempt_reserve
6059 );
6060 assert!(MergeKind::metas_can_merge(
6061 &invoke_context,
6062 &Meta::default(),
6063 &mismatched_rent_exempt_reserve_ok,
6064 None,
6065 )
6066 .is_ok());
6067 assert!(MergeKind::metas_can_merge(
6068 &invoke_context,
6069 &mismatched_rent_exempt_reserve_ok,
6070 &Meta::default(),
6071 None,
6072 )
6073 .is_ok());
6074
6075 let mismatched_authorized_fails = Meta {
6076 authorized: Authorized {
6077 staker: Pubkey::new_unique(),
6078 withdrawer: Pubkey::new_unique(),
6079 },
6080 ..Meta::default()
6081 };
6082 assert_ne!(
6083 mismatched_authorized_fails.authorized,
6084 Meta::default().authorized
6085 );
6086 assert!(MergeKind::metas_can_merge(
6087 &invoke_context,
6088 &Meta::default(),
6089 &mismatched_authorized_fails,
6090 None,
6091 )
6092 .is_err());
6093 assert!(MergeKind::metas_can_merge(
6094 &invoke_context,
6095 &mismatched_authorized_fails,
6096 &Meta::default(),
6097 None,
6098 )
6099 .is_err());
6100
6101 let mismatched_lockup_fails = Meta {
6102 lockup: Lockup {
6103 unix_timestamp: 424242424,
6104 epoch: 42,
6105 custodian: Pubkey::new_unique(),
6106 },
6107 ..Meta::default()
6108 };
6109 assert_ne!(mismatched_lockup_fails.lockup, Meta::default().lockup);
6110 assert!(MergeKind::metas_can_merge(
6111 &invoke_context,
6112 &Meta::default(),
6113 &mismatched_lockup_fails,
6114 None,
6115 )
6116 .is_err());
6117 assert!(MergeKind::metas_can_merge(
6118 &invoke_context,
6119 &mismatched_lockup_fails,
6120 &Meta::default(),
6121 None,
6122 )
6123 .is_err());
6124 }
6125
6126 #[test]
6127 fn test_metas_can_merge_v4() {
6128 let invoke_context = MockInvokeContext::new(vec![]);
6129 assert!(MergeKind::metas_can_merge(
6131 &invoke_context,
6132 &Meta::default(),
6133 &Meta::default(),
6134 Some(&Clock::default())
6135 )
6136 .is_ok());
6137
6138 let mismatched_rent_exempt_reserve_ok = Meta {
6139 rent_exempt_reserve: 42,
6140 ..Meta::default()
6141 };
6142 assert_ne!(
6143 mismatched_rent_exempt_reserve_ok.rent_exempt_reserve,
6144 Meta::default().rent_exempt_reserve,
6145 );
6146 assert!(MergeKind::metas_can_merge(
6147 &invoke_context,
6148 &Meta::default(),
6149 &mismatched_rent_exempt_reserve_ok,
6150 Some(&Clock::default())
6151 )
6152 .is_ok());
6153 assert!(MergeKind::metas_can_merge(
6154 &invoke_context,
6155 &mismatched_rent_exempt_reserve_ok,
6156 &Meta::default(),
6157 Some(&Clock::default())
6158 )
6159 .is_ok());
6160
6161 let mismatched_authorized_fails = Meta {
6162 authorized: Authorized {
6163 staker: Pubkey::new_unique(),
6164 withdrawer: Pubkey::new_unique(),
6165 },
6166 ..Meta::default()
6167 };
6168 assert_ne!(
6169 mismatched_authorized_fails.authorized,
6170 Meta::default().authorized,
6171 );
6172 assert!(MergeKind::metas_can_merge(
6173 &invoke_context,
6174 &Meta::default(),
6175 &mismatched_authorized_fails,
6176 Some(&Clock::default())
6177 )
6178 .is_err());
6179 assert!(MergeKind::metas_can_merge(
6180 &invoke_context,
6181 &mismatched_authorized_fails,
6182 &Meta::default(),
6183 Some(&Clock::default())
6184 )
6185 .is_err());
6186
6187 let lockup1_timestamp = 42;
6188 let lockup2_timestamp = 4242;
6189 let lockup1_epoch = 4;
6190 let lockup2_epoch = 42;
6191 let metas_with_lockup1 = Meta {
6192 lockup: Lockup {
6193 unix_timestamp: lockup1_timestamp,
6194 epoch: lockup1_epoch,
6195 custodian: Pubkey::new_unique(),
6196 },
6197 ..Meta::default()
6198 };
6199 let metas_with_lockup2 = Meta {
6200 lockup: Lockup {
6201 unix_timestamp: lockup2_timestamp,
6202 epoch: lockup2_epoch,
6203 custodian: Pubkey::new_unique(),
6204 },
6205 ..Meta::default()
6206 };
6207
6208 assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6210 assert!(MergeKind::metas_can_merge(
6211 &invoke_context,
6212 &metas_with_lockup1,
6213 &metas_with_lockup2,
6214 Some(&Clock::default())
6215 )
6216 .is_err());
6217 assert!(MergeKind::metas_can_merge(
6218 &invoke_context,
6219 &metas_with_lockup2,
6220 &metas_with_lockup1,
6221 Some(&Clock::default())
6222 )
6223 .is_err());
6224
6225 let clock = Clock {
6226 epoch: lockup1_epoch + 1,
6227 unix_timestamp: lockup1_timestamp + 1,
6228 ..Clock::default()
6229 };
6230
6231 assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6233 assert!(MergeKind::metas_can_merge(
6234 &invoke_context,
6235 &metas_with_lockup1,
6236 &metas_with_lockup2,
6237 Some(&clock)
6238 )
6239 .is_err());
6240 assert!(MergeKind::metas_can_merge(
6241 &invoke_context,
6242 &metas_with_lockup2,
6243 &metas_with_lockup1,
6244 Some(&clock)
6245 )
6246 .is_err());
6247
6248 let clock = Clock {
6249 epoch: lockup2_epoch + 1,
6250 unix_timestamp: lockup2_timestamp + 1,
6251 ..Clock::default()
6252 };
6253
6254 assert_ne!(metas_with_lockup1.lockup, Meta::default().lockup);
6256 assert!(MergeKind::metas_can_merge(
6257 &invoke_context,
6258 &metas_with_lockup1,
6259 &metas_with_lockup2,
6260 Some(&clock)
6261 )
6262 .is_ok());
6263 assert!(MergeKind::metas_can_merge(
6264 &invoke_context,
6265 &metas_with_lockup2,
6266 &metas_with_lockup1,
6267 Some(&clock)
6268 )
6269 .is_ok());
6270 }
6271
6272 #[test]
6273 fn test_merge_kind_get_if_mergeable() {
6274 let authority_pubkey = Pubkey::new_unique();
6275 let initial_carats = 4242424242;
6276 let rent = Rent::default();
6277 let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
6278 let stake_carats = rent_exempt_reserve + initial_carats;
6279
6280 let meta = Meta {
6281 rent_exempt_reserve,
6282 ..Meta::auto(&authority_pubkey)
6283 };
6284 let stake_account = AccountSharedData::new_ref_data_with_space(
6285 stake_carats,
6286 &StakeState::Uninitialized,
6287 std::mem::size_of::<StakeState>(),
6288 &id(),
6289 )
6290 .expect("stake_account");
6291 let stake_keyed_account = KeyedAccount::new(&authority_pubkey, true, &stake_account);
6292 let mut clock = Clock::default();
6293 let mut stake_history = StakeHistory::default();
6294 let invoke_context = MockInvokeContext::new(vec![]);
6295
6296 assert_eq!(
6298 MergeKind::get_if_mergeable(
6299 &invoke_context,
6300 &stake_keyed_account,
6301 &clock,
6302 &stake_history
6303 )
6304 .unwrap_err(),
6305 InstructionError::InvalidAccountData
6306 );
6307
6308 stake_keyed_account
6310 .set_state(&StakeState::RewardsPool)
6311 .unwrap();
6312 assert_eq!(
6313 MergeKind::get_if_mergeable(
6314 &invoke_context,
6315 &stake_keyed_account,
6316 &clock,
6317 &stake_history
6318 )
6319 .unwrap_err(),
6320 InstructionError::InvalidAccountData
6321 );
6322
6323 stake_keyed_account
6325 .set_state(&StakeState::Initialized(meta))
6326 .unwrap();
6327 assert_eq!(
6328 MergeKind::get_if_mergeable(
6329 &invoke_context,
6330 &stake_keyed_account,
6331 &clock,
6332 &stake_history
6333 )
6334 .unwrap(),
6335 MergeKind::Inactive(meta, stake_carats)
6336 );
6337
6338 clock.epoch = 0;
6339 let mut effective = 2 * initial_carats;
6340 let mut activating = 0;
6341 let mut deactivating = 0;
6342 stake_history.add(
6343 clock.epoch,
6344 StakeHistoryEntry {
6345 effective,
6346 activating,
6347 deactivating,
6348 },
6349 );
6350
6351 clock.epoch += 1;
6352 activating = initial_carats;
6353 stake_history.add(
6354 clock.epoch,
6355 StakeHistoryEntry {
6356 effective,
6357 activating,
6358 deactivating,
6359 },
6360 );
6361
6362 let stake = Stake {
6363 delegation: Delegation {
6364 stake: initial_carats,
6365 activation_epoch: 1,
6366 deactivation_epoch: 5,
6367 ..Delegation::default()
6368 },
6369 ..Stake::default()
6370 };
6371 stake_keyed_account
6372 .set_state(&StakeState::Stake(meta, stake))
6373 .unwrap();
6374 assert_eq!(
6376 MergeKind::get_if_mergeable(
6377 &invoke_context,
6378 &stake_keyed_account,
6379 &clock,
6380 &stake_history
6381 )
6382 .unwrap(),
6383 MergeKind::ActivationEpoch(meta, stake),
6384 );
6385
6386 loop {
6388 clock.epoch += 1;
6389 let delta =
6390 activating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
6391 effective += delta;
6392 activating -= delta;
6393 stake_history.add(
6394 clock.epoch,
6395 StakeHistoryEntry {
6396 effective,
6397 activating,
6398 deactivating,
6399 },
6400 );
6401 if activating == 0 {
6402 break;
6403 }
6404 assert_eq!(
6405 MergeKind::get_if_mergeable(
6406 &invoke_context,
6407 &stake_keyed_account,
6408 &clock,
6409 &stake_history
6410 )
6411 .unwrap_err(),
6412 InstructionError::from(StakeError::MergeTransientStake),
6413 );
6414 }
6415
6416 while clock.epoch < stake.delegation.deactivation_epoch - 1 {
6418 clock.epoch += 1;
6419 stake_history.add(
6420 clock.epoch,
6421 StakeHistoryEntry {
6422 effective,
6423 activating,
6424 deactivating,
6425 },
6426 );
6427 assert_eq!(
6428 MergeKind::get_if_mergeable(
6429 &invoke_context,
6430 &stake_keyed_account,
6431 &clock,
6432 &stake_history
6433 )
6434 .unwrap(),
6435 MergeKind::FullyActive(meta, stake),
6436 );
6437 }
6438
6439 clock.epoch += 1;
6440 deactivating = stake.delegation.stake;
6441 stake_history.add(
6442 clock.epoch,
6443 StakeHistoryEntry {
6444 effective,
6445 activating,
6446 deactivating,
6447 },
6448 );
6449 assert_eq!(
6451 MergeKind::get_if_mergeable(
6452 &invoke_context,
6453 &stake_keyed_account,
6454 &clock,
6455 &stake_history
6456 )
6457 .unwrap_err(),
6458 InstructionError::from(StakeError::MergeTransientStake),
6459 );
6460
6461 loop {
6463 clock.epoch += 1;
6464 let delta =
6465 deactivating.min((effective as f64 * stake.delegation.warmup_cooldown_rate) as u64);
6466 effective -= delta;
6467 deactivating -= delta;
6468 stake_history.add(
6469 clock.epoch,
6470 StakeHistoryEntry {
6471 effective,
6472 activating,
6473 deactivating,
6474 },
6475 );
6476 if deactivating == 0 {
6477 break;
6478 }
6479 assert_eq!(
6480 MergeKind::get_if_mergeable(
6481 &invoke_context,
6482 &stake_keyed_account,
6483 &clock,
6484 &stake_history
6485 )
6486 .unwrap_err(),
6487 InstructionError::from(StakeError::MergeTransientStake),
6488 );
6489 }
6490
6491 assert_eq!(
6493 MergeKind::get_if_mergeable(
6494 &invoke_context,
6495 &stake_keyed_account,
6496 &clock,
6497 &stake_history
6498 )
6499 .unwrap(),
6500 MergeKind::Inactive(meta, stake_carats),
6501 );
6502 }
6503
6504 #[test]
6505 fn test_merge_kind_merge() {
6506 let carats = 424242;
6507 let meta = Meta {
6508 rent_exempt_reserve: 42,
6509 ..Meta::default()
6510 };
6511 let stake = Stake {
6512 delegation: Delegation {
6513 stake: 4242,
6514 ..Delegation::default()
6515 },
6516 ..Stake::default()
6517 };
6518 let inactive = MergeKind::Inactive(Meta::default(), carats);
6519 let activation_epoch = MergeKind::ActivationEpoch(meta, stake);
6520 let fully_active = MergeKind::FullyActive(meta, stake);
6521 let invoke_context = MockInvokeContext::new(vec![]);
6522
6523 assert_eq!(
6524 inactive
6525 .clone()
6526 .merge(&invoke_context, inactive.clone(), None)
6527 .unwrap(),
6528 None
6529 );
6530 assert_eq!(
6531 inactive
6532 .clone()
6533 .merge(&invoke_context, activation_epoch.clone(), None)
6534 .unwrap(),
6535 None
6536 );
6537 assert!(inactive
6538 .clone()
6539 .merge(&invoke_context, fully_active.clone(), None)
6540 .is_err());
6541 assert!(activation_epoch
6542 .clone()
6543 .merge(&invoke_context, fully_active.clone(), None)
6544 .is_err());
6545 assert!(fully_active
6546 .clone()
6547 .merge(&invoke_context, inactive.clone(), None)
6548 .is_err());
6549 assert!(fully_active
6550 .clone()
6551 .merge(&invoke_context, activation_epoch.clone(), None)
6552 .is_err());
6553
6554 let new_state = activation_epoch
6555 .clone()
6556 .merge(&invoke_context, inactive, None)
6557 .unwrap()
6558 .unwrap();
6559 let delegation = new_state.delegation().unwrap();
6560 assert_eq!(delegation.stake, stake.delegation.stake + carats);
6561
6562 let new_state = activation_epoch
6563 .clone()
6564 .merge(&invoke_context, activation_epoch, None)
6565 .unwrap()
6566 .unwrap();
6567 let delegation = new_state.delegation().unwrap();
6568 assert_eq!(
6569 delegation.stake,
6570 2 * stake.delegation.stake + meta.rent_exempt_reserve
6571 );
6572
6573 let new_state = fully_active
6574 .clone()
6575 .merge(&invoke_context, fully_active, None)
6576 .unwrap()
6577 .unwrap();
6578 let delegation = new_state.delegation().unwrap();
6579 assert_eq!(delegation.stake, 2 * stake.delegation.stake);
6580 }
6581
6582 #[test]
6583 fn test_active_stake_merge() {
6584 let delegation_a = 4_242_424_242u64;
6585 let delegation_b = 6_200_000_000u64;
6586 let credits_a = 124_521_000u64;
6587 let rent_exempt_reserve = 227_000_000u64;
6588 let meta = Meta {
6589 rent_exempt_reserve,
6590 ..Meta::default()
6591 };
6592 let stake_a = Stake {
6593 delegation: Delegation {
6594 stake: delegation_a,
6595 ..Delegation::default()
6596 },
6597 credits_observed: credits_a,
6598 };
6599 let stake_b = Stake {
6600 delegation: Delegation {
6601 stake: delegation_b,
6602 ..Delegation::default()
6603 },
6604 credits_observed: credits_a,
6605 };
6606
6607 let invoke_context = MockInvokeContext::new(vec![]);
6608
6609 let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a);
6611 let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b);
6612 let new_stake = activation_epoch_a
6613 .merge(&invoke_context, activation_epoch_b, None)
6614 .unwrap()
6615 .unwrap()
6616 .stake()
6617 .unwrap();
6618 assert_eq!(new_stake.credits_observed, credits_a);
6619 assert_eq!(
6620 new_stake.delegation.stake,
6621 delegation_a + delegation_b + rent_exempt_reserve
6622 );
6623
6624 let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6626 let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6627 let new_stake = fully_active_a
6628 .merge(&invoke_context, fully_active_b, None)
6629 .unwrap()
6630 .unwrap()
6631 .stake()
6632 .unwrap();
6633 assert_eq!(new_stake.credits_observed, credits_a);
6634 assert_eq!(new_stake.delegation.stake, delegation_a + delegation_b);
6635
6636 let credits_b = 125_124_521u64;
6638 let stake_b = Stake {
6639 delegation: Delegation {
6640 stake: delegation_b,
6641 ..Delegation::default()
6642 },
6643 credits_observed: credits_b,
6644 };
6645 let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a);
6646 let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b);
6647 let new_stake = activation_epoch_a
6648 .merge(&invoke_context, activation_epoch_b, None)
6649 .unwrap()
6650 .unwrap()
6651 .stake()
6652 .unwrap();
6653 assert_eq!(
6654 new_stake.credits_observed,
6655 (credits_a * delegation_a + credits_b * (delegation_b + rent_exempt_reserve))
6656 / (delegation_a + delegation_b + rent_exempt_reserve)
6657 + 1
6658 );
6659 assert_eq!(
6660 new_stake.delegation.stake,
6661 delegation_a + delegation_b + rent_exempt_reserve
6662 );
6663
6664 let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6666 let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6667 let new_stake = fully_active_a
6668 .merge(&invoke_context, fully_active_b, None)
6669 .unwrap()
6670 .unwrap()
6671 .stake()
6672 .unwrap();
6673 assert_eq!(
6674 new_stake.credits_observed,
6675 (credits_a * delegation_a + credits_b * delegation_b) / (delegation_a + delegation_b)
6676 + 1
6677 );
6678 assert_eq!(new_stake.delegation.stake, delegation_a + delegation_b);
6679
6680 let delegation = 1_000_000u64;
6682 let credits_a = 200_000_000u64;
6683 let credits_b = 100_000_000u64;
6684 let rent_exempt_reserve = 227_000_000u64;
6685 let meta = Meta {
6686 rent_exempt_reserve,
6687 ..Meta::default()
6688 };
6689 let stake_a = Stake {
6690 delegation: Delegation {
6691 stake: delegation,
6692 ..Delegation::default()
6693 },
6694 credits_observed: credits_a,
6695 };
6696 let stake_b = Stake {
6697 delegation: Delegation {
6698 stake: delegation,
6699 ..Delegation::default()
6700 },
6701 credits_observed: credits_b,
6702 };
6703 let fully_active_a = MergeKind::FullyActive(meta, stake_a);
6704 let fully_active_b = MergeKind::FullyActive(meta, stake_b);
6705 let new_stake = fully_active_a
6706 .merge(&invoke_context, fully_active_b, None)
6707 .unwrap()
6708 .unwrap()
6709 .stake()
6710 .unwrap();
6711 assert_eq!(
6712 new_stake.credits_observed,
6713 (credits_a * delegation + credits_b * delegation) / (delegation + delegation)
6714 );
6715 assert_eq!(new_stake.delegation.stake, delegation * 2);
6716 }
6717
6718 prop_compose! {
6719 pub fn sum_within(max: u64)(total in 1..max)
6720 (intermediate in 1..total, total in Just(total))
6721 -> (u64, u64) {
6722 (intermediate, total - intermediate)
6723 }
6724 }
6725
6726 proptest! {
6727 #[test]
6728 fn test_stake_weighted_credits_observed(
6729 (credits_a, credits_b) in sum_within(u64::MAX),
6730 (delegation_a, delegation_b) in sum_within(u64::MAX),
6731 ) {
6732 let stake = Stake {
6733 delegation: Delegation {
6734 stake: delegation_a,
6735 ..Delegation::default()
6736 },
6737 credits_observed: credits_a
6738 };
6739 let credits_observed = stake_weighted_credits_observed(
6740 &stake,
6741 delegation_b,
6742 credits_b,
6743 ).unwrap();
6744
6745 if credits_a < credits_b {
6747 assert!(credits_a < credits_observed);
6748 assert!(credits_observed <= credits_b);
6749 } else {
6750 assert!(credits_b <= credits_observed);
6751 assert!(credits_observed <= credits_a);
6752 }
6753
6754 let weighted_credits_total = credits_observed as u128 * (delegation_a + delegation_b) as u128;
6757 let weighted_credits_a = credits_a as u128 * delegation_a as u128;
6758 let weighted_credits_b = credits_b as u128 * delegation_b as u128;
6759 let raw_diff = weighted_credits_total - (weighted_credits_a + weighted_credits_b);
6760 let credits_observed_diff = raw_diff / (delegation_a + delegation_b) as u128;
6761 assert!(credits_observed_diff <= 1);
6762 }
6763 }
6764}