solana_stake_interface/
state.rs

1#![allow(clippy::arithmetic_side_effects)]
2#![deny(clippy::wildcard_enum_match_arm)]
3// Remove the following `allow` when `StakeState` is removed, required to avoid
4// warnings from uses of deprecated types during trait derivations.
5#![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
24// Means that no more than RATE of current effective stake may be added or subtracted per
25// epoch.
26pub 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    /// The fixed number of bytes used to serialize each stake account
104    pub const fn size_of() -> usize {
105        200 // see test_size_of
106    }
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    /// The fixed number of bytes used to serialize each stake account
209    pub const fn size_of() -> usize {
210        200 // see test_size_of
211    }
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    /// UnixTimestamp at which this stake will allow withdrawal, unless the
286    ///   transaction is signed by the custodian
287    pub unix_timestamp: UnixTimestamp,
288    /// epoch height at which this stake will allow withdrawal, unless the
289    ///   transaction is signed by the custodian
290    pub epoch: Epoch,
291    /// custodian signature on a transaction exempts the operation from
292    ///  lockup constraints
293    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                // Allow either the staker or the withdrawer to change the staker key
354                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        // post-stake_program_v4 behavior:
411        // * custodian can update the lockup while in force
412        // * withdraw authority can set a new lockup
413        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    /// to whom the stake is delegated
453    pub voter_pubkey: Pubkey,
454    /// activated stake amount, set at delegate() time
455    pub stake: u64,
456    /// epoch at which this stake was activated, `std::u64::MAX` if is a bootstrap stake
457    pub activation_epoch: Epoch,
458    /// epoch the stake was deactivated, `std::u64::MAX` if not deactivated
459    pub deactivation_epoch: Epoch,
460    /// how much stake we can activate per-epoch as a fraction of currently effective stake
461    #[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        // first, calculate an effective and activating stake
512        let (effective_stake, activating_stake) =
513            self.stake_and_activating(target_epoch, history, new_rate_activation_epoch);
514
515        // then de-activate some portion if necessary
516        if target_epoch < self.deactivation_epoch {
517            // not deactivated
518            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            // can only deactivate what's activated
528            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            // target_epoch > self.deactivation_epoch
540
541            // loop from my deactivation epoch until the target epoch
542            // current effective stake is updated using its previous epoch's cluster stake
543            let mut current_epoch;
544            let mut current_effective_stake = effective_stake;
545            loop {
546                current_epoch = prev_epoch + 1;
547                // if there is no deactivating stake at prev epoch, we should have been
548                // fully undelegated at this moment
549                if prev_cluster_stake.deactivating == 0 {
550                    break;
551                }
552
553                // I'm trying to get to zero, how much of the deactivation in stake
554                //   this account is entitled to take
555                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                // portion of newly not-effective cluster stake I'm entitled to at current epoch
561                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            // deactivating stake should equal to all of currently remaining effective stake
584            StakeActivationStatus::with_deactivating(current_effective_stake)
585        } else {
586            // no history or I've dropped out of history, so assume fully deactivated
587            StakeActivationStatus::default()
588        }
589    }
590
591    // returned tuple is (effective, activating) stake
592    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            // fully effective immediately
602            (delegated_stake, 0)
603        } else if self.activation_epoch == self.deactivation_epoch {
604            // activated but instantly deactivated; no stake at all regardless of target_epoch
605            // this must be after the bootstrap check and before all-is-activating check
606            (0, 0)
607        } else if target_epoch == self.activation_epoch {
608            // all is activating
609            (0, delegated_stake)
610        } else if target_epoch < self.activation_epoch {
611            // not yet enabled
612            (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            // target_epoch > self.activation_epoch
624
625            // loop from my activation epoch until the target epoch summing up my entitlement
626            // current effective stake is updated using its previous epoch's cluster stake
627            let mut current_epoch;
628            let mut current_effective_stake = 0;
629            loop {
630                current_epoch = prev_epoch + 1;
631                // if there is no activating stake at prev epoch, we should have been
632                // fully effective at this moment
633                if prev_cluster_stake.activating == 0 {
634                    break;
635                }
636
637                // how much of the growth in stake this account is
638                //  entitled to take
639                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                // portion of newly effective cluster stake I'm entitled to at current epoch
646                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            // no history or I've dropped out of history, so assume fully effective
674            (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    /// credits observed is credits from vote account state when delegated or redeemed
693    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        // No lockup, no custodian
840        assert_eq!(
841            authorized.authorize(
842                &signers,
843                &staker,
844                StakeAuthorize::Withdrawer,
845                Some((&Lockup::default(), &clock, None))
846            ),
847            Ok(())
848        );
849
850        // No lockup, invalid custodian not a signer
851        assert_eq!(
852            authorized.authorize(
853                &signers,
854                &staker,
855                StakeAuthorize::Withdrawer,
856                Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
857            ),
858            Ok(()) // <== invalid custodian doesn't matter, there's no lockup
859        );
860
861        // Lockup active, invalid custodian not a signer
862        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        // No lockup, invalid custodian is a signer
875        assert_eq!(
876            authorized.authorize(
877                &signers,
878                &staker,
879                StakeAuthorize::Withdrawer,
880                Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
881            ),
882            Ok(()) // <== invalid custodian doesn't matter, there's no lockup
883        );
884
885        // Lockup active, invalid custodian is a signer
886        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()), // <== invalid custodian rejected
895        );
896
897        signers.remove(&invalid_custodian);
898
899        // Lockup active, no custodian
900        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        // Lockup active, custodian not a signer
911        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        // Lockup active, custodian is a signer
922        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, // activating at zero
965            deactivation_epoch: 5,
966            ..Delegation::default()
967        };
968
969        // save this off so stake.config.warmup_rate changes don't break this test
970        let increment = (1_000_f64 * warmup_cooldown_rate(0, None)) as u64;
971
972        let mut stake_history = StakeHistory::default();
973        // assert that this stake follows step function if there's no history
974        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 that this stake is full deactivating
985        assert_eq!(
986            stake.stake_activating_and_deactivating(stake.deactivation_epoch, &stake_history, None),
987            StakeActivationStatus::with_deactivating(stake.stake),
988        );
989        // assert that this stake is fully deactivated if there's no history
990        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, // entry for zero doesn't have my activating amount
1001            StakeHistoryEntry {
1002                effective: 1_000,
1003                ..StakeHistoryEntry::default()
1004            },
1005        );
1006        // assert that this stake is broken, because above setup is broken
1007        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, // entry for zero has my activating amount
1014            StakeHistoryEntry {
1015                effective: 1_000,
1016                activating: 1_000,
1017                ..StakeHistoryEntry::default()
1018            },
1019            // no entry for 1, so this stake gets shorted
1020        );
1021        // assert that this stake is broken, because above setup is broken
1022        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        // start over, test deactivation edge cases
1031        let mut stake_history = StakeHistory::default();
1032
1033        stake_history.add(
1034            stake.deactivation_epoch, // entry for zero doesn't have my de-activating amount
1035            StakeHistoryEntry {
1036                effective: 1_000,
1037                ..StakeHistoryEntry::default()
1038            },
1039        );
1040        // assert that this stake is broken, because above setup is broken
1041        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        // put in my initial deactivating amount, but don't put in an entry for next
1051        stake_history.add(
1052            stake.deactivation_epoch, // entry for zero has my de-activating amount
1053            StakeHistoryEntry {
1054                effective: 1_000,
1055                deactivating: 1_000,
1056                ..StakeHistoryEntry::default()
1057            },
1058        );
1059        // assert that this stake is broken, because above setup is broken
1060        assert_eq!(
1061            stake.stake_activating_and_deactivating(
1062                stake.deactivation_epoch + 2,
1063                &stake_history,
1064                None,
1065            ),
1066            // hung, should be lower
1067            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            // any stake accounts activated and deactivated at the same epoch
1143            // shouldn't been activated (then deactivated) at all!
1144
1145            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            // any stake accounts activated and deactivated at the same epoch
1162            // shouldn't been activated (then deactivated) at all!
1163
1164            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        // some really boring delegation and stake_history setup
1182        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        // helper closures
1259        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), // +1 is needed for rounding
1290            StakeActivationStatus::with_effective(700),
1291            StakeActivationStatus::with_deactivating(700),
1292            StakeActivationStatus::with_deactivating(275 + 1), // +1 is needed for rounding
1293            StakeActivationStatus::default(),
1294        ];
1295
1296        // normal stake activating and deactivating transition test, just in case
1297        assert_eq!(
1298            expected_staking_status_transition,
1299            calculate_each_staking_status(&stake, expected_staking_status_transition.len())
1300        );
1301
1302        // 10% inflation rewards assuming some sizable epochs passed!
1303        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        // 50% slashing!!!
1314        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        // Build a stake history where the test staker always consumes all of the available warm
1341        // up and cool down stake. However, simulate other stakers beginning to activate during
1342        // the test staker's deactivation.
1343        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, // activating at zero
1397            deactivation_epoch: 5,
1398            ..Delegation::default()
1399        }];
1400        // give 2 epochs of cooldown
1401        let epochs = 7;
1402        // make bootstrap stake smaller than warmup so warmup/cooldownn
1403        //  increment is always smaller than 1
1404        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                // never deactivates
1432                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        // chosen to ensure that the last activated stake (at 4) finishes
1468        //  warming up and cooling down
1469        //  a stake takes 2.0f64.log(1.0 + STAKE_WARMUP_RATE) epochs to warm up or cool down
1470        //  when all alone, but the above overlap a lot
1471        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        // uncomment and add ! for fun with graphing
1486        // eprintln("\n{:8} {:8} {:8}", "   epoch", "   total", "   delta");
1487        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            // uncomment and add ! for fun with graphing
1502            // eprint("{:8} {:8} {:8} ", epoch, total_effective_stake, delta);
1503            // (0..(total_effective_stake as usize / (delegations.len() * 5))).for_each(|_| eprint("#"));
1504            // eprintln();
1505
1506            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        // neither time
1527        assert!(lockup.is_in_force(
1528            &Clock {
1529                epoch: 0,
1530                unix_timestamp: 0,
1531                ..Clock::default()
1532            },
1533            None
1534        ));
1535        // not timestamp
1536        assert!(lockup.is_in_force(
1537            &Clock {
1538                epoch: 2,
1539                unix_timestamp: 0,
1540                ..Clock::default()
1541            },
1542            None
1543        ));
1544        // not epoch
1545        assert!(lockup.is_in_force(
1546            &Clock {
1547                epoch: 0,
1548                unix_timestamp: 2,
1549                ..Clock::default()
1550            },
1551            None
1552        ));
1553        // both, no custodian
1554        assert!(!lockup.is_in_force(
1555            &Clock {
1556                epoch: 1,
1557                unix_timestamp: 1,
1558                ..Clock::default()
1559            },
1560            None
1561        ));
1562        // neither, but custodian
1563        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        // As long as we get the 4-byte enum and the first field right, then
1675        // we're sure the rest works out
1676        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            // As long as we get the 4-byte enum and the first field right, then
1828            // we're sure the rest works out
1829            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}