eth_state_diff/types.rs
1use rkyv::{Archive, Deserialize, Serialize};
2
3/// Size, in bytes, of an SSZ-serialized validator record.
4pub const VALIDATOR_SSZ_SIZE: usize = 121;
5
6/// Protocol-defined withdrawability delay for non-slashed validators.
7pub const MIN_VALIDATOR_WITHDRAWABILITY_DELAY: u64 = 256;
8
9/// A field-level modification to a validator record.
10///
11/// Validator fields are diffed independently to avoid rewriting entire
12/// 121-byte SSZ validator records when only a small subset of fields change.
13#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
14pub enum ValidatorField {
15 WithdrawalCredentials,
16 EffectiveBalance,
17 Slashed,
18 ActivationEligibilityEpoch,
19 ActivationEpoch,
20 ExitEpoch,
21 WithdrawableEpochSlashed,
22}
23
24/// A modification to a single validator field.
25///
26/// Each patch identifies the validator index, the field being modified,
27/// and the replacement SSZ bytes for that field.
28#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
29pub struct ValidatorPatch {
30 pub index: u32,
31 pub field: ValidatorField,
32 pub value: Vec<u8>,
33}
34
35/// Compact representation of the difference between two validator registries.
36///
37/// Only modified validator fields are recorded. Newly appended validators are
38/// stored as raw SSZ bytes and appended during reconstruction.
39#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
40pub struct ValidatorsDiff {
41 pub patches: Vec<ValidatorPatch>,
42 pub appended_validators: Vec<u8>,
43}
44
45/// Compact representation of the difference between two balance snapshots.
46///
47/// A `BalanceDiffs` stores only the information required to transform one
48/// balance vector into another.
49///
50/// Small balance differences are encoded as mode-adjusted zig-zag varints,
51/// while values that can not be represented efficiently are stored explicitly.
52///
53/// This structure is intended to be serialized with `rkyv`.
54#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
55pub struct BalancesDiff {
56 pub tags: BitTagVec,
57
58 pub mode: i64,
59
60 pub varint_payload: Vec<u8>,
61 pub target_values: Vec<u64>,
62 pub appended_balances: Vec<u64>,
63}
64
65/// Compact delta encoding for participation flags.
66///
67/// A participation delta stores only the information required to transform one
68/// participation vector into another.
69///
70/// The encoder automatically selects the most compact representation:
71///
72/// - [`ParticipationDiff::AllZeros`] when the target vector contains only
73/// zero-valued flags.
74/// - [`ParticipationDiff::Sparse`] when only a subset of indices change.
75///
76/// This type is intended to be serialized using `rkyv`.
77#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
78pub enum ParticipationDiff {
79 /// Represents a participation vector consisting entirely of zeros.
80 ///
81 /// During reconstruction the destination vector is resized to `len`
82 /// and every entry is set to `0`.
83 AllZeros(usize),
84
85 /// Sparse delta representation.
86 ///
87 /// Only indices whose values differ from the base vector are stored.
88 ///
89 /// Changed indices are encoded as delta-varint gaps to reduce storage
90 /// requirements for sparsely changing participation flags.
91 Sparse {
92 /// Delta-varint encoded gaps between successive modified indices.
93 ///
94 /// Each decoded gap is added to the previous modified index to recover
95 /// the absolute index.
96 sparse_indices: Vec<u8>,
97
98 /// Replacement participation flags.
99 ///
100 /// Each entry corresponds one-to-one with a decoded index from
101 /// `sparse_indices`.
102 new_values: Vec<u8>,
103
104 /// Participation flags for validators that exist only in the target
105 /// vector.
106 extension: Vec<u8>,
107 },
108}
109
110/// Compact representation of the difference between two inactivity-score
111/// vectors.
112///
113/// The encoder automatically selects the most compact representation:
114///
115/// - [`InactivityDiff::AllZeros`] when the target vector contains only zero
116/// scores.
117/// - [`InactivityDiff::Sparse`] when only a subset of scores change.
118#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
119pub enum InactivityDiff {
120 /// Fast path for the 99.9% case where no scores change in the overlapping set.
121 AllZeros(u32),
122
123 /// Sparse inactivity-score updates.
124 ///
125 /// Only modified indices are stored.
126 Sparse {
127 /// Indices of modified inactivity scores.
128 indices: Vec<u32>,
129
130 /// Replacement scores corresponding one-to-one with `indices`.
131 new_values: Vec<u64>,
132
133 /// Scores for validators appended to the target vector.
134 extensions: Vec<u64>,
135 },
136}
137
138/// Sequence of roots written while advancing a circular root buffer.
139///
140/// Roots are stored in chronological slot order beginning at the supplied
141/// base slot used during reconstruction.
142///
143/// The buffer capacity is intentionally omitted from the encoding since it is
144/// determined by the destination buffer.
145#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
146pub struct RootsDiff {
147 /// Sequential list of 32-byte hashes added to the circular buffer
148 /// between the base slot and target slot.
149 pub roots: Vec<[u8; 32]>,
150}
151
152/// Sparse updates for a slashing ring buffer.
153///
154/// Each update stores the destination ring index together with its new slashing
155/// value.
156///
157/// Only epochs whose values changed are included.
158#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
159pub struct SlashingsDiff {
160 /// A sparse list of (index, new_slashing_amount).
161 /// Index fits in u16 (max size is 8192).
162 /// In a 32-epoch window, this Vec will have a length of 0 or 1.
163 pub updates: Vec<(u16, u64)>,
164}
165
166/// Sequence of RANDAO mixes written while advancing through epochs.
167///
168/// Mixes are stored in chronological epoch order beginning with the epoch
169/// containing the supplied base slot used during reconstruction.
170///
171/// The circular buffer capacity is intentionally omitted from the encoding, as
172/// it is determined by the destination buffer during application.
173#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
174pub struct RandaoDiff {
175 /// Sequential list of 32-byte randao reveals added during this window.
176 pub mixes: Vec<[u8; 32]>,
177}
178
179/// Delta representation of the Eth1 data vote list.
180///
181/// The encoder distinguishes between ordinary vote accumulation and the
182/// protocol-defined reset that occurs after an Eth1 voting period.
183#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
184pub enum Eth1DataVotesDiff {
185 /// Additional votes appended to the existing list.
186 Append(Vec<u8>),
187
188 /// The vote list was reset before appending new votes.
189 ResetAndAppend(Vec<u8>),
190}
191
192/// Delta representation for FIFO queues.
193///
194/// Rather than storing the full queue, the delta records how many items were
195/// consumed from the front and the newly appended items at the back.
196#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
197pub struct FifoQueueDiff {
198 /// The number of items consumed from the front of the base queue.
199 pub consumed_count: u32,
200
201 /// The raw SSZ bytes of *only* the newly appended items at the end.
202 pub appended_items: Vec<u8>,
203}
204
205#[derive(Eq, PartialEq, Debug, Clone, Default, Archive, Deserialize, Serialize)]
206pub struct BitTagVec {
207 /// Packed storage containing four 2-bit tags per byte.
208 pub data: Vec<u8>,
209 pub len: usize,
210}
211
212/// Balance is unchanged.
213pub const SET_NO_CHANGE: u8 = 0b00;
214
215/// Balance is replaced with zero.
216pub const SET_TO_ZERO: u8 = 0b10;
217
218/// Balance is reconstructed by applying a stored difference.
219pub const SET_TO_DIFF: u8 = 0b11;
220
221/// Balance is replaced with its absolute target value.
222pub const SET_TO_TARGET_VALUE: u8 = 0b01;
223
224/// Dense 2-bit tag vector.
225///
226/// Four entries are packed into each byte.
227///
228/// Each entry describes how the corresponding validator balance should be
229/// reconstructed.
230///
231/// Encoding:
232///
233/// - `00` — unchanged
234/// - `01` — absolute target value
235/// - `10` — set to zero
236/// - `11` — apply encoded difference
237impl BitTagVec {
238 pub fn new(len: usize) -> Self {
239 let bytes = len.div_ceil(4);
240 Self {
241 data: vec![0; bytes],
242 len,
243 }
244 }
245
246 /// Sets the tag at `idx`.
247 #[inline]
248 pub fn set(&mut self, idx: usize, tag: u8) {
249 let byte = idx / 4;
250 let shift = (idx % 4) * 2;
251
252 self.data[byte] |= (tag & 0b11) << shift;
253 }
254
255 /// Returns the tag stored at `idx`.
256 #[inline]
257 pub fn get(&self, idx: usize) -> u8 {
258 let byte = idx / 4;
259 let shift = (idx % 4) * 2;
260 (self.data[byte] >> shift) & 0b11
261 }
262}