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