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