diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 0eadf93c2d..9167e5de05 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -406,37 +406,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 2def6907e2..34d76e0e92 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -29,8 +29,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 = "69hYtmmcuqPbhpc64ZaNJDidaUcg66CW6wzPFiuYZ3To")] #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)] @@ -388,8 +387,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 dd1bb19d73..e3c53bc66a 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1224,7 +1224,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); @@ -1241,8 +1241,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( @@ -5938,7 +5938,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);