Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simd 118 recalc #375

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion accounts-db/src/partitioned_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Default for PartitionedEpochRewardsConfig {
// Target to store 64 rewards per entry/tick in a block. A block has a minimum of 64
// entries/tick. This gives 4096 total rewards to store in one block.
// This constant affects consensus.
stake_account_stores_per_block: 4096,
stake_account_stores_per_block: 4,
test_enable_partitioned_rewards: false,
test_compare_partitioned_epoch_rewards: false,
}
Expand Down
96 changes: 82 additions & 14 deletions programs/stake/src/points.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,72 @@ pub(crate) fn calculate_stake_points_and_credits(
let credits_in_stake = stake.credits_observed;
let credits_in_vote = new_vote_state.credits();
// if there is no newer credits since observed, return no point
if let Some(result) = compare_stake_vote_credits(
credits_in_vote,
credits_in_stake,
&inflation_point_calc_tracer,
) {
return result;
}

let (points, new_credits_observed) = handle_epoch_credits(
new_vote_state.epoch_credits().iter().copied(),
stake,
stake_history,
new_rate_activation_epoch,
&inflation_point_calc_tracer,
);

CalculatedStakePoints {
points,
new_credits_observed,
force_credits_update_with_skipped_reward: false,
}
}

pub(crate) fn calculate_stake_points_and_credits_through_epoch(
max_epoch: Epoch,
stake: &Stake,
new_vote_state: &VoteState,
stake_history: &StakeHistory,
inflation_point_calc_tracer: Option<impl Fn(&InflationPointCalculationEvent)>,
new_rate_activation_epoch: Option<Epoch>,
) -> CalculatedStakePoints {
let credits_in_stake = stake.credits_observed;
let credits_in_vote = new_vote_state.credits_for_recent_epoch(max_epoch);
// if there is no newer credits since observed, return no point
if let Some(result) = compare_stake_vote_credits(
credits_in_vote,
credits_in_stake,
&inflation_point_calc_tracer,
) {
return result;
}

let (points, new_credits_observed) = handle_epoch_credits(
new_vote_state
.epoch_credits()
.iter()
.take_while(|(epoch, _, _)| *epoch <= max_epoch)
.copied(),
stake,
stake_history,
new_rate_activation_epoch,
&inflation_point_calc_tracer,
);

CalculatedStakePoints {
points,
new_credits_observed,
force_credits_update_with_skipped_reward: false,
}
}

