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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
//! Delta encoding for Ethereum sync committees.
//!
//! A sync committee remains unchanged for an entire sync committee period and
//! is replaced when a new period begins. This makes equality-based encoding
//! sufficient: unchanged committees require no payload, while a committee
//! transition stores the complete serialized target committee.
//!
//! The module operates on the serialized SSZ representation of a
//! `SyncCommittee`. It does not inspect individual committee members or SSZ
//! fields; the serialized bytes are treated as an opaque payload.
//!
//! # Encoding strategy
//!
//! Two representations are supported:
//!
//! - [`SyncCommitteeDiff::Unchanged`] when the serialized base and target
//! committees are identical.
//! - [`SyncCommitteeDiff::FullReplacement`] when the serialized committees
//! differ. The complete serialized target committee is stored in the
//! replacement payload.
//!
//! A sparse representation is intentionally not provided. Unlike some
//! consensus-state fields, a sync committee is replaced as a whole when it
//! changes, so storing the complete target representation is straightforward
//! and deterministic.
//!
//! # State transition
//!
//! Applying a delta reconstructs the target bytes:
//!
//! ```text
//! base committee + delta -> target committee
//! ```
//!
//! [`SyncCommitteeDiff::Unchanged`] leaves the base bytes untouched, so the
//! base and target must already be identical for the delta to represent the
//! target state.
//!
//! [`SyncCommitteeDiff::FullReplacement`] replaces the base bytes entirely,
//! so the original base contents do not affect the resulting state.
//!
//! The delta does not depend on the sync committee period number. The caller
//! is responsible for determining which consensus-state field is being
//! diffed.
//!
//! # SSZ representation
//!
//! The functions accept the raw serialized SSZ representation of the
//! `SyncCommittee` container. The bytes are treated as opaque data and are
//! copied without decoding or re-encoding individual committee members.
//!
//! `SyncCommittee` is a fixed-size consensus container. Callers should
//! therefore provide the serialized container bytes directly rather than
//! adding a list-length prefix.
//!
//! The examples below use simple byte strings to represent opaque serialized
//! data; they are illustrative and are not intended to be valid
//! `SyncCommittee` SSZ encodings.
//!
//! # Complexity
//!
//! Let `n` be the serialized size of the committee.
//!
//! - [`diff_sync_committee`] performs an O(n) byte comparison.
//! - [`apply_sync_committee`] is O(1) for
//! [`SyncCommitteeDiff::Unchanged`].
//! - [`apply_sync_committee`] is O(n) for
//! [`SyncCommitteeDiff::FullReplacement`].
//!
//! Additional memory is O(n) when a changed committee is encoded because the
//! target bytes are copied into the replacement payload.
//!
//! # Example
//!
//! ```
//! use eth_state_diff::sync_committee::diff_sync_committee;
//! use eth_state_diff::types::SyncCommitteeDiff;
//!
//! let base = b"committee-a";
//! let target = b"committee-a";
//!
//! let delta = diff_sync_committee(base, target);
//!
//! assert_eq!(delta, SyncCommitteeDiff::Unchanged);
//! ```
//!
//! A committee transition produces a full replacement:
//!
//! ```
//! use eth_state_diff::sync_committee::diff_sync_committee;
//! use eth_state_diff::types::SyncCommitteeDiff;
//!
//! let base = b"committee-a";
//! let target = b"committee-b";
//!
//! let delta = diff_sync_committee(base, target);
//!
//! assert_eq!(
//! delta,
//! SyncCommitteeDiff::FullReplacement(target.to_vec())
//! );
//! ```
use crate;
/// Computes a delta between two serialized Ethereum sync committees.
///
/// The serialized committee bytes are compared as opaque byte sequences.
///
/// If `base_ssz` and `target_ssz` are identical, this function returns
/// [`SyncCommitteeDiff::Unchanged`] and stores no committee bytes.
///
/// If they differ, this function returns
/// [`SyncCommitteeDiff::FullReplacement`] containing a copy of `target_ssz`.
/// The target committee is therefore stored in its entirety rather than
/// attempting to encode individual member changes.
///
/// # Arguments
///
/// * `base_ssz` - Serialized SSZ bytes of the committee in the base state.
/// * `target_ssz` - Serialized SSZ bytes of the committee in the target state.
///
/// Both arguments must represent the same consensus-state field and use the
/// same serialization format.
///
/// # Returns
///
/// A [`SyncCommitteeDiff`] representing the transition from `base_ssz` to
/// `target_ssz`.
///
/// # Complexity
///
/// O(n) time, where `n` is the length of the serialized committee.
///
/// If the committees differ, O(n) additional space is required for the copied
/// replacement payload.
///
/// # Example
///
/// ```
/// use eth_state_diff::sync_committee::diff_sync_committee;
/// use eth_state_diff::types::SyncCommitteeDiff;
///
/// let base = b"committee-a";
/// let target = b"committee-b";
///
/// let delta = diff_sync_committee(base, target);
///
/// assert_eq!(
/// delta,
/// SyncCommitteeDiff::FullReplacement(target.to_vec())
/// );
/// ```
/// Applies a sync committee delta to a serialized SSZ committee in place.
///
/// [`SyncCommitteeDiff::Unchanged`] leaves `base` unchanged.
///
/// [`SyncCommitteeDiff::FullReplacement`] clears `base` and replaces it with
/// the serialized target committee stored in the delta.
///
/// After successful execution, `base` contains the target committee
/// represented by `delta`.
///
/// # Arguments
///
/// * `base` - Serialized SSZ bytes of the base committee. This buffer is
/// modified in place.
/// * `delta` - Archived sync committee delta to apply.
///
/// # Correctness
///
/// For [`SyncCommitteeDiff::Unchanged`], `base` is expected to already contain
/// the target committee because the delta represents no state change.
///
/// For [`SyncCommitteeDiff::FullReplacement`], the original contents of
/// `base` are irrelevant because the entire buffer is replaced by the target
/// bytes stored in the delta.
///
/// # Complexity
///
/// - [`SyncCommitteeDiff::Unchanged`]: O(1) time and O(1) additional space.
/// - [`SyncCommitteeDiff::FullReplacement`]: O(n) time, where `n` is the
/// replacement size.
///
/// The replacement case may allocate if `base` does not have sufficient
/// capacity for the target committee.
///
/// # Example
///
/// ```
/// use eth_state_diff::sync_committee::{apply_sync_committee, diff_sync_committee};
/// use eth_state_diff::types::ArchivedSyncCommitteeDiff;
///
/// let mut base = b"committee-a".to_vec();
/// let target = b"committee-b";
///
/// let delta = diff_sync_committee(&base, target);
///
/// let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&delta).expect("failed to serialize");
/// let archived = rkyv::access::<ArchivedSyncCommitteeDiff, rkyv::rancor::Error>(&bytes)
/// .expect("failed to access");
///
/// apply_sync_committee(&mut base, archived);
///
/// assert_eq!(base, target);
/// ```