use crate::participation::error::ParticipationError;
use crate::participation::flags::ParticipationFlags;
#[derive(Debug, Clone)]
pub struct ParticipationTracker {
current_epoch: Vec<ParticipationFlags>,
previous_epoch: Vec<ParticipationFlags>,
current_epoch_number: u64,
}
impl ParticipationTracker {
#[must_use]
pub fn new(validator_count: usize, initial_epoch: u64) -> Self {
Self {
current_epoch: vec![ParticipationFlags::default(); validator_count],
previous_epoch: vec![ParticipationFlags::default(); validator_count],
current_epoch_number: initial_epoch,
}
}
#[must_use]
pub fn current_epoch_number(&self) -> u64 {
self.current_epoch_number
}
#[must_use]
pub fn current_flags(&self, validator_index: u32) -> Option<ParticipationFlags> {
self.current_epoch.get(validator_index as usize).copied()
}
#[must_use]
pub fn previous_flags(&self, validator_index: u32) -> Option<ParticipationFlags> {
self.previous_epoch.get(validator_index as usize).copied()
}
#[must_use]
pub fn validator_count(&self) -> usize {
self.current_epoch.len()
}
pub fn rotate_epoch(&mut self, new_epoch: u64, validator_count: usize) {
std::mem::swap(&mut self.previous_epoch, &mut self.current_epoch);
self.current_epoch.clear();
self.current_epoch
.resize(validator_count, ParticipationFlags::default());
self.current_epoch_number = new_epoch;
}
pub fn rewind_on_reorg(&mut self, new_tip_epoch: u64, validator_count: usize) -> u64 {
let dropped = self.current_epoch_number.saturating_sub(new_tip_epoch);
if dropped == 0 {
return 0;
}
self.rotate_epoch(new_tip_epoch, validator_count);
self.previous_epoch.clear();
self.previous_epoch
.resize(validator_count, ParticipationFlags::default());
dropped
}
pub fn record_attestation(
&mut self,
_data: &crate::evidence::attestation_data::AttestationData,
attesting_indices: &[u32],
flags: ParticipationFlags,
) -> Result<(), ParticipationError> {
for w in attesting_indices.windows(2) {
if w[0] == w[1] {
return Err(ParticipationError::DuplicateIndex(w[0]));
}
if w[0] > w[1] {
return Err(ParticipationError::NonAscendingIndices);
}
}
for idx in attesting_indices {
let i = *idx as usize;
if i >= self.current_epoch.len() {
return Err(ParticipationError::IndexOutOfRange(*idx));
}
self.current_epoch[i].0 |= flags.0;
}
Ok(())
}
}