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