clone_solana_stake_interface/
state.rs

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