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