1#![allow(clippy::arithmetic_side_effects)]
2#![deny(clippy::wildcard_enum_match_arm)]
3#![allow(deprecated)]
6
7#[cfg(feature = "borsh")]
8use borsh::{io, BorshDeserialize, BorshSchema, BorshSerialize};
9use {
10 crate::{
11 error::StakeError,
12 instruction::LockupArgs,
13 stake_flags::StakeFlags,
14 stake_history::{StakeHistoryEntry, StakeHistoryGetEntry},
15 },
16 solana_clock::{Clock, Epoch, UnixTimestamp},
17 solana_instruction::error::InstructionError,
18 solana_pubkey::Pubkey,
19 std::collections::HashSet,
20};
21
22pub type StakeActivationStatus = StakeHistoryEntry;
23
24pub const DEFAULT_WARMUP_COOLDOWN_RATE: f64 = 0.25;
27pub const NEW_WARMUP_COOLDOWN_RATE: f64 = 0.09;
28pub const DEFAULT_SLASH_PENALTY: u8 = ((5 * u8::MAX as usize) / 100) as u8;
29
30pub fn warmup_cooldown_rate(current_epoch: Epoch, new_rate_activation_epoch: Option<Epoch>) -> f64 {
31 if current_epoch < new_rate_activation_epoch.unwrap_or(u64::MAX) {
32 DEFAULT_WARMUP_COOLDOWN_RATE
33 } else {
34 NEW_WARMUP_COOLDOWN_RATE
35 }
36}
37
38#[cfg(feature = "borsh")]
39macro_rules! impl_borsh_stake_state {
40 ($borsh:ident) => {
41 impl $borsh::BorshDeserialize for StakeState {
42 fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
43 let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
44 match enum_value {
45 0 => Ok(StakeState::Uninitialized),
46 1 => {
47 let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
48 Ok(StakeState::Initialized(meta))
49 }
50 2 => {
51 let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
52 let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
53 Ok(StakeState::Stake(meta, stake))
54 }
55 3 => Ok(StakeState::RewardsPool),
56 _ => Err(io::Error::new(
57 io::ErrorKind::InvalidData,
58 "Invalid enum value",
59 )),
60 }
61 }
62 }
63 impl $borsh::BorshSerialize for StakeState {
64 fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
65 match self {
66 StakeState::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
67 StakeState::Initialized(meta) => {
68 writer.write_all(&1u32.to_le_bytes())?;
69 $borsh::BorshSerialize::serialize(&meta, writer)
70 }
71 StakeState::Stake(meta, stake) => {
72 writer.write_all(&2u32.to_le_bytes())?;
73 $borsh::BorshSerialize::serialize(&meta, writer)?;
74 $borsh::BorshSerialize::serialize(&stake, writer)
75 }
76 StakeState::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
77 }
78 }
79 }
80 };
81}
82#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
83#[cfg_attr(
84 feature = "serde",
85 derive(serde_derive::Deserialize, serde_derive::Serialize)
86)]
87#[derive(Debug, Default, PartialEq, Clone, Copy)]
88#[allow(clippy::large_enum_variant)]
89#[deprecated(
90 since = "1.17.0",
91 note = "Please use `StakeStateV2` instead, and match the third `StakeFlags` field when matching `StakeStateV2::Stake` to resolve any breakage. For example, `if let StakeState::Stake(meta, stake)` becomes `if let StakeStateV2::Stake(meta, stake, _stake_flags)`."
92)]
93pub enum StakeState {
94 #[default]
95 Uninitialized,
96 Initialized(Meta),
97 Stake(Meta, Stake),
98 RewardsPool,
99}
100#[cfg(feature = "borsh")]
101impl_borsh_stake_state!(borsh);
102impl StakeState {
103 pub const fn size_of() -> usize {
105 200 }
107
108 pub fn stake(&self) -> Option<Stake> {
109 match self {
110 Self::Stake(_meta, stake) => Some(*stake),
111 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
112 }
113 }
114
115 pub fn delegation(&self) -> Option<Delegation> {
116 match self {
117 Self::Stake(_meta, stake) => Some(stake.delegation),
118 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
119 }
120 }
121
122 pub fn authorized(&self) -> Option<Authorized> {
123 match self {
124 Self::Stake(meta, _stake) => Some(meta.authorized),
125 Self::Initialized(meta) => Some(meta.authorized),
126 Self::Uninitialized | Self::RewardsPool => None,
127 }
128 }
129
130 pub fn lockup(&self) -> Option<Lockup> {
131 self.meta().map(|meta| meta.lockup)
132 }
133
134 pub fn meta(&self) -> Option<Meta> {
135 match self {
136 Self::Stake(meta, _stake) => Some(*meta),
137 Self::Initialized(meta) => Some(*meta),
138 Self::Uninitialized | Self::RewardsPool => None,
139 }
140 }
141}
142
143#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
144#[cfg_attr(
145 feature = "serde",
146 derive(serde_derive::Deserialize, serde_derive::Serialize)
147)]
148#[derive(Debug, Default, PartialEq, Clone, Copy)]
149#[allow(clippy::large_enum_variant)]
150pub enum StakeStateV2 {
151 #[default]
152 Uninitialized,
153 Initialized(Meta),
154 Stake(Meta, Stake, StakeFlags),
155 RewardsPool,
156}
157#[cfg(feature = "borsh")]
158macro_rules! impl_borsh_stake_state_v2 {
159 ($borsh:ident) => {
160 impl $borsh::BorshDeserialize for StakeStateV2 {
161 fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
162 let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
163 match enum_value {
164 0 => Ok(StakeStateV2::Uninitialized),
165 1 => {
166 let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
167 Ok(StakeStateV2::Initialized(meta))
168 }
169 2 => {
170 let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
171 let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
172 let stake_flags: StakeFlags =
173 $borsh::BorshDeserialize::deserialize_reader(reader)?;
174 Ok(StakeStateV2::Stake(meta, stake, stake_flags))
175 }
176 3 => Ok(StakeStateV2::RewardsPool),
177 _ => Err(io::Error::new(
178 io::ErrorKind::InvalidData,
179 "Invalid enum value",
180 )),
181 }
182 }
183 }
184 impl $borsh::BorshSerialize for StakeStateV2 {
185 fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
186 match self {
187 StakeStateV2::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
188 StakeStateV2::Initialized(meta) => {
189 writer.write_all(&1u32.to_le_bytes())?;
190 $borsh::BorshSerialize::serialize(&meta, writer)
191 }
192 StakeStateV2::Stake(meta, stake, stake_flags) => {
193 writer.write_all(&2u32.to_le_bytes())?;
194 $borsh::BorshSerialize::serialize(&meta, writer)?;
195 $borsh::BorshSerialize::serialize(&stake, writer)?;
196 $borsh::BorshSerialize::serialize(&stake_flags, writer)
197 }
198 StakeStateV2::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
199 }
200 }
201 }
202 };
203}
204#[cfg(feature = "borsh")]
205impl_borsh_stake_state_v2!(borsh);
206
207impl StakeStateV2 {
208 pub const fn size_of() -> usize {
210 200 }
212
213 pub fn stake(&self) -> Option<Stake> {
214 match self {
215 Self::Stake(_meta, stake, _stake_flags) => Some(*stake),
216 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
217 }
218 }
219
220 pub fn stake_ref(&self) -> Option<&Stake> {
221 match self {
222 Self::Stake(_meta, stake, _stake_flags) => Some(stake),
223 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
224 }
225 }
226
227 pub fn delegation(&self) -> Option<Delegation> {
228 match self {
229 Self::Stake(_meta, stake, _stake_flags) => Some(stake.delegation),
230 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
231 }
232 }
233
234 pub fn delegation_ref(&self) -> Option<&Delegation> {
235 match self {
236 StakeStateV2::Stake(_meta, stake, _stake_flags) => Some(&stake.delegation),
237 Self::Uninitialized | Self::Initialized(_) | Self::RewardsPool => None,
238 }
239 }
240
241 pub fn authorized(&self) -> Option<Authorized> {
242 match self {
243 Self::Stake(meta, _stake, _stake_flags) => Some(meta.authorized),
244 Self::Initialized(meta) => Some(meta.authorized),
245 Self::Uninitialized | Self::RewardsPool => None,
246 }
247 }
248
249 pub fn lockup(&self) -> Option<Lockup> {
250 self.meta().map(|meta| meta.lockup)
251 }
252
253 pub fn meta(&self) -> Option<Meta> {
254 match self {
255 Self::Stake(meta, _stake, _stake_flags) => Some(*meta),
256 Self::Initialized(meta) => Some(*meta),
257 Self::Uninitialized | Self::RewardsPool => None,
258 }
259 }
260}
261
262#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
263#[cfg_attr(
264 feature = "serde",
265 derive(serde_derive::Deserialize, serde_derive::Serialize)
266)]
267#[derive(Debug, PartialEq, Eq, Clone, Copy)]
268pub enum StakeAuthorize {
269 Staker,
270 Withdrawer,
271}
272
273#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
274#[cfg_attr(
275 feature = "borsh",
276 derive(BorshSerialize, BorshDeserialize, BorshSchema),
277 borsh(crate = "borsh")
278)]
279#[cfg_attr(
280 feature = "serde",
281 derive(serde_derive::Deserialize, serde_derive::Serialize)
282)]
283#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
284pub struct Lockup {
285 pub unix_timestamp: UnixTimestamp,
288 pub epoch: Epoch,
291 pub custodian: Pubkey,
294}
295impl Lockup {
296 pub fn is_in_force(&self, clock: &Clock, custodian: Option<&Pubkey>) -> bool {
297 if custodian == Some(&self.custodian) {
298 return false;
299 }
300 self.unix_timestamp > clock.unix_timestamp || self.epoch > clock.epoch
301 }
302}
303
304#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
305#[cfg_attr(
306 feature = "borsh",
307 derive(BorshSerialize, BorshDeserialize, BorshSchema),
308 borsh(crate = "borsh")
309)]
310#[cfg_attr(
311 feature = "serde",
312 derive(serde_derive::Deserialize, serde_derive::Serialize)
313)]
314#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
315pub struct Authorized {
316 pub staker: Pubkey,
317 pub withdrawer: Pubkey,
318}
319
320impl Authorized {
321 pub fn auto(authorized: &Pubkey) -> Self {
322 Self {
323 staker: *authorized,
324 withdrawer: *authorized,
325 }
326 }
327 pub fn check(
328 &self,
329 signers: &HashSet<Pubkey>,
330 stake_authorize: StakeAuthorize,
331 ) -> Result<(), InstructionError> {
332 let authorized_signer = match stake_authorize {
333 StakeAuthorize::Staker => &self.staker,
334 StakeAuthorize::Withdrawer => &self.withdrawer,
335 };
336
337 if signers.contains(authorized_signer) {
338 Ok(())
339 } else {
340 Err(InstructionError::MissingRequiredSignature)
341 }
342 }
343
344 pub fn authorize(
345 &mut self,
346 signers: &HashSet<Pubkey>,
347 new_authorized: &Pubkey,
348 stake_authorize: StakeAuthorize,
349 lockup_custodian_args: Option<(&Lockup, &Clock, Option<&Pubkey>)>,
350 ) -> Result<(), InstructionError> {
351 match stake_authorize {
352 StakeAuthorize::Staker => {
353 if !signers.contains(&self.staker) && !signers.contains(&self.withdrawer) {
355 return Err(InstructionError::MissingRequiredSignature);
356 }
357 self.staker = *new_authorized
358 }
359 StakeAuthorize::Withdrawer => {
360 if let Some((lockup, clock, custodian)) = lockup_custodian_args {
361 if lockup.is_in_force(clock, None) {
362 match custodian {
363 None => {
364 return Err(StakeError::CustodianMissing.into());
365 }
366 Some(custodian) => {
367 if !signers.contains(custodian) {
368 return Err(StakeError::CustodianSignatureMissing.into());
369 }
370
371 if lockup.is_in_force(clock, Some(custodian)) {
372 return Err(StakeError::LockupInForce.into());
373 }
374 }
375 }
376 }
377 }
378 self.check(signers, stake_authorize)?;
379 self.withdrawer = *new_authorized
380 }
381 }
382 Ok(())
383 }
384}
385
386#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
387#[cfg_attr(
388 feature = "borsh",
389 derive(BorshSerialize, BorshDeserialize, BorshSchema),
390 borsh(crate = "borsh")
391)]
392#[cfg_attr(
393 feature = "serde",
394 derive(serde_derive::Deserialize, serde_derive::Serialize)
395)]
396#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
397pub struct Meta {
398 pub rent_exempt_reserve: u64,
399 pub authorized: Authorized,
400 pub lockup: Lockup,
401}
402
403impl Meta {
404 pub fn set_lockup(
405 &mut self,
406 lockup: &LockupArgs,
407 signers: &HashSet<Pubkey>,
408 clock: &Clock,
409 ) -> Result<(), InstructionError> {
410 if self.lockup.is_in_force(clock, None) {
414 if !signers.contains(&self.lockup.custodian) {
415 return Err(InstructionError::MissingRequiredSignature);
416 }
417 } else if !signers.contains(&self.authorized.withdrawer) {
418 return Err(InstructionError::MissingRequiredSignature);
419 }
420 if let Some(unix_timestamp) = lockup.unix_timestamp {
421 self.lockup.unix_timestamp = unix_timestamp;
422 }
423 if let Some(epoch) = lockup.epoch {
424 self.lockup.epoch = epoch;
425 }
426 if let Some(custodian) = lockup.custodian {
427 self.lockup.custodian = custodian;
428 }
429 Ok(())
430 }
431
432 pub fn auto(authorized: &Pubkey) -> Self {
433 Self {
434 authorized: Authorized::auto(authorized),
435 ..Meta::default()
436 }
437 }
438}
439
440#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
441#[cfg_attr(
442 feature = "borsh",
443 derive(BorshSerialize, BorshDeserialize, BorshSchema),
444 borsh(crate = "borsh")
445)]
446#[cfg_attr(
447 feature = "serde",
448 derive(serde_derive::Deserialize, serde_derive::Serialize)
449)]
450#[derive(Debug, PartialEq, Clone, Copy)]
451pub struct Delegation {
452 pub voter_pubkey: Pubkey,
454 pub stake: u64,
456 pub activation_epoch: Epoch,
458 pub deactivation_epoch: Epoch,
460 #[deprecated(
462 since = "1.16.7",
463 note = "Please use `solana_sdk::stake::state::warmup_cooldown_rate()` instead"
464 )]
465 pub warmup_cooldown_rate: f64,
466}
467
468impl Default for Delegation {
469 fn default() -> Self {
470 #[allow(deprecated)]
471 Self {
472 voter_pubkey: Pubkey::default(),
473 stake: 0,
474 activation_epoch: 0,
475 deactivation_epoch: u64::MAX,
476 warmup_cooldown_rate: DEFAULT_WARMUP_COOLDOWN_RATE,
477 }
478 }
479}
480
481impl Delegation {
482 pub fn new(voter_pubkey: &Pubkey, stake: u64, activation_epoch: Epoch) -> Self {
483 Self {
484 voter_pubkey: *voter_pubkey,
485 stake,
486 activation_epoch,
487 ..Delegation::default()
488 }
489 }
490 pub fn is_bootstrap(&self) -> bool {
491 self.activation_epoch == u64::MAX
492 }
493
494 pub fn stake<T: StakeHistoryGetEntry>(
495 &self,
496 epoch: Epoch,
497 history: &T,
498 new_rate_activation_epoch: Option<Epoch>,
499 ) -> u64 {
500 self.stake_activating_and_deactivating(epoch, history, new_rate_activation_epoch)
501 .effective
502 }
503
504 #[allow(clippy::comparison_chain)]
505 pub fn stake_activating_and_deactivating<T: StakeHistoryGetEntry>(
506 &self,
507 target_epoch: Epoch,
508 history: &T,
509 new_rate_activation_epoch: Option<Epoch>,
510 ) -> StakeActivationStatus {
511 let (effective_stake, activating_stake) =
513 self.stake_and_activating(target_epoch, history, new_rate_activation_epoch);
514
515 if target_epoch < self.deactivation_epoch {
517 if activating_stake == 0 {
519 StakeActivationStatus::with_effective(effective_stake)
520 } else {
521 StakeActivationStatus::with_effective_and_activating(
522 effective_stake,
523 activating_stake,
524 )
525 }
526 } else if target_epoch == self.deactivation_epoch {
527 StakeActivationStatus::with_deactivating(effective_stake)
529 } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
530 .get_entry(self.deactivation_epoch)
531 .map(|cluster_stake_at_deactivation_epoch| {
532 (
533 history,
534 self.deactivation_epoch,
535 cluster_stake_at_deactivation_epoch,
536 )
537 })
538 {
539 let mut current_epoch;
544 let mut current_effective_stake = effective_stake;
545 loop {
546 current_epoch = prev_epoch + 1;
547 if prev_cluster_stake.deactivating == 0 {
550 break;
551 }
552
553 let weight =
556 current_effective_stake as f64 / prev_cluster_stake.deactivating as f64;
557 let warmup_cooldown_rate =
558 warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
559
560 let newly_not_effective_cluster_stake =
562 prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
563 let newly_not_effective_stake =
564 ((weight * newly_not_effective_cluster_stake) as u64).max(1);
565
566 current_effective_stake =
567 current_effective_stake.saturating_sub(newly_not_effective_stake);
568 if current_effective_stake == 0 {
569 break;
570 }
571
572 if current_epoch >= target_epoch {
573 break;
574 }
575 if let Some(current_cluster_stake) = history.get_entry(current_epoch) {
576 prev_epoch = current_epoch;
577 prev_cluster_stake = current_cluster_stake;
578 } else {
579 break;
580 }
581 }
582
583 StakeActivationStatus::with_deactivating(current_effective_stake)
585 } else {
586 StakeActivationStatus::default()
588 }
589 }
590
591 fn stake_and_activating<T: StakeHistoryGetEntry>(
593 &self,
594 target_epoch: Epoch,
595 history: &T,
596 new_rate_activation_epoch: Option<Epoch>,
597 ) -> (u64, u64) {
598 let delegated_stake = self.stake;
599
600 if self.is_bootstrap() {
601 (delegated_stake, 0)
603 } else if self.activation_epoch == self.deactivation_epoch {
604 (0, 0)
607 } else if target_epoch == self.activation_epoch {
608 (0, delegated_stake)
610 } else if target_epoch < self.activation_epoch {
611 (0, 0)
613 } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
614 .get_entry(self.activation_epoch)
615 .map(|cluster_stake_at_activation_epoch| {
616 (
617 history,
618 self.activation_epoch,
619 cluster_stake_at_activation_epoch,
620 )
621 })
622 {
623 let mut current_epoch;
628 let mut current_effective_stake = 0;
629 loop {
630 current_epoch = prev_epoch + 1;
631 if prev_cluster_stake.activating == 0 {
634 break;
635 }
636
637 let remaining_activating_stake = delegated_stake - current_effective_stake;
640 let weight =
641 remaining_activating_stake as f64 / prev_cluster_stake.activating as f64;
642 let warmup_cooldown_rate =
643 warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
644
645 let newly_effective_cluster_stake =
647 prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
648 let newly_effective_stake =
649 ((weight * newly_effective_cluster_stake) as u64).max(1);
650
651 current_effective_stake += newly_effective_stake;
652 if current_effective_stake >= delegated_stake {
653 current_effective_stake = delegated_stake;
654 break;
655 }
656
657 if current_epoch >= target_epoch || current_epoch >= self.deactivation_epoch {
658 break;
659 }
660 if let Some(current_cluster_stake) = history.get_entry(current_epoch) {
661 prev_epoch = current_epoch;
662 prev_cluster_stake = current_cluster_stake;
663 } else {
664 break;
665 }
666 }
667
668 (
669 current_effective_stake,
670 delegated_stake - current_effective_stake,
671 )
672 } else {
673 (delegated_stake, 0)
675 }
676 }
677}
678
679#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
680#[cfg_attr(
681 feature = "borsh",
682 derive(BorshSerialize, BorshDeserialize, BorshSchema),
683 borsh(crate = "borsh")
684)]
685#[cfg_attr(
686 feature = "serde",
687 derive(serde_derive::Deserialize, serde_derive::Serialize)
688)]
689#[derive(Debug, Default, PartialEq, Clone, Copy)]
690pub struct Stake {
691 pub delegation: Delegation,
692 pub credits_observed: u64,
694}
695
696impl Stake {
697 pub fn stake<T: StakeHistoryGetEntry>(
698 &self,
699 epoch: Epoch,
700 history: &T,
701 new_rate_activation_epoch: Option<Epoch>,
702 ) -> u64 {
703 self.delegation
704 .stake(epoch, history, new_rate_activation_epoch)
705 }
706
707 pub fn split(
708 &mut self,
709 remaining_stake_delta: u64,
710 split_stake_amount: u64,
711 ) -> Result<Self, StakeError> {
712 if remaining_stake_delta > self.delegation.stake {
713 return Err(StakeError::InsufficientStake);
714 }
715 self.delegation.stake -= remaining_stake_delta;
716 let new = Self {
717 delegation: Delegation {
718 stake: split_stake_amount,
719 ..self.delegation
720 },
721 ..*self
722 };
723 Ok(new)
724 }
725
726 pub fn deactivate(&mut self, epoch: Epoch) -> Result<(), StakeError> {
727 if self.delegation.deactivation_epoch != u64::MAX {
728 Err(StakeError::AlreadyDeactivated)
729 } else {
730 self.delegation.deactivation_epoch = epoch;
731 Ok(())
732 }
733 }
734}
735
736#[cfg(all(feature = "borsh", feature = "bincode"))]
737#[cfg(test)]
738mod tests {
739 use {
740 super::*,
741 crate::stake_history::StakeHistory,
742 assert_matches::assert_matches,
743 bincode::serialize,
744 solana_account::{state_traits::StateMut, AccountSharedData, ReadableAccount},
745 solana_borsh::v1::try_from_slice_unchecked,
746 solana_pubkey::Pubkey,
747 test_case::test_case,
748 };
749
750 fn from<T: ReadableAccount + StateMut<StakeStateV2>>(account: &T) -> Option<StakeStateV2> {
751 account.state().ok()
752 }
753
754 fn stake_from<T: ReadableAccount + StateMut<StakeStateV2>>(account: &T) -> Option<Stake> {
755 from(account).and_then(|state: StakeStateV2| state.stake())
756 }
757
758 fn new_stake_history_entry<'a, I>(
759 epoch: Epoch,
760 stakes: I,
761 history: &StakeHistory,
762 new_rate_activation_epoch: Option<Epoch>,
763 ) -> StakeHistoryEntry
764 where
765 I: Iterator<Item = &'a Delegation>,
766 {
767 stakes.fold(StakeHistoryEntry::default(), |sum, stake| {
768 sum + stake.stake_activating_and_deactivating(epoch, history, new_rate_activation_epoch)
769 })
770 }
771
772 fn create_stake_history_from_delegations(
773 bootstrap: Option<u64>,
774 epochs: std::ops::Range<Epoch>,
775 delegations: &[Delegation],
776 new_rate_activation_epoch: Option<Epoch>,
777 ) -> StakeHistory {
778 let mut stake_history = StakeHistory::default();
779
780 let bootstrap_delegation = if let Some(bootstrap) = bootstrap {
781 vec![Delegation {
782 activation_epoch: u64::MAX,
783 stake: bootstrap,
784 ..Delegation::default()
785 }]
786 } else {
787 vec![]
788 };
789
790 for epoch in epochs {
791 let entry = new_stake_history_entry(
792 epoch,
793 delegations.iter().chain(bootstrap_delegation.iter()),
794 &stake_history,
795 new_rate_activation_epoch,
796 );
797 stake_history.add(epoch, entry);
798 }
799
800 stake_history
801 }
802
803 #[test]
804 fn test_authorized_authorize() {
805 let staker = Pubkey::new_unique();
806 let mut authorized = Authorized::auto(&staker);
807 let mut signers = HashSet::new();
808 assert_eq!(
809 authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
810 Err(InstructionError::MissingRequiredSignature)
811 );
812 signers.insert(staker);
813 assert_eq!(
814 authorized.authorize(&signers, &staker, StakeAuthorize::Staker, None),
815 Ok(())
816 );
817 }
818
819 #[test]
820 fn test_authorized_authorize_with_custodian() {
821 let staker = Pubkey::new_unique();
822 let custodian = Pubkey::new_unique();
823 let invalid_custodian = Pubkey::new_unique();
824 let mut authorized = Authorized::auto(&staker);
825 let mut signers = HashSet::new();
826 signers.insert(staker);
827
828 let lockup = Lockup {
829 epoch: 1,
830 unix_timestamp: 1,
831 custodian,
832 };
833 let clock = Clock {
834 epoch: 0,
835 unix_timestamp: 0,
836 ..Clock::default()
837 };
838
839 assert_eq!(
841 authorized.authorize(
842 &signers,
843 &staker,
844 StakeAuthorize::Withdrawer,
845 Some((&Lockup::default(), &clock, None))
846 ),
847 Ok(())
848 );
849
850 assert_eq!(
852 authorized.authorize(
853 &signers,
854 &staker,
855 StakeAuthorize::Withdrawer,
856 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
857 ),
858 Ok(()) );
860
861 assert_eq!(
863 authorized.authorize(
864 &signers,
865 &staker,
866 StakeAuthorize::Withdrawer,
867 Some((&lockup, &clock, Some(&invalid_custodian)))
868 ),
869 Err(StakeError::CustodianSignatureMissing.into()),
870 );
871
872 signers.insert(invalid_custodian);
873
874 assert_eq!(
876 authorized.authorize(
877 &signers,
878 &staker,
879 StakeAuthorize::Withdrawer,
880 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
881 ),
882 Ok(()) );
884
885 signers.insert(invalid_custodian);
887 assert_eq!(
888 authorized.authorize(
889 &signers,
890 &staker,
891 StakeAuthorize::Withdrawer,
892 Some((&lockup, &clock, Some(&invalid_custodian)))
893 ),
894 Err(StakeError::LockupInForce.into()), );
896
897 signers.remove(&invalid_custodian);
898
899 assert_eq!(
901 authorized.authorize(
902 &signers,
903 &staker,
904 StakeAuthorize::Withdrawer,
905 Some((&lockup, &clock, None))
906 ),
907 Err(StakeError::CustodianMissing.into()),
908 );
909
910 assert_eq!(
912 authorized.authorize(
913 &signers,
914 &staker,
915 StakeAuthorize::Withdrawer,
916 Some((&lockup, &clock, Some(&custodian)))
917 ),
918 Err(StakeError::CustodianSignatureMissing.into()),
919 );
920
921 signers.insert(custodian);
923 assert_eq!(
924 authorized.authorize(
925 &signers,
926 &staker,
927 StakeAuthorize::Withdrawer,
928 Some((&lockup, &clock, Some(&custodian)))
929 ),
930 Ok(())
931 );
932 }
933
934 #[test]
935 fn test_stake_state_stake_from_fail() {
936 let mut stake_account =
937 AccountSharedData::new(0, StakeStateV2::size_of(), &crate::program::id());
938
939 stake_account
940 .set_state(&StakeStateV2::default())
941 .expect("set_state");
942
943 assert_eq!(stake_from(&stake_account), None);
944 }
945
946 #[test]
947 fn test_stake_is_bootstrap() {
948 assert!(Delegation {
949 activation_epoch: u64::MAX,
950 ..Delegation::default()
951 }
952 .is_bootstrap());
953 assert!(!Delegation {
954 activation_epoch: 0,
955 ..Delegation::default()
956 }
957 .is_bootstrap());
958 }
959
960 #[test]
961 fn test_stake_activating_and_deactivating() {
962 let stake = Delegation {
963 stake: 1_000,
964 activation_epoch: 0, deactivation_epoch: 5,
966 ..Delegation::default()
967 };
968
969 let increment = (1_000_f64 * warmup_cooldown_rate(0, None)) as u64;
971
972 let mut stake_history = StakeHistory::default();
973 assert_eq!(
975 stake.stake_activating_and_deactivating(stake.activation_epoch, &stake_history, None),
976 StakeActivationStatus::with_effective_and_activating(0, stake.stake),
977 );
978 for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
979 assert_eq!(
980 stake.stake_activating_and_deactivating(epoch, &stake_history, None),
981 StakeActivationStatus::with_effective(stake.stake),
982 );
983 }
984 assert_eq!(
986 stake.stake_activating_and_deactivating(stake.deactivation_epoch, &stake_history, None),
987 StakeActivationStatus::with_deactivating(stake.stake),
988 );
989 assert_eq!(
991 stake.stake_activating_and_deactivating(
992 stake.deactivation_epoch + 1,
993 &stake_history,
994 None
995 ),
996 StakeActivationStatus::default(),
997 );
998
999 stake_history.add(
1000 0u64, StakeHistoryEntry {
1002 effective: 1_000,
1003 ..StakeHistoryEntry::default()
1004 },
1005 );
1006 assert_eq!(
1008 stake.stake_activating_and_deactivating(1, &stake_history, None),
1009 StakeActivationStatus::with_effective_and_activating(0, stake.stake),
1010 );
1011
1012 stake_history.add(
1013 0u64, StakeHistoryEntry {
1015 effective: 1_000,
1016 activating: 1_000,
1017 ..StakeHistoryEntry::default()
1018 },
1019 );
1021 assert_eq!(
1023 stake.stake_activating_and_deactivating(2, &stake_history, None),
1024 StakeActivationStatus::with_effective_and_activating(
1025 increment,
1026 stake.stake - increment
1027 ),
1028 );
1029
1030 let mut stake_history = StakeHistory::default();
1032
1033 stake_history.add(
1034 stake.deactivation_epoch, StakeHistoryEntry {
1036 effective: 1_000,
1037 ..StakeHistoryEntry::default()
1038 },
1039 );
1040 assert_eq!(
1042 stake.stake_activating_and_deactivating(
1043 stake.deactivation_epoch + 1,
1044 &stake_history,
1045 None,
1046 ),
1047 StakeActivationStatus::with_deactivating(stake.stake),
1048 );
1049
1050 stake_history.add(
1052 stake.deactivation_epoch, StakeHistoryEntry {
1054 effective: 1_000,
1055 deactivating: 1_000,
1056 ..StakeHistoryEntry::default()
1057 },
1058 );
1059 assert_eq!(
1061 stake.stake_activating_and_deactivating(
1062 stake.deactivation_epoch + 2,
1063 &stake_history,
1064 None,
1065 ),
1066 StakeActivationStatus::with_deactivating(stake.stake - increment),
1068 );
1069 }
1070
1071 mod same_epoch_activation_then_deactivation {
1072 use super::*;
1073
1074 enum OldDeactivationBehavior {
1075 Stuck,
1076 Slow,
1077 }
1078
1079 fn do_test(
1080 old_behavior: OldDeactivationBehavior,
1081 expected_stakes: &[StakeActivationStatus],
1082 ) {
1083 let cluster_stake = 1_000;
1084 let activating_stake = 10_000;
1085 let some_stake = 700;
1086 let some_epoch = 0;
1087
1088 let stake = Delegation {
1089 stake: some_stake,
1090 activation_epoch: some_epoch,
1091 deactivation_epoch: some_epoch,
1092 ..Delegation::default()
1093 };
1094
1095 let mut stake_history = StakeHistory::default();
1096 let cluster_deactivation_at_stake_modified_epoch = match old_behavior {
1097 OldDeactivationBehavior::Stuck => 0,
1098 OldDeactivationBehavior::Slow => 1000,
1099 };
1100
1101 let stake_history_entries = vec![
1102 (
1103 cluster_stake,
1104 activating_stake,
1105 cluster_deactivation_at_stake_modified_epoch,
1106 ),
1107 (cluster_stake, activating_stake, 1000),
1108 (cluster_stake, activating_stake, 1000),
1109 (cluster_stake, activating_stake, 100),
1110 (cluster_stake, activating_stake, 100),
1111 (cluster_stake, activating_stake, 100),
1112 (cluster_stake, activating_stake, 100),
1113 ];
1114
1115 for (epoch, (effective, activating, deactivating)) in
1116 stake_history_entries.into_iter().enumerate()
1117 {
1118 stake_history.add(
1119 epoch as Epoch,
1120 StakeHistoryEntry {
1121 effective,
1122 activating,
1123 deactivating,
1124 },
1125 );
1126 }
1127
1128 assert_eq!(
1129 expected_stakes,
1130 (0..expected_stakes.len())
1131 .map(|epoch| stake.stake_activating_and_deactivating(
1132 epoch as u64,
1133 &stake_history,
1134 None,
1135 ))
1136 .collect::<Vec<_>>()
1137 );
1138 }
1139
1140 #[test]
1141 fn test_new_behavior_previously_slow() {
1142 do_test(
1146 OldDeactivationBehavior::Slow,
1147 &[
1148 StakeActivationStatus::default(),
1149 StakeActivationStatus::default(),
1150 StakeActivationStatus::default(),
1151 StakeActivationStatus::default(),
1152 StakeActivationStatus::default(),
1153 StakeActivationStatus::default(),
1154 StakeActivationStatus::default(),
1155 ],
1156 );
1157 }
1158
1159 #[test]
1160 fn test_new_behavior_previously_stuck() {
1161 do_test(
1165 OldDeactivationBehavior::Stuck,
1166 &[
1167 StakeActivationStatus::default(),
1168 StakeActivationStatus::default(),
1169 StakeActivationStatus::default(),
1170 StakeActivationStatus::default(),
1171 StakeActivationStatus::default(),
1172 StakeActivationStatus::default(),
1173 StakeActivationStatus::default(),
1174 ],
1175 );
1176 }
1177 }
1178
1179 #[test]
1180 fn test_inflation_and_slashing_with_activating_and_deactivating_stake() {
1181 let (delegated_stake, mut stake, stake_history) = {
1183 let cluster_stake = 1_000;
1184 let delegated_stake = 700;
1185
1186 let stake = Delegation {
1187 stake: delegated_stake,
1188 activation_epoch: 0,
1189 deactivation_epoch: 4,
1190 ..Delegation::default()
1191 };
1192
1193 let mut stake_history = StakeHistory::default();
1194 stake_history.add(
1195 0,
1196 StakeHistoryEntry {
1197 effective: cluster_stake,
1198 activating: delegated_stake,
1199 ..StakeHistoryEntry::default()
1200 },
1201 );
1202 let newly_effective_at_epoch1 = (cluster_stake as f64 * 0.25) as u64;
1203 assert_eq!(newly_effective_at_epoch1, 250);
1204 stake_history.add(
1205 1,
1206 StakeHistoryEntry {
1207 effective: cluster_stake + newly_effective_at_epoch1,
1208 activating: delegated_stake - newly_effective_at_epoch1,
1209 ..StakeHistoryEntry::default()
1210 },
1211 );
1212 let newly_effective_at_epoch2 =
1213 ((cluster_stake + newly_effective_at_epoch1) as f64 * 0.25) as u64;
1214 assert_eq!(newly_effective_at_epoch2, 312);
1215 stake_history.add(
1216 2,
1217 StakeHistoryEntry {
1218 effective: cluster_stake
1219 + newly_effective_at_epoch1
1220 + newly_effective_at_epoch2,
1221 activating: delegated_stake
1222 - newly_effective_at_epoch1
1223 - newly_effective_at_epoch2,
1224 ..StakeHistoryEntry::default()
1225 },
1226 );
1227 stake_history.add(
1228 3,
1229 StakeHistoryEntry {
1230 effective: cluster_stake + delegated_stake,
1231 ..StakeHistoryEntry::default()
1232 },
1233 );
1234 stake_history.add(
1235 4,
1236 StakeHistoryEntry {
1237 effective: cluster_stake + delegated_stake,
1238 deactivating: delegated_stake,
1239 ..StakeHistoryEntry::default()
1240 },
1241 );
1242 let newly_not_effective_stake_at_epoch5 =
1243 ((cluster_stake + delegated_stake) as f64 * 0.25) as u64;
1244 assert_eq!(newly_not_effective_stake_at_epoch5, 425);
1245 stake_history.add(
1246 5,
1247 StakeHistoryEntry {
1248 effective: cluster_stake + delegated_stake
1249 - newly_not_effective_stake_at_epoch5,
1250 deactivating: delegated_stake - newly_not_effective_stake_at_epoch5,
1251 ..StakeHistoryEntry::default()
1252 },
1253 );
1254
1255 (delegated_stake, stake, stake_history)
1256 };
1257
1258 let calculate_each_staking_status = |stake: &Delegation, epoch_count: usize| -> Vec<_> {
1260 (0..epoch_count)
1261 .map(|epoch| {
1262 stake.stake_activating_and_deactivating(epoch as u64, &stake_history, None)
1263 })
1264 .collect::<Vec<_>>()
1265 };
1266 let adjust_staking_status = |rate: f64, status: &[StakeActivationStatus]| {
1267 status
1268 .iter()
1269 .map(|entry| StakeActivationStatus {
1270 effective: (entry.effective as f64 * rate) as u64,
1271 activating: (entry.activating as f64 * rate) as u64,
1272 deactivating: (entry.deactivating as f64 * rate) as u64,
1273 })
1274 .collect::<Vec<_>>()
1275 };
1276
1277 let expected_staking_status_transition = vec![
1278 StakeActivationStatus::with_effective_and_activating(0, 700),
1279 StakeActivationStatus::with_effective_and_activating(250, 450),
1280 StakeActivationStatus::with_effective_and_activating(562, 138),
1281 StakeActivationStatus::with_effective(700),
1282 StakeActivationStatus::with_deactivating(700),
1283 StakeActivationStatus::with_deactivating(275),
1284 StakeActivationStatus::default(),
1285 ];
1286 let expected_staking_status_transition_base = vec![
1287 StakeActivationStatus::with_effective_and_activating(0, 700),
1288 StakeActivationStatus::with_effective_and_activating(250, 450),
1289 StakeActivationStatus::with_effective_and_activating(562, 138 + 1), StakeActivationStatus::with_effective(700),
1291 StakeActivationStatus::with_deactivating(700),
1292 StakeActivationStatus::with_deactivating(275 + 1), StakeActivationStatus::default(),
1294 ];
1295
1296 assert_eq!(
1298 expected_staking_status_transition,
1299 calculate_each_staking_status(&stake, expected_staking_status_transition.len())
1300 );
1301
1302 let rate = 1.10;
1304 stake.stake = (delegated_stake as f64 * rate) as u64;
1305 let expected_staking_status_transition =
1306 adjust_staking_status(rate, &expected_staking_status_transition_base);
1307
1308 assert_eq!(
1309 expected_staking_status_transition,
1310 calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
1311 );
1312
1313 let rate = 0.5;
1315 stake.stake = (delegated_stake as f64 * rate) as u64;
1316 let expected_staking_status_transition =
1317 adjust_staking_status(rate, &expected_staking_status_transition_base);
1318
1319 assert_eq!(
1320 expected_staking_status_transition,
1321 calculate_each_staking_status(&stake, expected_staking_status_transition_base.len()),
1322 );
1323 }
1324
1325 #[test]
1326 fn test_stop_activating_after_deactivation() {
1327 let stake = Delegation {
1328 stake: 1_000,
1329 activation_epoch: 0,
1330 deactivation_epoch: 3,
1331 ..Delegation::default()
1332 };
1333
1334 let base_stake = 1_000;
1335 let mut stake_history = StakeHistory::default();
1336 let mut effective = base_stake;
1337 let other_activation = 100;
1338 let mut other_activations = vec![0];
1339
1340 for epoch in 0..=stake.deactivation_epoch + 1 {
1344 let (activating, deactivating) = if epoch < stake.deactivation_epoch {
1345 (stake.stake + base_stake - effective, 0)
1346 } else {
1347 let other_activation_sum: u64 = other_activations.iter().sum();
1348 let deactivating = effective - base_stake - other_activation_sum;
1349 (other_activation, deactivating)
1350 };
1351
1352 stake_history.add(
1353 epoch,
1354 StakeHistoryEntry {
1355 effective,
1356 activating,
1357 deactivating,
1358 },
1359 );
1360
1361 let effective_rate_limited = (effective as f64 * warmup_cooldown_rate(0, None)) as u64;
1362 if epoch < stake.deactivation_epoch {
1363 effective += effective_rate_limited.min(activating);
1364 other_activations.push(0);
1365 } else {
1366 effective -= effective_rate_limited.min(deactivating);
1367 effective += other_activation;
1368 other_activations.push(other_activation);
1369 }
1370 }
1371
1372 for epoch in 0..=stake.deactivation_epoch + 1 {
1373 let history = stake_history.get(epoch).unwrap();
1374 let other_activations: u64 = other_activations[..=epoch as usize].iter().sum();
1375 let expected_stake = history.effective - base_stake - other_activations;
1376 let (expected_activating, expected_deactivating) = if epoch < stake.deactivation_epoch {
1377 (history.activating, 0)
1378 } else {
1379 (0, history.deactivating)
1380 };
1381 assert_eq!(
1382 stake.stake_activating_and_deactivating(epoch, &stake_history, None),
1383 StakeActivationStatus {
1384 effective: expected_stake,
1385 activating: expected_activating,
1386 deactivating: expected_deactivating,
1387 },
1388 );
1389 }
1390 }
1391
1392 #[test]
1393 fn test_stake_warmup_cooldown_sub_integer_moves() {
1394 let delegations = [Delegation {
1395 stake: 2,
1396 activation_epoch: 0, deactivation_epoch: 5,
1398 ..Delegation::default()
1399 }];
1400 let epochs = 7;
1402 let bootstrap = (warmup_cooldown_rate(0, None) * 100.0 / 2.0) as u64;
1405 let stake_history =
1406 create_stake_history_from_delegations(Some(bootstrap), 0..epochs, &delegations, None);
1407 let mut max_stake = 0;
1408 let mut min_stake = 2;
1409
1410 for epoch in 0..epochs {
1411 let stake = delegations
1412 .iter()
1413 .map(|delegation| delegation.stake(epoch, &stake_history, None))
1414 .sum::<u64>();
1415 max_stake = max_stake.max(stake);
1416 min_stake = min_stake.min(stake);
1417 }
1418 assert_eq!(max_stake, 2);
1419 assert_eq!(min_stake, 0);
1420 }
1421
1422 #[test_case(None ; "old rate")]
1423 #[test_case(Some(1) ; "new rate activated in epoch 1")]
1424 #[test_case(Some(10) ; "new rate activated in epoch 10")]
1425 #[test_case(Some(30) ; "new rate activated in epoch 30")]
1426 #[test_case(Some(50) ; "new rate activated in epoch 50")]
1427 #[test_case(Some(60) ; "new rate activated in epoch 60")]
1428 fn test_stake_warmup_cooldown(new_rate_activation_epoch: Option<Epoch>) {
1429 let delegations = [
1430 Delegation {
1431 stake: 1_000,
1433 activation_epoch: u64::MAX,
1434 ..Delegation::default()
1435 },
1436 Delegation {
1437 stake: 1_000,
1438 activation_epoch: 0,
1439 deactivation_epoch: 9,
1440 ..Delegation::default()
1441 },
1442 Delegation {
1443 stake: 1_000,
1444 activation_epoch: 1,
1445 deactivation_epoch: 6,
1446 ..Delegation::default()
1447 },
1448 Delegation {
1449 stake: 1_000,
1450 activation_epoch: 2,
1451 deactivation_epoch: 5,
1452 ..Delegation::default()
1453 },
1454 Delegation {
1455 stake: 1_000,
1456 activation_epoch: 2,
1457 deactivation_epoch: 4,
1458 ..Delegation::default()
1459 },
1460 Delegation {
1461 stake: 1_000,
1462 activation_epoch: 4,
1463 deactivation_epoch: 4,
1464 ..Delegation::default()
1465 },
1466 ];
1467 let epochs = 60;
1472
1473 let stake_history = create_stake_history_from_delegations(
1474 None,
1475 0..epochs,
1476 &delegations,
1477 new_rate_activation_epoch,
1478 );
1479
1480 let mut prev_total_effective_stake = delegations
1481 .iter()
1482 .map(|delegation| delegation.stake(0, &stake_history, new_rate_activation_epoch))
1483 .sum::<u64>();
1484
1485 for epoch in 1..epochs {
1488 let total_effective_stake = delegations
1489 .iter()
1490 .map(|delegation| {
1491 delegation.stake(epoch, &stake_history, new_rate_activation_epoch)
1492 })
1493 .sum::<u64>();
1494
1495 let delta = if total_effective_stake > prev_total_effective_stake {
1496 total_effective_stake - prev_total_effective_stake
1497 } else {
1498 prev_total_effective_stake - total_effective_stake
1499 };
1500
1501 assert!(
1507 delta
1508 <= ((prev_total_effective_stake as f64
1509 * warmup_cooldown_rate(epoch, new_rate_activation_epoch))
1510 as u64)
1511 .max(1)
1512 );
1513
1514 prev_total_effective_stake = total_effective_stake;
1515 }
1516 }
1517
1518 #[test]
1519 fn test_lockup_is_expired() {
1520 let custodian = Pubkey::new_unique();
1521 let lockup = Lockup {
1522 epoch: 1,
1523 unix_timestamp: 1,
1524 custodian,
1525 };
1526 assert!(lockup.is_in_force(
1528 &Clock {
1529 epoch: 0,
1530 unix_timestamp: 0,
1531 ..Clock::default()
1532 },
1533 None
1534 ));
1535 assert!(lockup.is_in_force(
1537 &Clock {
1538 epoch: 2,
1539 unix_timestamp: 0,
1540 ..Clock::default()
1541 },
1542 None
1543 ));
1544 assert!(lockup.is_in_force(
1546 &Clock {
1547 epoch: 0,
1548 unix_timestamp: 2,
1549 ..Clock::default()
1550 },
1551 None
1552 ));
1553 assert!(!lockup.is_in_force(
1555 &Clock {
1556 epoch: 1,
1557 unix_timestamp: 1,
1558 ..Clock::default()
1559 },
1560 None
1561 ));
1562 assert!(!lockup.is_in_force(
1564 &Clock {
1565 epoch: 0,
1566 unix_timestamp: 0,
1567 ..Clock::default()
1568 },
1569 Some(&custodian),
1570 ));
1571 }
1572
1573 fn check_borsh_deserialization(stake: StakeStateV2) {
1574 let serialized = serialize(&stake).unwrap();
1575 let deserialized = StakeStateV2::try_from_slice(&serialized).unwrap();
1576 assert_eq!(stake, deserialized);
1577 }
1578
1579 fn check_borsh_serialization(stake: StakeStateV2) {
1580 let bincode_serialized = serialize(&stake).unwrap();
1581 let borsh_serialized = borsh::to_vec(&stake).unwrap();
1582 assert_eq!(bincode_serialized, borsh_serialized);
1583 }
1584
1585 #[test]
1586 fn test_size_of() {
1587 assert_eq!(StakeStateV2::size_of(), std::mem::size_of::<StakeStateV2>());
1588 }
1589
1590 #[test]
1591 fn bincode_vs_borsh_deserialization() {
1592 check_borsh_deserialization(StakeStateV2::Uninitialized);
1593 check_borsh_deserialization(StakeStateV2::RewardsPool);
1594 check_borsh_deserialization(StakeStateV2::Initialized(Meta {
1595 rent_exempt_reserve: u64::MAX,
1596 authorized: Authorized {
1597 staker: Pubkey::new_unique(),
1598 withdrawer: Pubkey::new_unique(),
1599 },
1600 lockup: Lockup::default(),
1601 }));
1602 check_borsh_deserialization(StakeStateV2::Stake(
1603 Meta {
1604 rent_exempt_reserve: 1,
1605 authorized: Authorized {
1606 staker: Pubkey::new_unique(),
1607 withdrawer: Pubkey::new_unique(),
1608 },
1609 lockup: Lockup::default(),
1610 },
1611 Stake {
1612 delegation: Delegation {
1613 voter_pubkey: Pubkey::new_unique(),
1614 stake: u64::MAX,
1615 activation_epoch: Epoch::MAX,
1616 deactivation_epoch: Epoch::MAX,
1617 ..Delegation::default()
1618 },
1619 credits_observed: 1,
1620 },
1621 StakeFlags::empty(),
1622 ));
1623 }
1624
1625 #[test]
1626 fn bincode_vs_borsh_serialization() {
1627 check_borsh_serialization(StakeStateV2::Uninitialized);
1628 check_borsh_serialization(StakeStateV2::RewardsPool);
1629 check_borsh_serialization(StakeStateV2::Initialized(Meta {
1630 rent_exempt_reserve: u64::MAX,
1631 authorized: Authorized {
1632 staker: Pubkey::new_unique(),
1633 withdrawer: Pubkey::new_unique(),
1634 },
1635 lockup: Lockup::default(),
1636 }));
1637 #[allow(deprecated)]
1638 check_borsh_serialization(StakeStateV2::Stake(
1639 Meta {
1640 rent_exempt_reserve: 1,
1641 authorized: Authorized {
1642 staker: Pubkey::new_unique(),
1643 withdrawer: Pubkey::new_unique(),
1644 },
1645 lockup: Lockup::default(),
1646 },
1647 Stake {
1648 delegation: Delegation {
1649 voter_pubkey: Pubkey::new_unique(),
1650 stake: u64::MAX,
1651 activation_epoch: Epoch::MAX,
1652 deactivation_epoch: Epoch::MAX,
1653 ..Default::default()
1654 },
1655 credits_observed: 1,
1656 },
1657 StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED,
1658 ));
1659 }
1660
1661 #[test]
1662 fn borsh_deserialization_live_data() {
1663 let data = [
1664 1, 0, 0, 0, 128, 213, 34, 0, 0, 0, 0, 0, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35,
1665 119, 124, 168, 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149,
1666 224, 109, 52, 100, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, 119, 124, 168, 12, 120,
1667 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, 224, 109, 52, 100, 0, 0,
1668 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1669 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1670 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1671 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1672 0, 0, 0, 0, 0, 0,
1673 ];
1674 let deserialized = try_from_slice_unchecked::<StakeStateV2>(&data).unwrap();
1677 assert_matches!(
1678 deserialized,
1679 StakeStateV2::Initialized(Meta {
1680 rent_exempt_reserve: 2282880,
1681 ..
1682 })
1683 );
1684 }
1685
1686 #[test]
1687 fn stake_flag_member_offset() {
1688 const FLAG_OFFSET: usize = 196;
1689 let check_flag = |flag, expected| {
1690 let stake = StakeStateV2::Stake(
1691 Meta {
1692 rent_exempt_reserve: 1,
1693 authorized: Authorized {
1694 staker: Pubkey::new_unique(),
1695 withdrawer: Pubkey::new_unique(),
1696 },
1697 lockup: Lockup::default(),
1698 },
1699 Stake {
1700 delegation: Delegation {
1701 voter_pubkey: Pubkey::new_unique(),
1702 stake: u64::MAX,
1703 activation_epoch: Epoch::MAX,
1704 deactivation_epoch: Epoch::MAX,
1705 warmup_cooldown_rate: f64::MAX,
1706 },
1707 credits_observed: 1,
1708 },
1709 flag,
1710 );
1711
1712 let bincode_serialized = serialize(&stake).unwrap();
1713 let borsh_serialized = borsh::to_vec(&stake).unwrap();
1714
1715 assert_eq!(bincode_serialized[FLAG_OFFSET], expected);
1716 assert_eq!(borsh_serialized[FLAG_OFFSET], expected);
1717 };
1718 #[allow(deprecated)]
1719 check_flag(
1720 StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED,
1721 1,
1722 );
1723 check_flag(StakeFlags::empty(), 0);
1724 }
1725
1726 mod deprecated {
1727 use super::*;
1728
1729 fn check_borsh_deserialization(stake: StakeState) {
1730 let serialized = serialize(&stake).unwrap();
1731 let deserialized = StakeState::try_from_slice(&serialized).unwrap();
1732 assert_eq!(stake, deserialized);
1733 }
1734
1735 fn check_borsh_serialization(stake: StakeState) {
1736 let bincode_serialized = serialize(&stake).unwrap();
1737 let borsh_serialized = borsh::to_vec(&stake).unwrap();
1738 assert_eq!(bincode_serialized, borsh_serialized);
1739 }
1740
1741 #[test]
1742 fn test_size_of() {
1743 assert_eq!(StakeState::size_of(), std::mem::size_of::<StakeState>());
1744 }
1745
1746 #[test]
1747 fn bincode_vs_borsh_deserialization() {
1748 check_borsh_deserialization(StakeState::Uninitialized);
1749 check_borsh_deserialization(StakeState::RewardsPool);
1750 check_borsh_deserialization(StakeState::Initialized(Meta {
1751 rent_exempt_reserve: u64::MAX,
1752 authorized: Authorized {
1753 staker: Pubkey::new_unique(),
1754 withdrawer: Pubkey::new_unique(),
1755 },
1756 lockup: Lockup::default(),
1757 }));
1758 check_borsh_deserialization(StakeState::Stake(
1759 Meta {
1760 rent_exempt_reserve: 1,
1761 authorized: Authorized {
1762 staker: Pubkey::new_unique(),
1763 withdrawer: Pubkey::new_unique(),
1764 },
1765 lockup: Lockup::default(),
1766 },
1767 Stake {
1768 delegation: Delegation {
1769 voter_pubkey: Pubkey::new_unique(),
1770 stake: u64::MAX,
1771 activation_epoch: Epoch::MAX,
1772 deactivation_epoch: Epoch::MAX,
1773 warmup_cooldown_rate: f64::MAX,
1774 },
1775 credits_observed: 1,
1776 },
1777 ));
1778 }
1779
1780 #[test]
1781 fn bincode_vs_borsh_serialization() {
1782 check_borsh_serialization(StakeState::Uninitialized);
1783 check_borsh_serialization(StakeState::RewardsPool);
1784 check_borsh_serialization(StakeState::Initialized(Meta {
1785 rent_exempt_reserve: u64::MAX,
1786 authorized: Authorized {
1787 staker: Pubkey::new_unique(),
1788 withdrawer: Pubkey::new_unique(),
1789 },
1790 lockup: Lockup::default(),
1791 }));
1792 check_borsh_serialization(StakeState::Stake(
1793 Meta {
1794 rent_exempt_reserve: 1,
1795 authorized: Authorized {
1796 staker: Pubkey::new_unique(),
1797 withdrawer: Pubkey::new_unique(),
1798 },
1799 lockup: Lockup::default(),
1800 },
1801 Stake {
1802 delegation: Delegation {
1803 voter_pubkey: Pubkey::new_unique(),
1804 stake: u64::MAX,
1805 activation_epoch: Epoch::MAX,
1806 deactivation_epoch: Epoch::MAX,
1807 warmup_cooldown_rate: f64::MAX,
1808 },
1809 credits_observed: 1,
1810 },
1811 ));
1812 }
1813
1814 #[test]
1815 fn borsh_deserialization_live_data() {
1816 let data = [
1817 1, 0, 0, 0, 128, 213, 34, 0, 0, 0, 0, 0, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35,
1818 119, 124, 168, 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246,
1819 149, 224, 109, 52, 100, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, 119, 124, 168,
1820 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, 224, 109, 52,
1821 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1822 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1823 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1824 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1825 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1826 ];
1827 let deserialized = try_from_slice_unchecked::<StakeState>(&data).unwrap();
1830 assert_matches!(
1831 deserialized,
1832 StakeState::Initialized(Meta {
1833 rent_exempt_reserve: 2282880,
1834 ..
1835 })
1836 );
1837 }
1838 }
1839}