eth_state_diff/randao_mixes.rs
1//! Delta encoding for Ethereum RANDAO mix buffers.
2//!
3//! Ethereum consensus stores historical RANDAO mixes in a fixed-capacity
4//! circular buffer indexed by epoch.
5//!
6//! Rather than storing the entire buffer, this module records only the sequence
7//! of mixes written while advancing from one epoch to another. Applying the
8//! delta replays those writes into another buffer using the same circular
9//! indexing logic.
10//!
11//! The encoding is independent of the buffer capacity and relies only on the
12//! starting slot and the destination buffer.
13
14use crate::types::{ArchivedRandaoDiff, RandaoDiff, SLOTS_PER_EPOCH};
15
16/// Computes the sequence of RANDAO mixes written between two slots.
17///
18/// The returned delta contains one mix for every epoch in the inclusive range
19/// from the epoch containing `base_slot` through the epoch containing
20/// `target_slot`.
21///
22/// The target buffer is treated as a circular buffer, with its length defining
23/// the modulo capacity.
24///
25/// # Arguments
26///
27/// * `base_slot` - Starting slot.
28/// * `target_slot` - Ending slot.
29/// * `target_buffer` - Circular RANDAO mix buffer.
30/// * `slots_per_epoch` - Number of slots in a consensus epoch.
31///
32/// # Complexity
33///
34/// O(number of epochs)
35///
36/// # Panics
37///
38/// Panics if `target_slot < base_slot`.
39pub fn diff_randao(base_slot: u64, target_slot: u64, target_buffer: &[[u8; 32]]) -> RandaoDiff {
40 let base_epoch = base_slot / SLOTS_PER_EPOCH;
41 let target_epoch = target_slot / SLOTS_PER_EPOCH;
42 let capacity = target_buffer.len() as u64;
43 let mut mixes = Vec::with_capacity((target_epoch - base_epoch + 1) as usize);
44 for epoch in base_epoch..=target_epoch {
45 let idx = (epoch % capacity) as usize;
46 mixes.push(target_buffer[idx]);
47 }
48 RandaoDiff { mixes }
49}
50
51/// Applies a RANDAO delta to a circular mix buffer.
52///
53/// Each recorded mix is written back into the destination buffer beginning at
54/// the epoch containing `base_slot`.
55///
56/// After successful application, the destination buffer contains the same
57/// RANDAO mixes as the original target buffer for every epoch represented by
58/// the delta.
59///
60/// # Complexity
61///
62/// O(number of encoded epochs)
63pub fn apply_randao(base_slot: u64, base_buffer: &mut [[u8; 32]], delta: &ArchivedRandaoDiff) {
64 let capacity = base_buffer.len() as u64;
65 let mut current_epoch = base_slot / SLOTS_PER_EPOCH;
66 for mix in delta.mixes.iter() {
67 let idx = (current_epoch % capacity) as usize;
68 base_buffer[idx] = *mix;
69 current_epoch += 1;
70 }
71}