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 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
24pub 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 pub const fn size_of() -> usize {
110 200 }
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 pub const fn size_of() -> usize {
220 200 }
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 pub unix_timestamp: UnixTimestamp,
304 pub epoch: Epoch,
307 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 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 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 pub voter_pubkey: Pubkey,
658 pub stake: u64,
660 pub activation_epoch: Epoch,
662 pub deactivation_epoch: Epoch,
664 #[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 let (effective_stake, activating_stake) =
717 self.stake_and_activating(target_epoch, history, new_rate_activation_epoch);
718
719 if target_epoch < self.deactivation_epoch {
721 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 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 let mut current_epoch;
748 let mut current_effective_stake = effective_stake;
749 loop {
750 current_epoch = prev_epoch + 1;
751 if prev_cluster_stake.deactivating == 0 {
754 break;
755 }
756
757 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 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 StakeActivationStatus::with_deactivating(current_effective_stake)
789 } else {
790 StakeActivationStatus::default()
792 }
793 }
794
795 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 (delegated_stake, 0)
807 } else if self.activation_epoch == self.deactivation_epoch {
808 (0, 0)
811 } else if target_epoch == self.activation_epoch {
812 (0, delegated_stake)
814 } else if target_epoch < self.activation_epoch {
815 (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 let mut current_epoch;
832 let mut current_effective_stake = 0;
833 loop {
834 current_epoch = prev_epoch + 1;
835 if prev_cluster_stake.activating == 0 {
838 break;
839 }
840
841 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 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 (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 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 assert_eq!(
1179 authorized.authorize(
1180 &signers,
1181 &staker,
1182 StakeAuthorize::Withdrawer,
1183 Some((&Lockup::default(), &clock, None))
1184 ),
1185 Ok(())
1186 );
1187
1188 assert_eq!(
1190 authorized.authorize(
1191 &signers,
1192 &staker,
1193 StakeAuthorize::Withdrawer,
1194 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1195 ),
1196 Ok(()) );
1198
1199 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 assert_eq!(
1214 authorized.authorize(
1215 &signers,
1216 &staker,
1217 StakeAuthorize::Withdrawer,
1218 Some((&Lockup::default(), &clock, Some(&invalid_custodian)))
1219 ),
1220 Ok(()) );
1222
1223 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()), );
1234
1235 signers.remove(&invalid_custodian);
1236
1237 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 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 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, deactivation_epoch: 5,
1304 ..Delegation::default()
1305 };
1306
1307 let increment = (1_000_f64 * warmup_cooldown_rate(0, None)) as u64;
1309
1310 let mut stake_history = StakeHistory::default();
1311 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_eq!(
1324 stake.stake_activating_and_deactivating(stake.deactivation_epoch, &stake_history, None),
1325 StakeActivationStatus::with_deactivating(stake.stake),
1326 );
1327 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, StakeHistoryEntry {
1340 effective: 1_000,
1341 ..StakeHistoryEntry::default()
1342 },
1343 );
1344 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, StakeHistoryEntry {
1353 effective: 1_000,
1354 activating: 1_000,
1355 ..StakeHistoryEntry::default()
1356 },
1357 );
1359 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 let mut stake_history = StakeHistory::default();
1370
1371 stake_history.add(
1372 stake.deactivation_epoch, StakeHistoryEntry {
1374 effective: 1_000,
1375 ..StakeHistoryEntry::default()
1376 },
1377 );
1378 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 stake_history.add(
1390 stake.deactivation_epoch, StakeHistoryEntry {
1392 effective: 1_000,
1393 deactivating: 1_000,
1394 ..StakeHistoryEntry::default()
1395 },
1396 );
1397 assert_eq!(
1399 stake.stake_activating_and_deactivating(
1400 stake.deactivation_epoch + 2,
1401 &stake_history,
1402 None,
1403 ),
1404 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 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 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 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 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), StakeActivationStatus::with_effective(700),
1629 StakeActivationStatus::with_deactivating(700),
1630 StakeActivationStatus::with_deactivating(275 + 1), StakeActivationStatus::default(),
1632 ];
1633
1634 assert_eq!(
1636 expected_staking_status_transition,
1637 calculate_each_staking_status(&stake, expected_staking_status_transition.len())
1638 );
1639
1640 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 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 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, deactivation_epoch: 5,
1736 ..Delegation::default()
1737 }];
1738 let epochs = 7;
1740 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 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 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 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 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 assert!(lockup.is_in_force(
1866 &Clock {
1867 epoch: 0,
1868 unix_timestamp: 0,
1869 ..Clock::default()
1870 },
1871 None
1872 ));
1873 assert!(lockup.is_in_force(
1875 &Clock {
1876 epoch: 2,
1877 unix_timestamp: 0,
1878 ..Clock::default()
1879 },
1880 None
1881 ));
1882 assert!(lockup.is_in_force(
1884 &Clock {
1885 epoch: 0,
1886 unix_timestamp: 2,
1887 ..Clock::default()
1888 },
1889 None
1890 ));
1891 assert!(!lockup.is_in_force(
1893 &Clock {
1894 epoch: 1,
1895 unix_timestamp: 1,
1896 ..Clock::default()
1897 },
1898 None
1899 ));
1900 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 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 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}