diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index ac9d6e75f5e993..3f02383e2a9f65 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -2030,7 +2030,7 @@ fn main() { epochs: usize, voter: Pubkey, point: u128, - stake: u128, + stake: u64, total_stake: u64, rent_exempt_reserve: u64, credits: u128, @@ -2055,17 +2055,18 @@ fn main() { match event { InflationPointCalculationEvent::CalculatedPoints( point, - stake, credits, ) => { // Don't sum for epochs where no credits are earned if *credits > 0 { detail.epochs += 1; detail.point += *point; - detail.stake += *stake; detail.credits += *credits; } } + InflationPointCalculationEvent::Stake(stake) => { + detail.stake = *stake; + } InflationPointCalculationEvent::SplitRewards( all, voter, diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index badded1b2f231e..df7731b738a753 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -33,7 +33,8 @@ pub enum StakeState { #[derive(Debug)] pub enum InflationPointCalculationEvent { - CalculatedPoints(u128, u128, u128), + CalculatedPoints(u128, u128), + Stake(u64), SplitRewards(u64, u64, u64, PointValue), RentExemptReserve(u64), Delegation(Delegation), @@ -467,14 +468,14 @@ impl Stake { &mut self, point_value: &PointValue, vote_state: &VoteState, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, inflation_point_calc_tracer: &mut Option, fix_stake_deactivate: bool, ) -> Option<(u64, u64)> { self.calculate_rewards( point_value, vote_state, - stake_history, + stake_context, inflation_point_calc_tracer, fix_stake_deactivate, ) @@ -488,13 +489,13 @@ impl Stake { pub fn calculate_points( &self, vote_state: &VoteState, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, inflation_point_calc_tracer: &mut Option, fix_stake_deactivate: bool, ) -> u128 { self.calculate_points_and_credits( vote_state, - stake_history, + stake_context, inflation_point_calc_tracer, fix_stake_deactivate, ) @@ -507,11 +508,23 @@ impl Stake { pub fn calculate_points_and_credits( &self, new_vote_state: &VoteState, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, inflation_point_calc_tracer: &mut Option, fix_stake_deactivate: bool, ) -> (u128, u64) { // if there is no newer credits since observed, return no point + let mut stake_at_target_epoch = None; + if let Some((target_epoch, stake_history)) = stake_context { + let stake = + self.delegation + .stake(target_epoch, Some(stake_history), fix_stake_deactivate); + stake_at_target_epoch = Some(stake); + + if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer { + inflation_point_calc_tracer(&InflationPointCalculationEvent::Stake(stake)); + } + } + if new_vote_state.credits() <= self.credits_observed { return (0, 0); } @@ -519,14 +532,18 @@ impl Stake { let mut points = 0; let mut new_credits_observed = self.credits_observed; - for (epoch, final_epoch_credits, initial_epoch_credits) in - new_vote_state.epoch_credits().iter().copied() + if let Some((epoch, final_epoch_credits, initial_epoch_credits)) = + new_vote_state.epoch_credits().last().copied() { - let stake = u128::from(self.delegation.stake( - epoch, - stake_history, - fix_stake_deactivate, - )); + let stake = if let Some((target_epoch, _stake_history)) = stake_context { + if epoch != target_epoch { + return (0, 0); + } + stake_at_target_epoch.unwrap() + } else { + self.delegation.stake(epoch, None, fix_stake_deactivate) + }; + let stake = u128::from(stake); // figure out how much this stake has seen that // for which the vote account has a record @@ -535,7 +552,7 @@ impl Stake { final_epoch_credits - initial_epoch_credits } else if self.credits_observed < final_epoch_credits { // the staker registered sometime during the epoch, partial credit - final_epoch_credits - new_credits_observed + final_epoch_credits - self.credits_observed } else { // the staker has already observed or been redeemed this epoch // or was activated after this epoch @@ -543,8 +560,8 @@ impl Stake { }; let earned_credits = u128::from(earned_credits); - // don't want to assume anything about order of the iterator... - new_credits_observed = new_credits_observed.max(final_epoch_credits); + // try to update credits_observed + new_credits_observed = final_epoch_credits; // finally calculate points for this epoch points += stake * earned_credits; @@ -552,7 +569,6 @@ impl Stake { if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer { inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints( points, - stake, earned_credits, )); } @@ -571,13 +587,13 @@ impl Stake { &self, point_value: &PointValue, vote_state: &VoteState, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, inflation_point_calc_tracer: &mut Option, fix_stake_deactivate: bool, ) -> Option<(u64, u64, u64)> { let (points, credits_observed) = self.calculate_points_and_credits( vote_state, - stake_history, + stake_context, inflation_point_calc_tracer, fix_stake_deactivate, ); @@ -1100,7 +1116,7 @@ pub fn redeem_rewards( stake_account: &mut Account, vote_account: &mut Account, point_value: &PointValue, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, inflation_point_calc_tracer: &mut Option, fix_stake_deactivate: bool, ) -> Result<(u64, u64), InstructionError> { @@ -1119,7 +1135,7 @@ pub fn redeem_rewards( if let Some((stakers_reward, voters_reward)) = stake.redeem_rewards( point_value, &vote_state, - stake_history, + stake_context, inflation_point_calc_tracer, fix_stake_deactivate, ) { @@ -1141,7 +1157,7 @@ pub fn redeem_rewards( pub fn calculate_points( stake_account: &Account, vote_account: &Account, - stake_history: Option<&StakeHistory>, + stake_context: Option<(Epoch, &StakeHistory)>, fix_stake_deactivate: bool, ) -> Result { if let StakeState::Stake(_meta, stake) = stake_account.state()? { @@ -1150,7 +1166,7 @@ pub fn calculate_points( Ok(stake.calculate_points( &vote_state, - stake_history, + stake_context, &mut null_tracer(), fix_stake_deactivate, )) @@ -2853,13 +2869,17 @@ mod tests { ) ); + let mut current_credits = 0; + // put 2 credits in at epoch 0 vote_state.increment_credits(0); + current_credits += 1; vote_state.increment_credits(0); + current_credits += 1; // this one should be able to collect exactly 2 assert_eq!( - Some((stake.delegation.stake * 2, 0, 2)), + Some((stake.delegation.stake * 2, 0, current_credits)), stake.calculate_rewards( &PointValue { rewards: 2, @@ -2875,7 +2895,7 @@ mod tests { stake.credits_observed = 1; // this one should be able to collect exactly 1 (already observed one) assert_eq!( - Some((stake.delegation.stake, 0, 2)), + Some((stake.delegation.stake, 0, current_credits)), stake.calculate_rewards( &PointValue { rewards: 1, @@ -2890,11 +2910,12 @@ mod tests { // put 1 credit in epoch 1 vote_state.increment_credits(1); + current_credits += 1; stake.credits_observed = 2; // this one should be able to collect the one just added assert_eq!( - Some((stake.delegation.stake, 0, 3)), + Some((stake.delegation.stake, 0, current_credits)), stake.calculate_rewards( &PointValue { rewards: 2, @@ -2909,9 +2930,12 @@ mod tests { // put 1 credit in epoch 2 vote_state.increment_credits(2); + current_credits += 1; + vote_state.increment_credits(2); + current_credits += 1; // this one should be able to collect 2 now assert_eq!( - Some((stake.delegation.stake * 2, 0, 4)), + Some((stake.delegation.stake * 2, 0, current_credits)), stake.calculate_rewards( &PointValue { rewards: 2, @@ -2925,15 +2949,13 @@ mod tests { ); stake.credits_observed = 0; - // this one should be able to collect everything from t=0 a warmed up stake of 2 - // (2 credits at stake of 1) + (1 credit at a stake of 2) + // even if creidts_observed is so small, + // this one should collect only the last epoch assert_eq!( Some(( - stake.delegation.stake * 2 // epoch 0 - + stake.delegation.stake // epoch 1 - + stake.delegation.stake, // epoch 2 + stake.delegation.stake * 2, // only epoch 2 0, - 4 + current_credits )), stake.calculate_rewards( &PointValue { @@ -2951,7 +2973,7 @@ mod tests { // verify that None comes back on small redemptions where no one gets paid vote_state.commission = 1; assert_eq!( - None, // would be Some((0, 2 * 1 + 1 * 2, 4)), + None, // would be Some((0, 2 * 1 + 1 * 2, current_credits)), stake.calculate_rewards( &PointValue { rewards: 4, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 6630a186928e8a..534f641a9984e3 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1324,6 +1324,7 @@ impl Bank { let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked(); let validator_point_value = self.pay_validator_rewards( + prev_epoch, validator_rewards, reward_calc_tracer, self.stake_program_v2_enabled(), @@ -1438,6 +1439,7 @@ impl Bank { /// successfully load and parse, return the lamport value of one point fn pay_validator_rewards( &mut self, + target_epoch: Epoch, rewards: u64, reward_calc_tracer: &mut Option, fix_stake_deactivate: bool, @@ -1457,7 +1459,7 @@ impl Bank { stake_state::calculate_points( &stake_account, &vote_account, - Some(&stake_history), + Some((target_epoch, &stake_history)), fix_stake_deactivate, ) .unwrap_or(0) @@ -1489,7 +1491,7 @@ impl Bank { stake_account, vote_account, &point_value, - Some(&stake_history), + Some((target_epoch, &stake_history)), &mut reward_calc_tracer.as_mut(), fix_stake_deactivate, );