eth_state_diff/recent_roots.rs
1//! Delta encoding for fixed-size Ethereum consensus root buffers.
2//!
3//! Ethereum consensus stores historical roots (such as block roots and state
4//! roots) in fixed-capacity circular buffers. Advancing the chain overwrites
5//! the oldest entries once the buffer wraps.
6//!
7//! Rather than diffing the entire buffer, this module records only the sequence
8//! of newly written roots between two slots. Applying the delta replays those
9//! writes into another buffer using the same circular indexing logic.
10//!
11//! This representation is independent of the buffer capacity and works with
12//! any fixed-size root ring.
13
14use crate::types::{ArchivedRootsDiff, RootsDiff};
15
16/// Computes the sequence of newly written roots between two slots.
17///
18/// The returned delta contains every root written during the half-open slot
19/// range `[base_slot, target_slot)`.
20///
21/// The input `buffer` is treated as a circular buffer, with its length defining
22/// the modulo capacity.
23///
24/// # Arguments
25///
26/// * `base_slot` - First slot represented by the delta.
27/// * `target_slot` - Slot immediately following the final recorded root.
28/// * `buffer` - Circular root buffer.
29///
30/// # Complexity
31///
32/// O(target_slot - base_slot)
33pub fn diff_roots(base_slot: u64, target_slot: u64, buffer: &[[u8; 32]]) -> RootsDiff {
34 debug_assert!(
35 target_slot >= base_slot,
36 "target_slot must not precede base_slot"
37 );
38
39 let span = target_slot - base_slot;
40 let capacity = buffer.len() as u64;
41
42 let mut roots = Vec::with_capacity(span as usize);
43
44 for i in 0..span {
45 let slot = base_slot + i;
46 let idx = (slot % capacity) as usize;
47 roots.push(buffer[idx]);
48 }
49
50 RootsDiff { roots }
51}
52
53/// Applies a root delta to a circular root buffer.
54///
55/// Each stored root is written back into the destination buffer using the same
56/// slot-to-index mapping employed during diff generation.
57///
58/// After application, the destination buffer contains the same root values for
59/// every slot represented by the delta.
60///
61/// # Complexity
62///
63/// O(number of recorded roots)
64pub fn apply_roots(base_slot: u64, base_buffer: &mut [[u8; 32]], delta: &ArchivedRootsDiff) {
65 let capacity = base_buffer.len() as u64;
66
67 for (i, root) in delta.roots.iter().enumerate() {
68 let slot = base_slot + i as u64;
69 let idx = (slot % capacity) as usize;
70 base_buffer[idx] = *root;
71 }
72}