1pub mod attestations;
17pub mod balances;
18pub mod eth1_data_votes;
19pub mod historical_log;
20pub mod inactivity_scores;
21pub mod participation;
22pub mod pending_queue;
23pub mod randao_mixes;
24pub mod recent_roots;
25pub mod slashings;
26pub mod sync_committee;
27pub mod types;
28pub mod validators;
29
30pub mod error;
31use error::Error;
32
33use rkyv::{Archive, Deserialize, Serialize};
34
35use crate::types::{
36 AttestationsDiff, BalancesDiff, Eth1DataVotesDiff, HistoricalLogDiff, InactivityDiff,
37 ParticipationDiff, QueueDiff, RandaoDiff, RootsDiff, SlashingsDiff, SyncCommitteeDiff,
38 ValidatorsDiff, HISTORICAL_ROOTS_SSZ_SIZE, HISTORICAL_SUMMARIES_SSZ_SIZE,
39};
40
41#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
46#[repr(u8)]
47pub enum ForkName {
48 Phase0 = 0,
49 Altair = 1,
50 Bellatrix = 2,
51 Capella = 3,
52 Deneb = 4,
53 Electra = 5,
54 Fulu = 6,
55 Gloas = 7,
56 Heze = 8,
57}
58
59#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
66pub struct BeaconStateDelta {
67 pub fork: ForkName,
68 pub base_slot: u64,
69 pub scalar_header: Vec<u8>,
70
71 pub balances: BalancesDiff,
73 pub validators: ValidatorsDiff,
74 pub block_roots: RootsDiff,
75 pub state_roots: RootsDiff,
76 pub randao_mixes: RandaoDiff,
77 pub slashings: SlashingsDiff,
78 pub eth1_data_votes: Eth1DataVotesDiff,
79 pub historical_roots: Option<HistoricalLogDiff>,
80
81 pub previous_epoch_attestations: Option<AttestationsDiff>,
84 pub current_epoch_attestations: Option<AttestationsDiff>,
85
86 pub previous_participation: Option<ParticipationDiff>,
89 pub current_participation: Option<ParticipationDiff>,
90 pub inactivity_scores: Option<InactivityDiff>,
91 pub current_sync_committee: Option<SyncCommitteeDiff>,
92 pub next_sync_committee: Option<SyncCommitteeDiff>,
93
94 pub historical_summaries: Option<HistoricalLogDiff>,
97
98 pub pending_deposits: Option<QueueDiff>,
101 pub pending_partial_withdrawals: Option<QueueDiff>,
102 pub pending_consolidations: Option<QueueDiff>,
103}
104
105const PENDING_DEPOSIT_SSZ_SIZE: usize = 192;
106const PARTIAL_WITHDRAWAL_SSZ_SIZE: usize = 24;
107const PENDING_CONSOLIDATION_SSZ_SIZE: usize = 16;
108
109pub trait DiffTarget {
118 fn get_fork(&self) -> ForkName;
119 fn scalar_header_mut(&mut self) -> &mut Vec<u8>;
120
121 fn balances_mut(&mut self) -> &mut Vec<u64>;
123 fn validators_mut(&mut self) -> &mut Vec<u8>;
124 fn block_roots_mut(&mut self) -> &mut [[u8; 32]];
125 fn state_roots_mut(&mut self) -> &mut [[u8; 32]];
126 fn randao_mixes_mut(&mut self) -> &mut [[u8; 32]];
127 fn slashings_mut(&mut self) -> &mut [u64];
128 fn eth1_data_votes_mut(&mut self) -> &mut Vec<u8>;
129 fn historical_roots_mut(&mut self) -> Option<&mut Vec<u8>>;
130
131 fn previous_epoch_attestations_mut(&mut self) -> Option<&mut Vec<u8>>;
133 fn current_epoch_attestations_mut(&mut self) -> Option<&mut Vec<u8>>;
134
135 fn previous_participation_mut(&mut self) -> Option<&mut Vec<u8>>;
137 fn current_participation_mut(&mut self) -> Option<&mut Vec<u8>>;
138 fn inactivity_scores_mut(&mut self) -> Option<&mut Vec<u64>>;
139 fn current_sync_committee_mut(&mut self) -> Option<&mut Vec<u8>>;
140 fn next_sync_committee_mut(&mut self) -> Option<&mut Vec<u8>>;
141
142 fn historical_summaries_mut(&mut self) -> Option<&mut Vec<u8>>;
144
145 fn pending_deposits_mut(&mut self) -> Option<&mut Vec<u8>>;
147 fn pending_partial_withdrawals_mut(&mut self) -> Option<&mut Vec<u8>>;
148 fn pending_consolidations_mut(&mut self) -> Option<&mut Vec<u8>>;
149}
150
151pub fn apply<M: DiffTarget>(mut state: M, delta: &ArchivedBeaconStateDelta) -> Result<M, Error> {
158 use rkyv::deserialize;
159
160 let delta_fork: ForkName = deserialize::<ForkName, rkyv::rancor::Error>(&delta.fork)
161 .map_err(|e| Error::MalformedDelta(format!("failed to deserialize fork: {e}")))?;
162
163 let state_fork = state.get_fork();
164 if state_fork != delta_fork {
165 return Err(Error::ForkMismatch {
166 state_fork,
167 delta_fork,
168 });
169 }
170
171 macro_rules! validate_removed_field {
172 ($field:ident, $removed_in:expr) => {
173 if delta.$field.is_some() && delta_fork >= $removed_in {
174 return Err(Error::InvalidFieldForFork {
175 field: stringify!($field),
176 fork: delta_fork,
177 });
178 }
179 };
180 }
181
182 macro_rules! validate_field {
184 ($field:ident, $fork:expr) => {
185 if delta.$field.is_some() && delta_fork < $fork {
186 return Err(Error::InvalidFieldForFork {
187 field: stringify!($field),
188 fork: delta_fork,
189 });
190 }
191 };
192 }
193
194 validate_field!(previous_participation, ForkName::Altair);
196 validate_field!(current_participation, ForkName::Altair);
197 validate_field!(inactivity_scores, ForkName::Altair);
198 validate_field!(current_sync_committee, ForkName::Altair);
199 validate_field!(next_sync_committee, ForkName::Altair);
200
201 validate_field!(historical_summaries, ForkName::Capella);
203
204 validate_field!(pending_deposits, ForkName::Electra);
206 validate_field!(pending_partial_withdrawals, ForkName::Electra);
207 validate_field!(pending_consolidations, ForkName::Electra);
208
209 validate_removed_field!(previous_epoch_attestations, ForkName::Altair);
211 validate_removed_field!(current_epoch_attestations, ForkName::Altair);
212
213 validate_removed_field!(historical_roots, ForkName::Capella);
215
216 let base_slot = delta.base_slot.to_native();
217
218 *state.scalar_header_mut() = delta.scalar_header.as_slice().to_vec();
219
220 balances::apply_balances(state.balances_mut(), &delta.balances);
222 validators::apply_validators(state.validators_mut(), &delta.validators);
223 recent_roots::apply_roots(base_slot, state.block_roots_mut(), &delta.block_roots);
224 recent_roots::apply_roots(base_slot, state.state_roots_mut(), &delta.state_roots);
225 randao_mixes::apply_randao(base_slot, state.randao_mixes_mut(), &delta.randao_mixes);
226 slashings::apply_slashings(state.slashings_mut(), &delta.slashings);
227 eth1_data_votes::apply_eth1_votes(state.eth1_data_votes_mut(), &delta.eth1_data_votes);
228
229 if let (Some(s), Some(d)) = (
230 state.historical_roots_mut(),
231 delta.historical_roots.as_ref(),
232 ) {
233 historical_log::apply_historical_log(s, d);
234 }
235
236 if let (Some(s), Some(d)) = (
237 state.previous_epoch_attestations_mut(),
238 delta.previous_epoch_attestations.as_ref(),
239 ) {
240 attestations::apply_attestations(s, d);
241 }
242
243 if let (Some(s), Some(d)) = (
244 state.current_epoch_attestations_mut(),
245 delta.current_epoch_attestations.as_ref(),
246 ) {
247 attestations::apply_attestations(s, d);
248 }
249
250 if let (Some(s), Some(d)) = (
251 state.previous_participation_mut(),
252 delta.previous_participation.as_ref(),
253 ) {
254 participation::apply_participation(s, d);
255 }
256
257 if let (Some(s), Some(d)) = (
258 state.current_participation_mut(),
259 delta.current_participation.as_ref(),
260 ) {
261 participation::apply_participation(s, d);
262 }
263
264 if let (Some(s), Some(d)) = (
265 state.inactivity_scores_mut(),
266 delta.inactivity_scores.as_ref(),
267 ) {
268 inactivity_scores::apply_inactivity(s, d);
269 }
270
271 if let (Some(s), Some(d)) = (
272 state.current_sync_committee_mut(),
273 delta.current_sync_committee.as_ref(),
274 ) {
275 sync_committee::apply_sync_committee(s, d);
276 }
277
278 if let (Some(s), Some(d)) = (
279 state.next_sync_committee_mut(),
280 delta.next_sync_committee.as_ref(),
281 ) {
282 sync_committee::apply_sync_committee(s, d);
283 }
284
285 if let (Some(s), Some(d)) = (
286 state.historical_summaries_mut(),
287 delta.historical_summaries.as_ref(),
288 ) {
289 historical_log::apply_historical_log(s, d);
290 }
291
292 if let (Some(s), Some(d)) = (
293 state.pending_deposits_mut(),
294 delta.pending_deposits.as_ref(),
295 ) {
296 pending_queue::apply_queue(s, d, PENDING_DEPOSIT_SSZ_SIZE);
297 }
298
299 if let (Some(s), Some(d)) = (
300 state.pending_partial_withdrawals_mut(),
301 delta.pending_partial_withdrawals.as_ref(),
302 ) {
303 pending_queue::apply_queue(s, d, PARTIAL_WITHDRAWAL_SSZ_SIZE);
304 }
305
306 if let (Some(s), Some(d)) = (
307 state.pending_consolidations_mut(),
308 delta.pending_consolidations.as_ref(),
309 ) {
310 pending_queue::apply_queue(s, d, PENDING_CONSOLIDATION_SSZ_SIZE);
311 }
312
313 Ok(state)
314}
315
316pub trait DiffSource {
326 fn fork(&self) -> ForkName;
327 fn slot(&self) -> (u64, u64);
328 fn capella_fork_slot(&self) -> u64; fn scalar_header(&self) -> Vec<u8>;
355
356 fn balances(&self) -> (&[u64], &[u64]);
358 fn validators(&self) -> (&[u8], &[u8]);
359 fn block_roots(&self) -> &[[u8; 32]];
360 fn state_roots(&self) -> &[[u8; 32]];
361 fn randao_mixes(&self) -> &[[u8; 32]];
362 fn slashings(&self) -> (&[u64], &[u64]);
363 fn eth1_data_votes(&self) -> (&[u8], &[u8]);
364 fn historical_roots(&self) -> Option<&[u8]>;
365
366 fn previous_epoch_attestations(&self) -> Option<(&[u8], &[u8])>;
368 fn current_epoch_attestations(&self) -> Option<(&[u8], &[u8])>;
369
370 fn previous_participation(&self) -> Option<(&[u8], &[u8])>;
372 fn current_participation(&self) -> Option<(&[u8], &[u8])>;
373 fn inactivity_scores(&self) -> Option<(&[u64], &[u64])>;
374 fn current_sync_committee(&self) -> Option<(&[u8], &[u8])>;
375 fn next_sync_committee(&self) -> Option<(&[u8], &[u8])>;
376
377 fn historical_summaries(&self) -> Option<&[u8]>;
379
380 fn pending_deposits(&self) -> Option<(&[u8], &[u8])>;
382 fn pending_partial_withdrawals(&self) -> Option<(&[u8], &[u8])>;
383 fn pending_consolidations(&self) -> Option<(&[u8], &[u8])>;
384}
385
386pub fn create<R: DiffSource>(state: &R) -> BeaconStateDelta {
398 let (base_slot, target_slot) = state.slot();
399
400 let delta = BeaconStateDelta {
401 fork: state.fork(),
402 base_slot,
403 scalar_header: state.scalar_header(),
404
405 balances: balances::diff_balances(state.balances().0, state.balances().1),
407 validators: validators::diff_validators(state.validators().0, state.validators().1),
408 block_roots: recent_roots::diff_roots(base_slot, target_slot, state.block_roots()),
409 state_roots: recent_roots::diff_roots(base_slot, target_slot, state.state_roots()),
410 randao_mixes: randao_mixes::diff_randao(base_slot, target_slot, state.randao_mixes()),
411 slashings: slashings::diff_slashings(
412 base_slot,
413 target_slot,
414 state.slashings().0,
415 state.slashings().1,
416 ),
417 eth1_data_votes: eth1_data_votes::diff_eth1_votes(
418 state.eth1_data_votes().0,
419 state.eth1_data_votes().1,
420 ),
421 historical_roots: state.historical_roots().map(|t| {
422 historical_log::diff_historical_log(
423 base_slot,
424 target_slot,
425 t,
426 HISTORICAL_ROOTS_SSZ_SIZE,
427 None,
428 )
429 }),
430
431 previous_epoch_attestations: state
433 .previous_epoch_attestations()
434 .map(|(b, t)| attestations::diff_attestations_replacement(b, t)),
435 current_epoch_attestations: state
436 .current_epoch_attestations()
437 .map(|(b, t)| attestations::diff_attestations_append(b, t)),
438
439 previous_participation: state
441 .previous_participation()
442 .map(|(b, t)| participation::diff_participation(b, t)),
443 current_participation: state
444 .current_participation()
445 .map(|(b, t)| participation::diff_participation(b, t)),
446 inactivity_scores: state
447 .inactivity_scores()
448 .map(|(b, t)| inactivity_scores::diff_inactivity(b, t)),
449 current_sync_committee: state
450 .current_sync_committee()
451 .map(|(b, t)| sync_committee::diff_sync_committee(b, t)),
452 next_sync_committee: state
453 .next_sync_committee()
454 .map(|(b, t)| sync_committee::diff_sync_committee(b, t)),
455
456 historical_summaries: state.historical_summaries().map(|t| {
458 historical_log::diff_historical_log(
459 base_slot,
460 target_slot,
461 t,
462 HISTORICAL_SUMMARIES_SSZ_SIZE,
463 Some(state.capella_fork_slot()),
464 )
465 }),
466
467 pending_deposits: state
469 .pending_deposits()
470 .map(|(b, t)| pending_queue::diff_queue(b, t, PENDING_DEPOSIT_SSZ_SIZE)),
471 pending_partial_withdrawals: state
472 .pending_partial_withdrawals()
473 .map(|(b, t)| pending_queue::diff_queue(b, t, PARTIAL_WITHDRAWAL_SSZ_SIZE)),
474 pending_consolidations: state
475 .pending_consolidations()
476 .map(|(b, t)| pending_queue::diff_queue(b, t, PENDING_CONSOLIDATION_SSZ_SIZE)),
477 };
478
479 debug_assert_eq!(
480 delta.previous_participation.is_some(),
481 delta.fork >= ForkName::Altair,
482 "DiffSource bug: previous_participation must exist iff fork >= Altair (got {:?})",
483 delta.fork
484 );
485
486 debug_assert_eq!(
487 delta.current_participation.is_some(),
488 delta.fork >= ForkName::Altair,
489 "DiffSource bug: current_participation must exist iff fork >= Altair (got {:?})",
490 delta.fork
491 );
492
493 debug_assert_eq!(
494 delta.inactivity_scores.is_some(),
495 delta.fork >= ForkName::Altair,
496 "DiffSource bug: inactivity_scores must exist iff fork >= Altair (got {:?})",
497 delta.fork
498 );
499
500 debug_assert_eq!(
501 delta.current_sync_committee.is_some(),
502 delta.fork >= ForkName::Altair,
503 "DiffSource bug: current_sync_committee must exist iff fork >= Altair (got {:?})",
504 delta.fork
505 );
506
507 debug_assert_eq!(
508 delta.next_sync_committee.is_some(),
509 delta.fork >= ForkName::Altair,
510 "DiffSource bug: next_sync_committee must exist iff fork >= Altair (got {:?})",
511 delta.fork
512 );
513
514 debug_assert_eq!(
515 delta.historical_summaries.is_some(),
516 delta.fork >= ForkName::Capella,
517 "DiffSource bug: historical_summaries must exist iff fork >= Capella (got {:?})",
518 delta.fork
519 );
520
521 debug_assert_eq!(
522 delta.pending_deposits.is_some(),
523 delta.fork >= ForkName::Electra,
524 "DiffSource bug: pending_deposits must exist iff fork >= Electra (got {:?})",
525 delta.fork
526 );
527
528 debug_assert_eq!(
529 delta.pending_partial_withdrawals.is_some(),
530 delta.fork >= ForkName::Electra,
531 "DiffSource bug: pending_partial_withdrawals must exist iff fork >= Electra (got {:?})",
532 delta.fork
533 );
534
535 debug_assert_eq!(
536 delta.pending_consolidations.is_some(),
537 delta.fork >= ForkName::Electra,
538 "DiffSource bug: pending_consolidations must exist iff fork >= Electra (got {:?})",
539 delta.fork
540 );
541
542 delta
543}