fn compare_stake_vote_credits(
credits_in_vote: u64,
credits_in_stake: u64,
inflation_point_calc_tracer: &Option<impl Fn(&InflationPointCalculationEvent)>,
) -> Option<CalculatedStakePoints> {
match credits_in_vote.cmp(&credits_in_stake) {
Ordering::Less => {
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer.as_ref() {
Expand All @@ -139,32 +205,39 @@ pub(crate) fn calculate_stake_points_and_credits(
// delinquent validator with no differentiation.

// hint with true to indicate some exceptional credits handling is needed
return CalculatedStakePoints {
Some(CalculatedStakePoints {
points: 0,
new_credits_observed: credits_in_vote,
force_credits_update_with_skipped_reward: true,
};
})
}
Ordering::Equal => {
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer.as_ref() {
inflation_point_calc_tracer(&SkippedReason::ZeroCreditsAndReturnCurrent.into());
}
// don't hint caller and return current value if credits remain unchanged (= delinquent)
return CalculatedStakePoints {
Some(CalculatedStakePoints {
points: 0,
new_credits_observed: credits_in_stake,
force_credits_update_with_skipped_reward: false,
};
})
}
Ordering::Greater => {}
Ordering::Greater => None,
}
}

fn handle_epoch_credits(
epoch_credits_iter: impl Iterator<Item = (Epoch, u64, u64)>,
stake: &Stake,
stake_history: &StakeHistory,
new_rate_activation_epoch: Option<Epoch>,
inflation_point_calc_tracer: &Option<impl Fn(&InflationPointCalculationEvent)>,
) -> (u128, u64) {
let credits_in_stake = stake.credits_observed;
let mut points = 0;
let mut new_credits_observed = credits_in_stake;

for (epoch, final_epoch_credits, initial_epoch_credits) in
new_vote_state.epoch_credits().iter().copied()
{
for (epoch, final_epoch_credits, initial_epoch_credits) in epoch_credits_iter {
let stake_amount = u128::from(stake.delegation.stake(
epoch,
stake_history,
Expand Down Expand Up @@ -202,12 +275,7 @@ pub(crate) fn calculate_stake_points_and_credits(
));
}
}

CalculatedStakePoints {
points,
new_credits_observed,
force_credits_update_with_skipped_reward: false,
}
(points, new_credits_observed)
}

#[cfg(test)]
Expand Down
12 changes: 8 additions & 4 deletions programs/stake/src/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use {
crate::points::{
calculate_stake_points_and_credits, CalculatedStakePoints, InflationPointCalculationEvent,
PointValue, SkippedReason,
calculate_stake_points_and_credits_through_epoch, CalculatedStakePoints,
InflationPointCalculationEvent, PointValue, SkippedReason,
},
solana_sdk::{
account::{AccountSharedData, WritableAccount},
Expand Down Expand Up @@ -138,7 +138,8 @@ fn calculate_stake_rewards(
points,
new_credits_observed,
mut force_credits_update_with_skipped_reward,
} = calculate_stake_points_and_credits(
} = calculate_stake_points_and_credits_through_epoch(
rewarded_epoch,
stake,
vote_state,
stake_history,
Expand Down Expand Up @@ -228,7 +229,10 @@ fn calculate_stake_rewards(
mod tests {
use {
super::*,
crate::{points::null_tracer, stake_state::new_stake},
crate::{
points::{calculate_stake_points_and_credits, null_tracer},
stake_state::new_stake,
},
solana_sdk::{native_token, pubkey::Pubkey},
};

Expand Down
54 changes: 54 additions & 0 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,60 @@ impl Bank {
.fill_missing_sysvar_cache_entries(&bank);
bank.rebuild_skipped_rewrites();

let epoch_rewards_sysvar = bank.get_epoch_rewards_sysvar();
warn!("{:?}", epoch_rewards_sysvar);
if epoch_rewards_sysvar.active {
use crate::bank::partitioned_epoch_rewards::StartBlockHeightAndRewards;
let (thread_pool, _thread_pool_time) = measure!(
ThreadPoolBuilder::new()
.thread_name(|i| format!("solBnkNewFlds{i:02}"))
.build()
.expect("new rayon threadpool"),
"thread_pool_creation",
);
let mut rewards_metrics = RewardsMetrics::default();
let recalculated = bank.recalculate_partitions(
bank.epoch() - 1,
null_tracer(),
&thread_pool,
&mut rewards_metrics,
);
let alternate_epoch_reward_status =
EpochRewardStatus::Active(StartBlockHeightAndRewards {
start_block_height: epoch_rewards_sysvar.distribution_starting_block_height - 1,
stake_rewards_by_partition: Arc::new(recalculated),
});
warn!("SLOT {:?}", bank.slot());
warn!("BLOCK HEIGHT {:?}", bank.block_height());
warn!("rewards {:?}", bank.rewards);
warn!(
"{:?}",
alternate_epoch_reward_status == bank.epoch_reward_status
);
if let EpochRewardStatus::Active(StartBlockHeightAndRewards {
stake_rewards_by_partition,
..
}) = alternate_epoch_reward_status {
warn!("RECALC num_partitions {:?}", stake_rewards_by_partition.len());
for parition in stake_rewards_by_partition.iter() {
warn!("RECALC {:?}", parition.len());
}
}
// warn!("RECALC {:?}", alternate_epoch_reward_status);
if let EpochRewardStatus::Active(StartBlockHeightAndRewards {
stake_rewards_by_partition,
..
}) = &bank.epoch_reward_status {
warn!("bank num_partitions {:?}", stake_rewards_by_partition.len());
for parition in stake_rewards_by_partition.iter() {
warn!("bank {:?}", parition.len());
}
}
// warn!("bank {:?}", bank.epoch_reward_status);
} else {
warn!("rewards period not active");
}

// Sanity assertions between bank snapshot and genesis config
// Consider removing from serializable bank state
// (BankFieldsToSerialize/BankFieldsToDeserialize) and initializing
Expand Down
46 changes: 45 additions & 1 deletion runtime/src/bank/partitioned_epoch_rewards/calculation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use {
epoch_rewards_hasher::hash_rewards_into_partitions, Bank,
CalculateRewardsAndDistributeVoteRewardsResult, CalculateValidatorRewardsResult,
EpochRewardCalculateParamInfo, PartitionedRewardsCalculation, StakeRewardCalculation,
StakeRewardCalculationPartitioned, VoteRewardsAccounts,
StakeRewardCalculationPartitioned, StakeRewards, VoteRewardsAccounts,
},
crate::bank::{
PrevEpochInflationRewards, RewardCalcTracer, RewardCalculationEvent, RewardsMetrics,
Expand Down Expand Up @@ -364,6 +364,7 @@ impl Bank {
reward_calc_tracer.as_ref(),
new_warmup_cooldown_rate_epoch,
);
log::warn!("redeemed {:?}", redeemed);

let post_lamport = stake_account.lamports();

Expand Down Expand Up @@ -485,6 +486,49 @@ impl Bank {

(points > 0).then_some(PointValue { rewards, points })
}

pub(in crate::bank) fn recalculate_partitions(
&self,
rewarded_epoch: Epoch,
reward_calc_tracer: Option<impl RewardCalcTracer>,
thread_pool: &ThreadPool,
metrics: &mut RewardsMetrics,
) -> Vec<StakeRewards> {
let epoch_rewards_sysvar = self.get_epoch_rewards_sysvar();
if !epoch_rewards_sysvar.active {
return vec![];
}

let point_value = PointValue {
rewards: epoch_rewards_sysvar.total_rewards,
points: epoch_rewards_sysvar.total_points,
};

// TODO: will these stakes be unmolested enough?
let stakes = self.stakes_cache.stakes();
// TODO: update this?
let reward_calculate_param = self.get_epoch_reward_calculate_param_info(&stakes);

let (
_,
StakeRewardCalculation {
mut stake_rewards, ..
},
) = self.calculate_stake_vote_rewards(
&reward_calculate_param,
rewarded_epoch,
point_value,
thread_pool,
reward_calc_tracer,
metrics,
);
log::warn!("recalc stake_rewards {:?}", stake_rewards.len());
hash_rewards_into_partitions(
std::mem::take(&mut stake_rewards),
&epoch_rewards_sysvar.parent_blockhash,
epoch_rewards_sysvar.num_partitions as usize,
)
}
}

#[cfg(test)]
Expand Down
4 changes: 1 addition & 3 deletions runtime/src/bank/partitioned_epoch_rewards/sysvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ impl Bank {

/// Get EpochRewards sysvar. Returns EpochRewards::default() if sysvar
/// account cannot be found or cannot be deserialized.
pub(in crate::bank::partitioned_epoch_rewards) fn get_epoch_rewards_sysvar(
&self,
) -> sysvar::epoch_rewards::EpochRewards {
pub(in crate::bank) fn get_epoch_rewards_sysvar(&self) -> sysvar::epoch_rewards::EpochRewards {
from_account(
&self
.get_account(&sysvar::epoch_rewards::id())
Expand Down
12 changes: 12 additions & 0 deletions sdk/program/src/vote/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,18 @@ impl VoteState {
}
}

/// Number of "credits" owed to this account for a particular epoch.
/// Iterates VoteState::epoch_credits in reverse order, so will be most
/// performant for a recent epoch. Returns zero if epoch cannot be found.
pub fn credits_for_recent_epoch(&self, desired_epoch: Epoch) -> u64 {
for (epoch, credits, _prev_credits) in self.epoch_credits.iter().rev() {
if *epoch == desired_epoch {
return *credits;
}
}
return 0;
}

/// Number of "credits" owed to this account from the mining pool on a per-epoch basis,
/// starting from credits observed.
/// Each tuple of (Epoch, u64, u64) is read as (epoch, credits, prev_credits), where
Expand Down
5 changes: 1 addition & 4 deletions validator/src/bin/solana-test-validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use {
account::AccountSharedData,
clock::Slot,
epoch_schedule::EpochSchedule,
feature_set,
native_token::sol_to_lamports,
pubkey::Pubkey,
rent::Rent,
Expand Down Expand Up @@ -352,9 +351,7 @@ fn main() {
exit(1);
});

let mut features_to_deactivate = pubkeys_of(&matches, "deactivate_feature").unwrap_or_default();
// Remove this when client support is ready for the enable_partitioned_epoch_reward feature
features_to_deactivate.push(feature_set::enable_partitioned_epoch_reward::id());
let features_to_deactivate = pubkeys_of(&matches, "deactivate_feature").unwrap_or_default();

if TestValidatorGenesis::ledger_exists(&ledger_path) {
for (name, long) in &[
Expand Down