1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//! Delta encoding for append-only historical logs.
//!
//! Both `historical_roots` (Phase0 through Bellatrix) and
//! `historical_summaries` (Capella and later) are append-only logs that grow
//! according to the historical-period boundary defined by the consensus
//! protocol.
//!
//! This module uses the base and target slots to determine how many historical
//! items must have been appended. It therefore avoids comparing the complete
//! serialized log contents.
//!
//! The resulting delta contains only the newly appended SSZ items.
use crate;
/// Number of slots in one historical period.
const SLOTS_PER_HISTORICAL_PERIOD: u64 = 8192;
/// Calculates the expected number of historical log items at a given slot.
///
/// When `activation_slot` is provided, the log is considered inactive through
/// that slot and begins accumulating items afterwards. When no activation slot
/// is provided, the calculation starts from genesis.
///
/// This helper follows the historical-period calculation used by the
/// corresponding consensus state field.
/// Computes a delta between two historical log states.
///
/// The number of appended items is derived from `base_slot` and `target_slot`
/// using the protocol-defined historical period rather than by comparing the
/// serialized base and target logs.
///
/// Only the newly appended portion of `target_ssz` is stored in the returned
/// [`HistoricalLogDiff`].
///
/// # Arguments
///
/// * `base_slot` - Slot corresponding to the base state.
/// * `target_slot` - Slot corresponding to the target state.
/// * `target_ssz` - Complete serialized SSZ representation of the target
/// historical log.
/// * `item_ssz_size` - Serialized SSZ size of one historical log item.
/// * `activation_slot` - Optional slot at which the historical log becomes
/// active. This is used for logs introduced by a later fork.
///
/// # Returns
///
/// - [`HistoricalLogDiff::Unchanged`] if the protocol-derived log length has
/// not increased between the two slots.
/// - [`HistoricalLogDiff::Append`] containing the newly appended serialized
/// items otherwise.
///
/// # Panics
///
/// This function does not intentionally panic in release builds. In debug
/// builds, it asserts that `target_ssz` contains enough bytes for the number
/// of items derived from the slot calculation.
///
/// The caller must provide a non-zero `item_ssz_size` and a target SSZ buffer
/// consistent with the protocol-derived log length.
///
/// # Complexity
///
/// O(k) time and O(k) additional space, where *k* is the number of newly
/// appended serialized bytes. The number of existing log entries does not
/// affect the computation.
///
/// # Example
///
/// ```
/// use eth_state_diff::historical_log::diff_historical_log;
/// use eth_state_diff::types::HistoricalLogDiff;
///
/// const ITEM_SIZE: usize = 32;
///
/// // One historical period has elapsed, so one item is appended.
/// let target = vec![0u8; ITEM_SIZE];
///
/// let delta = diff_historical_log(
/// 0,
/// 8192,
/// &target,
/// ITEM_SIZE,
/// None,
/// );
///
/// assert_eq!(
/// delta,
/// HistoricalLogDiff::Append(target),
/// );
/// ```
/// Applies a historical log delta to a serialized historical log in place.
///
/// [`HistoricalLogDiff::Unchanged`] leaves the existing log untouched.
///
/// [`HistoricalLogDiff::Append`] appends the serialized historical items
/// contained in the delta to the existing log.
///
/// The delta is assumed to have been generated for the supplied base state.
/// This function does not validate that the base log corresponds to the
/// state from which the delta was created.
///
/// # Complexity
///
/// - [`HistoricalLogDiff::Unchanged`][]: O(1).
/// - [`HistoricalLogDiff::Append`]: O(k), where *k* is the number of appended
/// bytes.
///
/// # Example
///
/// ```
/// use eth_state_diff::historical_log::{
/// apply_historical_log,
/// diff_historical_log,
/// };
/// use eth_state_diff::types::ArchivedHistoricalLogDiff;
///
/// const ITEM_SIZE: usize = 4;
///
/// let mut base = vec![1u8, 2, 3, 4];
/// let target = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
///
/// let delta = diff_historical_log(
/// 8191,
/// 16384,
/// &target,
/// ITEM_SIZE,
/// None,
/// );
///
/// let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&delta).unwrap();
/// let archived = unsafe {
/// rkyv::access_unchecked::<ArchivedHistoricalLogDiff>(&bytes)
/// };
///
/// apply_historical_log(&mut base, archived);
///
/// assert_eq!(base, target);
/// ```