use crate::constants::{
INACTIVITY_PENALTY_QUOTIENT, INACTIVITY_SCORE_BIAS, INACTIVITY_SCORE_RECOVERY_RATE,
};
use crate::participation::ParticipationTracker;
use crate::traits::EffectiveBalanceView;
#[derive(Debug, Clone)]
pub struct InactivityScoreTracker {
scores: Vec<u64>,
}
impl InactivityScoreTracker {
#[must_use]
pub fn new(validator_count: usize) -> Self {
Self {
scores: vec![0u64; validator_count],
}
}
#[must_use]
pub fn score(&self, validator_index: u32) -> Option<u64> {
self.scores.get(validator_index as usize).copied()
}
#[must_use]
pub fn validator_count(&self) -> usize {
self.scores.len()
}
pub fn set_score(&mut self, validator_index: u32, score: u64) -> bool {
if let Some(s) = self.scores.get_mut(validator_index as usize) {
*s = score;
true
} else {
false
}
}
pub fn update_for_epoch(
&mut self,
participation: &ParticipationTracker,
in_finality_stall: bool,
) {
let n = self.scores.len().min(participation.validator_count());
for i in 0..n {
let idx = i as u32;
let flags = participation.previous_flags(idx).unwrap_or_default();
if flags.is_target_timely() {
self.scores[i] = self.scores[i].saturating_sub(1);
} else if in_finality_stall {
self.scores[i] = self.scores[i].saturating_add(INACTIVITY_SCORE_BIAS);
}
}
if !in_finality_stall {
for score in &mut self.scores {
*score = score.saturating_sub(INACTIVITY_SCORE_RECOVERY_RATE);
}
}
}
#[must_use]
pub fn epoch_penalties(
&self,
effective_balances: &dyn EffectiveBalanceView,
in_finality_stall: bool,
) -> Vec<(u32, u64)> {
if !in_finality_stall {
return Vec::new();
}
let mut out: Vec<(u32, u64)> = Vec::new();
for (i, &score) in self.scores.iter().enumerate() {
if score == 0 {
continue;
}
let idx = i as u32;
let eff_bal = effective_balances.get(idx);
let penalty = (u128::from(eff_bal) * u128::from(score)
/ u128::from(INACTIVITY_PENALTY_QUOTIENT)) as u64;
if penalty > 0 {
out.push((idx, penalty));
}
}
out
}
pub fn rewind_on_reorg(&mut self, depth: u64) -> u64 {
if depth == 0 {
return 0;
}
for score in &mut self.scores {
*score = 0;
}
depth
}
pub fn resize_for(&mut self, validator_count: usize) {
self.scores.resize(validator_count, 0);
}
}