pub mod attestations;
pub mod balances;
pub mod eth1_data_votes;
pub mod historical_log;
pub mod inactivity_scores;
pub mod participation;
pub mod pending_queue;
pub mod randao_mixes;
pub mod recent_roots;
pub mod slashings;
pub mod sync_committee;
pub mod types;
pub mod validators;
pub mod error;
use error::Error;
use rkyv::{Archive, Deserialize, Serialize};
use crate::types::{
AttestationsDiff, BalancesDiff, Eth1DataVotesDiff, HistoricalLogDiff, InactivityDiff,
ParticipationDiff, QueueDiff, RandaoDiff, RootsDiff, SlashingsDiff, SyncCommitteeDiff,
ValidatorsDiff, HISTORICAL_ROOTS_SSZ_SIZE, HISTORICAL_SUMMARIES_SSZ_SIZE,
};
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum ForkName {
Phase0 = 0,
Altair = 1,
Bellatrix = 2,
Capella = 3,
Deneb = 4,
Electra = 5,
Fulu = 6,
Gloas = 7,
Heze = 8,
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BeaconStateDelta {
pub fork: ForkName,
pub base_slot: u64,
pub scalar_header: Vec<u8>,
pub balances: BalancesDiff,
pub validators: ValidatorsDiff,
pub block_roots: RootsDiff,
pub state_roots: RootsDiff,
pub randao_mixes: RandaoDiff,
pub slashings: SlashingsDiff,
pub eth1_data_votes: Eth1DataVotesDiff,
pub historical_roots: Option<HistoricalLogDiff>,
pub previous_epoch_attestations: Option<AttestationsDiff>,
pub current_epoch_attestations: Option<AttestationsDiff>,
pub previous_participation: Option<ParticipationDiff>,
pub current_participation: Option<ParticipationDiff>,
pub inactivity_scores: Option<InactivityDiff>,
pub current_sync_committee: Option<SyncCommitteeDiff>,
pub next_sync_committee: Option<SyncCommitteeDiff>,
pub historical_summaries: Option<HistoricalLogDiff>,
pub pending_deposits: Option<QueueDiff>,
pub pending_partial_withdrawals: Option<QueueDiff>,
pub pending_consolidations: Option<QueueDiff>,
}
const PENDING_DEPOSIT_SSZ_SIZE: usize = 192;
const PARTIAL_WITHDRAWAL_SSZ_SIZE: usize = 24;
const PENDING_CONSOLIDATION_SSZ_SIZE: usize = 16;
pub trait DiffTarget {
fn get_fork(&self) -> ForkName;
fn scalar_header_mut(&mut self) -> &mut Vec<u8>;
fn balances_mut(&mut self) -> &mut Vec<u64>;
fn validators_mut(&mut self) -> &mut Vec<u8>;
fn block_roots_mut(&mut self) -> &mut [[u8; 32]];
fn state_roots_mut(&mut self) -> &mut [[u8; 32]];
fn randao_mixes_mut(&mut self) -> &mut [[u8; 32]];
fn slashings_mut(&mut self) -> &mut [u64];
fn eth1_data_votes_mut(&mut self) -> &mut Vec<u8>;
fn historical_roots_mut(&mut self) -> Option<&mut Vec<u8>>;
fn previous_epoch_attestations_mut(&mut self) -> Option<&mut Vec<u8>>;
fn current_epoch_attestations_mut(&mut self) -> Option<&mut Vec<u8>>;
fn previous_participation_mut(&mut self) -> Option<&mut Vec<u8>>;
fn current_participation_mut(&mut self) -> Option<&mut Vec<u8>>;
fn inactivity_scores_mut(&mut self) -> Option<&mut Vec<u64>>;
fn current_sync_committee_mut(&mut self) -> Option<&mut Vec<u8>>;
fn next_sync_committee_mut(&mut self) -> Option<&mut Vec<u8>>;
fn historical_summaries_mut(&mut self) -> Option<&mut Vec<u8>>;
fn pending_deposits_mut(&mut self) -> Option<&mut Vec<u8>>;
fn pending_partial_withdrawals_mut(&mut self) -> Option<&mut Vec<u8>>;
fn pending_consolidations_mut(&mut self) -> Option<&mut Vec<u8>>;
}
pub fn apply<M: DiffTarget>(mut state: M, delta: &ArchivedBeaconStateDelta) -> Result<M, Error> {
use rkyv::deserialize;
let delta_fork: ForkName = deserialize::<ForkName, rkyv::rancor::Error>(&delta.fork)
.map_err(|e| Error::MalformedDelta(format!("failed to deserialize fork: {e}")))?;
let state_fork = state.get_fork();
if state_fork != delta_fork {
return Err(Error::ForkMismatch {
state_fork,
delta_fork,
});
}
macro_rules! validate_removed_field {
($field:ident, $removed_in:expr) => {
if delta.$field.is_some() && delta_fork >= $removed_in {
return Err(Error::InvalidFieldForFork {
field: stringify!($field),
fork: delta_fork,
});
}
};
}
macro_rules! validate_field {
($field:ident, $fork:expr) => {
if delta.$field.is_some() && delta_fork < $fork {
return Err(Error::InvalidFieldForFork {
field: stringify!($field),
fork: delta_fork,
});
}
};
}
validate_field!(previous_participation, ForkName::Altair);
validate_field!(current_participation, ForkName::Altair);
validate_field!(inactivity_scores, ForkName::Altair);
validate_field!(current_sync_committee, ForkName::Altair);
validate_field!(next_sync_committee, ForkName::Altair);
validate_field!(historical_summaries, ForkName::Capella);
validate_field!(pending_deposits, ForkName::Electra);
validate_field!(pending_partial_withdrawals, ForkName::Electra);
validate_field!(pending_consolidations, ForkName::Electra);
validate_removed_field!(previous_epoch_attestations, ForkName::Altair);
validate_removed_field!(current_epoch_attestations, ForkName::Altair);
validate_removed_field!(historical_roots, ForkName::Capella);
let base_slot = delta.base_slot.to_native();
*state.scalar_header_mut() = delta.scalar_header.as_slice().to_vec();
balances::apply_balances(state.balances_mut(), &delta.balances);
validators::apply_validators(state.validators_mut(), &delta.validators);
recent_roots::apply_roots(base_slot, state.block_roots_mut(), &delta.block_roots);
recent_roots::apply_roots(base_slot, state.state_roots_mut(), &delta.state_roots);
randao_mixes::apply_randao(base_slot, state.randao_mixes_mut(), &delta.randao_mixes);
slashings::apply_slashings(state.slashings_mut(), &delta.slashings);
eth1_data_votes::apply_eth1_votes(state.eth1_data_votes_mut(), &delta.eth1_data_votes);
if let (Some(s), Some(d)) = (
state.historical_roots_mut(),
delta.historical_roots.as_ref(),
) {
historical_log::apply_historical_log(s, d);
}
if let (Some(s), Some(d)) = (
state.previous_epoch_attestations_mut(),
delta.previous_epoch_attestations.as_ref(),
) {
attestations::apply_attestations(s, d);
}
if let (Some(s), Some(d)) = (
state.current_epoch_attestations_mut(),
delta.current_epoch_attestations.as_ref(),
) {
attestations::apply_attestations(s, d);
}
if let (Some(s), Some(d)) = (
state.previous_participation_mut(),
delta.previous_participation.as_ref(),
) {
participation::apply_participation(s, d);
}
if let (Some(s), Some(d)) = (
state.current_participation_mut(),
delta.current_participation.as_ref(),
) {
participation::apply_participation(s, d);
}
if let (Some(s), Some(d)) = (
state.inactivity_scores_mut(),
delta.inactivity_scores.as_ref(),
) {
inactivity_scores::apply_inactivity(s, d);
}
if let (Some(s), Some(d)) = (
state.current_sync_committee_mut(),
delta.current_sync_committee.as_ref(),
) {
sync_committee::apply_sync_committee(s, d);
}
if let (Some(s), Some(d)) = (
state.next_sync_committee_mut(),
delta.next_sync_committee.as_ref(),
) {
sync_committee::apply_sync_committee(s, d);
}
if let (Some(s), Some(d)) = (
state.historical_summaries_mut(),
delta.historical_summaries.as_ref(),
) {
historical_log::apply_historical_log(s, d);
}
if let (Some(s), Some(d)) = (
state.pending_deposits_mut(),
delta.pending_deposits.as_ref(),
) {
pending_queue::apply_queue(s, d, PENDING_DEPOSIT_SSZ_SIZE);
}
if let (Some(s), Some(d)) = (
state.pending_partial_withdrawals_mut(),
delta.pending_partial_withdrawals.as_ref(),
) {
pending_queue::apply_queue(s, d, PARTIAL_WITHDRAWAL_SSZ_SIZE);
}
if let (Some(s), Some(d)) = (
state.pending_consolidations_mut(),
delta.pending_consolidations.as_ref(),
) {
pending_queue::apply_queue(s, d, PENDING_CONSOLIDATION_SSZ_SIZE);
}
Ok(state)
}
pub trait DiffSource {
fn fork(&self) -> ForkName;
fn slot(&self) -> (u64, u64);
fn capella_fork_slot(&self) -> u64;
fn scalar_header(&self) -> Vec<u8>;
fn balances(&self) -> (&[u64], &[u64]);
fn validators(&self) -> (&[u8], &[u8]);
fn block_roots(&self) -> &[[u8; 32]];
fn state_roots(&self) -> &[[u8; 32]];
fn randao_mixes(&self) -> &[[u8; 32]];
fn slashings(&self) -> (&[u64], &[u64]);
fn eth1_data_votes(&self) -> (&[u8], &[u8]);
fn historical_roots(&self) -> Option<&[u8]>;
fn previous_epoch_attestations(&self) -> Option<(&[u8], &[u8])>;
fn current_epoch_attestations(&self) -> Option<(&[u8], &[u8])>;
fn previous_participation(&self) -> Option<(&[u8], &[u8])>;
fn current_participation(&self) -> Option<(&[u8], &[u8])>;
fn inactivity_scores(&self) -> Option<(&[u64], &[u64])>;
fn current_sync_committee(&self) -> Option<(&[u8], &[u8])>;
fn next_sync_committee(&self) -> Option<(&[u8], &[u8])>;
fn historical_summaries(&self) -> Option<&[u8]>;
fn pending_deposits(&self) -> Option<(&[u8], &[u8])>;
fn pending_partial_withdrawals(&self) -> Option<(&[u8], &[u8])>;
fn pending_consolidations(&self) -> Option<(&[u8], &[u8])>;
}
pub fn create<R: DiffSource>(state: &R) -> BeaconStateDelta {
let (base_slot, target_slot) = state.slot();
let delta = BeaconStateDelta {
fork: state.fork(),
base_slot,
scalar_header: state.scalar_header(),
balances: balances::diff_balances(state.balances().0, state.balances().1),
validators: validators::diff_validators(state.validators().0, state.validators().1),
block_roots: recent_roots::diff_roots(base_slot, target_slot, state.block_roots()),
state_roots: recent_roots::diff_roots(base_slot, target_slot, state.state_roots()),
randao_mixes: randao_mixes::diff_randao(base_slot, target_slot, state.randao_mixes()),
slashings: slashings::diff_slashings(
base_slot,
target_slot,
state.slashings().0,
state.slashings().1,
),
eth1_data_votes: eth1_data_votes::diff_eth1_votes(
state.eth1_data_votes().0,
state.eth1_data_votes().1,
),
historical_roots: state.historical_roots().map(|t| {
historical_log::diff_historical_log(
base_slot,
target_slot,
t,
HISTORICAL_ROOTS_SSZ_SIZE,
None,
)
}),
previous_epoch_attestations: state
.previous_epoch_attestations()
.map(|(b, t)| attestations::diff_attestations_replacement(b, t)),
current_epoch_attestations: state
.current_epoch_attestations()
.map(|(b, t)| attestations::diff_attestations_append(b, t)),
previous_participation: state
.previous_participation()
.map(|(b, t)| participation::diff_participation(b, t)),
current_participation: state
.current_participation()
.map(|(b, t)| participation::diff_participation(b, t)),
inactivity_scores: state
.inactivity_scores()
.map(|(b, t)| inactivity_scores::diff_inactivity(b, t)),
current_sync_committee: state
.current_sync_committee()
.map(|(b, t)| sync_committee::diff_sync_committee(b, t)),
next_sync_committee: state
.next_sync_committee()
.map(|(b, t)| sync_committee::diff_sync_committee(b, t)),
historical_summaries: state.historical_summaries().map(|t| {
historical_log::diff_historical_log(
base_slot,
target_slot,
t,
HISTORICAL_SUMMARIES_SSZ_SIZE,
Some(state.capella_fork_slot()),
)
}),
pending_deposits: state
.pending_deposits()
.map(|(b, t)| pending_queue::diff_queue(b, t, PENDING_DEPOSIT_SSZ_SIZE)),
pending_partial_withdrawals: state
.pending_partial_withdrawals()
.map(|(b, t)| pending_queue::diff_queue(b, t, PARTIAL_WITHDRAWAL_SSZ_SIZE)),
pending_consolidations: state
.pending_consolidations()
.map(|(b, t)| pending_queue::diff_queue(b, t, PENDING_CONSOLIDATION_SSZ_SIZE)),
};
debug_assert_eq!(
delta.previous_participation.is_some(),
delta.fork >= ForkName::Altair,
"DiffSource bug: previous_participation must exist iff fork >= Altair (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.current_participation.is_some(),
delta.fork >= ForkName::Altair,
"DiffSource bug: current_participation must exist iff fork >= Altair (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.inactivity_scores.is_some(),
delta.fork >= ForkName::Altair,
"DiffSource bug: inactivity_scores must exist iff fork >= Altair (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.current_sync_committee.is_some(),
delta.fork >= ForkName::Altair,
"DiffSource bug: current_sync_committee must exist iff fork >= Altair (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.next_sync_committee.is_some(),
delta.fork >= ForkName::Altair,
"DiffSource bug: next_sync_committee must exist iff fork >= Altair (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.historical_summaries.is_some(),
delta.fork >= ForkName::Capella,
"DiffSource bug: historical_summaries must exist iff fork >= Capella (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.pending_deposits.is_some(),
delta.fork >= ForkName::Electra,
"DiffSource bug: pending_deposits must exist iff fork >= Electra (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.pending_partial_withdrawals.is_some(),
delta.fork >= ForkName::Electra,
"DiffSource bug: pending_partial_withdrawals must exist iff fork >= Electra (got {:?})",
delta.fork
);
debug_assert_eq!(
delta.pending_consolidations.is_some(),
delta.fork >= ForkName::Electra,
"DiffSource bug: pending_consolidations must exist iff fork >= Electra (got {:?})",
delta.fork
);
delta
}