miraland_program/stake/
state.rs

1#![allow(clippy::arithmetic_side_effects)]
2// Remove the following `allow` when `StakeState` is removed, required to avoid
3// warnings from uses of deprecated types during trait derivations.
4#![allow(deprecated)]
5
6use {
7    crate::{
8        clock::{Clock, Epoch, UnixTimestamp},
9        instruction::InstructionError,
10        pubkey::Pubkey,
11        stake::{
12            instruction::{LockupArgs, StakeError},
13            stake_flags::StakeFlags,
14        },
15        stake_history::{StakeHistory, StakeHistoryEntry},
16    },
17    borsh::{io, BorshDeserialize, BorshSchema, BorshSerialize},
18    std::collections::HashSet,
19};
20
21pub type StakeActivationStatus = StakeHistoryEntry;
22
23// means that no more than RATE of current effective stake may be added or subtracted per
24// epoch
25pub const DEFAULT_WARMUP_COOLDOWN_RATE: f64 = 0.25;
26pub const NEW_WARMUP_COOLDOWN_RATE: f64 = 0.09;
27pub const DEFAULT_SLASH_PENALTY: u8 = ((5 * std::u8::MAX as usize) / 100) as u8;
28
29pub fn warmup_cooldown_rate(current_epoch: Epoch, new_rate_activation_epoch: Option<Epoch>) -> f64 {
30    if current_epoch < new_rate_activation_epoch.unwrap_or(u64::MAX) {
31        DEFAULT_WARMUP_COOLDOWN_RATE
32    } else {
33        NEW_WARMUP_COOLDOWN_RATE
34    }
35}
36
37macro_rules! impl_borsh_stake_state {
38    ($borsh:ident) => {
39        impl $borsh::BorshDeserialize for StakeState {
40            fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
41                let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
42                match enum_value {
43                    0 => Ok(StakeState::Uninitialized),
44                    1 => {
45                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
46                        Ok(StakeState::Initialized(meta))
47                    }
48                    2 => {
49                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
50                        let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
51                        Ok(StakeState::Stake(meta, stake))
52                    }
53                    3 => Ok(StakeState::RewardsPool),
54                    _ => Err(io::Error::new(
55                        io::ErrorKind::InvalidData,
56                        "Invalid enum value",
57                    )),
58                }
59            }
60        }
61        impl $borsh::BorshSerialize for StakeState {
62            fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
63                match self {
64                    StakeState::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
65                    StakeState::Initialized(meta) => {
66                        writer.write_all(&1u32.to_le_bytes())?;
67                        $borsh::BorshSerialize::serialize(&meta, writer)
68                    }
69                    StakeState::Stake(meta, stake) => {
70                        writer.write_all(&2u32.to_le_bytes())?;
71                        $borsh::BorshSerialize::serialize(&meta, writer)?;
72                        $borsh::BorshSerialize::serialize(&stake, writer)
73                    }
74                    StakeState::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
75                }
76            }
77        }
78    };
79}
80#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
81#[allow(clippy::large_enum_variant)]
82#[deprecated(
83    since = "1.17.0",
84    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)`."
85)]
86pub enum StakeState {
87    #[default]
88    Uninitialized,
89    Initialized(Meta),
90    Stake(Meta, Stake),
91    RewardsPool,
92}
93impl_borsh_stake_state!(borsh);
94impl_borsh_stake_state!(borsh0_10);
95impl StakeState {
96    /// The fixed number of bytes used to serialize each stake account
97    pub const fn size_of() -> usize {
98        200 // see test_size_of
99    }
100
101    pub fn stake(&self) -> Option<Stake> {
102        match self {
103            StakeState::Stake(_meta, stake) => Some(*stake),
104            _ => None,
105        }
106    }
107
108    pub fn delegation(&self) -> Option<Delegation> {
109        match self {
110            StakeState::Stake(_meta, stake) => Some(stake.delegation),
111            _ => None,
112        }
113    }
114
115    pub fn authorized(&self) -> Option<Authorized> {
116        match self {
117            StakeState::Stake(meta, _stake) => Some(meta.authorized),
118            StakeState::Initialized(meta) => Some(meta.authorized),
119            _ => None,
120        }
121    }
122
123    pub fn lockup(&self) -> Option<Lockup> {
124        self.meta().map(|meta| meta.lockup)
125    }
126
127    pub fn meta(&self) -> Option<Meta> {
128        match self {
129            StakeState::Stake(meta, _stake) => Some(*meta),
130            StakeState::Initialized(meta) => Some(*meta),
131            _ => None,
132        }
133    }
134}
135
136#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
137#[allow(clippy::large_enum_variant)]
138pub enum StakeStateV2 {
139    #[default]
140    Uninitialized,
141    Initialized(Meta),
142    Stake(Meta, Stake, StakeFlags),
143    RewardsPool,
144}
145macro_rules! impl_borsh_stake_state_v2 {
146    ($borsh:ident) => {
147        impl $borsh::BorshDeserialize for StakeStateV2 {
148            fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
149                let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
150                match enum_value {
151                    0 => Ok(StakeStateV2::Uninitialized),
152                    1 => {
153                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
154                        Ok(StakeStateV2::Initialized(meta))
155                    }
156                    2 => {
157                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
158                        let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
159                        let stake_flags: StakeFlags =
160                            $borsh::BorshDeserialize::deserialize_reader(reader)?;
161                        Ok(StakeStateV2::Stake(meta, stake, stake_flags))
162                    }
163                    3 => Ok(StakeStateV2::RewardsPool),
164                    _ => Err(io::Error::new(
165                        io::ErrorKind::InvalidData,
166                        "Invalid enum value",
167                    )),
168                }
169            }
170        }
171        impl $borsh::BorshSerialize for StakeStateV2 {
172            fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
173                match self {
174                    StakeStateV2::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
175                    StakeStateV2::Initialized(meta) => {
176                        writer.write_all(&1u32.to_le_bytes())?;
177                        $borsh::BorshSerialize::serialize(&meta, writer)
178                    }
179                    StakeStateV2::Stake(meta, stake, stake_flags) => {
180                        writer.write_all(&2u32.to_le_bytes())?;
181                        $borsh::BorshSerialize::serialize(&meta, writer)?;
182                        $borsh::BorshSerialize::serialize(&stake, writer)?;
183                        $borsh::BorshSerialize::serialize(&stake_flags, writer)
184                    }
185                    StakeStateV2::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
186                }
187            }
188        }
189    };
190}
191impl_borsh_stake_state_v2!(borsh);
192impl_borsh_stake_state_v2!(borsh0_10);
193
194impl StakeStateV2 {
195    /// The fixed number of bytes used to serialize each stake account
196    pub const fn size_of() -> usize {
197        200 // see test_size_of
198    }
199
200    pub fn stake(&self) -> Option<Stake> {
201        match self {
202            StakeStateV2::Stake(_meta, stake, _stake_flags) => Some(*stake),
203            _ => None,
204        }
205    }
206
207    pub fn delegation(&self) -> Option<Delegation> {
208        match self {
209            StakeStateV2::Stake(_meta, stake, _stake_flags) => Some(stake.delegation),
210            _ => None,
211        }
212    }
213
214    pub fn authorized(&self) -> Option<Authorized> {
215        match self {
216            StakeStateV2::Stake(meta, _stake, _stake_flags) => Some(meta.authorized),
217            StakeStateV2::Initialized(meta) => Some(meta.authorized),
218            _ => None,
219        }
220    }
221
222    pub fn lockup(&self) -> Option<Lockup> {
223        self.meta().map(|meta| meta.lockup)
224    }
225
226    pub fn meta(&self) -> Option<Meta> {
227        match self {
228            StakeStateV2::Stake(meta, _stake, _stake_flags) => Some(*meta),
229            StakeStateV2::Initialized(meta) => Some(*meta),
230            _ => None,
231        }
232    }
233}
234
235#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)]
236pub enum StakeAuthorize {
237    Staker,
238    Withdrawer,
239}
240
241#[derive(
242    Default,
243    Debug,
244    Serialize,
245    Deserialize,
246    PartialEq,
247    Eq,
248    Clone,
249    Copy,
250    AbiExample,
251    BorshDeserialize,
252    BorshSchema,
253    BorshSerialize,
254)]
255#[borsh(crate = "borsh")]
256pub struct Lockup {
257    /// UnixTimestamp at which this stake will allow withdrawal, unless the
258    ///   transaction is signed by the custodian
259    pub unix_timestamp: UnixTimestamp,
260    /// epoch height at which this stake will allow withdrawal, unless the
261    ///   transaction is signed by the custodian
262    pub epoch: Epoch,
263    /// custodian signature on a transaction exempts the operation from
264    ///  lockup constraints
265    pub custodian: Pubkey,
266}
267impl Lockup {
268    pub fn is_in_force(&self, clock: &Clock, custodian: Option<&Pubkey>) -> bool {
269        if custodian == Some(&self.custodian) {
270            return false;
271        }
272        self.unix_timestamp > clock.unix_timestamp || self.epoch > clock.epoch
273    }
274}
275impl borsh0_10::de::BorshDeserialize for Lockup {
276    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
277        reader: &mut R,
278    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
279        Ok(Self {
280            unix_timestamp: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
281            epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
282            custodian: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
283        })
284    }
285}
286impl borsh0_10::BorshSchema for Lockup {
287    fn declaration() -> borsh0_10::schema::Declaration {
288        "Lockup".to_string()
289    }
290    fn add_definitions_recursively(
291        definitions: &mut borsh0_10::maybestd::collections::HashMap<
292            borsh0_10::schema::Declaration,
293            borsh0_10::schema::Definition,
294        >,
295    ) {
296        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
297            borsh0_10::maybestd::boxed::Box::new([
298                (
299                    "unix_timestamp".to_string(),
300                    <UnixTimestamp as borsh0_10::BorshSchema>::declaration(),
301                ),
302                (
303                    "epoch".to_string(),
304                    <Epoch as borsh0_10::BorshSchema>::declaration(),
305                ),
306                (
307                    "custodian".to_string(),
308                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
309                ),
310            ]),
311        ));
312        let definition = borsh0_10::schema::Definition::Struct { fields };
313        Self::add_definition(
314            <Self as borsh0_10::BorshSchema>::declaration(),
315            definition,
316            definitions,
317        );
318        <UnixTimestamp as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
319        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
320        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
321    }
322}
323impl borsh0_10::ser::BorshSerialize for Lockup {
324    fn serialize<W: borsh0_10::maybestd::io::Write>(
325        &self,
326        writer: &mut W,
327    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
328        borsh0_10::BorshSerialize::serialize(&self.unix_timestamp, writer)?;
329        borsh0_10::BorshSerialize::serialize(&self.epoch, writer)?;
330        borsh0_10::BorshSerialize::serialize(&self.custodian, writer)?;
331        Ok(())
332    }
333}
334
335#[derive(
336    Default,
337    Debug,
338    Serialize,
339    Deserialize,
340    PartialEq,
341    Eq,
342    Clone,
343    Copy,
344    AbiExample,
345    BorshDeserialize,
346    BorshSchema,
347    BorshSerialize,
348)]
349#[borsh(crate = "borsh")]
350pub struct Authorized {
351    pub staker: Pubkey,
352    pub withdrawer: Pubkey,
353}
354
355impl Authorized {
356    pub fn auto(authorized: &Pubkey) -> Self {
357        Self {
358            staker: *authorized,
359            withdrawer: *authorized,
360        }
361    }
362    pub fn check(
363        &self,
364        signers: &HashSet<Pubkey>,
365        stake_authorize: StakeAuthorize,
366    ) -> Result<(), InstructionError> {
367        match stake_authorize {
368            StakeAuthorize::Staker if signers.contains(&self.staker) => Ok(()),
369            StakeAuthorize::Withdrawer if signers.contains(&self.withdrawer) => Ok(()),
370            _ => Err(InstructionError::MissingRequiredSignature),
371        }
372    }
373
374    pub fn authorize(
375        &mut self,
376        signers: &HashSet<Pubkey>,
377        new_authorized: &Pubkey,
378        stake_authorize: StakeAuthorize,
379        lockup_custodian_args: Option<(&Lockup, &Clock, Option<&Pubkey>)>,
380    ) -> Result<(), InstructionError> {
381        match stake_authorize {
382            StakeAuthorize::Staker => {
383                // Allow either the staker or the withdrawer to change the staker key
384                if !signers.contains(&self.staker) && !signers.contains(&self.withdrawer) {
385                    return Err(InstructionError::MissingRequiredSignature);
386                }
387                self.staker = *new_authorized
388            }
389            StakeAuthorize::Withdrawer => {
390                if let Some((lockup, clock, custodian)) = lockup_custodian_args {
391                    if lockup.is_in_force(clock, None) {
392                        match custodian {
393                            None => {
394                                return Err(StakeError::CustodianMissing.into());
395                            }
396                            Some(custodian) => {
397                                if !signers.contains(custodian) {
398                                    return Err(StakeError::CustodianSignatureMissing.into());
399                                }
400
401                                if lockup.is_in_force(clock, Some(custodian)) {
402                                    return Err(StakeError::LockupInForce.into());
403                                }
404                            }
405                        }
406                    }
407                }
408                self.check(signers, stake_authorize)?;
409                self.withdrawer = *new_authorized
410            }
411        }
412        Ok(())
413    }
414}
415impl borsh0_10::de::BorshDeserialize for Authorized {
416    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
417        reader: &mut R,
418    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
419        Ok(Self {
420            staker: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
421            withdrawer: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
422        })
423    }
424}
425impl borsh0_10::BorshSchema for Authorized {
426    fn declaration() -> borsh0_10::schema::Declaration {
427        "Authorized".to_string()
428    }
429    fn add_definitions_recursively(
430        definitions: &mut borsh0_10::maybestd::collections::HashMap<
431            borsh0_10::schema::Declaration,
432            borsh0_10::schema::Definition,
433        >,
434    ) {
435        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
436            borsh0_10::maybestd::boxed::Box::new([
437                (
438                    "staker".to_string(),
439                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
440                ),
441                (
442                    "withdrawer".to_string(),
443                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
444                ),
445            ]),
446        ));
447        let definition = borsh0_10::schema::Definition::Struct { fields };
448        Self::add_definition(
449            <Self as borsh0_10::BorshSchema>::declaration(),
450            definition,
451            definitions,
452        );
453        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
454        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
455    }
456}
457impl borsh0_10::ser::BorshSerialize for Authorized {
458    fn serialize<W: borsh0_10::maybestd::io::Write>(
459        &self,
460        writer: &mut W,
461    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
462        borsh0_10::BorshSerialize::serialize(&self.staker, writer)?;
463        borsh0_10::BorshSerialize::serialize(&self.withdrawer, writer)?;
464        Ok(())
465    }
466}
467
468#[derive(
469    Default,
470    Debug,
471    Serialize,
472    Deserialize,
473    PartialEq,
474    Eq,
475    Clone,
476    Copy,
477    AbiExample,
478    BorshDeserialize,
479    BorshSchema,
480    BorshSerialize,
481)]
482#[borsh(crate = "borsh")]
483pub struct Meta {
484    pub rent_exempt_reserve: u64,
485    pub authorized: Authorized,
486    pub lockup: Lockup,
487}
488
489impl Meta {
490    pub fn set_lockup(
491        &mut self,
492        lockup: &LockupArgs,
493        signers: &HashSet<Pubkey>,
494        clock: &Clock,
495    ) -> Result<(), InstructionError> {
496        // post-stake_program_v4 behavior:
497        // * custodian can update the lockup while in force
498        // * withdraw authority can set a new lockup
499        if self.lockup.is_in_force(clock, None) {
500            if !signers.contains(&self.lockup.custodian) {
501                return Err(InstructionError::MissingRequiredSignature);
502            }
503        } else if !signers.contains(&self.authorized.withdrawer) {
504            return Err(InstructionError::MissingRequiredSignature);
505        }
506        if let Some(unix_timestamp) = lockup.unix_timestamp {
507            self.lockup.unix_timestamp = unix_timestamp;
508        }
509        if let Some(epoch) = lockup.epoch {
510            self.lockup.epoch = epoch;
511        }
512        if let Some(custodian) = lockup.custodian {
513            self.lockup.custodian = custodian;
514        }
515        Ok(())
516    }
517
518    pub fn auto(authorized: &Pubkey) -> Self {
519        Self {
520            authorized: Authorized::auto(authorized),
521            ..Meta::default()
522        }
523    }
524}
525impl borsh0_10::de::BorshDeserialize for Meta {
526    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
527        reader: &mut R,
528    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
529        Ok(Self {
530            rent_exempt_reserve: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
531            authorized: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
532            lockup: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
533        })
534    }
535}
536impl borsh0_10::BorshSchema for Meta {
537    fn declaration() -> borsh0_10::schema::Declaration {
538        "Meta".to_string()
539    }
540    fn add_definitions_recursively(
541        definitions: &mut borsh0_10::maybestd::collections::HashMap<
542            borsh0_10::schema::Declaration,
543            borsh0_10::schema::Definition,
544        >,
545    ) {
546        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
547            borsh0_10::maybestd::boxed::Box::new([
548                (
549                    "rent_exempt_reserve".to_string(),
550                    <u64 as borsh0_10::BorshSchema>::declaration(),
551                ),
552                (
553                    "authorized".to_string(),
554                    <Authorized as borsh0_10::BorshSchema>::declaration(),
555                ),
556                (
557                    "lockup".to_string(),
558                    <Lockup as borsh0_10::BorshSchema>::declaration(),
559                ),
560            ]),
561        ));
562        let definition = borsh0_10::schema::Definition::Struct { fields };
563        Self::add_definition(
564            <Self as borsh0_10::BorshSchema>::declaration(),
565            definition,
566            definitions,
567        );
568        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
569        <Authorized as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
570        <Lockup as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
571    }
572}
573impl borsh0_10::ser::BorshSerialize for Meta {
574    fn serialize<W: borsh0_10::maybestd::io::Write>(
575        &self,
576        writer: &mut W,
577    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
578        borsh0_10::BorshSerialize::serialize(&self.rent_exempt_reserve, writer)?;
579        borsh0_10::BorshSerialize::serialize(&self.authorized, writer)?;
580        borsh0_10::BorshSerialize::serialize(&self.lockup, writer)?;
581        Ok(())
582    }
583}
584
585#[derive(
586    Debug,
587    Serialize,
588    Deserialize,
589    PartialEq,
590    Clone,
591    Copy,
592    AbiExample,
593    BorshDeserialize,
594    BorshSchema,
595    BorshSerialize,
596)]
597#[borsh(crate = "borsh")]
598pub struct Delegation {
599    /// to whom the stake is delegated
600    pub voter_pubkey: Pubkey,
601    /// activated stake amount, set at delegate() time
602    pub stake: u64,
603    /// epoch at which this stake was activated, std::Epoch::MAX if is a bootstrap stake
604    pub activation_epoch: Epoch,
605    /// epoch the stake was deactivated, std::Epoch::MAX if not deactivated
606    pub deactivation_epoch: Epoch,
607    /// how much stake we can activate per-epoch as a fraction of currently effective stake
608    #[deprecated(
609        since = "1.16.7",
610        note = "Please use `miraland_sdk::stake::state::warmup_cooldown_rate()` instead"
611    )]
612    pub warmup_cooldown_rate: f64,
613}
614
615impl Default for Delegation {
616    fn default() -> Self {
617        #[allow(deprecated)]
618        Self {
619            voter_pubkey: Pubkey::default(),
620            stake: 0,
621            activation_epoch: 0,
622            deactivation_epoch: std::u64::MAX,
623            warmup_cooldown_rate: DEFAULT_WARMUP_COOLDOWN_RATE,
624        }
625    }
626}
627
628impl Delegation {
629    pub fn new(voter_pubkey: &Pubkey, stake: u64, activation_epoch: Epoch) -> Self {
630        Self {
631            voter_pubkey: *voter_pubkey,
632            stake,
633            activation_epoch,
634            ..Delegation::default()
635        }
636    }
637    pub fn is_bootstrap(&self) -> bool {
638        self.activation_epoch == std::u64::MAX
639    }
640
641    pub fn stake(
642        &self,
643        epoch: Epoch,
644        history: &StakeHistory,
645        new_rate_activation_epoch: Option<Epoch>,
646    ) -> u64 {
647        self.stake_activating_and_deactivating(epoch, history, new_rate_activation_epoch)
648            .effective
649    }
650
651    #[allow(clippy::comparison_chain)]
652    pub fn stake_activating_and_deactivating(
653        &self,
654        target_epoch: Epoch,
655        history: &StakeHistory,
656        new_rate_activation_epoch: Option<Epoch>,
657    ) -> StakeActivationStatus {
658        // first, calculate an effective and activating stake
659        let (effective_stake, activating_stake) =
660            self.stake_and_activating(target_epoch, history, new_rate_activation_epoch);
661
662        // then de-activate some portion if necessary
663        if target_epoch < self.deactivation_epoch {
664            // not deactivated
665            if activating_stake == 0 {
666                StakeActivationStatus::with_effective(effective_stake)
667            } else {
668                StakeActivationStatus::with_effective_and_activating(
669                    effective_stake,
670                    activating_stake,
671                )
672            }
673        } else if target_epoch == self.deactivation_epoch {
674            // can only deactivate what's activated
675            StakeActivationStatus::with_deactivating(effective_stake)
676        } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
677            .get(self.deactivation_epoch)
678            .map(|cluster_stake_at_deactivation_epoch| {
679                (
680                    history,
681                    self.deactivation_epoch,
682                    cluster_stake_at_deactivation_epoch,
683                )
684            })
685        {
686            // target_epoch > self.deactivation_epoch
687
688            // loop from my deactivation epoch until the target epoch
689            // current effective stake is updated using its previous epoch's cluster stake
690            let mut current_epoch;
691            let mut current_effective_stake = effective_stake;
692            loop {
693                current_epoch = prev_epoch + 1;
694                // if there is no deactivating stake at prev epoch, we should have been
695                // fully undelegated at this moment
696                if prev_cluster_stake.deactivating == 0 {
697                    break;
698                }
699
700                // I'm trying to get to zero, how much of the deactivation in stake
701                //   this account is entitled to take
702                let weight =
703                    current_effective_stake as f64 / prev_cluster_stake.deactivating as f64;
704                let warmup_cooldown_rate =
705                    warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
706
707                // portion of newly not-effective cluster stake I'm entitled to at current epoch
708                let newly_not_effective_cluster_stake =
709                    prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
710                let newly_not_effective_stake =
711                    ((weight * newly_not_effective_cluster_stake) as u64).max(1);
712
713                current_effective_stake =
714                    current_effective_stake.saturating_sub(newly_not_effective_stake);
715                if current_effective_stake == 0 {
716                    break;
717                }
718
719                if current_epoch >= target_epoch {
720                    break;
721                }
722                if let Some(current_cluster_stake) = history.get(current_epoch) {
723                    prev_epoch = current_epoch;
724                    prev_cluster_stake = current_cluster_stake;
725                } else {
726                    break;
727                }
728            }
729
730            // deactivating stake should equal to all of currently remaining effective stake
731            StakeActivationStatus::with_deactivating(current_effective_stake)
732        } else {
733            // no history or I've dropped out of history, so assume fully deactivated
734            StakeActivationStatus::default()
735        }
736    }
737
738    // returned tuple is (effective, activating) stake
739    fn stake_and_activating(
740        &self,
741        target_epoch: Epoch,
742        history: &StakeHistory,
743        new_rate_activation_epoch: Option<Epoch>,
744    ) -> (u64, u64) {
745        let delegated_stake = self.stake;
746
747        if self.is_bootstrap() {
748            // fully effective immediately
749            (delegated_stake, 0)
750        } else if self.activation_epoch == self.deactivation_epoch {
751            // activated but instantly deactivated; no stake at all regardless of target_epoch
752            // this must be after the bootstrap check and before all-is-activating check
753            (0, 0)
754        } else if target_epoch == self.activation_epoch {
755            // all is activating
756            (0, delegated_stake)
757        } else if target_epoch < self.activation_epoch {
758            // not yet enabled
759            (0, 0)
760        } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
761            .get(self.activation_epoch)
762            .map(|cluster_stake_at_activation_epoch| {
763                (
764                    history,
765                    self.activation_epoch,
766                    cluster_stake_at_activation_epoch,
767                )
768            })
769        {
770            // target_epoch > self.activation_epoch
771
772            // loop from my activation epoch until the target epoch summing up my entitlement
773            // current effective stake is updated using its previous epoch's cluster stake
774            let mut current_epoch;
775            let mut current_effective_stake = 0;
776            loop {
777                current_epoch = prev_epoch + 1;
778                // if there is no activating stake at prev epoch, we should have been
779                // fully effective at this moment
780                if prev_cluster_stake.activating == 0 {
781                    break;
782                }
783
784                // how much of the growth in stake this account is
785                //  entitled to take
786                let remaining_activating_stake = delegated_stake - current_effective_stake;
787                let weight =
788                    remaining_activating_stake as f64 / prev_cluster_stake.activating as f64;
789                let warmup_cooldown_rate =
790                    warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
791
792                // portion of newly effective cluster stake I'm entitled to at current epoch
793                let newly_effective_cluster_stake =
794                    prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
795                let newly_effective_stake =
796                    ((weight * newly_effective_cluster_stake) as u64).max(1);
797
798                current_effective_stake += newly_effective_stake;
799                if current_effective_stake >= delegated_stake {
800                    current_effective_stake = delegated_stake;
801                    break;
802                }
803
804                if current_epoch >= target_epoch || current_epoch >= self.deactivation_epoch {
805                    break;
806                }
807                if let Some(current_cluster_stake) = history.get(current_epoch) {
808                    prev_epoch = current_epoch;
809                    prev_cluster_stake = current_cluster_stake;
810                } else {
811                    break;
812                }
813            }
814
815            (
816                current_effective_stake,
817                delegated_stake - current_effective_stake,
818            )
819        } else {
820            // no history or I've dropped out of history, so assume fully effective
821            (delegated_stake, 0)
822        }
823    }
824}
825impl borsh0_10::de::BorshDeserialize for Delegation {
826    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
827        reader: &mut R,
828    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
829        Ok(Self {
830            voter_pubkey: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
831            stake: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
832            activation_epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
833            deactivation_epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
834            warmup_cooldown_rate: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
835        })
836    }
837}
838impl borsh0_10::BorshSchema for Delegation {
839    fn declaration() -> borsh0_10::schema::Declaration {
840        "Delegation".to_string()
841    }
842    fn add_definitions_recursively(
843        definitions: &mut borsh0_10::maybestd::collections::HashMap<
844            borsh0_10::schema::Declaration,
845            borsh0_10::schema::Definition,
846        >,
847    ) {
848        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
849            borsh0_10::maybestd::boxed::Box::new([
850                (
851                    "voter_pubkey".to_string(),
852                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
853                ),
854                (
855                    "stake".to_string(),
856                    <u64 as borsh0_10::BorshSchema>::declaration(),
857                ),
858                (
859                    "activation_epoch".to_string(),
860                    <Epoch as borsh0_10::BorshSchema>::declaration(),
861                ),
862                (
863                    "deactivation_epoch".to_string(),
864                    <Epoch as borsh0_10::BorshSchema>::declaration(),
865                ),
866                (
867                    "warmup_cooldown_rate".to_string(),
868                    <f64 as borsh0_10::BorshSchema>::declaration(),
869                ),
870            ]),
871        ));
872        let definition = borsh0_10::schema::Definition::Struct { fields };
873        Self::add_definition(
874            <Self as borsh0_10::BorshSchema>::declaration(),
875            definition,
876            definitions,
877        );
878        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
879        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
880        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
881        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
882        <f64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
883    }
884}
885impl borsh0_10::ser::BorshSerialize for Delegation {
886    fn serialize<W: borsh0_10::maybestd::io::Write>(
887        &self,
888        writer: &mut W,
889    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
890        borsh0_10::BorshSerialize::serialize(&self.voter_pubkey, writer)?;
891        borsh0_10::BorshSerialize::serialize(&self.stake, writer)?;
892        borsh0_10::BorshSerialize::serialize(&self.activation_epoch, writer)?;
893        borsh0_10::BorshSerialize::serialize(&self.deactivation_epoch, writer)?;
894        borsh0_10::BorshSerialize::serialize(&self.warmup_cooldown_rate, writer)?;
895        Ok(())
896    }
897}
898
899#[derive(
900    Debug,
901    Default,
902    Serialize,
903    Deserialize,
904    PartialEq,
905    Clone,
906    Copy,
907    AbiExample,
908    BorshDeserialize,
909    BorshSchema,
910    BorshSerialize,
911)]
912#[borsh(crate = "borsh")]
913pub struct Stake {
914    pub delegation: Delegation,
915    /// credits observed is credits from vote account state when delegated or redeemed
916    pub credits_observed: u64,
917}
918
919impl Stake {
920    pub fn stake(
921        &self,
922        epoch: Epoch,
923        history: &StakeHistory,
924        new_rate_activation_epoch: Option<Epoch>,
925    ) -> u64 {
926        self.delegation
927            .stake(epoch, history, new_rate_activation_epoch)
928    }
929
930    pub fn split(
931        &mut self,
932        remaining_stake_delta: u64,
933        split_stake_amount: u64,
934    ) -> Result<Self, StakeError> {
935        if remaining_stake_delta > self.delegation.stake {
936            return Err(StakeError::InsufficientStake);
937        }
938        self.delegation.stake -= remaining_stake_delta;
939        let new = Self {
940            delegation: Delegation {
941                stake: split_stake_amount,
942                ..self.delegation
943            },
944            ..*self
945        };
946        Ok(new)
947    }
948
949    pub fn deactivate(&mut self, epoch: Epoch) -> Result<(), StakeError> {
950        if self.delegation.deactivation_epoch != std::u64::MAX {
951            Err(StakeError::AlreadyDeactivated)
952        } else {
953            self.delegation.deactivation_epoch = epoch;
954            Ok(())
955        }
956    }
957}
958impl borsh0_10::de::BorshDeserialize for Stake {
959    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
960        reader: &mut R,
961    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
962        Ok(Self {
963            delegation: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
964            credits_observed: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
965        })
966    }
967}
968impl borsh0_10::BorshSchema for Stake {
969    fn declaration() -> borsh0_10::schema::Declaration {
970        "Stake".to_string()
971    }
972    fn add_definitions_recursively(
973        definitions: &mut borsh0_10::maybestd::collections::HashMap<
974            borsh0_10::schema::Declaration,
975            borsh0_10::schema::Definition,
976        >,
977    ) {
978        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
979            borsh0_10::maybestd::boxed::Box::new([
980                (
981                    "delegation".to_string(),
982                    <Delegation as borsh0_10::BorshSchema>::declaration(),
983                ),
984                (
985                    "credits_observed".to_string(),
986                    <u64 as borsh0_10::BorshSchema>::declaration(),
987                ),
988            ]),
989        ));
990        let definition = borsh0_10::schema::Definition::Struct { fields };
991        Self::add_definition(
992            <Self as borsh0_10::BorshSchema>::declaration(),
993            definition,
994            definitions,
995        );
996        <Delegation as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
997        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
998    }
999}
1000impl borsh0_10::ser::BorshSerialize for Stake {
1001    fn serialize<W: borsh0_10::maybestd::io::Write>(
1002        &self,
1003        writer: &mut W,
1004    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
1005        borsh0_10::BorshSerialize::serialize(&self.delegation, writer)?;
1006        borsh0_10::BorshSerialize::serialize(&self.credits_observed, writer)?;
1007        Ok(())
1008    }
1009}
1010
1011#[cfg(test)]
1012mod test {
1013    use {
1014        super::*, crate::borsh1::try_from_slice_unchecked, assert_matches::assert_matches,
1015        bincode::serialize,
1016    };
1017
1018    fn check_borsh_deserialization(stake: StakeStateV2) {
1019        let serialized = serialize(&stake).unwrap();
1020        let deserialized = StakeStateV2::try_from_slice(&serialized).unwrap();
1021        assert_eq!(stake, deserialized);
1022    }
1023
1024    fn check_borsh_serialization(stake: StakeStateV2) {
1025        let bincode_serialized = serialize(&stake).unwrap();
1026        let borsh_serialized = borsh::to_vec(&stake).unwrap();
1027        assert_eq!(bincode_serialized, borsh_serialized);
1028    }
1029
1030    #[test]
1031    fn test_size_of() {
1032        assert_eq!(StakeStateV2::size_of(), std::mem::size_of::<StakeStateV2>());
1033    }
1034
1035    #[test]
1036    fn bincode_vs_borsh_deserialization() {
1037        check_borsh_deserialization(StakeStateV2::Uninitialized);
1038        check_borsh_deserialization(StakeStateV2::RewardsPool);
1039        check_borsh_deserialization(StakeStateV2::Initialized(Meta {
1040            rent_exempt_reserve: u64::MAX,
1041            authorized: Authorized {
1042                staker: Pubkey::new_unique(),
1043                withdrawer: Pubkey::new_unique(),
1044            },
1045            lockup: Lockup::default(),
1046        }));
1047        check_borsh_deserialization(StakeStateV2::Stake(
1048            Meta {
1049                rent_exempt_reserve: 1,
1050                authorized: Authorized {
1051                    staker: Pubkey::new_unique(),
1052                    withdrawer: Pubkey::new_unique(),
1053                },
1054                lockup: Lockup::default(),
1055            },
1056            Stake {
1057                delegation: Delegation {
1058                    voter_pubkey: Pubkey::new_unique(),
1059                    stake: u64::MAX,
1060                    activation_epoch: Epoch::MAX,
1061                    deactivation_epoch: Epoch::MAX,
1062                    ..Delegation::default()
1063                },
1064                credits_observed: 1,
1065            },
1066            StakeFlags::empty(),
1067        ));
1068    }
1069
1070    #[test]
1071    fn bincode_vs_borsh_serialization() {
1072        check_borsh_serialization(StakeStateV2::Uninitialized);
1073        check_borsh_serialization(StakeStateV2::RewardsPool);
1074        check_borsh_serialization(StakeStateV2::Initialized(Meta {
1075            rent_exempt_reserve: u64::MAX,
1076            authorized: Authorized {
1077                staker: Pubkey::new_unique(),
1078                withdrawer: Pubkey::new_unique(),
1079            },
1080            lockup: Lockup::default(),
1081        }));
1082        check_borsh_serialization(StakeStateV2::Stake(
1083            Meta {
1084                rent_exempt_reserve: 1,
1085                authorized: Authorized {
1086                    staker: Pubkey::new_unique(),
1087                    withdrawer: Pubkey::new_unique(),
1088                },
1089                lockup: Lockup::default(),
1090            },
1091            Stake {
1092                delegation: Delegation {
1093                    voter_pubkey: Pubkey::new_unique(),
1094                    stake: u64::MAX,
1095                    activation_epoch: Epoch::MAX,
1096                    deactivation_epoch: Epoch::MAX,
1097                    ..Default::default()
1098                },
1099                credits_observed: 1,
1100            },
1101            StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED,
1102        ));
1103    }
1104
1105    #[test]
1106    fn borsh_deserialization_live_data() {
1107        let data = [
1108            1, 0, 0, 0, 128, 213, 34, 0, 0, 0, 0, 0, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35,
1109            119, 124, 168, 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149,
1110            224, 109, 52, 100, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, 119, 124, 168, 12, 120,
1111            216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, 224, 109, 52, 100, 0, 0,
1112            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,
1113            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,
1114            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,
1115            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,
1116            0, 0, 0, 0, 0, 0,
1117        ];
1118        // As long as we get the 4-byte enum and the first field right, then
1119        // we're sure the rest works out
1120        let deserialized = try_from_slice_unchecked::<StakeStateV2>(&data).unwrap();
1121        assert_matches!(
1122            deserialized,
1123            StakeStateV2::Initialized(Meta {
1124                rent_exempt_reserve: 2282880,
1125                ..
1126            })
1127        );
1128    }
1129
1130    #[test]
1131    fn stake_flag_member_offset() {
1132        const FLAG_OFFSET: usize = 196;
1133        let check_flag = |flag, expected| {
1134            let stake = StakeStateV2::Stake(
1135                Meta {
1136                    rent_exempt_reserve: 1,
1137                    authorized: Authorized {
1138                        staker: Pubkey::new_unique(),
1139                        withdrawer: Pubkey::new_unique(),
1140                    },
1141                    lockup: Lockup::default(),
1142                },
1143                Stake {
1144                    delegation: Delegation {
1145                        voter_pubkey: Pubkey::new_unique(),
1146                        stake: u64::MAX,
1147                        activation_epoch: Epoch::MAX,
1148                        deactivation_epoch: Epoch::MAX,
1149                        warmup_cooldown_rate: f64::MAX,
1150                    },
1151                    credits_observed: 1,
1152                },
1153                flag,
1154            );
1155
1156            let bincode_serialized = serialize(&stake).unwrap();
1157            let borsh_serialized = borsh::to_vec(&stake).unwrap();
1158
1159            assert_eq!(bincode_serialized[FLAG_OFFSET], expected);
1160            assert_eq!(borsh_serialized[FLAG_OFFSET], expected);
1161        };
1162        check_flag(
1163            StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED,
1164            1,
1165        );
1166        check_flag(StakeFlags::empty(), 0);
1167    }
1168
1169    mod deprecated {
1170        use super::*;
1171        fn check_borsh_deserialization(stake: StakeState) {
1172            let serialized = serialize(&stake).unwrap();
1173            let deserialized = StakeState::try_from_slice(&serialized).unwrap();
1174            assert_eq!(stake, deserialized);
1175        }
1176
1177        fn check_borsh_serialization(stake: StakeState) {
1178            let bincode_serialized = serialize(&stake).unwrap();
1179            let borsh_serialized = borsh::to_vec(&stake).unwrap();
1180            assert_eq!(bincode_serialized, borsh_serialized);
1181        }
1182
1183        #[test]
1184        fn test_size_of() {
1185            assert_eq!(StakeState::size_of(), std::mem::size_of::<StakeState>());
1186        }
1187
1188        #[test]
1189        fn bincode_vs_borsh_deserialization() {
1190            check_borsh_deserialization(StakeState::Uninitialized);
1191            check_borsh_deserialization(StakeState::RewardsPool);
1192            check_borsh_deserialization(StakeState::Initialized(Meta {
1193                rent_exempt_reserve: u64::MAX,
1194                authorized: Authorized {
1195                    staker: Pubkey::new_unique(),
1196                    withdrawer: Pubkey::new_unique(),
1197                },
1198                lockup: Lockup::default(),
1199            }));
1200            check_borsh_deserialization(StakeState::Stake(
1201                Meta {
1202                    rent_exempt_reserve: 1,
1203                    authorized: Authorized {
1204                        staker: Pubkey::new_unique(),
1205                        withdrawer: Pubkey::new_unique(),
1206                    },
1207                    lockup: Lockup::default(),
1208                },
1209                Stake {
1210                    delegation: Delegation {
1211                        voter_pubkey: Pubkey::new_unique(),
1212                        stake: u64::MAX,
1213                        activation_epoch: Epoch::MAX,
1214                        deactivation_epoch: Epoch::MAX,
1215                        warmup_cooldown_rate: f64::MAX,
1216                    },
1217                    credits_observed: 1,
1218                },
1219            ));
1220        }
1221
1222        #[test]
1223        fn bincode_vs_borsh_serialization() {
1224            check_borsh_serialization(StakeState::Uninitialized);
1225            check_borsh_serialization(StakeState::RewardsPool);
1226            check_borsh_serialization(StakeState::Initialized(Meta {
1227                rent_exempt_reserve: u64::MAX,
1228                authorized: Authorized {
1229                    staker: Pubkey::new_unique(),
1230                    withdrawer: Pubkey::new_unique(),
1231                },
1232                lockup: Lockup::default(),
1233            }));
1234            check_borsh_serialization(StakeState::Stake(
1235                Meta {
1236                    rent_exempt_reserve: 1,
1237                    authorized: Authorized {
1238                        staker: Pubkey::new_unique(),
1239                        withdrawer: Pubkey::new_unique(),
1240                    },
1241                    lockup: Lockup::default(),
1242                },
1243                Stake {
1244                    delegation: Delegation {
1245                        voter_pubkey: Pubkey::new_unique(),
1246                        stake: u64::MAX,
1247                        activation_epoch: Epoch::MAX,
1248                        deactivation_epoch: Epoch::MAX,
1249                        warmup_cooldown_rate: f64::MAX,
1250                    },
1251                    credits_observed: 1,
1252                },
1253            ));
1254        }
1255
1256        #[test]
1257        fn borsh_deserialization_live_data() {
1258            let data = [
1259                1, 0, 0, 0, 128, 213, 34, 0, 0, 0, 0, 0, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35,
1260                119, 124, 168, 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246,
1261                149, 224, 109, 52, 100, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, 119, 124, 168,
1262                12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, 224, 109, 52,
1263                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,
1264                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,
1265                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,
1266                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,
1267                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1268            ];
1269            // As long as we get the 4-byte enum and the first field right, then
1270            // we're sure the rest works out
1271            let deserialized = try_from_slice_unchecked::<StakeState>(&data).unwrap();
1272            assert_matches!(
1273                deserialized,
1274                StakeState::Initialized(Meta {
1275                    rent_exempt_reserve: 2282880,
1276                    ..
1277                })
1278            );
1279        }
1280    }
1281}