Skip to main content

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}