Skip to main content

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