#![allow(clippy::arithmetic_side_effects)]
#![allow(deprecated)]
use {
    crate::{
        clock::{Clock, Epoch, UnixTimestamp},
        instruction::InstructionError,
        pubkey::Pubkey,
        stake::{
            instruction::{LockupArgs, StakeError},
            stake_flags::StakeFlags,
        },
        stake_history::{StakeHistory, StakeHistoryEntry},
    },
    borsh::{io, BorshDeserialize, BorshSchema, BorshSerialize},
    std::collections::HashSet,
};
pub type StakeActivationStatus = StakeHistoryEntry;
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
    }
}
macro_rules! impl_borsh_stake_state {
    ($borsh:ident) => {
        impl $borsh::BorshDeserialize for StakeState {
            fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
                let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                match enum_value {
                    0 => Ok(StakeState::Uninitialized),
                    1 => {
                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        Ok(StakeState::Initialized(meta))
                    }
                    2 => {
                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        Ok(StakeState::Stake(meta, stake))
                    }
                    3 => Ok(StakeState::RewardsPool),
                    _ => Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "Invalid enum value",
                    )),
                }
            }
        }
        impl $borsh::BorshSerialize for StakeState {
            fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
                match self {
                    StakeState::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
                    StakeState::Initialized(meta) => {
                        writer.write_all(&1u32.to_le_bytes())?;
                        $borsh::BorshSerialize::serialize(&meta, writer)
                    }
                    StakeState::Stake(meta, stake) => {
                        writer.write_all(&2u32.to_le_bytes())?;
                        $borsh::BorshSerialize::serialize(&meta, writer)?;
                        $borsh::BorshSerialize::serialize(&stake, writer)
                    }
                    StakeState::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
                }
            }
        }
    };
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
#[allow(clippy::large_enum_variant)]
#[deprecated(
    since = "1.17.0",
    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)`."
)]
pub enum StakeState {
    #[default]
    Uninitialized,
    Initialized(Meta),
    Stake(Meta, Stake),
    RewardsPool,
}
impl_borsh_stake_state!(borsh);
impl_borsh_stake_state!(borsh0_10);
impl StakeState {
    pub const fn size_of() -> usize {
        200 }
    pub fn stake(&self) -> Option<Stake> {
        match self {
            StakeState::Stake(_meta, stake) => Some(*stake),
            _ => None,
        }
    }
    pub fn delegation(&self) -> Option<Delegation> {
        match self {
            StakeState::Stake(_meta, stake) => Some(stake.delegation),
            _ => None,
        }
    }
    pub fn authorized(&self) -> Option<Authorized> {
        match self {
            StakeState::Stake(meta, _stake) => Some(meta.authorized),
            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 {
            StakeState::Stake(meta, _stake) => Some(*meta),
            StakeState::Initialized(meta) => Some(*meta),
            _ => None,
        }
    }
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
#[allow(clippy::large_enum_variant)]
pub enum StakeStateV2 {
    #[default]
    Uninitialized,
    Initialized(Meta),
    Stake(Meta, Stake, StakeFlags),
    RewardsPool,
}
macro_rules! impl_borsh_stake_state_v2 {
    ($borsh:ident) => {
        impl $borsh::BorshDeserialize for StakeStateV2 {
            fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
                let enum_value: u32 = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                match enum_value {
                    0 => Ok(StakeStateV2::Uninitialized),
                    1 => {
                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        Ok(StakeStateV2::Initialized(meta))
                    }
                    2 => {
                        let meta: Meta = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        let stake: Stake = $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        let stake_flags: StakeFlags =
                            $borsh::BorshDeserialize::deserialize_reader(reader)?;
                        Ok(StakeStateV2::Stake(meta, stake, stake_flags))
                    }
                    3 => Ok(StakeStateV2::RewardsPool),
                    _ => Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "Invalid enum value",
                    )),
                }
            }
        }
        impl $borsh::BorshSerialize for StakeStateV2 {
            fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
                match self {
                    StakeStateV2::Uninitialized => writer.write_all(&0u32.to_le_bytes()),
                    StakeStateV2::Initialized(meta) => {
                        writer.write_all(&1u32.to_le_bytes())?;
                        $borsh::BorshSerialize::serialize(&meta, writer)
                    }
                    StakeStateV2::Stake(meta, stake, stake_flags) => {
                        writer.write_all(&2u32.to_le_bytes())?;
                        $borsh::BorshSerialize::serialize(&meta, writer)?;
                        $borsh::BorshSerialize::serialize(&stake, writer)?;
                        $borsh::BorshSerialize::serialize(&stake_flags, writer)
                    }
                    StakeStateV2::RewardsPool => writer.write_all(&3u32.to_le_bytes()),
                }
            }
        }
    };
}
impl_borsh_stake_state_v2!(borsh);
impl_borsh_stake_state_v2!(borsh0_10);
impl StakeStateV2 {
    pub const fn size_of() -> usize {
        200 }
    pub fn stake(&self) -> Option<Stake> {
        match self {
            StakeStateV2::Stake(_meta, stake, _stake_flags) => Some(*stake),
            _ => None,
        }
    }
    pub fn delegation(&self) -> Option<Delegation> {
        match self {
            StakeStateV2::Stake(_meta, stake, _stake_flags) => Some(stake.delegation),
            _ => None,
        }
    }
    pub fn authorized(&self) -> Option<Authorized> {
        match self {
            StakeStateV2::Stake(meta, _stake, _stake_flags) => Some(meta.authorized),
            StakeStateV2::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 {
            StakeStateV2::Stake(meta, _stake, _stake_flags) => Some(*meta),
            StakeStateV2::Initialized(meta) => Some(*meta),
            _ => None,
        }
    }
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)]
pub enum StakeAuthorize {
    Staker,
    Withdrawer,
}
#[derive(
    Default,
    Debug,
    Serialize,
    Deserialize,
    PartialEq,
    Eq,
    Clone,
    Copy,
    AbiExample,
    BorshDeserialize,
    BorshSchema,
    BorshSerialize,
)]
#[borsh(crate = "borsh")]
pub struct Lockup {
    pub unix_timestamp: UnixTimestamp,
    pub epoch: Epoch,
    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
    }
}
impl borsh0_10::de::BorshDeserialize for Lockup {
    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
        reader: &mut R,
    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
        Ok(Self {
            unix_timestamp: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            custodian: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
        })
    }
}
impl borsh0_10::BorshSchema for Lockup {
    fn declaration() -> borsh0_10::schema::Declaration {
        "Lockup".to_string()
    }
    fn add_definitions_recursively(
        definitions: &mut borsh0_10::maybestd::collections::HashMap<
            borsh0_10::schema::Declaration,
            borsh0_10::schema::Definition,
        >,
    ) {
        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
            borsh0_10::maybestd::boxed::Box::new([
                (
                    "unix_timestamp".to_string(),
                    <UnixTimestamp as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "epoch".to_string(),
                    <Epoch as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "custodian".to_string(),
                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
                ),
            ]),
        ));
        let definition = borsh0_10::schema::Definition::Struct { fields };
        Self::add_definition(
            <Self as borsh0_10::BorshSchema>::declaration(),
            definition,
            definitions,
        );
        <UnixTimestamp as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
    }
}
impl borsh0_10::ser::BorshSerialize for Lockup {
    fn serialize<W: borsh0_10::maybestd::io::Write>(
        &self,
        writer: &mut W,
    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
        borsh0_10::BorshSerialize::serialize(&self.unix_timestamp, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.epoch, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.custodian, writer)?;
        Ok(())
    }
}
#[derive(
    Default,
    Debug,
    Serialize,
    Deserialize,
    PartialEq,
    Eq,
    Clone,
    Copy,
    AbiExample,
    BorshDeserialize,
    BorshSchema,
    BorshSerialize,
)]
#[borsh(crate = "borsh")]
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 => {
                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 {
                    if lockup.is_in_force(clock, None) {
                        match custodian {
                            None => {
                                return Err(StakeError::CustodianMissing.into());
                            }
                            Some(custodian) => {
                                if !signers.contains(custodian) {
                                    return Err(StakeError::CustodianSignatureMissing.into());
                                }
                                if lockup.is_in_force(clock, Some(custodian)) {
                                    return Err(StakeError::LockupInForce.into());
                                }
                            }
                        }
                    }
                }
                self.check(signers, stake_authorize)?;
                self.withdrawer = *new_authorized
            }
        }
        Ok(())
    }
}
impl borsh0_10::de::BorshDeserialize for Authorized {
    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
        reader: &mut R,
    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
        Ok(Self {
            staker: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            withdrawer: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
        })
    }
}
impl borsh0_10::BorshSchema for Authorized {
    fn declaration() -> borsh0_10::schema::Declaration {
        "Authorized".to_string()
    }
    fn add_definitions_recursively(
        definitions: &mut borsh0_10::maybestd::collections::HashMap<
            borsh0_10::schema::Declaration,
            borsh0_10::schema::Definition,
        >,
    ) {
        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
            borsh0_10::maybestd::boxed::Box::new([
                (
                    "staker".to_string(),
                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "withdrawer".to_string(),
                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
                ),
            ]),
        ));
        let definition = borsh0_10::schema::Definition::Struct { fields };
        Self::add_definition(
            <Self as borsh0_10::BorshSchema>::declaration(),
            definition,
            definitions,
        );
        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
    }
}
impl borsh0_10::ser::BorshSerialize for Authorized {
    fn serialize<W: borsh0_10::maybestd::io::Write>(
        &self,
        writer: &mut W,
    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
        borsh0_10::BorshSerialize::serialize(&self.staker, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.withdrawer, writer)?;
        Ok(())
    }
}
#[derive(
    Default,
    Debug,
    Serialize,
    Deserialize,
    PartialEq,
    Eq,
    Clone,
    Copy,
    AbiExample,
    BorshDeserialize,
    BorshSchema,
    BorshSerialize,
)]
#[borsh(crate = "borsh")]
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>,
        clock: &Clock,
    ) -> Result<(), InstructionError> {
        if self.lockup.is_in_force(clock, None) {
            if !signers.contains(&self.lockup.custodian) {
                return Err(InstructionError::MissingRequiredSignature);
            }
        } else if !signers.contains(&self.authorized.withdrawer) {
            return Err(InstructionError::MissingRequiredSignature);
        }
        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()
        }
    }
}
impl borsh0_10::de::BorshDeserialize for Meta {
    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
        reader: &mut R,
    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
        Ok(Self {
            rent_exempt_reserve: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            authorized: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            lockup: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
        })
    }
}
impl borsh0_10::BorshSchema for Meta {
    fn declaration() -> borsh0_10::schema::Declaration {
        "Meta".to_string()
    }
    fn add_definitions_recursively(
        definitions: &mut borsh0_10::maybestd::collections::HashMap<
            borsh0_10::schema::Declaration,
            borsh0_10::schema::Definition,
        >,
    ) {
        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
            borsh0_10::maybestd::boxed::Box::new([
                (
                    "rent_exempt_reserve".to_string(),
                    <u64 as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "authorized".to_string(),
                    <Authorized as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "lockup".to_string(),
                    <Lockup as borsh0_10::BorshSchema>::declaration(),
                ),
            ]),
        ));
        let definition = borsh0_10::schema::Definition::Struct { fields };
        Self::add_definition(
            <Self as borsh0_10::BorshSchema>::declaration(),
            definition,
            definitions,
        );
        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Authorized as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Lockup as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
    }
}
impl borsh0_10::ser::BorshSerialize for Meta {
    fn serialize<W: borsh0_10::maybestd::io::Write>(
        &self,
        writer: &mut W,
    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
        borsh0_10::BorshSerialize::serialize(&self.rent_exempt_reserve, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.authorized, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.lockup, writer)?;
        Ok(())
    }
}
#[derive(
    Debug,
    Serialize,
    Deserialize,
    PartialEq,
    Clone,
    Copy,
    AbiExample,
    BorshDeserialize,
    BorshSchema,
    BorshSerialize,
)]
#[borsh(crate = "borsh")]
pub struct Delegation {
    pub voter_pubkey: Pubkey,
    pub stake: u64,
    pub activation_epoch: Epoch,
    pub deactivation_epoch: Epoch,
    #[deprecated(
        since = "1.16.7",
        note = "Please use `solana_sdk::stake::state::warmup_cooldown_rate()` instead"
    )]
    pub warmup_cooldown_rate: f64,
}
impl Default for Delegation {
    fn default() -> Self {
        #[allow(deprecated)]
        Self {
            voter_pubkey: Pubkey::default(),
            stake: 0,
            activation_epoch: 0,
            deactivation_epoch: std::u64::MAX,
            warmup_cooldown_rate: DEFAULT_WARMUP_COOLDOWN_RATE,
        }
    }
}
impl Delegation {
    pub fn new(voter_pubkey: &Pubkey, stake: u64, activation_epoch: Epoch) -> Self {
        Self {
            voter_pubkey: *voter_pubkey,
            stake,
            activation_epoch,
            ..Delegation::default()
        }
    }
    pub fn is_bootstrap(&self) -> bool {
        self.activation_epoch == std::u64::MAX
    }
    pub fn stake(
        &self,
        epoch: Epoch,
        history: &StakeHistory,
        new_rate_activation_epoch: Option<Epoch>,
    ) -> u64 {
        self.stake_activating_and_deactivating(epoch, history, new_rate_activation_epoch)
            .effective
    }
    #[allow(clippy::comparison_chain)]
    pub fn stake_activating_and_deactivating(
        &self,
        target_epoch: Epoch,
        history: &StakeHistory,
        new_rate_activation_epoch: Option<Epoch>,
    ) -> StakeActivationStatus {
        let (effective_stake, activating_stake) =
            self.stake_and_activating(target_epoch, history, new_rate_activation_epoch);
        if target_epoch < self.deactivation_epoch {
            if activating_stake == 0 {
                StakeActivationStatus::with_effective(effective_stake)
            } else {
                StakeActivationStatus::with_effective_and_activating(
                    effective_stake,
                    activating_stake,
                )
            }
        } else if target_epoch == self.deactivation_epoch {
            StakeActivationStatus::with_deactivating(effective_stake)
        } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
            .get(self.deactivation_epoch)
            .map(|cluster_stake_at_deactivation_epoch| {
                (
                    history,
                    self.deactivation_epoch,
                    cluster_stake_at_deactivation_epoch,
                )
            })
        {
            let mut current_epoch;
            let mut current_effective_stake = effective_stake;
            loop {
                current_epoch = prev_epoch + 1;
                if prev_cluster_stake.deactivating == 0 {
                    break;
                }
                let weight =
                    current_effective_stake as f64 / prev_cluster_stake.deactivating as f64;
                let warmup_cooldown_rate =
                    warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
                let newly_not_effective_cluster_stake =
                    prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
                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;
                }
                if let Some(current_cluster_stake) = history.get(current_epoch) {
                    prev_epoch = current_epoch;
                    prev_cluster_stake = current_cluster_stake;
                } else {
                    break;
                }
            }
            StakeActivationStatus::with_deactivating(current_effective_stake)
        } else {
            StakeActivationStatus::default()
        }
    }
    fn stake_and_activating(
        &self,
        target_epoch: Epoch,
        history: &StakeHistory,
        new_rate_activation_epoch: Option<Epoch>,
    ) -> (u64, u64) {
        let delegated_stake = self.stake;
        if self.is_bootstrap() {
            (delegated_stake, 0)
        } else if self.activation_epoch == self.deactivation_epoch {
            (0, 0)
        } else if target_epoch == self.activation_epoch {
            (0, delegated_stake)
        } else if target_epoch < self.activation_epoch {
            (0, 0)
        } else if let Some((history, mut prev_epoch, mut prev_cluster_stake)) = history
            .get(self.activation_epoch)
            .map(|cluster_stake_at_activation_epoch| {
                (
                    history,
                    self.activation_epoch,
                    cluster_stake_at_activation_epoch,
                )
            })
        {
            let mut current_epoch;
            let mut current_effective_stake = 0;
            loop {
                current_epoch = prev_epoch + 1;
                if prev_cluster_stake.activating == 0 {
                    break;
                }
                let remaining_activating_stake = delegated_stake - current_effective_stake;
                let weight =
                    remaining_activating_stake as f64 / prev_cluster_stake.activating as f64;
                let warmup_cooldown_rate =
                    warmup_cooldown_rate(current_epoch, new_rate_activation_epoch);
                let newly_effective_cluster_stake =
                    prev_cluster_stake.effective as f64 * warmup_cooldown_rate;
                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;
                }
                if let Some(current_cluster_stake) = history.get(current_epoch) {
                    prev_epoch = current_epoch;
                    prev_cluster_stake = current_cluster_stake;
                } else {
                    break;
                }
            }
            (
                current_effective_stake,
                delegated_stake - current_effective_stake,
            )
        } else {
            (delegated_stake, 0)
        }
    }
}
impl borsh0_10::de::BorshDeserialize for Delegation {
    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
        reader: &mut R,
    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
        Ok(Self {
            voter_pubkey: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            stake: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            activation_epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            deactivation_epoch: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            warmup_cooldown_rate: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
        })
    }
}
impl borsh0_10::BorshSchema for Delegation {
    fn declaration() -> borsh0_10::schema::Declaration {
        "Delegation".to_string()
    }
    fn add_definitions_recursively(
        definitions: &mut borsh0_10::maybestd::collections::HashMap<
            borsh0_10::schema::Declaration,
            borsh0_10::schema::Definition,
        >,
    ) {
        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
            borsh0_10::maybestd::boxed::Box::new([
                (
                    "voter_pubkey".to_string(),
                    <Pubkey as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "stake".to_string(),
                    <u64 as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "activation_epoch".to_string(),
                    <Epoch as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "deactivation_epoch".to_string(),
                    <Epoch as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "warmup_cooldown_rate".to_string(),
                    <f64 as borsh0_10::BorshSchema>::declaration(),
                ),
            ]),
        ));
        let definition = borsh0_10::schema::Definition::Struct { fields };
        Self::add_definition(
            <Self as borsh0_10::BorshSchema>::declaration(),
            definition,
            definitions,
        );
        <Pubkey as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <Epoch as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <f64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
    }
}
impl borsh0_10::ser::BorshSerialize for Delegation {
    fn serialize<W: borsh0_10::maybestd::io::Write>(
        &self,
        writer: &mut W,
    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
        borsh0_10::BorshSerialize::serialize(&self.voter_pubkey, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.stake, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.activation_epoch, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.deactivation_epoch, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.warmup_cooldown_rate, writer)?;
        Ok(())
    }
}
#[derive(
    Debug,
    Default,
    Serialize,
    Deserialize,
    PartialEq,
    Clone,
    Copy,
    AbiExample,
    BorshDeserialize,
    BorshSchema,
    BorshSerialize,
)]
#[borsh(crate = "borsh")]
pub struct Stake {
    pub delegation: Delegation,
    pub credits_observed: u64,
}
impl Stake {
    pub fn stake(
        &self,
        epoch: Epoch,
        history: &StakeHistory,
        new_rate_activation_epoch: Option<Epoch>,
    ) -> u64 {
        self.delegation
            .stake(epoch, history, new_rate_activation_epoch)
    }
    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(())
        }
    }
}
impl borsh0_10::de::BorshDeserialize for Stake {
    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
        reader: &mut R,
    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
        Ok(Self {
            delegation: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
            credits_observed: borsh0_10::BorshDeserialize::deserialize_reader(reader)?,
        })
    }
}
impl borsh0_10::BorshSchema for Stake {
    fn declaration() -> borsh0_10::schema::Declaration {
        "Stake".to_string()
    }
    fn add_definitions_recursively(
        definitions: &mut borsh0_10::maybestd::collections::HashMap<
            borsh0_10::schema::Declaration,
            borsh0_10::schema::Definition,
        >,
    ) {
        let fields = borsh0_10::schema::Fields::NamedFields(<[_]>::into_vec(
            borsh0_10::maybestd::boxed::Box::new([
                (
                    "delegation".to_string(),
                    <Delegation as borsh0_10::BorshSchema>::declaration(),
                ),
                (
                    "credits_observed".to_string(),
                    <u64 as borsh0_10::BorshSchema>::declaration(),
                ),
            ]),
        ));
        let definition = borsh0_10::schema::Definition::Struct { fields };
        Self::add_definition(
            <Self as borsh0_10::BorshSchema>::declaration(),
            definition,
            definitions,
        );
        <Delegation as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
        <u64 as borsh0_10::BorshSchema>::add_definitions_recursively(definitions);
    }
}
impl borsh0_10::ser::BorshSerialize for Stake {
    fn serialize<W: borsh0_10::maybestd::io::Write>(
        &self,
        writer: &mut W,
    ) -> ::core::result::Result<(), borsh0_10::maybestd::io::Error> {
        borsh0_10::BorshSerialize::serialize(&self.delegation, writer)?;
        borsh0_10::BorshSerialize::serialize(&self.credits_observed, writer)?;
        Ok(())
    }
}
#[cfg(test)]
mod test {
    use {
        super::*, crate::borsh1::try_from_slice_unchecked, assert_matches::assert_matches,
        bincode::serialize,
    };
    fn check_borsh_deserialization(stake: StakeStateV2) {
        let serialized = serialize(&stake).unwrap();
        let deserialized = StakeStateV2::try_from_slice(&serialized).unwrap();
        assert_eq!(stake, deserialized);
    }
    fn check_borsh_serialization(stake: StakeStateV2) {
        let bincode_serialized = serialize(&stake).unwrap();
        let borsh_serialized = borsh::to_vec(&stake).unwrap();
        assert_eq!(bincode_serialized, borsh_serialized);
    }
    #[test]
    fn test_size_of() {
        assert_eq!(StakeStateV2::size_of(), std::mem::size_of::<StakeStateV2>());
    }
    #[test]
    fn bincode_vs_borsh_deserialization() {
        check_borsh_deserialization(StakeStateV2::Uninitialized);
        check_borsh_deserialization(StakeStateV2::RewardsPool);
        check_borsh_deserialization(StakeStateV2::Initialized(Meta {
            rent_exempt_reserve: u64::MAX,
            authorized: Authorized {
                staker: Pubkey::new_unique(),
                withdrawer: Pubkey::new_unique(),
            },
            lockup: Lockup::default(),
        }));
        check_borsh_deserialization(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,
                    ..Delegation::default()
                },
                credits_observed: 1,
            },
            StakeFlags::empty(),
        ));
    }
    #[test]
    fn bincode_vs_borsh_serialization() {
        check_borsh_serialization(StakeStateV2::Uninitialized);
        check_borsh_serialization(StakeStateV2::RewardsPool);
        check_borsh_serialization(StakeStateV2::Initialized(Meta {
            rent_exempt_reserve: u64::MAX,
            authorized: Authorized {
                staker: Pubkey::new_unique(),
                withdrawer: Pubkey::new_unique(),
            },
            lockup: Lockup::default(),
        }));
        check_borsh_serialization(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,
                    ..Default::default()
                },
                credits_observed: 1,
            },
            StakeFlags::MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED,
        ));
    }
    #[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,
        ];
        let deserialized = try_from_slice_unchecked::<StakeStateV2>(&data).unwrap();
        assert_matches!(
            deserialized,
            StakeStateV2::Initialized(Meta {
                rent_exempt_reserve: 2282880,
                ..
            })
        );
    }
    #[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 = borsh::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);
    }
    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 = borsh::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,
            ];
            let deserialized = try_from_slice_unchecked::<StakeState>(&data).unwrap();
            assert_matches!(
                deserialized,
                StakeState::Initialized(Meta {
                    rent_exempt_reserve: 2282880,
                    ..
                })
            );
        }
    }
}