* Fix slow/stuck unstaking due to toggling in epoch (#13501)
* Fix slow/stuck unstaking due to toggling in epoch
* nits
* nits
* Add stake_program_v2 feature status check to cli
Co-authored-by: Tyera Eulberg <tyera@solana.com>
(cherry picked from commit 89b474e192
)
* Fix conflict
* PartialEq<Vec<T>> is not impl for &[T] on rust v1.45.1
Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||||
|
stake::is_stake_program_v2_enabled,
|
||||||
};
|
};
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
@ -1349,6 +1350,8 @@ pub fn process_show_stakes(
|
|||||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
// At v1.6, this check can be removed and simply passed as `true`
|
||||||
|
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client);
|
||||||
|
|
||||||
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
||||||
for (stake_pubkey, stake_account) in all_stake_accounts {
|
for (stake_pubkey, stake_account) in all_stake_accounts {
|
||||||
@ -1364,6 +1367,7 @@ pub fn process_show_stakes(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
stake_program_v2_enabled,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1382,6 +1386,7 @@ pub fn process_show_stakes(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
stake_program_v2_enabled,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ use solana_client::{
|
|||||||
rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE},
|
rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE},
|
||||||
};
|
};
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
|
use solana_runtime::{feature::Feature, feature_set};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::{Clock, Epoch, Slot, UnixTimestamp, SECONDS_PER_DAY},
|
clock::{Clock, Epoch, Slot, UnixTimestamp, SECONDS_PER_DAY},
|
||||||
@ -1501,6 +1502,7 @@ pub fn build_stake_state(
|
|||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
|
stake_program_v2_enabled: bool,
|
||||||
) -> CliStakeState {
|
) -> CliStakeState {
|
||||||
match stake_state {
|
match stake_state {
|
||||||
StakeState::Stake(
|
StakeState::Stake(
|
||||||
@ -1512,9 +1514,12 @@ pub fn build_stake_state(
|
|||||||
stake,
|
stake,
|
||||||
) => {
|
) => {
|
||||||
let current_epoch = clock.epoch;
|
let current_epoch = clock.epoch;
|
||||||
let (active_stake, activating_stake, deactivating_stake) = stake
|
let (active_stake, activating_stake, deactivating_stake) =
|
||||||
.delegation
|
stake.delegation.stake_activating_and_deactivating(
|
||||||
.stake_activating_and_deactivating(current_epoch, Some(stake_history));
|
current_epoch,
|
||||||
|
Some(stake_history),
|
||||||
|
stake_program_v2_enabled,
|
||||||
|
);
|
||||||
let lockup = if lockup.is_in_force(clock, None) {
|
let lockup = if lockup.is_in_force(clock, None) {
|
||||||
Some(lockup.into())
|
Some(lockup.into())
|
||||||
} else {
|
} else {
|
||||||
@ -1711,6 +1716,7 @@ pub fn process_show_stake_account(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
is_stake_program_v2_enabled(rpc_client), // At v1.6, this check can be removed and simply passed as `true`
|
||||||
);
|
);
|
||||||
|
|
||||||
if state.stake_type == CliStakeType::Stake {
|
if state.stake_type == CliStakeType::Stake {
|
||||||
@ -1881,6 +1887,15 @@ pub fn process_delegate_stake(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_stake_program_v2_enabled(rpc_client: &RpcClient) -> bool {
|
||||||
|
rpc_client
|
||||||
|
.get_account(&feature_set::stake_program_v2::id())
|
||||||
|
.ok()
|
||||||
|
.and_then(|account| Feature::from_account(&account))
|
||||||
|
.and_then(|feature| feature.activated_at)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1046,8 +1046,11 @@ impl JsonRpcRequestProcessor {
|
|||||||
let stake_history =
|
let stake_history =
|
||||||
StakeHistory::from_account(&stake_history_account).ok_or_else(Error::internal_error)?;
|
StakeHistory::from_account(&stake_history_account).ok_or_else(Error::internal_error)?;
|
||||||
|
|
||||||
let (active, activating, deactivating) =
|
let (active, activating, deactivating) = delegation.stake_activating_and_deactivating(
|
||||||
delegation.stake_activating_and_deactivating(epoch, Some(&stake_history));
|
epoch,
|
||||||
|
Some(&stake_history),
|
||||||
|
bank.stake_program_v2_enabled(),
|
||||||
|
);
|
||||||
let stake_activation_state = if deactivating > 0 {
|
let stake_activation_state = if deactivating > 0 {
|
||||||
StakeActivationState::Deactivating
|
StakeActivationState::Deactivating
|
||||||
} else if activating > 0 {
|
} else if activating > 0 {
|
||||||
|
@ -236,7 +236,10 @@ pub(crate) mod tests {
|
|||||||
let result: Vec<_> = epoch_stakes_and_lockouts(&bank, first_leader_schedule_epoch);
|
let result: Vec<_> = epoch_stakes_and_lockouts(&bank, first_leader_schedule_epoch);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
vec![(leader_stake.stake(first_leader_schedule_epoch, None), None)]
|
vec![(
|
||||||
|
leader_stake.stake(first_leader_schedule_epoch, None, true),
|
||||||
|
None
|
||||||
|
)]
|
||||||
);
|
);
|
||||||
|
|
||||||
// epoch stakes and lockouts are saved off for the future epoch, should
|
// epoch stakes and lockouts are saved off for the future epoch, should
|
||||||
@ -246,8 +249,14 @@ pub(crate) mod tests {
|
|||||||
let stake_history =
|
let stake_history =
|
||||||
StakeHistory::from_account(&bank.get_account(&stake_history::id()).unwrap()).unwrap();
|
StakeHistory::from_account(&bank.get_account(&stake_history::id()).unwrap()).unwrap();
|
||||||
let mut expected = vec![
|
let mut expected = vec![
|
||||||
(leader_stake.stake(bank.epoch(), Some(&stake_history)), None),
|
(
|
||||||
(other_stake.stake(bank.epoch(), Some(&stake_history)), None),
|
leader_stake.stake(bank.epoch(), Some(&stake_history), true),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
other_stake.stake(bank.epoch(), Some(&stake_history), true),
|
||||||
|
None,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
expected.sort();
|
expected.sort();
|
||||||
|
@ -99,7 +99,7 @@ pub struct Stake {
|
|||||||
|
|
||||||
impl Stake {
|
impl Stake {
|
||||||
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
||||||
self.delegation.stake(epoch, history)
|
self.delegation.stake(epoch, history, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redeem_rewards(
|
pub fn redeem_rewards(
|
||||||
@ -144,7 +144,7 @@ impl Stake {
|
|||||||
for (epoch, final_epoch_credits, initial_epoch_credits) in
|
for (epoch, final_epoch_credits, initial_epoch_credits) in
|
||||||
new_vote_state.epoch_credits().iter().copied()
|
new_vote_state.epoch_credits().iter().copied()
|
||||||
{
|
{
|
||||||
let stake = u128::from(self.delegation.stake(epoch, stake_history));
|
let stake = u128::from(self.delegation.stake(epoch, stake_history, false));
|
||||||
|
|
||||||
// figure out how much this stake has seen that
|
// figure out how much this stake has seen that
|
||||||
// for which the vote account has a record
|
// for which the vote account has a record
|
||||||
@ -595,7 +595,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
.check(&signers, StakeAuthorize::Withdrawer)?;
|
.check(&signers, StakeAuthorize::Withdrawer)?;
|
||||||
// if we have a deactivation epoch and we're in cooldown
|
// if we have a deactivation epoch and we're in cooldown
|
||||||
let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
|
let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
|
||||||
stake.delegation.stake(clock.epoch, Some(stake_history))
|
stake
|
||||||
|
.delegation
|
||||||
|
.stake(clock.epoch, Some(stake_history), false)
|
||||||
} else {
|
} else {
|
||||||
// Assume full stake if the stake account hasn't been
|
// Assume full stake if the stake account hasn't been
|
||||||
// de-activated, because in the future the exposed stake
|
// de-activated, because in the future the exposed stake
|
||||||
@ -692,11 +694,12 @@ pub fn calculate_points(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function, used by runtime::Stakes, tests
|
// utility function, used by runtime::Stakes and tests
|
||||||
pub fn new_stake_history_entry<'a, I>(
|
pub fn new_stake_history_entry<'a, I>(
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
stakes: I,
|
stakes: I,
|
||||||
history: Option<&StakeHistory>,
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> StakeHistoryEntry
|
) -> StakeHistoryEntry
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a Delegation>,
|
I: Iterator<Item = &'a Delegation>,
|
||||||
@ -707,7 +710,10 @@ where
|
|||||||
(a.0 + b.0, a.1 + b.1, a.2 + b.2)
|
(a.0 + b.0, a.1 + b.1, a.2 + b.2)
|
||||||
}
|
}
|
||||||
let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
|
let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
|
||||||
add(sum, stake.stake_activating_and_deactivating(epoch, history))
|
add(
|
||||||
|
sum,
|
||||||
|
stake.stake_activating_and_deactivating(epoch, history, fix_stake_deactivate),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
StakeHistoryEntry {
|
StakeHistoryEntry {
|
||||||
@ -1040,6 +1046,7 @@ mod tests {
|
|||||||
epoch,
|
epoch,
|
||||||
delegations.iter().chain(bootstrap_delegation.iter()),
|
delegations.iter().chain(bootstrap_delegation.iter()),
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
stake_history.add(epoch, entry);
|
stake_history.add(epoch, entry);
|
||||||
}
|
}
|
||||||
@ -1062,25 +1069,34 @@ mod tests {
|
|||||||
let mut stake_history = StakeHistory::default();
|
let mut stake_history = StakeHistory::default();
|
||||||
// assert that this stake follows step function if there's no history
|
// assert that this stake follows step function if there's no history
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(stake.activation_epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(
|
||||||
|
stake.activation_epoch,
|
||||||
|
Some(&stake_history),
|
||||||
|
false
|
||||||
|
),
|
||||||
(0, stake.stake, 0)
|
(0, stake.stake, 0)
|
||||||
);
|
);
|
||||||
for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
|
for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(epoch, Some(&stake_history), false),
|
||||||
(stake.stake, 0, 0)
|
(stake.stake, 0, 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// assert that this stake is full deactivating
|
// assert that this stake is full deactivating
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(stake.deactivation_epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(
|
||||||
|
stake.deactivation_epoch,
|
||||||
|
Some(&stake_history),
|
||||||
|
false
|
||||||
|
),
|
||||||
(stake.stake, 0, stake.stake)
|
(stake.stake, 0, stake.stake)
|
||||||
);
|
);
|
||||||
// assert that this stake is fully deactivated if there's no history
|
// assert that this stake is fully deactivated if there's no history
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 1,
|
stake.deactivation_epoch + 1,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
(0, 0, 0)
|
(0, 0, 0)
|
||||||
);
|
);
|
||||||
@ -1095,7 +1111,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// assert that this stake is broken, because above setup is broken
|
// assert that this stake is broken, because above setup is broken
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(1, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(1, Some(&stake_history), false),
|
||||||
(0, stake.stake, 0)
|
(0, stake.stake, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1110,7 +1126,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// assert that this stake is broken, because above setup is broken
|
// assert that this stake is broken, because above setup is broken
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(2, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(2, Some(&stake_history), false),
|
||||||
(increment, stake.stake - increment, 0)
|
(increment, stake.stake - increment, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1129,7 +1145,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 1,
|
stake.deactivation_epoch + 1,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
(stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
|
(stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
|
||||||
);
|
);
|
||||||
@ -1147,7 +1164,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 2,
|
stake.deactivation_epoch + 2,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
(stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
|
(stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
|
||||||
);
|
);
|
||||||
@ -1212,7 +1230,7 @@ mod tests {
|
|||||||
(0, history.deactivating)
|
(0, history.deactivating)
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(epoch, Some(&stake_history), false),
|
||||||
(expected_stake, expected_activating, expected_deactivating)
|
(expected_stake, expected_activating, expected_deactivating)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1239,7 +1257,7 @@ mod tests {
|
|||||||
for epoch in 0..epochs {
|
for epoch in 0..epochs {
|
||||||
let stake = delegations
|
let stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(epoch, Some(&stake_history)))
|
.map(|delegation| delegation.stake(epoch, Some(&stake_history), false))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
max_stake = max_stake.max(stake);
|
max_stake = max_stake.max(stake);
|
||||||
min_stake = min_stake.min(stake);
|
min_stake = min_stake.min(stake);
|
||||||
@ -1298,7 +1316,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut prev_total_effective_stake = delegations
|
let mut prev_total_effective_stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(0, Some(&stake_history)))
|
.map(|delegation| delegation.stake(0, Some(&stake_history), false))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
// uncomment and add ! for fun with graphing
|
// uncomment and add ! for fun with graphing
|
||||||
@ -1306,7 +1324,7 @@ mod tests {
|
|||||||
for epoch in 1..epochs {
|
for epoch in 1..epochs {
|
||||||
let total_effective_stake = delegations
|
let total_effective_stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(epoch, Some(&stake_history)))
|
.map(|delegation| delegation.stake(epoch, Some(&stake_history), false))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
let delta = if total_effective_stake > prev_total_effective_stake {
|
let delta = if total_effective_stake > prev_total_effective_stake {
|
||||||
|
@ -212,8 +212,14 @@ impl Delegation {
|
|||||||
self.activation_epoch == std::u64::MAX
|
self.activation_epoch == std::u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
pub fn stake(
|
||||||
self.stake_activating_and_deactivating(epoch, history).0
|
&self,
|
||||||
|
epoch: Epoch,
|
||||||
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
|
) -> u64 {
|
||||||
|
self.stake_activating_and_deactivating(epoch, history, fix_stake_deactivate)
|
||||||
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::comparison_chain)]
|
#[allow(clippy::comparison_chain)]
|
||||||
@ -221,11 +227,13 @@ impl Delegation {
|
|||||||
&self,
|
&self,
|
||||||
target_epoch: Epoch,
|
target_epoch: Epoch,
|
||||||
history: Option<&StakeHistory>,
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> (u64, u64, u64) {
|
) -> (u64, u64, u64) {
|
||||||
let delegated_stake = self.stake;
|
let delegated_stake = self.stake;
|
||||||
|
|
||||||
// first, calculate an effective and activating stake
|
// first, calculate an effective and activating stake
|
||||||
let (effective_stake, activating_stake) = self.stake_and_activating(target_epoch, history);
|
let (effective_stake, activating_stake) =
|
||||||
|
self.stake_and_activating(target_epoch, history, fix_stake_deactivate);
|
||||||
|
|
||||||
// then de-activate some portion if necessary
|
// then de-activate some portion if necessary
|
||||||
if target_epoch < self.deactivation_epoch {
|
if target_epoch < self.deactivation_epoch {
|
||||||
@ -301,12 +309,17 @@ impl Delegation {
|
|||||||
&self,
|
&self,
|
||||||
target_epoch: Epoch,
|
target_epoch: Epoch,
|
||||||
history: Option<&StakeHistory>,
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> (u64, u64) {
|
) -> (u64, u64) {
|
||||||
let delegated_stake = self.stake;
|
let delegated_stake = self.stake;
|
||||||
|
|
||||||
if self.is_bootstrap() {
|
if self.is_bootstrap() {
|
||||||
// fully effective immediately
|
// fully effective immediately
|
||||||
(delegated_stake, 0)
|
(delegated_stake, 0)
|
||||||
|
} else if fix_stake_deactivate && self.activation_epoch == self.deactivation_epoch {
|
||||||
|
// 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 {
|
} else if target_epoch == self.activation_epoch {
|
||||||
// all is activating
|
// all is activating
|
||||||
(0, delegated_stake)
|
(0, delegated_stake)
|
||||||
@ -440,8 +453,13 @@ pub struct PointValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Stake {
|
impl Stake {
|
||||||
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
pub fn stake(
|
||||||
self.delegation.stake(epoch, history)
|
&self,
|
||||||
|
epoch: Epoch,
|
||||||
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
|
) -> u64 {
|
||||||
|
self.delegation.stake(epoch, history, fix_stake_deactivate)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redeem_rewards(
|
pub fn redeem_rewards(
|
||||||
@ -450,12 +468,14 @@ impl Stake {
|
|||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> Option<(u64, u64)> {
|
) -> Option<(u64, u64)> {
|
||||||
self.calculate_rewards(
|
self.calculate_rewards(
|
||||||
point_value,
|
point_value,
|
||||||
vote_state,
|
vote_state,
|
||||||
stake_history,
|
stake_history,
|
||||||
inflation_point_calc_tracer,
|
inflation_point_calc_tracer,
|
||||||
|
fix_stake_deactivate,
|
||||||
)
|
)
|
||||||
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
||||||
self.credits_observed = credits_observed;
|
self.credits_observed = credits_observed;
|
||||||
@ -469,9 +489,15 @@ impl Stake {
|
|||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> u128 {
|
) -> u128 {
|
||||||
self.calculate_points_and_credits(vote_state, stake_history, inflation_point_calc_tracer)
|
self.calculate_points_and_credits(
|
||||||
.0
|
vote_state,
|
||||||
|
stake_history,
|
||||||
|
inflation_point_calc_tracer,
|
||||||
|
fix_stake_deactivate,
|
||||||
|
)
|
||||||
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// for a given stake and vote_state, calculate how many
|
/// for a given stake and vote_state, calculate how many
|
||||||
@ -482,6 +508,7 @@ impl Stake {
|
|||||||
new_vote_state: &VoteState,
|
new_vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> (u128, u64) {
|
) -> (u128, u64) {
|
||||||
// if there is no newer credits since observed, return no point
|
// if there is no newer credits since observed, return no point
|
||||||
if new_vote_state.credits() <= self.credits_observed {
|
if new_vote_state.credits() <= self.credits_observed {
|
||||||
@ -494,7 +521,11 @@ impl Stake {
|
|||||||
for (epoch, final_epoch_credits, initial_epoch_credits) in
|
for (epoch, final_epoch_credits, initial_epoch_credits) in
|
||||||
new_vote_state.epoch_credits().iter().copied()
|
new_vote_state.epoch_credits().iter().copied()
|
||||||
{
|
{
|
||||||
let stake = u128::from(self.delegation.stake(epoch, stake_history));
|
let stake = u128::from(self.delegation.stake(
|
||||||
|
epoch,
|
||||||
|
stake_history,
|
||||||
|
fix_stake_deactivate,
|
||||||
|
));
|
||||||
|
|
||||||
// figure out how much this stake has seen that
|
// figure out how much this stake has seen that
|
||||||
// for which the vote account has a record
|
// for which the vote account has a record
|
||||||
@ -541,11 +572,13 @@ impl Stake {
|
|||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> Option<(u64, u64, u64)> {
|
) -> Option<(u64, u64, u64)> {
|
||||||
let (points, credits_observed) = self.calculate_points_and_credits(
|
let (points, credits_observed) = self.calculate_points_and_credits(
|
||||||
vote_state,
|
vote_state,
|
||||||
stake_history,
|
stake_history,
|
||||||
inflation_point_calc_tracer,
|
inflation_point_calc_tracer,
|
||||||
|
fix_stake_deactivate,
|
||||||
);
|
);
|
||||||
|
|
||||||
if points == 0 || point_value.points == 0 {
|
if points == 0 || point_value.points == 0 {
|
||||||
@ -596,7 +629,7 @@ impl Stake {
|
|||||||
// can't redelegate if stake is active. either the stake
|
// can't redelegate if stake is active. either the stake
|
||||||
// is freshly activated or has fully de-activated. redelegation
|
// is freshly activated or has fully de-activated. redelegation
|
||||||
// implies re-activation
|
// implies re-activation
|
||||||
if self.stake(clock.epoch, Some(stake_history)) != 0 {
|
if self.stake(clock.epoch, Some(stake_history), true) != 0 {
|
||||||
return Err(StakeError::TooSoonToRedelegate);
|
return Err(StakeError::TooSoonToRedelegate);
|
||||||
}
|
}
|
||||||
self.delegation.stake = stake_lamports;
|
self.delegation.stake = stake_lamports;
|
||||||
@ -950,7 +983,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
let meta = match self.state()? {
|
let meta = match self.state()? {
|
||||||
StakeState::Stake(meta, stake) => {
|
StakeState::Stake(meta, stake) => {
|
||||||
// stake must be fully de-activated
|
// stake must be fully de-activated
|
||||||
if stake.stake(clock.epoch, Some(stake_history)) != 0 {
|
if stake.stake(clock.epoch, Some(stake_history), true) != 0 {
|
||||||
return Err(StakeError::MergeActivatedStake.into());
|
return Err(StakeError::MergeActivatedStake.into());
|
||||||
}
|
}
|
||||||
meta
|
meta
|
||||||
@ -964,7 +997,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
let source_meta = match source_stake.state()? {
|
let source_meta = match source_stake.state()? {
|
||||||
StakeState::Stake(meta, stake) => {
|
StakeState::Stake(meta, stake) => {
|
||||||
// stake must be fully de-activated
|
// stake must be fully de-activated
|
||||||
if stake.stake(clock.epoch, Some(stake_history)) != 0 {
|
if stake.stake(clock.epoch, Some(stake_history), true) != 0 {
|
||||||
return Err(StakeError::MergeActivatedStake.into());
|
return Err(StakeError::MergeActivatedStake.into());
|
||||||
}
|
}
|
||||||
meta
|
meta
|
||||||
@ -1006,7 +1039,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
.check(&signers, StakeAuthorize::Withdrawer)?;
|
.check(&signers, StakeAuthorize::Withdrawer)?;
|
||||||
// if we have a deactivation epoch and we're in cooldown
|
// if we have a deactivation epoch and we're in cooldown
|
||||||
let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
|
let staked = if clock.epoch >= stake.delegation.deactivation_epoch {
|
||||||
stake.delegation.stake(clock.epoch, Some(stake_history))
|
stake
|
||||||
|
.delegation
|
||||||
|
.stake(clock.epoch, Some(stake_history), true)
|
||||||
} else {
|
} else {
|
||||||
// Assume full stake if the stake account hasn't been
|
// Assume full stake if the stake account hasn't been
|
||||||
// de-activated, because in the future the exposed stake
|
// de-activated, because in the future the exposed stake
|
||||||
@ -1066,6 +1101,7 @@ pub fn redeem_rewards(
|
|||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> Result<(u64, u64), InstructionError> {
|
) -> Result<(u64, u64), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
||||||
let vote_state: VoteState =
|
let vote_state: VoteState =
|
||||||
@ -1084,6 +1120,7 @@ pub fn redeem_rewards(
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
stake_history,
|
stake_history,
|
||||||
inflation_point_calc_tracer,
|
inflation_point_calc_tracer,
|
||||||
|
fix_stake_deactivate,
|
||||||
) {
|
) {
|
||||||
stake_account.lamports += stakers_reward;
|
stake_account.lamports += stakers_reward;
|
||||||
vote_account.lamports += voters_reward;
|
vote_account.lamports += voters_reward;
|
||||||
@ -1104,12 +1141,18 @@ pub fn calculate_points(
|
|||||||
stake_account: &Account,
|
stake_account: &Account,
|
||||||
vote_account: &Account,
|
vote_account: &Account,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> Result<u128, InstructionError> {
|
) -> Result<u128, InstructionError> {
|
||||||
if let StakeState::Stake(_meta, stake) = stake_account.state()? {
|
if let StakeState::Stake(_meta, stake) = stake_account.state()? {
|
||||||
let vote_state: VoteState =
|
let vote_state: VoteState =
|
||||||
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
Ok(stake.calculate_points(&vote_state, stake_history, &mut null_tracer()))
|
Ok(stake.calculate_points(
|
||||||
|
&vote_state,
|
||||||
|
stake_history,
|
||||||
|
&mut null_tracer(),
|
||||||
|
fix_stake_deactivate,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
@ -1133,6 +1176,7 @@ pub fn new_stake_history_entry<'a, I>(
|
|||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
stakes: I,
|
stakes: I,
|
||||||
history: Option<&StakeHistory>,
|
history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> StakeHistoryEntry
|
) -> StakeHistoryEntry
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a Delegation>,
|
I: Iterator<Item = &'a Delegation>,
|
||||||
@ -1143,7 +1187,10 @@ where
|
|||||||
(a.0 + b.0, a.1 + b.1, a.2 + b.2)
|
(a.0 + b.0, a.1 + b.1, a.2 + b.2)
|
||||||
}
|
}
|
||||||
let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
|
let (effective, activating, deactivating) = stakes.fold((0, 0, 0), |sum, stake| {
|
||||||
add(sum, stake.stake_activating_and_deactivating(epoch, history))
|
add(
|
||||||
|
sum,
|
||||||
|
stake.stake_activating_and_deactivating(epoch, history, fix_stake_deactivate),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
StakeHistoryEntry {
|
StakeHistoryEntry {
|
||||||
@ -1485,6 +1532,7 @@ mod tests {
|
|||||||
epoch,
|
epoch,
|
||||||
delegations.iter().chain(bootstrap_delegation.iter()),
|
delegations.iter().chain(bootstrap_delegation.iter()),
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
stake_history.add(epoch, entry);
|
stake_history.add(epoch, entry);
|
||||||
}
|
}
|
||||||
@ -1507,25 +1555,34 @@ mod tests {
|
|||||||
let mut stake_history = StakeHistory::default();
|
let mut stake_history = StakeHistory::default();
|
||||||
// assert that this stake follows step function if there's no history
|
// assert that this stake follows step function if there's no history
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(stake.activation_epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(
|
||||||
|
stake.activation_epoch,
|
||||||
|
Some(&stake_history),
|
||||||
|
true
|
||||||
|
),
|
||||||
(0, stake.stake, 0)
|
(0, stake.stake, 0)
|
||||||
);
|
);
|
||||||
for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
|
for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(epoch, Some(&stake_history), true),
|
||||||
(stake.stake, 0, 0)
|
(stake.stake, 0, 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// assert that this stake is full deactivating
|
// assert that this stake is full deactivating
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(stake.deactivation_epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(
|
||||||
|
stake.deactivation_epoch,
|
||||||
|
Some(&stake_history),
|
||||||
|
true
|
||||||
|
),
|
||||||
(stake.stake, 0, stake.stake)
|
(stake.stake, 0, stake.stake)
|
||||||
);
|
);
|
||||||
// assert that this stake is fully deactivated if there's no history
|
// assert that this stake is fully deactivated if there's no history
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 1,
|
stake.deactivation_epoch + 1,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
(0, 0, 0)
|
(0, 0, 0)
|
||||||
);
|
);
|
||||||
@ -1540,7 +1597,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// assert that this stake is broken, because above setup is broken
|
// assert that this stake is broken, because above setup is broken
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(1, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(1, Some(&stake_history), true),
|
||||||
(0, stake.stake, 0)
|
(0, stake.stake, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1555,7 +1612,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// assert that this stake is broken, because above setup is broken
|
// assert that this stake is broken, because above setup is broken
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(2, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(2, Some(&stake_history), true),
|
||||||
(increment, stake.stake - increment, 0)
|
(increment, stake.stake - increment, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1574,7 +1631,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 1,
|
stake.deactivation_epoch + 1,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
(stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
|
(stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
|
||||||
);
|
);
|
||||||
@ -1592,12 +1650,158 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(
|
stake.stake_activating_and_deactivating(
|
||||||
stake.deactivation_epoch + 2,
|
stake.deactivation_epoch + 2,
|
||||||
Some(&stake_history)
|
Some(&stake_history),
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
(stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
|
(stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod same_epoch_activation_then_deactivation {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
enum OldDeactivationBehavior {
|
||||||
|
Stuck,
|
||||||
|
Slow,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_test(
|
||||||
|
old_behavior: OldDeactivationBehavior,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
|
expected_stakes: &[(u64, u64, u64)],
|
||||||
|
) {
|
||||||
|
let cluster_stake = 1_000;
|
||||||
|
let activating_stake = 10_000;
|
||||||
|
let some_stake = 700;
|
||||||
|
let some_epoch = 0;
|
||||||
|
|
||||||
|
let stake = Delegation {
|
||||||
|
stake: some_stake,
|
||||||
|
activation_epoch: some_epoch,
|
||||||
|
deactivation_epoch: some_epoch,
|
||||||
|
..Delegation::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stake_history = StakeHistory::default();
|
||||||
|
let cluster_deactivation_at_stake_modified_epoch = match old_behavior {
|
||||||
|
OldDeactivationBehavior::Stuck => 0,
|
||||||
|
OldDeactivationBehavior::Slow => 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
let stake_history_entries = vec![
|
||||||
|
(
|
||||||
|
cluster_stake,
|
||||||
|
activating_stake,
|
||||||
|
cluster_deactivation_at_stake_modified_epoch,
|
||||||
|
),
|
||||||
|
(cluster_stake, activating_stake, 1000),
|
||||||
|
(cluster_stake, activating_stake, 1000),
|
||||||
|
(cluster_stake, activating_stake, 100),
|
||||||
|
(cluster_stake, activating_stake, 100),
|
||||||
|
(cluster_stake, activating_stake, 100),
|
||||||
|
(cluster_stake, activating_stake, 100),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (epoch, (effective, activating, deactivating)) in
|
||||||
|
stake_history_entries.into_iter().enumerate()
|
||||||
|
{
|
||||||
|
stake_history.add(
|
||||||
|
epoch as Epoch,
|
||||||
|
StakeHistoryEntry {
|
||||||
|
effective,
|
||||||
|
activating,
|
||||||
|
deactivating,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected_stakes,
|
||||||
|
&(0..expected_stakes.len())
|
||||||
|
.map(|epoch| stake.stake_activating_and_deactivating(
|
||||||
|
epoch as u64,
|
||||||
|
Some(&stake_history),
|
||||||
|
fix_stake_deactivate
|
||||||
|
))
|
||||||
|
.collect::<Vec<_>>()[..]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_old_behavior_slow() {
|
||||||
|
do_test(
|
||||||
|
OldDeactivationBehavior::Slow,
|
||||||
|
false,
|
||||||
|
&[
|
||||||
|
(0, 0, 0),
|
||||||
|
(13, 0, 13),
|
||||||
|
(10, 0, 10),
|
||||||
|
(8, 0, 8),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_old_behavior_stuck() {
|
||||||
|
do_test(
|
||||||
|
OldDeactivationBehavior::Stuck,
|
||||||
|
false,
|
||||||
|
&[
|
||||||
|
(0, 0, 0),
|
||||||
|
(17, 0, 17),
|
||||||
|
(17, 0, 17),
|
||||||
|
(17, 0, 17),
|
||||||
|
(17, 0, 17),
|
||||||
|
(17, 0, 17),
|
||||||
|
(17, 0, 17),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_behavior_previously_slow() {
|
||||||
|
// any stake accounts activated and deactivated at the same epoch
|
||||||
|
// shouldn't been activated (then deactivated) at all!
|
||||||
|
|
||||||
|
do_test(
|
||||||
|
OldDeactivationBehavior::Slow,
|
||||||
|
true,
|
||||||
|
&[
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_behavior_previously_stuck() {
|
||||||
|
// any stake accounts activated and deactivated at the same epoch
|
||||||
|
// shouldn't been activated (then deactivated) at all!
|
||||||
|
|
||||||
|
do_test(
|
||||||
|
OldDeactivationBehavior::Stuck,
|
||||||
|
true,
|
||||||
|
&[
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stop_activating_after_deactivation() {
|
fn test_stop_activating_after_deactivation() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
@ -1657,7 +1861,7 @@ mod tests {
|
|||||||
(0, history.deactivating)
|
(0, history.deactivating)
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
|
stake.stake_activating_and_deactivating(epoch, Some(&stake_history), true),
|
||||||
(expected_stake, expected_activating, expected_deactivating)
|
(expected_stake, expected_activating, expected_deactivating)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1684,7 +1888,7 @@ mod tests {
|
|||||||
for epoch in 0..epochs {
|
for epoch in 0..epochs {
|
||||||
let stake = delegations
|
let stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(epoch, Some(&stake_history)))
|
.map(|delegation| delegation.stake(epoch, Some(&stake_history), true))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
max_stake = max_stake.max(stake);
|
max_stake = max_stake.max(stake);
|
||||||
min_stake = min_stake.min(stake);
|
min_stake = min_stake.min(stake);
|
||||||
@ -1743,7 +1947,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut prev_total_effective_stake = delegations
|
let mut prev_total_effective_stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(0, Some(&stake_history)))
|
.map(|delegation| delegation.stake(0, Some(&stake_history), true))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
// uncomment and add ! for fun with graphing
|
// uncomment and add ! for fun with graphing
|
||||||
@ -1751,7 +1955,7 @@ mod tests {
|
|||||||
for epoch in 1..epochs {
|
for epoch in 1..epochs {
|
||||||
let total_effective_stake = delegations
|
let total_effective_stake = delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|delegation| delegation.stake(epoch, Some(&stake_history)))
|
.map(|delegation| delegation.stake(epoch, Some(&stake_history), true))
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
let delta = if total_effective_stake > prev_total_effective_stake {
|
let delta = if total_effective_stake > prev_total_effective_stake {
|
||||||
@ -2547,6 +2751,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2565,6 +2770,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2600,6 +2806,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2613,7 +2820,7 @@ mod tests {
|
|||||||
// no overflow on points
|
// no overflow on points
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
u128::from(stake.delegation.stake) * epoch_slots,
|
u128::from(stake.delegation.stake) * epoch_slots,
|
||||||
stake.calculate_points(&vote_state, None, &mut null_tracer())
|
stake.calculate_points(&vote_state, None, &mut null_tracer(), true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2641,6 +2848,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2659,6 +2867,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2674,6 +2883,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2692,6 +2902,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2708,6 +2919,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2730,6 +2942,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2746,6 +2959,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
vote_state.commission = 99;
|
vote_state.commission = 99;
|
||||||
@ -2759,6 +2973,7 @@ mod tests {
|
|||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -843,7 +843,7 @@ impl Bank {
|
|||||||
capitalization: AtomicU64::new(parent.capitalization()),
|
capitalization: AtomicU64::new(parent.capitalization()),
|
||||||
inflation: parent.inflation.clone(),
|
inflation: parent.inflation.clone(),
|
||||||
transaction_count: AtomicU64::new(parent.transaction_count()),
|
transaction_count: AtomicU64::new(parent.transaction_count()),
|
||||||
stakes: RwLock::new(parent.stakes.read().unwrap().clone_with_epoch(epoch)),
|
stakes: RwLock::new(parent.stakes.read().unwrap().clone()),
|
||||||
epoch_stakes: parent.epoch_stakes.clone(),
|
epoch_stakes: parent.epoch_stakes.clone(),
|
||||||
parent_hash: parent.hash(),
|
parent_hash: parent.hash(),
|
||||||
parent_slot: parent.slot(),
|
parent_slot: parent.slot(),
|
||||||
@ -874,8 +874,6 @@ impl Bank {
|
|||||||
("block_height", new.block_height, i64)
|
("block_height", new.block_height, i64)
|
||||||
);
|
);
|
||||||
|
|
||||||
let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot);
|
|
||||||
new.update_epoch_stakes(leader_schedule_epoch);
|
|
||||||
new.ancestors.insert(new.slot(), 0);
|
new.ancestors.insert(new.slot(), 0);
|
||||||
new.parents().iter().enumerate().for_each(|(i, p)| {
|
new.parents().iter().enumerate().for_each(|(i, p)| {
|
||||||
new.ancestors.insert(p.slot(), i + 1);
|
new.ancestors.insert(p.slot(), i + 1);
|
||||||
@ -885,7 +883,15 @@ impl Bank {
|
|||||||
if parent.epoch() < new.epoch() {
|
if parent.epoch() < new.epoch() {
|
||||||
new.apply_feature_activations(false);
|
new.apply_feature_activations(false);
|
||||||
}
|
}
|
||||||
|
let cloned = new
|
||||||
|
.stakes
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.clone_with_epoch(epoch, new.stake_program_v2_enabled());
|
||||||
|
*new.stakes.write().unwrap() = cloned;
|
||||||
|
|
||||||
|
let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot);
|
||||||
|
new.update_epoch_stakes(leader_schedule_epoch);
|
||||||
new.update_slot_hashes();
|
new.update_slot_hashes();
|
||||||
new.update_rewards(parent.epoch(), reward_calc_tracer);
|
new.update_rewards(parent.epoch(), reward_calc_tracer);
|
||||||
new.update_stake_history(Some(parent.epoch()));
|
new.update_stake_history(Some(parent.epoch()));
|
||||||
@ -1259,8 +1265,11 @@ impl Bank {
|
|||||||
|
|
||||||
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked();
|
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked();
|
||||||
|
|
||||||
let validator_point_value =
|
let validator_point_value = self.pay_validator_rewards(
|
||||||
self.pay_validator_rewards(validator_rewards, reward_calc_tracer);
|
validator_rewards,
|
||||||
|
reward_calc_tracer,
|
||||||
|
self.stake_program_v2_enabled(),
|
||||||
|
);
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
.feature_set
|
.feature_set
|
||||||
@ -1373,6 +1382,7 @@ impl Bank {
|
|||||||
&mut self,
|
&mut self,
|
||||||
rewards: u64,
|
rewards: u64,
|
||||||
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let stake_history = self.stakes.read().unwrap().history().clone();
|
let stake_history = self.stakes.read().unwrap().history().clone();
|
||||||
|
|
||||||
@ -1386,8 +1396,13 @@ impl Bank {
|
|||||||
.map(move |(_stake_pubkey, stake_account)| (stake_account, vote_account))
|
.map(move |(_stake_pubkey, stake_account)| (stake_account, vote_account))
|
||||||
})
|
})
|
||||||
.map(|(stake_account, vote_account)| {
|
.map(|(stake_account, vote_account)| {
|
||||||
stake_state::calculate_points(&stake_account, &vote_account, Some(&stake_history))
|
stake_state::calculate_points(
|
||||||
.unwrap_or(0)
|
&stake_account,
|
||||||
|
&vote_account,
|
||||||
|
Some(&stake_history),
|
||||||
|
fix_stake_deactivate,
|
||||||
|
)
|
||||||
|
.unwrap_or(0)
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
@ -1418,6 +1433,7 @@ impl Bank {
|
|||||||
&point_value,
|
&point_value,
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
&mut reward_calc_tracer.as_mut(),
|
&mut reward_calc_tracer.as_mut(),
|
||||||
|
fix_stake_deactivate,
|
||||||
);
|
);
|
||||||
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
||||||
self.store_account(&stake_pubkey, &stake_account);
|
self.store_account(&stake_pubkey, &stake_account);
|
||||||
@ -3295,7 +3311,10 @@ impl Bank {
|
|||||||
self.rc.accounts.store_slow(self.slot(), pubkey, account);
|
self.rc.accounts.store_slow(self.slot(), pubkey, account);
|
||||||
|
|
||||||
if Stakes::is_stake(account) {
|
if Stakes::is_stake(account) {
|
||||||
self.stakes.write().unwrap().store(pubkey, account);
|
self.stakes
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.store(pubkey, account, self.stake_program_v2_enabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3721,9 +3740,11 @@ impl Bank {
|
|||||||
.filter(|(_key, account)| (Stakes::is_stake(account)))
|
.filter(|(_key, account)| (Stakes::is_stake(account)))
|
||||||
{
|
{
|
||||||
if Stakes::is_stake(account) {
|
if Stakes::is_stake(account) {
|
||||||
if let Some(old_vote_account) =
|
if let Some(old_vote_account) = self.stakes.write().unwrap().store(
|
||||||
self.stakes.write().unwrap().store(pubkey, account)
|
pubkey,
|
||||||
{
|
account,
|
||||||
|
self.stake_program_v2_enabled(),
|
||||||
|
) {
|
||||||
overwritten_vote_accounts.push(OverwrittenVoteAccount {
|
overwritten_vote_accounts.push(OverwrittenVoteAccount {
|
||||||
account: old_vote_account,
|
account: old_vote_account,
|
||||||
transaction_index,
|
transaction_index,
|
||||||
@ -3935,6 +3956,11 @@ impl Bank {
|
|||||||
self.feature_set.cumulative_rent_related_fixes_enabled()
|
self.feature_set.cumulative_rent_related_fixes_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stake_program_v2_enabled(&self) -> bool {
|
||||||
|
self.feature_set
|
||||||
|
.is_active(&feature_set::stake_program_v2::id())
|
||||||
|
}
|
||||||
|
|
||||||
// This is called from snapshot restore AND for each epoch boundary
|
// This is called from snapshot restore AND for each epoch boundary
|
||||||
// The entire code path herein must be idempotent
|
// The entire code path herein must be idempotent
|
||||||
fn apply_feature_activations(&mut self, init_finish_or_warp: bool) {
|
fn apply_feature_activations(&mut self, init_finish_or_warp: bool) {
|
||||||
@ -5906,7 +5932,8 @@ mod tests {
|
|||||||
.map(move |(_stake_pubkey, stake_account)| (stake_account, vote_account))
|
.map(move |(_stake_pubkey, stake_account)| (stake_account, vote_account))
|
||||||
})
|
})
|
||||||
.map(|(stake_account, vote_account)| {
|
.map(|(stake_account, vote_account)| {
|
||||||
stake_state::calculate_points(&stake_account, &vote_account, None).unwrap_or(0)
|
stake_state::calculate_points(&stake_account, &vote_account, None, true)
|
||||||
|
.unwrap_or(0)
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
@ -7323,7 +7350,7 @@ mod tests {
|
|||||||
// epoch_stakes are a snapshot at the leader_schedule_slot_offset boundary
|
// epoch_stakes are a snapshot at the leader_schedule_slot_offset boundary
|
||||||
// in the prior epoch (0 in this case)
|
// in the prior epoch (0 in this case)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
leader_stake.stake(0, None),
|
leader_stake.stake(0, None, true),
|
||||||
vote_accounts.unwrap().get(&leader_vote_account).unwrap().0
|
vote_accounts.unwrap().get(&leader_vote_account).unwrap().0
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -7339,7 +7366,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(child.epoch_vote_accounts(epoch).is_some());
|
assert!(child.epoch_vote_accounts(epoch).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
leader_stake.stake(child.epoch(), None),
|
leader_stake.stake(child.epoch(), None, true),
|
||||||
child
|
child
|
||||||
.epoch_vote_accounts(epoch)
|
.epoch_vote_accounts(epoch)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -7357,7 +7384,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(child.epoch_vote_accounts(epoch).is_some());
|
assert!(child.epoch_vote_accounts(epoch).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
leader_stake.stake(child.epoch(), None),
|
leader_stake.stake(child.epoch(), None, true),
|
||||||
child
|
child
|
||||||
.epoch_vote_accounts(epoch)
|
.epoch_vote_accounts(epoch)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -29,7 +29,7 @@ impl Stakes {
|
|||||||
pub fn history(&self) -> &StakeHistory {
|
pub fn history(&self) -> &StakeHistory {
|
||||||
&self.stake_history
|
&self.stake_history
|
||||||
}
|
}
|
||||||
pub fn clone_with_epoch(&self, next_epoch: Epoch) -> Self {
|
pub fn clone_with_epoch(&self, next_epoch: Epoch, fix_stake_deactivate: bool) -> Self {
|
||||||
let prev_epoch = self.epoch;
|
let prev_epoch = self.epoch;
|
||||||
if prev_epoch == next_epoch {
|
if prev_epoch == next_epoch {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -44,6 +44,7 @@ impl Stakes {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(_pubkey, stake_delegation)| stake_delegation),
|
.map(|(_pubkey, stake_delegation)| stake_delegation),
|
||||||
Some(&self.stake_history),
|
Some(&self.stake_history),
|
||||||
|
fix_stake_deactivate,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ impl Stakes {
|
|||||||
pubkey,
|
pubkey,
|
||||||
next_epoch,
|
next_epoch,
|
||||||
Some(&stake_history_upto_prev_epoch),
|
Some(&stake_history_upto_prev_epoch),
|
||||||
|
fix_stake_deactivate,
|
||||||
),
|
),
|
||||||
account.clone(),
|
account.clone(),
|
||||||
),
|
),
|
||||||
@ -82,12 +84,13 @@ impl Stakes {
|
|||||||
voter_pubkey: &Pubkey,
|
voter_pubkey: &Pubkey,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
self.stake_delegations
|
self.stake_delegations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, stake_delegation)| {
|
.map(|(_, stake_delegation)| {
|
||||||
if &stake_delegation.voter_pubkey == voter_pubkey {
|
if &stake_delegation.voter_pubkey == voter_pubkey {
|
||||||
stake_delegation.stake(epoch, stake_history)
|
stake_delegation.stake(epoch, stake_history, fix_stake_deactivate)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -113,12 +116,24 @@ impl Stakes {
|
|||||||
&& account.data.len() >= std::mem::size_of::<StakeState>()
|
&& account.data.len() >= std::mem::size_of::<StakeState>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) -> Option<Account> {
|
pub fn store(
|
||||||
|
&mut self,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
account: &Account,
|
||||||
|
fix_stake_deactivate: bool,
|
||||||
|
) -> Option<Account> {
|
||||||
if solana_vote_program::check_id(&account.owner) {
|
if solana_vote_program::check_id(&account.owner) {
|
||||||
let old = self.vote_accounts.remove(pubkey);
|
let old = self.vote_accounts.remove(pubkey);
|
||||||
if account.lamports != 0 {
|
if account.lamports != 0 {
|
||||||
let stake = old.as_ref().map_or_else(
|
let stake = old.as_ref().map_or_else(
|
||||||
|| self.calculate_stake(pubkey, self.epoch, Some(&self.stake_history)),
|
|| {
|
||||||
|
self.calculate_stake(
|
||||||
|
pubkey,
|
||||||
|
self.epoch,
|
||||||
|
Some(&self.stake_history),
|
||||||
|
fix_stake_deactivate,
|
||||||
|
)
|
||||||
|
},
|
||||||
|v| v.0,
|
|v| v.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -130,7 +145,7 @@ impl Stakes {
|
|||||||
let old_stake = self.stake_delegations.get(pubkey).map(|delegation| {
|
let old_stake = self.stake_delegations.get(pubkey).map(|delegation| {
|
||||||
(
|
(
|
||||||
delegation.voter_pubkey,
|
delegation.voter_pubkey,
|
||||||
delegation.stake(self.epoch, Some(&self.stake_history)),
|
delegation.stake(self.epoch, Some(&self.stake_history), fix_stake_deactivate),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -140,7 +155,11 @@ impl Stakes {
|
|||||||
(
|
(
|
||||||
delegation.voter_pubkey,
|
delegation.voter_pubkey,
|
||||||
if account.lamports != 0 {
|
if account.lamports != 0 {
|
||||||
delegation.stake(self.epoch, Some(&self.stake_history))
|
delegation.stake(
|
||||||
|
self.epoch,
|
||||||
|
Some(&self.stake_history),
|
||||||
|
fix_stake_deactivate,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
@ -264,44 +283,44 @@ pub mod tests {
|
|||||||
let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
|
let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
let stake = StakeState::stake_from(&stake_account).unwrap();
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(i, None)
|
stake.stake(i, None, true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
stake_account.lamports = 42;
|
stake_account.lamports = 42;
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(i, None)
|
stake.stake(i, None, true)
|
||||||
); // stays old stake, because only 10 is activated
|
); // stays old stake, because only 10 is activated
|
||||||
}
|
}
|
||||||
|
|
||||||
// activate more
|
// activate more
|
||||||
let (_stake_pubkey, mut stake_account) = create_stake_account(42, &vote_pubkey);
|
let (_stake_pubkey, mut stake_account) = create_stake_account(42, &vote_pubkey);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
let stake = StakeState::stake_from(&stake_account).unwrap();
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(i, None)
|
stake.stake(i, None, true)
|
||||||
); // now stake of 42 is activated
|
); // now stake of 42 is activated
|
||||||
}
|
}
|
||||||
|
|
||||||
stake_account.lamports = 0;
|
stake_account.lamports = 0;
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
||||||
@ -319,14 +338,14 @@ pub mod tests {
|
|||||||
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
|
|
||||||
let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
|
let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
|
||||||
create_staked_node_accounts(20);
|
create_staked_node_accounts(20);
|
||||||
|
|
||||||
stakes.store(&vote11_pubkey, &vote11_account);
|
stakes.store(&vote11_pubkey, &vote11_account, true);
|
||||||
stakes.store(&stake11_pubkey, &stake11_account);
|
stakes.store(&stake11_pubkey, &stake11_account, true);
|
||||||
|
|
||||||
let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
|
let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
|
||||||
|
|
||||||
@ -341,8 +360,8 @@ pub mod tests {
|
|||||||
let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
|
let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -351,14 +370,14 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vote_account.lamports = 0;
|
vote_account.lamports = 0;
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert!(vote_accounts.get(&vote_pubkey).is_none());
|
assert!(vote_accounts.get(&vote_pubkey).is_none());
|
||||||
}
|
}
|
||||||
vote_account.lamports = 1;
|
vote_account.lamports = 1;
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -378,11 +397,11 @@ pub mod tests {
|
|||||||
let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
|
let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&vote_pubkey2, &vote_account2);
|
stakes.store(&vote_pubkey2, &vote_account2, true);
|
||||||
|
|
||||||
// delegates to vote_pubkey
|
// delegates to vote_pubkey
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
|
|
||||||
let stake = StakeState::stake_from(&stake_account).unwrap();
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
||||||
|
|
||||||
@ -391,14 +410,14 @@ pub mod tests {
|
|||||||
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
stake.stake(stakes.epoch, Some(&stakes.stake_history), true)
|
||||||
);
|
);
|
||||||
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
||||||
assert_eq!(vote_accounts.get(&vote_pubkey2).unwrap().0, 0);
|
assert_eq!(vote_accounts.get(&vote_pubkey2).unwrap().0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delegates to vote_pubkey2
|
// delegates to vote_pubkey2
|
||||||
stakes.store(&stake_pubkey, &stake_account2);
|
stakes.store(&stake_pubkey, &stake_account2, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -407,7 +426,7 @@ pub mod tests {
|
|||||||
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey2).unwrap().0,
|
vote_accounts.get(&vote_pubkey2).unwrap().0,
|
||||||
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
stake.stake(stakes.epoch, Some(&stakes.stake_history), true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,11 +440,11 @@ pub mod tests {
|
|||||||
|
|
||||||
let (stake_pubkey2, stake_account2) = create_stake_account(10, &vote_pubkey);
|
let (stake_pubkey2, stake_account2) = create_stake_account(10, &vote_pubkey);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
|
|
||||||
// delegates to vote_pubkey
|
// delegates to vote_pubkey
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
stakes.store(&stake_pubkey2, &stake_account2);
|
stakes.store(&stake_pubkey2, &stake_account2, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -440,23 +459,23 @@ pub mod tests {
|
|||||||
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
let stake = StakeState::stake_from(&stake_account).unwrap();
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
stake.stake(stakes.epoch, Some(&stakes.stake_history), true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let stakes = stakes.clone_with_epoch(3);
|
let stakes = stakes.clone_with_epoch(3, true);
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vote_accounts.get(&vote_pubkey).unwrap().0,
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
||||||
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
stake.stake(stakes.epoch, Some(&stakes.stake_history), true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -469,8 +488,8 @@ pub mod tests {
|
|||||||
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
||||||
create_staked_node_accounts(10);
|
create_staked_node_accounts(10);
|
||||||
|
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -482,6 +501,7 @@ pub mod tests {
|
|||||||
stakes.store(
|
stakes.store(
|
||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Account::new(1, 0, &solana_stake_program::id()),
|
&Account::new(1, 0, &solana_stake_program::id()),
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let vote_accounts = stakes.vote_accounts();
|
let vote_accounts = stakes.vote_accounts();
|
||||||
@ -511,14 +531,14 @@ pub mod tests {
|
|||||||
let genesis_epoch = 0;
|
let genesis_epoch = 0;
|
||||||
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
||||||
create_warming_staked_node_accounts(10, genesis_epoch);
|
create_warming_staked_node_accounts(10, genesis_epoch);
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account, true);
|
||||||
stakes.store(&stake_pubkey, &stake_account);
|
stakes.store(&stake_pubkey, &stake_account, true);
|
||||||
|
|
||||||
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
||||||
assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
|
assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
|
||||||
|
|
||||||
for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
|
for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
|
||||||
stakes = stakes.clone_with_epoch(epoch);
|
stakes = stakes.clone_with_epoch(epoch, true);
|
||||||
// vote_balance_and_staked() always remain to return same lamports
|
// vote_balance_and_staked() always remain to return same lamports
|
||||||
// while vote_balance_and_warmed_staked() gradually increases
|
// while vote_balance_and_warmed_staked() gradually increases
|
||||||
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
||||||
|
@ -80,6 +80,7 @@ fn warmed_up(bank: &Bank, stake_pubkey: &Pubkey) -> bool {
|
|||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +95,7 @@ fn get_staked(bank: &Bank, stake_pubkey: &Pubkey) -> u64 {
|
|||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user