Skip to content

Commit

Permalink
Merge pull request #1 from ebatsell/field-macros
Browse files Browse the repository at this point in the history
Utilities for accessing validator history state
  • Loading branch information
ebatsell authored Jan 3, 2024
2 parents d3f1e9f + 19dd1cf commit 2d595f6
Showing 1 changed file with 144 additions and 0 deletions.
144 changes: 144 additions & 0 deletions programs/validator-history/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@ impl Default for CircBuf {
}
}

macro_rules! field_latest {
($self:expr, $field:ident) => {
if let Some(entry) = $self.last() {
if entry.$field != ValidatorHistoryEntry::default().$field {
return Some(entry.$field);
} else {
None
}
} else {
None
}
};
}

macro_rules! field_range {
($self:expr, $start_epoch:expr, $end_epoch:expr, $field:ident, $type:ty) => {{
let epoch_range = $self
.epoch_range($start_epoch, $end_epoch)
.unwrap_or_default();
epoch_range
.iter()
.map(|entry| {
if entry.$field != ValidatorHistoryEntry::default().$field {
Some(entry.$field)
} else {
None
}
})
.collect::<Vec<Option<$type>>>()
}};
}

impl CircBuf {
pub fn push(&mut self, item: ValidatorHistoryEntry) {
self.idx = (self.idx + 1) % self.arr.len() as u64;
Expand Down Expand Up @@ -148,6 +180,69 @@ impl CircBuf {
pub fn arr_mut(&mut self) -> &mut [ValidatorHistoryEntry] {
&mut self.arr
}

pub fn epoch_range(
&self,
start_epoch: u16,
end_epoch: u16,
) -> Option<Vec<&ValidatorHistoryEntry>> {
// Returns &ValidatorHistoryEntry for each existing entry in range [start_epoch, end_epoch], factoring for wraparound
// Returns None if either start_epoch or end_epoch is not in the CircBuf
let start_epoch_index = self
.arr
.iter()
.position(|entry| entry.epoch == start_epoch)?;

let mut end_epoch_index = self.arr.iter().position(|entry| entry.epoch == end_epoch)?;

if start_epoch_index > end_epoch_index {
end_epoch_index += self.arr.len();
}

let epoch_range: Vec<&ValidatorHistoryEntry> = (start_epoch_index..=end_epoch_index)
.map(|i| &self.arr[i % self.arr.len()])
.collect();

Some(epoch_range)
}

pub fn commission_latest(&self) -> Option<u8> {
field_latest!(self, commission)
}

pub fn commission_range(&self, start_epoch: u16, end_epoch: u16) -> Vec<Option<u8>> {
field_range!(self, start_epoch, end_epoch, commission, u8)
}

pub fn mev_commission_latest(&self) -> Option<u16> {
field_latest!(self, mev_commission)
}

pub fn mev_commission_range(&self, start_epoch: u16, end_epoch: u16) -> Vec<Option<u16>> {
field_range!(self, start_epoch, end_epoch, mev_commission, u16)
}

pub fn epoch_credits_latest(&self) -> Option<u32> {
field_latest!(self, epoch_credits)
}

pub fn epoch_credits_range(&self, start_epoch: u16, end_epoch: u16) -> Vec<Option<u32>> {
field_range!(self, start_epoch, end_epoch, epoch_credits, u32)
}

pub fn superminority_latest(&self) -> Option<u8> {
// Protect against unexpected values
if let Some(value) = field_latest!(self, is_superminority) {
if value == 0 || value == 1 {
return Some(value);
}
}
None
}

pub fn vote_account_last_update_slot_latest(&self) -> Option<u64> {
field_latest!(self, vote_account_last_update_slot)
}
}

pub enum ValidatorHistoryVersion {
Expand Down Expand Up @@ -454,4 +549,53 @@ mod tests {
fn test_validator_history_layout() {
println!("{}", ValidatorHistoryEntry::type_layout());
}

#[test]
fn test_epoch_range() {
// Add in 4 CircBuf entries, with epoch 0, 1, 2, 3
let mut circ_buf = CircBuf::default();
for i in 0..4 {
let entry = ValidatorHistoryEntry {
epoch: i,
..ValidatorHistoryEntry::default()
};
circ_buf.push(entry);
}
// Test epoch range [0, 3]
let epoch_range = circ_buf.epoch_range(0, 3).unwrap();
assert_eq!(epoch_range.len(), 4);

// skip an epoch in the middle
circ_buf = CircBuf::default();
for i in 0..4 {
if i == 2 {
continue;
}
let entry = ValidatorHistoryEntry {
epoch: i,
..ValidatorHistoryEntry::default()
};
circ_buf.push(entry);
}

// Test epoch range [0, 3]
let epoch_range = circ_buf.epoch_range(0, 3).unwrap();
assert_eq!(epoch_range.len(), 3);

// same start and end epoch
let epoch_range = circ_buf.epoch_range(0, 0).unwrap();
assert_eq!(epoch_range.len(), 1);

// Test start epoch out of range
let epoch_range = circ_buf.epoch_range(4, 3);
assert!(epoch_range.is_none());

// Test end epoch out of range
let epoch_range = circ_buf.epoch_range(0, 5);
assert!(epoch_range.is_none());

// None if start epoch is none
let epoch_range = circ_buf.epoch_range(2, 3);
assert!(epoch_range.is_none());
}
}

0 comments on commit 2d595f6

Please sign in to comment.