use crate::types::{
ArchivedValidatorField, ArchivedValidatorsDiff, ValidatorField, ValidatorPatch, ValidatorsDiff,
MIN_VALIDATOR_WITHDRAWABILITY_DELAY, VALIDATOR_SSZ_SIZE,
};
pub trait ValidatorSnapshot {
fn withdrawal_credentials(&self) -> &[u8; 32];
fn effective_balance(&self) -> u64;
fn is_slashed(&self) -> bool;
fn activation_eligibility_epoch(&self) -> u64;
fn activation_epoch(&self) -> u64;
fn exit_epoch(&self) -> u64;
fn withdrawable_epoch(&self) -> u64;
fn to_ssz_bytes(&self) -> Vec<u8>;
}
pub trait ValidatorMut {
fn is_slashed(&self) -> bool;
fn set_withdrawal_credentials(&mut self, value: &[u8; 32]);
fn set_effective_balance(&mut self, value: u64);
fn set_slashed(&mut self, value: bool);
fn set_activation_eligibility_epoch(&mut self, value: u64);
fn set_activation_epoch(&mut self, value: u64);
fn set_exit_epoch(&mut self, value: u64);
fn set_withdrawable_epoch(&mut self, value: u64);
}
pub trait ValidatorMutTarget {
type Validator<'a>: ValidatorMut
where
Self: 'a;
fn get_mut(&mut self, index: usize) -> Option<Self::Validator<'_>>;
fn push_from_ssz(&mut self, ssz_bytes: &[u8]);
}
pub fn diff_validators(base_bytes: &[u8], target_bytes: &[u8]) -> ValidatorsDiff {
diff_validators_impl(
base_bytes
.chunks_exact(VALIDATOR_SSZ_SIZE)
.map(ByteValidator),
target_bytes
.chunks_exact(VALIDATOR_SSZ_SIZE)
.map(ByteValidator),
)
}
pub fn diff_validators_iter<I1, I2, V1, V2>(base: I1, target: I2) -> ValidatorsDiff
where
I1: ExactSizeIterator<Item = V1>,
I2: ExactSizeIterator<Item = V2>,
V1: ValidatorSnapshot,
V2: ValidatorSnapshot,
{
diff_validators_impl(base, target)
}
fn diff_validators_impl<I1, I2, V1, V2>(mut base: I1, mut target: I2) -> ValidatorsDiff
where
I1: ExactSizeIterator<Item = V1>,
I2: ExactSizeIterator<Item = V2>,
V1: ValidatorSnapshot,
V2: ValidatorSnapshot,
{
let common_len = base.len().min(target.len());
let mut patches = Vec::with_capacity(512);
let mut appended_validators = Vec::new();
for i in 0..common_len {
let b = base.next().unwrap();
let t = target.next().unwrap();
let wc = t.withdrawal_credentials();
let eb = t.effective_balance();
let slashed = t.is_slashed();
let aee = t.activation_eligibility_epoch();
let ae = t.activation_epoch();
let ee = t.exit_epoch();
if b.withdrawal_credentials() == wc
&& b.effective_balance() == eb
&& b.is_slashed() == slashed
&& b.activation_eligibility_epoch() == aee
&& b.activation_epoch() == ae
&& b.exit_epoch() == ee
&& (!slashed || b.withdrawable_epoch() == t.withdrawable_epoch())
{
continue;
}
let index = i as u32;
if b.withdrawal_credentials() != wc {
patches.push(ValidatorPatch {
index,
field: ValidatorField::WithdrawalCredentials,
value: wc.to_vec(),
});
}
if b.effective_balance() != eb {
patches.push(ValidatorPatch {
index,
field: ValidatorField::EffectiveBalance,
value: eb.to_le_bytes().to_vec(),
});
}
if b.is_slashed() != slashed {
patches.push(ValidatorPatch {
index,
field: ValidatorField::Slashed,
value: vec![slashed as u8],
});
}
if b.activation_eligibility_epoch() != aee {
patches.push(ValidatorPatch {
index,
field: ValidatorField::ActivationEligibilityEpoch,
value: aee.to_le_bytes().to_vec(),
});
}
if b.activation_epoch() != ae {
patches.push(ValidatorPatch {
index,
field: ValidatorField::ActivationEpoch,
value: ae.to_le_bytes().to_vec(),
});
}
if b.exit_epoch() != ee {
patches.push(ValidatorPatch {
index,
field: ValidatorField::ExitEpoch,
value: ee.to_le_bytes().to_vec(),
});
}
if slashed && b.withdrawable_epoch() != t.withdrawable_epoch() {
patches.push(ValidatorPatch {
index,
field: ValidatorField::WithdrawableEpochSlashed,
value: t.withdrawable_epoch().to_le_bytes().to_vec(),
});
}
}
for t_val in target {
appended_validators.extend(t_val.to_ssz_bytes());
}
ValidatorsDiff {
patches,
appended_validators,
}
}
pub fn apply_validators(base: &mut Vec<u8>, delta: &ArchivedValidatorsDiff) {
apply_validators_iter(&mut ByteValidatorTarget(base), delta)
}
pub fn apply_validators_iter<T: ValidatorMutTarget>(
target: &mut T,
delta: &ArchivedValidatorsDiff,
) {
for patch in delta.patches.iter() {
let idx = patch.index.to_native() as usize;
let val_bytes = patch.value.as_slice();
if let Some(mut validator) = target.get_mut(idx) {
match &patch.field {
ArchivedValidatorField::WithdrawalCredentials => {
let bytes: [u8; 32] = val_bytes.try_into().unwrap();
validator.set_withdrawal_credentials(&bytes);
}
ArchivedValidatorField::EffectiveBalance => {
let eb = u64::from_le_bytes(val_bytes.try_into().unwrap());
validator.set_effective_balance(eb);
}
ArchivedValidatorField::Slashed => {
validator.set_slashed(val_bytes[0] != 0);
}
ArchivedValidatorField::ActivationEligibilityEpoch => {
let epoch = u64::from_le_bytes(val_bytes.try_into().unwrap());
validator.set_activation_eligibility_epoch(epoch);
}
ArchivedValidatorField::ActivationEpoch => {
let epoch = u64::from_le_bytes(val_bytes.try_into().unwrap());
validator.set_activation_epoch(epoch);
}
ArchivedValidatorField::ExitEpoch => {
let ee = u64::from_le_bytes(val_bytes.try_into().unwrap());
validator.set_exit_epoch(ee);
if !validator.is_slashed() {
let we = ee.saturating_add(MIN_VALIDATOR_WITHDRAWABILITY_DELAY);
validator.set_withdrawable_epoch(we);
}
}
ArchivedValidatorField::WithdrawableEpochSlashed => {
let we = u64::from_le_bytes(val_bytes.try_into().unwrap());
validator.set_withdrawable_epoch(we);
}
}
}
}
debug_assert_eq!(
delta.appended_validators.len() % VALIDATOR_SSZ_SIZE,
0,
"appended validator data must contain complete SSZ records",
);
for chunk in delta
.appended_validators
.as_slice()
.chunks_exact(VALIDATOR_SSZ_SIZE)
{
target.push_from_ssz(chunk);
}
}
struct ByteValidator<'a>(&'a [u8]);
impl<'a> ValidatorSnapshot for ByteValidator<'a> {
#[inline]
fn withdrawal_credentials(&self) -> &[u8; 32] {
self.0[48..80].try_into().unwrap()
}
#[inline]
fn effective_balance(&self) -> u64 {
u64::from_le_bytes(self.0[80..88].try_into().unwrap())
}
#[inline]
fn is_slashed(&self) -> bool {
self.0[88] != 0
}
#[inline]
fn activation_eligibility_epoch(&self) -> u64 {
u64::from_le_bytes(self.0[89..97].try_into().unwrap())
}
#[inline]
fn activation_epoch(&self) -> u64 {
u64::from_le_bytes(self.0[97..105].try_into().unwrap())
}
#[inline]
fn exit_epoch(&self) -> u64 {
u64::from_le_bytes(self.0[105..113].try_into().unwrap())
}
#[inline]
fn withdrawable_epoch(&self) -> u64 {
u64::from_le_bytes(self.0[113..121].try_into().unwrap())
}
#[inline]
fn to_ssz_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}
struct ByteValidatorMut<'a>(&'a mut [u8]);
impl<'a> ValidatorMut for ByteValidatorMut<'a> {
#[inline]
fn is_slashed(&self) -> bool {
self.0[88] != 0
}
#[inline]
fn set_withdrawal_credentials(&mut self, v: &[u8; 32]) {
self.0[48..80].copy_from_slice(v);
}
#[inline]
fn set_effective_balance(&mut self, v: u64) {
self.0[80..88].copy_from_slice(&v.to_le_bytes());
}
#[inline]
fn set_slashed(&mut self, v: bool) {
self.0[88] = v as u8;
}
#[inline]
fn set_activation_eligibility_epoch(&mut self, v: u64) {
self.0[89..97].copy_from_slice(&v.to_le_bytes());
}
#[inline]
fn set_activation_epoch(&mut self, v: u64) {
self.0[97..105].copy_from_slice(&v.to_le_bytes());
}
#[inline]
fn set_exit_epoch(&mut self, v: u64) {
self.0[105..113].copy_from_slice(&v.to_le_bytes());
}
#[inline]
fn set_withdrawable_epoch(&mut self, v: u64) {
self.0[113..121].copy_from_slice(&v.to_le_bytes());
}
}
struct ByteValidatorTarget<'a>(&'a mut Vec<u8>);
impl<'a> ValidatorMutTarget for ByteValidatorTarget<'a> {
type Validator<'b>
= ByteValidatorMut<'b>
where
Self: 'b;
fn get_mut(&mut self, index: usize) -> Option<Self::Validator<'_>> {
let start = index.checked_mul(VALIDATOR_SSZ_SIZE)?;
let end = start.checked_add(VALIDATOR_SSZ_SIZE)?;
if end > self.0.len() {
return None;
}
let slice = unsafe {
let ptr = self.0.as_mut_ptr().add(start);
std::slice::from_raw_parts_mut(ptr, VALIDATOR_SSZ_SIZE)
};
Some(ByteValidatorMut(slice))
}
fn push_from_ssz(&mut self, ssz_bytes: &[u8]) {
self.0.extend_from_slice(ssz_bytes);
}
}