2023-09-01 00:26:13 -07:00
#![ allow(clippy::arithmetic_side_effects) ]
2023-08-08 15:05:40 -07:00
// Remove the following `allow` when `StakeState` is removed, required to avoid
// warnings from uses of deprecated types during trait derivations.
#![ allow(deprecated) ]
2023-08-07 13:23:24 -07:00
2021-06-15 09:04:00 -07:00
use {
crate ::{
clock ::{ Clock , Epoch , UnixTimestamp } ,
instruction ::InstructionError ,
pubkey ::Pubkey ,
stake ::{
instruction ::{ LockupArgs , StakeError } ,
2023-07-24 07:09:40 -07:00
stake_flags ::StakeFlags ,
2021-06-15 09:04:00 -07:00
} ,
2021-10-04 15:59:11 -07:00
stake_history ::{ StakeHistory , StakeHistoryEntry } ,
2021-06-15 09:04:00 -07:00
} ,
2021-10-19 11:10:15 -07:00
borsh ::{ maybestd ::io , BorshDeserialize , BorshSchema , BorshSerialize } ,
2021-06-15 09:04:00 -07:00
std ::collections ::HashSet ,
} ;
2021-10-04 15:59:11 -07:00
pub type StakeActivationStatus = StakeHistoryEntry ;
2023-08-07 13:23:24 -07:00
// means that no more than RATE of current effective stake may be added or subtracted per
// epoch
pub const DEFAULT_WARMUP_COOLDOWN_RATE : f64 = 0.25 ;
pub const NEW_WARMUP_COOLDOWN_RATE : f64 = 0.09 ;
pub const DEFAULT_SLASH_PENALTY : u8 = ( ( 5 * std ::u8 ::MAX as usize ) / 100 ) as u8 ;
pub fn warmup_cooldown_rate ( current_epoch : Epoch , new_rate_activation_epoch : Option < Epoch > ) -> f64 {
if current_epoch < new_rate_activation_epoch . unwrap_or ( u64 ::MAX ) {
DEFAULT_WARMUP_COOLDOWN_RATE
} else {
NEW_WARMUP_COOLDOWN_RATE
}
}
2023-01-19 11:25:30 -08:00
#[ derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample) ]
2021-06-15 09:04:00 -07:00
#[ allow(clippy::large_enum_variant) ]
2023-08-08 15:05:40 -07:00
#[ deprecated(
since = " 1.17.0 " ,
2023-08-10 16:07:21 -07:00
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)`. "
2023-08-08 15:05:40 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub enum StakeState {
2023-01-19 11:25:30 -08:00
#[ default ]
2021-06-15 09:04:00 -07:00
Uninitialized ,
Initialized ( Meta ) ,
2023-08-08 15:05:40 -07:00
Stake ( Meta , Stake ) ,
2021-06-15 09:04:00 -07:00
RewardsPool ,
}
2021-09-17 01:14:23 -07:00
impl BorshDeserialize for StakeState {
2023-05-30 14:33:21 -07:00
fn deserialize_reader < R : io ::Read > ( reader : & mut R ) -> io ::Result < Self > {
let enum_value = u32 ::deserialize_reader ( reader ) ? ;
2021-09-17 01:14:23 -07:00
match enum_value {
0 = > Ok ( StakeState ::Uninitialized ) ,
1 = > {
2023-05-30 14:33:21 -07:00
let meta = Meta ::deserialize_reader ( reader ) ? ;
2021-09-17 01:14:23 -07:00
Ok ( StakeState ::Initialized ( meta ) )
}
2 = > {
2023-07-24 07:09:40 -07:00
let meta : Meta = BorshDeserialize ::deserialize_reader ( reader ) ? ;
let stake : Stake = BorshDeserialize ::deserialize_reader ( reader ) ? ;
2023-08-08 15:05:40 -07:00
Ok ( StakeState ::Stake ( meta , stake ) )
2021-09-17 01:14:23 -07:00
}
3 = > Ok ( StakeState ::RewardsPool ) ,
_ = > Err ( io ::Error ::new (
io ::ErrorKind ::InvalidData ,
" Invalid enum value " ,
) ) ,
}
}
}
2021-10-19 11:10:15 -07:00
impl BorshSerialize for StakeState {
fn serialize < W : io ::Write > ( & self , writer : & mut W ) -> io ::Result < ( ) > {
match self {
StakeState ::Uninitialized = > writer . write_all ( & 0 u32 . to_le_bytes ( ) ) ,
StakeState ::Initialized ( meta ) = > {
writer . write_all ( & 1 u32 . to_le_bytes ( ) ) ? ;
meta . serialize ( writer )
}
2023-08-08 15:05:40 -07:00
StakeState ::Stake ( meta , stake ) = > {
2021-10-19 11:10:15 -07:00
writer . write_all ( & 2 u32 . to_le_bytes ( ) ) ? ;
meta . serialize ( writer ) ? ;
2023-08-08 15:05:40 -07:00
stake . serialize ( writer )
2021-10-19 11:10:15 -07:00
}
StakeState ::RewardsPool = > writer . write_all ( & 3 u32 . to_le_bytes ( ) ) ,
}
}
}
2021-06-15 09:04:00 -07:00
impl StakeState {
2022-04-19 10:04:12 -07:00
/// The fixed number of bytes used to serialize each stake account
pub const fn size_of ( ) -> usize {
200 // see test_size_of
}
2021-06-15 09:04:00 -07:00
pub fn stake ( & self ) -> Option < Stake > {
match self {
2023-08-08 15:05:40 -07:00
StakeState ::Stake ( _meta , stake ) = > Some ( * stake ) ,
2021-06-15 09:04:00 -07:00
_ = > None ,
}
}
pub fn delegation ( & self ) -> Option < Delegation > {
match self {
2023-08-08 15:05:40 -07:00
StakeState ::Stake ( _meta , stake ) = > Some ( stake . delegation ) ,
2021-06-15 09:04:00 -07:00
_ = > None ,
}
}
pub fn authorized ( & self ) -> Option < Authorized > {
match self {
2023-08-08 15:05:40 -07:00
StakeState ::Stake ( meta , _stake ) = > Some ( meta . authorized ) ,
2021-06-15 09:04:00 -07:00
StakeState ::Initialized ( meta ) = > Some ( meta . authorized ) ,
_ = > None ,
}
}
pub fn lockup ( & self ) -> Option < Lockup > {
self . meta ( ) . map ( | meta | meta . lockup )
}
pub fn meta ( & self ) -> Option < Meta > {
match self {
2023-08-08 15:05:40 -07:00
StakeState ::Stake ( meta , _stake ) = > Some ( * meta ) ,
2021-06-15 09:04:00 -07:00
StakeState ::Initialized ( meta ) = > Some ( * meta ) ,
_ = > None ,
}
}
}
2023-08-08 15:05:40 -07:00
#[ derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample) ]
#[ allow(clippy::large_enum_variant) ]
2023-08-10 16:07:21 -07:00
pub enum StakeStateV2 {
2023-08-08 15:05:40 -07:00
#[ default ]
Uninitialized ,
Initialized ( Meta ) ,
Stake ( Meta , Stake , StakeFlags ) ,
RewardsPool ,
}
2023-08-10 16:07:21 -07:00
impl BorshDeserialize for StakeStateV2 {
2023-08-08 15:05:40 -07:00
fn deserialize_reader < R : io ::Read > ( reader : & mut R ) -> io ::Result < Self > {
let enum_value = u32 ::deserialize_reader ( reader ) ? ;
match enum_value {
2023-08-10 16:07:21 -07:00
0 = > Ok ( StakeStateV2 ::Uninitialized ) ,
2023-08-08 15:05:40 -07:00
1 = > {
let meta = Meta ::deserialize_reader ( reader ) ? ;
2023-08-10 16:07:21 -07:00
Ok ( StakeStateV2 ::Initialized ( meta ) )
2023-08-08 15:05:40 -07:00
}
2 = > {
let meta : Meta = BorshDeserialize ::deserialize_reader ( reader ) ? ;
let stake : Stake = BorshDeserialize ::deserialize_reader ( reader ) ? ;
let stake_flags : StakeFlags = BorshDeserialize ::deserialize_reader ( reader ) ? ;
2023-08-10 16:07:21 -07:00
Ok ( StakeStateV2 ::Stake ( meta , stake , stake_flags ) )
2023-08-08 15:05:40 -07:00
}
2023-08-10 16:07:21 -07:00
3 = > Ok ( StakeStateV2 ::RewardsPool ) ,
2023-08-08 15:05:40 -07:00
_ = > Err ( io ::Error ::new (
io ::ErrorKind ::InvalidData ,
" Invalid enum value " ,
) ) ,
}
}
}
2023-08-10 16:07:21 -07:00
impl BorshSerialize for StakeStateV2 {
2023-08-08 15:05:40 -07:00
fn serialize < W : io ::Write > ( & self , writer : & mut W ) -> io ::Result < ( ) > {
match self {
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Uninitialized = > writer . write_all ( & 0 u32 . to_le_bytes ( ) ) ,
StakeStateV2 ::Initialized ( meta ) = > {
2023-08-08 15:05:40 -07:00
writer . write_all ( & 1 u32 . to_le_bytes ( ) ) ? ;
meta . serialize ( writer )
}
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Stake ( meta , stake , stake_flags ) = > {
2023-08-08 15:05:40 -07:00
writer . write_all ( & 2 u32 . to_le_bytes ( ) ) ? ;
meta . serialize ( writer ) ? ;
stake . serialize ( writer ) ? ;
stake_flags . serialize ( writer )
}
2023-08-10 16:07:21 -07:00
StakeStateV2 ::RewardsPool = > writer . write_all ( & 3 u32 . to_le_bytes ( ) ) ,
2023-08-08 15:05:40 -07:00
}
}
}
2023-08-10 16:07:21 -07:00
impl StakeStateV2 {
2023-08-08 15:05:40 -07:00
/// The fixed number of bytes used to serialize each stake account
pub const fn size_of ( ) -> usize {
200 // see test_size_of
}
pub fn stake ( & self ) -> Option < Stake > {
match self {
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Stake ( _meta , stake , _stake_flags ) = > Some ( * stake ) ,
2023-08-08 15:05:40 -07:00
_ = > None ,
}
}
pub fn delegation ( & self ) -> Option < Delegation > {
match self {
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Stake ( _meta , stake , _stake_flags ) = > Some ( stake . delegation ) ,
2023-08-08 15:05:40 -07:00
_ = > None ,
}
}
pub fn authorized ( & self ) -> Option < Authorized > {
match self {
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Stake ( meta , _stake , _stake_flags ) = > Some ( meta . authorized ) ,
StakeStateV2 ::Initialized ( meta ) = > Some ( meta . authorized ) ,
2023-08-08 15:05:40 -07:00
_ = > None ,
}
}
pub fn lockup ( & self ) -> Option < Lockup > {
self . meta ( ) . map ( | meta | meta . lockup )
}
pub fn meta ( & self ) -> Option < Meta > {
match self {
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Stake ( meta , _stake , _stake_flags ) = > Some ( * meta ) ,
StakeStateV2 ::Initialized ( meta ) = > Some ( * meta ) ,
2023-08-08 15:05:40 -07:00
_ = > None ,
}
}
}
2022-05-22 18:00:42 -07:00
#[ derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample) ]
2021-06-15 09:04:00 -07:00
pub enum StakeAuthorize {
Staker ,
Withdrawer ,
}
2021-09-17 01:14:23 -07:00
#[ derive(
Default ,
Debug ,
Serialize ,
Deserialize ,
PartialEq ,
2022-05-22 18:00:42 -07:00
Eq ,
2021-09-17 01:14:23 -07:00
Clone ,
Copy ,
AbiExample ,
BorshDeserialize ,
BorshSchema ,
2021-10-19 11:10:15 -07:00
BorshSerialize ,
2021-09-17 01:14:23 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub struct Lockup {
/// UnixTimestamp at which this stake will allow withdrawal, unless the
/// transaction is signed by the custodian
pub unix_timestamp : UnixTimestamp ,
/// epoch height at which this stake will allow withdrawal, unless the
/// transaction is signed by the custodian
pub epoch : Epoch ,
/// custodian signature on a transaction exempts the operation from
/// lockup constraints
pub custodian : Pubkey ,
}
impl Lockup {
pub fn is_in_force ( & self , clock : & Clock , custodian : Option < & Pubkey > ) -> bool {
if custodian = = Some ( & self . custodian ) {
return false ;
}
self . unix_timestamp > clock . unix_timestamp | | self . epoch > clock . epoch
}
}
2021-09-17 01:14:23 -07:00
#[ derive(
Default ,
Debug ,
Serialize ,
Deserialize ,
PartialEq ,
2022-05-22 18:00:42 -07:00
Eq ,
2021-09-17 01:14:23 -07:00
Clone ,
Copy ,
AbiExample ,
BorshDeserialize ,
BorshSchema ,
2021-10-19 11:10:15 -07:00
BorshSerialize ,
2021-09-17 01:14:23 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub struct Authorized {
pub staker : Pubkey ,
pub withdrawer : Pubkey ,
}
impl Authorized {
pub fn auto ( authorized : & Pubkey ) -> Self {
Self {
staker : * authorized ,
withdrawer : * authorized ,
}
}
pub fn check (
& self ,
signers : & HashSet < Pubkey > ,
stake_authorize : StakeAuthorize ,
) -> Result < ( ) , InstructionError > {
match stake_authorize {
StakeAuthorize ::Staker if signers . contains ( & self . staker ) = > Ok ( ( ) ) ,
StakeAuthorize ::Withdrawer if signers . contains ( & self . withdrawer ) = > Ok ( ( ) ) ,
_ = > Err ( InstructionError ::MissingRequiredSignature ) ,
}
}
pub fn authorize (
& mut self ,
signers : & HashSet < Pubkey > ,
new_authorized : & Pubkey ,
stake_authorize : StakeAuthorize ,
lockup_custodian_args : Option < ( & Lockup , & Clock , Option < & Pubkey > ) > ,
) -> Result < ( ) , InstructionError > {
match stake_authorize {
StakeAuthorize ::Staker = > {
// Allow either the staker or the withdrawer to change the staker key
if ! signers . contains ( & self . staker ) & & ! signers . contains ( & self . withdrawer ) {
return Err ( InstructionError ::MissingRequiredSignature ) ;
}
self . staker = * new_authorized
}
StakeAuthorize ::Withdrawer = > {
if let Some ( ( lockup , clock , custodian ) ) = lockup_custodian_args {
2021-06-18 06:34:46 -07:00
if lockup . is_in_force ( clock , None ) {
2021-06-15 09:04:00 -07:00
match custodian {
None = > {
return Err ( StakeError ::CustodianMissing . into ( ) ) ;
}
Some ( custodian ) = > {
if ! signers . contains ( custodian ) {
return Err ( StakeError ::CustodianSignatureMissing . into ( ) ) ;
}
2021-06-18 06:34:46 -07:00
if lockup . is_in_force ( clock , Some ( custodian ) ) {
2021-06-15 09:04:00 -07:00
return Err ( StakeError ::LockupInForce . into ( ) ) ;
}
}
}
}
}
self . check ( signers , stake_authorize ) ? ;
self . withdrawer = * new_authorized
}
}
Ok ( ( ) )
}
}
2021-09-17 01:14:23 -07:00
#[ derive(
Default ,
Debug ,
Serialize ,
Deserialize ,
PartialEq ,
2022-05-22 18:00:42 -07:00
Eq ,
2021-09-17 01:14:23 -07:00
Clone ,
Copy ,
AbiExample ,
BorshDeserialize ,
BorshSchema ,
2021-10-19 11:10:15 -07:00
BorshSerialize ,
2021-09-17 01:14:23 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub struct Meta {
pub rent_exempt_reserve : u64 ,
pub authorized : Authorized ,
pub lockup : Lockup ,
}
impl Meta {
pub fn set_lockup (
& mut self ,
lockup : & LockupArgs ,
signers : & HashSet < Pubkey > ,
2022-01-21 17:27:53 -08:00
clock : & Clock ,
2021-06-15 09:04:00 -07:00
) -> Result < ( ) , InstructionError > {
2022-01-21 17:27:53 -08:00
// post-stake_program_v4 behavior:
// * custodian can update the lockup while in force
// * withdraw authority can set a new lockup
if self . lockup . is_in_force ( clock , None ) {
if ! signers . contains ( & self . lockup . custodian ) {
return Err ( InstructionError ::MissingRequiredSignature ) ;
2021-06-15 09:04:00 -07:00
}
2022-01-21 17:27:53 -08:00
} else if ! signers . contains ( & self . authorized . withdrawer ) {
return Err ( InstructionError ::MissingRequiredSignature ) ;
2021-06-15 09:04:00 -07:00
}
if let Some ( unix_timestamp ) = lockup . unix_timestamp {
self . lockup . unix_timestamp = unix_timestamp ;
}
if let Some ( epoch ) = lockup . epoch {
self . lockup . epoch = epoch ;
}
if let Some ( custodian ) = lockup . custodian {
self . lockup . custodian = custodian ;
}
Ok ( ( ) )
}
pub fn auto ( authorized : & Pubkey ) -> Self {
Self {
authorized : Authorized ::auto ( authorized ) ,
.. Meta ::default ( )
}
}
}
2021-09-17 01:14:23 -07:00
#[ derive(
2021-10-19 11:10:15 -07:00
Debug ,
Serialize ,
Deserialize ,
PartialEq ,
Clone ,
Copy ,
AbiExample ,
BorshDeserialize ,
BorshSchema ,
BorshSerialize ,
2021-09-17 01:14:23 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub struct Delegation {
/// to whom the stake is delegated
pub voter_pubkey : Pubkey ,
/// activated stake amount, set at delegate() time
pub stake : u64 ,
/// epoch at which this stake was activated, std::Epoch::MAX if is a bootstrap stake
pub activation_epoch : Epoch ,
/// epoch the stake was deactivated, std::Epoch::MAX if not deactivated
pub deactivation_epoch : Epoch ,
/// how much stake we can activate per-epoch as a fraction of currently effective stake
2023-08-07 13:23:24 -07:00
#[ deprecated(
since = " 1.16.7 " ,
note = " Please use `solana_sdk::stake::state::warmup_cooldown_rate()` instead "
) ]
2021-06-15 09:04:00 -07:00
pub warmup_cooldown_rate : f64 ,
}
impl Default for Delegation {
fn default ( ) -> Self {
2023-08-07 13:23:24 -07:00
#[ allow(deprecated) ]
2021-06-15 09:04:00 -07:00
Self {
voter_pubkey : Pubkey ::default ( ) ,
stake : 0 ,
activation_epoch : 0 ,
deactivation_epoch : std ::u64 ::MAX ,
2023-08-09 08:45:26 -07:00
warmup_cooldown_rate : DEFAULT_WARMUP_COOLDOWN_RATE ,
2021-06-15 09:04:00 -07:00
}
}
}
impl Delegation {
2023-08-07 13:23:24 -07:00
pub fn new ( voter_pubkey : & Pubkey , stake : u64 , activation_epoch : Epoch ) -> Self {
2021-06-15 09:04:00 -07:00
Self {
voter_pubkey : * voter_pubkey ,
stake ,
activation_epoch ,
.. Delegation ::default ( )
}
}
pub fn is_bootstrap ( & self ) -> bool {
self . activation_epoch = = std ::u64 ::MAX
}
2023-08-07 13:23:24 -07:00
pub fn stake (
& self ,
epoch : Epoch ,
history : Option < & StakeHistory > ,
new_rate_activation_epoch : Option < Epoch > ,
) -> u64 {
self . stake_activating_and_deactivating ( epoch , history , new_rate_activation_epoch )
2021-10-04 15:59:11 -07:00
. effective
2021-06-15 09:04:00 -07:00
}
#[ allow(clippy::comparison_chain) ]
pub fn stake_activating_and_deactivating (
& self ,
target_epoch : Epoch ,
history : Option < & StakeHistory > ,
2023-08-07 13:23:24 -07:00
new_rate_activation_epoch : Option < Epoch > ,
2021-10-04 15:59:11 -07:00
) -> StakeActivationStatus {
2021-06-15 09:04:00 -07:00
// first, calculate an effective and activating stake
2023-08-07 13:23:24 -07:00
let ( effective_stake , activating_stake ) =
self . stake_and_activating ( target_epoch , history , new_rate_activation_epoch ) ;
2021-06-15 09:04:00 -07:00
// then de-activate some portion if necessary
if target_epoch < self . deactivation_epoch {
// not deactivated
2021-10-04 15:59:11 -07:00
if activating_stake = = 0 {
StakeActivationStatus ::with_effective ( effective_stake )
} else {
StakeActivationStatus ::with_effective_and_activating (
effective_stake ,
activating_stake ,
)
}
2021-06-15 09:04:00 -07:00
} else if target_epoch = = self . deactivation_epoch {
// can only deactivate what's activated
2021-10-04 15:59:11 -07:00
StakeActivationStatus ::with_deactivating ( effective_stake )
2021-06-15 09:04:00 -07:00
} else if let Some ( ( history , mut prev_epoch , mut prev_cluster_stake ) ) =
history . and_then ( | history | {
history
2021-12-01 06:57:29 -08:00
. get ( self . deactivation_epoch )
2021-06-15 09:04:00 -07:00
. map ( | cluster_stake_at_deactivation_epoch | {
(
history ,
self . deactivation_epoch ,
cluster_stake_at_deactivation_epoch ,
)
} )
} )
{
// target_epoch > self.deactivation_epoch
// loop from my deactivation epoch until the target epoch
// current effective stake is updated using its previous epoch's cluster stake
let mut current_epoch ;
let mut current_effective_stake = effective_stake ;
loop {
current_epoch = prev_epoch + 1 ;
// if there is no deactivating stake at prev epoch, we should have been
// fully undelegated at this moment
if prev_cluster_stake . deactivating = = 0 {
break ;
}
// I'm trying to get to zero, how much of the deactivation in stake
// this account is entitled to take
let weight =
current_effective_stake as f64 / prev_cluster_stake . deactivating as f64 ;
2023-08-07 13:23:24 -07:00
let warmup_cooldown_rate =
warmup_cooldown_rate ( current_epoch , new_rate_activation_epoch ) ;
2021-06-15 09:04:00 -07:00
// portion of newly not-effective cluster stake I'm entitled to at current epoch
let newly_not_effective_cluster_stake =
2023-08-07 13:23:24 -07:00
prev_cluster_stake . effective as f64 * warmup_cooldown_rate ;
2021-06-15 09:04:00 -07:00
let newly_not_effective_stake =
( ( weight * newly_not_effective_cluster_stake ) as u64 ) . max ( 1 ) ;
current_effective_stake =
current_effective_stake . saturating_sub ( newly_not_effective_stake ) ;
if current_effective_stake = = 0 {
break ;
}
if current_epoch > = target_epoch {
break ;
}
2021-12-01 06:57:29 -08:00
if let Some ( current_cluster_stake ) = history . get ( current_epoch ) {
2021-06-15 09:04:00 -07:00
prev_epoch = current_epoch ;
prev_cluster_stake = current_cluster_stake ;
} else {
break ;
}
}
// deactivating stake should equal to all of currently remaining effective stake
2021-10-04 15:59:11 -07:00
StakeActivationStatus ::with_deactivating ( current_effective_stake )
2021-06-15 09:04:00 -07:00
} else {
// no history or I've dropped out of history, so assume fully deactivated
2021-10-04 15:59:11 -07:00
StakeActivationStatus ::default ( )
2021-06-15 09:04:00 -07:00
}
}
// returned tuple is (effective, activating) stake
fn stake_and_activating (
& self ,
target_epoch : Epoch ,
history : Option < & StakeHistory > ,
2023-08-07 13:23:24 -07:00
new_rate_activation_epoch : Option < Epoch > ,
2021-06-15 09:04:00 -07:00
) -> ( u64 , u64 ) {
let delegated_stake = self . stake ;
if self . is_bootstrap ( ) {
// fully effective immediately
( delegated_stake , 0 )
2021-08-19 22:08:44 -07:00
} else if self . activation_epoch = = self . deactivation_epoch {
2021-06-15 09:04:00 -07:00
// activated but instantly deactivated; no stake at all regardless of target_epoch
// this must be after the bootstrap check and before all-is-activating check
( 0 , 0 )
} else if target_epoch = = self . activation_epoch {
// all is activating
( 0 , delegated_stake )
} else if target_epoch < self . activation_epoch {
// not yet enabled
( 0 , 0 )
} else if let Some ( ( history , mut prev_epoch , mut prev_cluster_stake ) ) =
history . and_then ( | history | {
history
2021-12-01 06:57:29 -08:00
. get ( self . activation_epoch )
2021-06-15 09:04:00 -07:00
. map ( | cluster_stake_at_activation_epoch | {
(
history ,
self . activation_epoch ,
cluster_stake_at_activation_epoch ,
)
} )
} )
{
// target_epoch > self.activation_epoch
// loop from my activation epoch until the target epoch summing up my entitlement
// current effective stake is updated using its previous epoch's cluster stake
let mut current_epoch ;
let mut current_effective_stake = 0 ;
loop {
current_epoch = prev_epoch + 1 ;
// if there is no activating stake at prev epoch, we should have been
// fully effective at this moment
if prev_cluster_stake . activating = = 0 {
break ;
}
// how much of the growth in stake this account is
// entitled to take
let remaining_activating_stake = delegated_stake - current_effective_stake ;
let weight =
remaining_activating_stake as f64 / prev_cluster_stake . activating as f64 ;
2023-08-07 13:23:24 -07:00
let warmup_cooldown_rate =
warmup_cooldown_rate ( current_epoch , new_rate_activation_epoch ) ;
2021-06-15 09:04:00 -07:00
// portion of newly effective cluster stake I'm entitled to at current epoch
let newly_effective_cluster_stake =
2023-08-07 13:23:24 -07:00
prev_cluster_stake . effective as f64 * warmup_cooldown_rate ;
2021-06-15 09:04:00 -07:00
let newly_effective_stake =
( ( weight * newly_effective_cluster_stake ) as u64 ) . max ( 1 ) ;
current_effective_stake + = newly_effective_stake ;
if current_effective_stake > = delegated_stake {
current_effective_stake = delegated_stake ;
break ;
}
if current_epoch > = target_epoch | | current_epoch > = self . deactivation_epoch {
break ;
}
2021-12-01 06:57:29 -08:00
if let Some ( current_cluster_stake ) = history . get ( current_epoch ) {
2021-06-15 09:04:00 -07:00
prev_epoch = current_epoch ;
prev_cluster_stake = current_cluster_stake ;
} else {
break ;
}
}
(
current_effective_stake ,
delegated_stake - current_effective_stake ,
)
} else {
// no history or I've dropped out of history, so assume fully effective
( delegated_stake , 0 )
}
}
}
2021-09-17 01:14:23 -07:00
#[ derive(
Debug ,
Default ,
Serialize ,
Deserialize ,
PartialEq ,
Clone ,
Copy ,
AbiExample ,
BorshDeserialize ,
BorshSchema ,
2021-10-19 11:10:15 -07:00
BorshSerialize ,
2021-09-17 01:14:23 -07:00
) ]
2021-06-15 09:04:00 -07:00
pub struct Stake {
pub delegation : Delegation ,
/// credits observed is credits from vote account state when delegated or redeemed
pub credits_observed : u64 ,
}
impl Stake {
2023-08-07 13:23:24 -07:00
pub fn stake (
& self ,
epoch : Epoch ,
history : Option < & StakeHistory > ,
new_rate_activation_epoch : Option < Epoch > ,
) -> u64 {
self . delegation
. stake ( epoch , history , new_rate_activation_epoch )
2021-06-15 09:04:00 -07:00
}
pub fn split (
& mut self ,
remaining_stake_delta : u64 ,
split_stake_amount : u64 ,
) -> Result < Self , StakeError > {
if remaining_stake_delta > self . delegation . stake {
return Err ( StakeError ::InsufficientStake ) ;
}
self . delegation . stake - = remaining_stake_delta ;
let new = Self {
delegation : Delegation {
stake : split_stake_amount ,
.. self . delegation
} ,
.. * self
} ;
Ok ( new )
}
pub fn deactivate ( & mut self , epoch : Epoch ) -> Result < ( ) , StakeError > {
if self . delegation . deactivation_epoch ! = std ::u64 ::MAX {
Err ( StakeError ::AlreadyDeactivated )
} else {
self . delegation . deactivation_epoch = epoch ;
Ok ( ( ) )
}
}
}
2021-09-17 01:14:23 -07:00
#[ cfg(test) ]
mod test {
use {
2023-08-02 14:15:24 -07:00
super ::* , crate ::borsh0_10 ::try_from_slice_unchecked , assert_matches ::assert_matches ,
2021-09-17 01:14:23 -07:00
bincode ::serialize ,
} ;
2023-08-10 16:07:21 -07:00
fn check_borsh_deserialization ( stake : StakeStateV2 ) {
2021-09-17 01:14:23 -07:00
let serialized = serialize ( & stake ) . unwrap ( ) ;
2023-08-10 16:07:21 -07:00
let deserialized = StakeStateV2 ::try_from_slice ( & serialized ) . unwrap ( ) ;
2021-09-17 01:14:23 -07:00
assert_eq! ( stake , deserialized ) ;
}
2023-08-10 16:07:21 -07:00
fn check_borsh_serialization ( stake : StakeStateV2 ) {
2021-10-19 11:10:15 -07:00
let bincode_serialized = serialize ( & stake ) . unwrap ( ) ;
2023-08-10 16:07:21 -07:00
let borsh_serialized = StakeStateV2 ::try_to_vec ( & stake ) . unwrap ( ) ;
2021-10-19 11:10:15 -07:00
assert_eq! ( bincode_serialized , borsh_serialized ) ;
}
2022-04-19 10:04:12 -07:00
#[ test ]
fn test_size_of ( ) {
2023-08-10 16:07:21 -07:00
assert_eq! ( StakeStateV2 ::size_of ( ) , std ::mem ::size_of ::< StakeStateV2 > ( ) ) ;
2022-04-19 10:04:12 -07:00
}
2021-09-17 01:14:23 -07:00
#[ test ]
2021-10-19 11:10:15 -07:00
fn bincode_vs_borsh_deserialization ( ) {
2023-08-10 16:07:21 -07:00
check_borsh_deserialization ( StakeStateV2 ::Uninitialized ) ;
check_borsh_deserialization ( StakeStateV2 ::RewardsPool ) ;
check_borsh_deserialization ( StakeStateV2 ::Initialized ( Meta {
2021-09-17 01:14:23 -07:00
rent_exempt_reserve : u64 ::MAX ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ) ) ;
2023-08-10 16:07:21 -07:00
check_borsh_deserialization ( StakeStateV2 ::Stake (
2021-09-17 01:14:23 -07:00
Meta {
rent_exempt_reserve : 1 ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ,
Stake {
delegation : Delegation {
voter_pubkey : Pubkey ::new_unique ( ) ,
stake : u64 ::MAX ,
activation_epoch : Epoch ::MAX ,
deactivation_epoch : Epoch ::MAX ,
2023-08-07 13:23:24 -07:00
.. Delegation ::default ( )
2021-09-17 01:14:23 -07:00
} ,
credits_observed : 1 ,
} ,
2023-07-24 07:09:40 -07:00
StakeFlags ::empty ( ) ,
2021-09-17 01:14:23 -07:00
) ) ;
}
2021-10-19 11:10:15 -07:00
#[ test ]
fn bincode_vs_borsh_serialization ( ) {
2023-08-10 16:07:21 -07:00
check_borsh_serialization ( StakeStateV2 ::Uninitialized ) ;
check_borsh_serialization ( StakeStateV2 ::RewardsPool ) ;
check_borsh_serialization ( StakeStateV2 ::Initialized ( Meta {
2021-10-19 11:10:15 -07:00
rent_exempt_reserve : u64 ::MAX ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ) ) ;
2023-08-10 16:07:21 -07:00
check_borsh_serialization ( StakeStateV2 ::Stake (
2021-10-19 11:10:15 -07:00
Meta {
rent_exempt_reserve : 1 ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ,
Stake {
delegation : Delegation {
voter_pubkey : Pubkey ::new_unique ( ) ,
stake : u64 ::MAX ,
activation_epoch : Epoch ::MAX ,
deactivation_epoch : Epoch ::MAX ,
2023-08-07 13:23:24 -07:00
.. Default ::default ( )
2021-10-19 11:10:15 -07:00
} ,
credits_observed : 1 ,
} ,
2023-07-24 07:09:40 -07:00
StakeFlags ::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED ,
2021-10-19 11:10:15 -07:00
) ) ;
}
2021-09-17 01:14:23 -07:00
#[ test ]
fn borsh_deserialization_live_data ( ) {
let data = [
1 , 0 , 0 , 0 , 128 , 213 , 34 , 0 , 0 , 0 , 0 , 0 , 133 , 0 , 79 , 231 , 141 , 29 , 73 , 61 , 232 , 35 ,
119 , 124 , 168 , 12 , 120 , 216 , 195 , 29 , 12 , 166 , 139 , 28 , 36 , 182 , 186 , 154 , 246 , 149 ,
224 , 109 , 52 , 100 , 133 , 0 , 79 , 231 , 141 , 29 , 73 , 61 , 232 , 35 , 119 , 124 , 168 , 12 , 120 ,
216 , 195 , 29 , 12 , 166 , 139 , 28 , 36 , 182 , 186 , 154 , 246 , 149 , 224 , 109 , 52 , 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 , 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 , 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 , 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 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 ,
] ;
// As long as we get the 4-byte enum and the first field right, then
// we're sure the rest works out
2023-08-10 16:07:21 -07:00
let deserialized = try_from_slice_unchecked ::< StakeStateV2 > ( & data ) . unwrap ( ) ;
2021-09-17 01:14:23 -07:00
assert_matches! (
deserialized ,
2023-08-10 16:07:21 -07:00
StakeStateV2 ::Initialized ( Meta {
2021-09-17 01:14:23 -07:00
rent_exempt_reserve : 2282880 ,
..
} )
) ;
}
2023-08-08 15:05:40 -07:00
2023-09-25 14:35:40 -07:00
#[ test ]
fn stake_flag_member_offset ( ) {
const FLAG_OFFSET : usize = 196 ;
let check_flag = | flag , expected | {
let stake = StakeStateV2 ::Stake (
Meta {
rent_exempt_reserve : 1 ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ,
Stake {
delegation : Delegation {
voter_pubkey : Pubkey ::new_unique ( ) ,
stake : u64 ::MAX ,
activation_epoch : Epoch ::MAX ,
deactivation_epoch : Epoch ::MAX ,
warmup_cooldown_rate : f64 ::MAX ,
} ,
credits_observed : 1 ,
} ,
flag ,
) ;
let bincode_serialized = serialize ( & stake ) . unwrap ( ) ;
let borsh_serialized = StakeStateV2 ::try_to_vec ( & stake ) . unwrap ( ) ;
assert_eq! ( bincode_serialized [ FLAG_OFFSET ] , expected ) ;
assert_eq! ( borsh_serialized [ FLAG_OFFSET ] , expected ) ;
} ;
check_flag (
StakeFlags ::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED ,
1 ,
) ;
check_flag ( StakeFlags ::empty ( ) , 0 ) ;
}
2023-08-08 15:05:40 -07:00
mod deprecated {
use super ::* ;
fn check_borsh_deserialization ( stake : StakeState ) {
let serialized = serialize ( & stake ) . unwrap ( ) ;
let deserialized = StakeState ::try_from_slice ( & serialized ) . unwrap ( ) ;
assert_eq! ( stake , deserialized ) ;
}
fn check_borsh_serialization ( stake : StakeState ) {
let bincode_serialized = serialize ( & stake ) . unwrap ( ) ;
let borsh_serialized = StakeState ::try_to_vec ( & stake ) . unwrap ( ) ;
assert_eq! ( bincode_serialized , borsh_serialized ) ;
}
#[ test ]
fn test_size_of ( ) {
assert_eq! ( StakeState ::size_of ( ) , std ::mem ::size_of ::< StakeState > ( ) ) ;
}
#[ test ]
fn bincode_vs_borsh_deserialization ( ) {
check_borsh_deserialization ( StakeState ::Uninitialized ) ;
check_borsh_deserialization ( StakeState ::RewardsPool ) ;
check_borsh_deserialization ( StakeState ::Initialized ( Meta {
rent_exempt_reserve : u64 ::MAX ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ) ) ;
check_borsh_deserialization ( StakeState ::Stake (
Meta {
rent_exempt_reserve : 1 ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ,
Stake {
delegation : Delegation {
voter_pubkey : Pubkey ::new_unique ( ) ,
stake : u64 ::MAX ,
activation_epoch : Epoch ::MAX ,
deactivation_epoch : Epoch ::MAX ,
warmup_cooldown_rate : f64 ::MAX ,
} ,
credits_observed : 1 ,
} ,
) ) ;
}
#[ test ]
fn bincode_vs_borsh_serialization ( ) {
check_borsh_serialization ( StakeState ::Uninitialized ) ;
check_borsh_serialization ( StakeState ::RewardsPool ) ;
check_borsh_serialization ( StakeState ::Initialized ( Meta {
rent_exempt_reserve : u64 ::MAX ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ) ) ;
check_borsh_serialization ( StakeState ::Stake (
Meta {
rent_exempt_reserve : 1 ,
authorized : Authorized {
staker : Pubkey ::new_unique ( ) ,
withdrawer : Pubkey ::new_unique ( ) ,
} ,
lockup : Lockup ::default ( ) ,
} ,
Stake {
delegation : Delegation {
voter_pubkey : Pubkey ::new_unique ( ) ,
stake : u64 ::MAX ,
activation_epoch : Epoch ::MAX ,
deactivation_epoch : Epoch ::MAX ,
warmup_cooldown_rate : f64 ::MAX ,
} ,
credits_observed : 1 ,
} ,
) ) ;
}
#[ test ]
fn borsh_deserialization_live_data ( ) {
let data = [
1 , 0 , 0 , 0 , 128 , 213 , 34 , 0 , 0 , 0 , 0 , 0 , 133 , 0 , 79 , 231 , 141 , 29 , 73 , 61 , 232 , 35 ,
119 , 124 , 168 , 12 , 120 , 216 , 195 , 29 , 12 , 166 , 139 , 28 , 36 , 182 , 186 , 154 , 246 ,
149 , 224 , 109 , 52 , 100 , 133 , 0 , 79 , 231 , 141 , 29 , 73 , 61 , 232 , 35 , 119 , 124 , 168 ,
12 , 120 , 216 , 195 , 29 , 12 , 166 , 139 , 28 , 36 , 182 , 186 , 154 , 246 , 149 , 224 , 109 , 52 ,
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 ,
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 , 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 , 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 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
] ;
// As long as we get the 4-byte enum and the first field right, then
// we're sure the rest works out
let deserialized = try_from_slice_unchecked ::< StakeState > ( & data ) . unwrap ( ) ;
assert_matches! (
deserialized ,
StakeState ::Initialized ( Meta {
rent_exempt_reserve : 2282880 ,
..
} )
) ;
}
}
2021-09-17 01:14:23 -07:00
}