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};
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(
40    base_slot: u64,
41    target_slot: u64,
42    target_buffer: &[[u8; 32]],
43    slots_per_epoch: u64,
44) -> RandaoDiff {
45    debug_assert!(
46        target_slot >= base_slot,
47        "target_slot must not precede base_slot"
48    );
49
50    let base_epoch = base_slot / slots_per_epoch;
51    let target_epoch = target_slot / slots_per_epoch;
52    let capacity = target_buffer.len() as u64;
53    let mut mixes = Vec::with_capacity((target_epoch - base_epoch + 1) as usize);
54    for epoch in base_epoch..=target_epoch {
55        let idx = (epoch % capacity) as usize;
56        mixes.push(target_buffer[idx]);
57    }
58    RandaoDiff { mixes }
59}
60
61/// Applies a RANDAO delta to a circular mix buffer.
62///
63/// Each recorded mix is written back into the destination buffer beginning at
64/// the epoch containing `base_slot`.
65///
66/// After successful application, the destination buffer contains the same
67/// RANDAO mixes as the original target buffer for every epoch represented by
68/// the delta.
69///
70/// # Complexity
71///
72/// O(number of encoded epochs)
73pub fn apply_randao(
74    base_slot: u64,
75    base_buffer: &mut [[u8; 32]],
76    delta: &ArchivedRandaoDiff,
77    slots_per_epoch: u64,
78) {
79    let capacity = base_buffer.len() as u64;
80    let mut current_epoch = base_slot / slots_per_epoch;
81    for mix in delta.mixes.iter() {
82        let idx = (current_epoch % capacity) as usize;
83        base_buffer[idx] = *mix;
84        current_epoch += 1;
85    }
86}