diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 73b860c0db..90a6ebad45 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -407,37 +407,45 @@ impl Stake { /// for credits_observed were the points paid pub fn calculate_points_and_credits( &self, - vote_state: &VoteState, + new_vote_state: &VoteState, stake_history: Option<&StakeHistory>, ) -> (u128, u64) { - if self.credits_observed >= vote_state.credits() { + // if there is no newer credits since observed, return no point + if new_vote_state.credits() <= self.credits_observed { return (0, 0); } - let mut credits_observed = self.credits_observed; - let mut points = 0u128; - for (epoch, credits, prev_credits) in vote_state.epoch_credits() { + 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() + { + let stake = u128::from(self.delegation.stake(epoch, stake_history)); + // figure out how much this stake has seen that // for which the vote account has a record - let epoch_credits = if self.credits_observed < *prev_credits { + let earned_credits = if self.credits_observed < initial_epoch_credits { // the staker observed the entire epoch - credits - prev_credits - } else if self.credits_observed < *credits { + final_epoch_credits - initial_epoch_credits + } else if self.credits_observed < final_epoch_credits { // the staker registered sometime during the epoch, partial credit - credits - credits_observed + final_epoch_credits - new_credits_observed } else { // the staker has already observed or been redeemed this epoch // or was activated after this epoch 0 }; - - points += u128::from(self.delegation.stake(*epoch, stake_history)) - * u128::from(epoch_credits); + let earned_credits = u128::from(earned_credits); // don't want to assume anything about order of the iterator... - credits_observed = credits_observed.max(*credits); + new_credits_observed = new_credits_observed.max(final_epoch_credits); + + // finally calculate points for this epoch + points += stake * earned_credits; } - (points, credits_observed) + + (points, new_credits_observed) } /// for a given stake and vote_state, calculate what distributions and what updates should be made diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 9e3801a150..9b61d84727 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -30,8 +30,7 @@ pub const MAX_LOCKOUT_HISTORY: usize = 31; pub const INITIAL_LOCKOUT: usize = 2; // Maximum number of credits history to keep around -// smaller numbers makes -pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64; +const MAX_EPOCH_CREDITS_HISTORY: usize = 64; #[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")] #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)] @@ -392,8 +391,7 @@ impl VoteState { self.epoch_credits.last_mut().unwrap().0 = epoch; } - // if stakers do not claim before the epoch goes away they lose the - // credits... + // Remove too old epoch_credits if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY { self.epoch_credits.remove(0); } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 503326268d..e197a9712f 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1280,7 +1280,7 @@ impl Bank { let validator_rewards = (validator_rate * capitalization as f64 * epoch_duration_in_years) as u64; - let 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 = self.pay_validator_rewards(validator_rewards); @@ -1297,8 +1297,8 @@ impl Bank { }); } - let validator_rewards_paid = - self.stakes.read().unwrap().vote_balance_and_staked() - vote_balance_and_staked; + let new_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked(); + let validator_rewards_paid = new_vote_balance_and_staked - old_vote_balance_and_staked; assert_eq!( validator_rewards_paid, u64::try_from( @@ -6068,7 +6068,7 @@ mod tests { // The same reward should be distributed given same credits let expected_capitalization = do_test_bank_update_rewards_determinism(); // Repeat somewhat large number of iterations to expose possible different behavior - // depending on the randamly-seeded HashMap ordering + // depending on the randomly-seeded HashMap ordering for _ in 0..30 { let actual_capitalization = do_test_bank_update_rewards_determinism(); assert_eq!(actual_capitalization, expected_capitalization);