eth-state-diff 0.2.1

Fork-aware, domain-specific delta encoding for Ethereum consensus-layer state.
Documentation
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# eth_state_diff

[![Crates.io](https://img.shields.io/crates/v/eth-state-diff.svg)](https://crates.io/crates/eth-state-diff)
[![Docs.rs](https://docs.rs/eth-state-diff/badge.svg)](https://docs.rs/eth-state-diff)

Compact binary delta encoding for Ethereum consensus-layer state.

`eth_state_diff` computes a delta between two beacon states and applies that
delta to reconstruct the target state. Instead of storing or transmitting a
complete state snapshot for every state transition, the library encodes only
the changes required to move from one state to the next.

The crate is intended for **archival storage, state synchronization, fast-sync,
and historical state reconstruction** where storing full consensus-state
snapshots is unnecessarily expensive.

---

## Why deltas?

Ethereum consensus state contains many fields with highly predictable update
patterns.

Some fields are append-only:

- historical roots
- historical summaries
- attestations
- Eth1 data votes

Some are sparse:

- validator fields
- inactivity scores
- participation flags
- slashing totals

Some are fixed-capacity circular buffers:

- block roots
- state roots
- RANDAO mixes
- slashings

Others behave like FIFO queues:

- pending deposits
- pending partial withdrawals
- pending consolidations

A generic byte-level diff cannot take advantage of these properties.

`eth_state_diff` therefore uses **field-specific encodings** that match the
semantics of each consensus-state component.

The result is a delta representation that is small before compression and
highly compressible when passed through a general-purpose compressor such as
zstd.

---

## Architecture

The library sits at the state/disk boundary rather than defining a beacon-state
implementation of its own.

```text
                Consensus Client
              ┌────────┴────────┐
              │                 │
         DiffSource        DiffTarget
              │                 ▲
              ▼                 │
           create()         apply()
              │                 │
              ▼                 │
      BeaconStateDelta ─────────┘
        rkyv / storage
       Archived delta
```

The client provides access to its state through two traits:

* `DiffSource` — exposes the base and target state to the diff algorithms.
* `DiffTarget` — exposes mutable state storage to the application algorithms.

This keeps the delta engine independent of the consensus client's internal
state representation.

The client may use its own state types, containers, memory layout, or storage
engine as long as it can provide the interfaces required by these traits.

---

## Delta strategies

Each state component uses a representation appropriate to its update pattern.

| State component             | Delta strategy                                            |
| --------------------------- | --------------------------------------------------------- |
| Balances                    | Packed 2-bit operation tags + compact difference encoding |
| Validators                  | Field-level patches + appended SSZ records                |
| Block roots                 | Circular-buffer writes                                    |
| State roots                 | Circular-buffer writes                                    |
| RANDAO mixes                | Epoch-indexed circular-buffer writes                      |
| Slashings                   | Sparse circular-buffer updates                            |
| Eth1 data votes             | Append/reset encoding                                     |
| Historical roots            | Protocol-defined append intervals                         |
| Historical summaries        | Protocol-defined append intervals                         |
| Phase 0 attestations        | Unchanged / append / replacement                          |
| Participation               | Sparse delta-varint updates / all-zero representation     |
| Inactivity scores           | Sparse updates / all-zero representation                  |
| Sync committees             | Unchanged / full replacement                              |
| Pending deposits            | Validated FIFO delta / full replacement                   |
| Pending partial withdrawals | Validated FIFO delta / full replacement                   |
| Pending consolidations      | Validated FIFO delta / full replacement                   |

The algorithms operate on the serialized representation where doing so avoids
deserializing individual SSZ objects unnecessarily.

---

## State reconstruction

A typical workflow is:

```rust
let delta = eth_state_diff::create(&source);

// Serialize or archive `delta` for storage or transmission.
//
// Later, obtain an archived representation and apply it:
//
// let target = eth_state_diff::apply(state, archived_delta)?;
```

`create` computes the transition using the state exposed by `DiffSource`.

`apply` validates the fork and fork-specific fields before modifying the
destination state through `DiffTarget`.

The destination state must correspond to the base state from which the delta
was created.

---

## Client integration

A consensus client integrates with the library by implementing
`DiffSource` and `DiffTarget`.

The traits intentionally expose primitive buffers, slices, and iterators rather
than client-specific consensus types.

This allows the delta algorithms to operate independently of the client's
internal state representation.

### `DiffSource`

`DiffSource` provides the base and target values used to construct a delta.

Conceptually:

```text
DiffSource
    ├── fork
    ├── base slot / target slot
    ├── scalar state
    ├── balances
    ├── validators
    ├── roots
    ├── RANDAO
    ├── slashings
    ├── Eth1 votes
    └── fork-specific fields
           create()
```

Fork-specific accessors should return `None` when the field does not exist for
the current fork.

### `DiffTarget`

`DiffTarget` provides mutable access to the destination state.

```text
ArchivedBeaconStateDelta
          apply()
            ├── fork validation
            ├── universal fields
            ├── Phase 0 fields
            ├── Altair+ fields
            ├── Capella+ fields
            └── Electra+ fields
       reconstructed state
```

Fork-specific accessors should return `None` when the corresponding field does
not exist in the destination state.

---

## Scalar state

Not every consensus-state field requires a specialized delta algorithm.

Fields without a dedicated encoder are represented by `scalar_header`.

The client must serialize these fields to SSZ and concatenate them in the exact
order required by the consensus specification for the target fork.

The scalar header generally contains fields such as:

* `genesis_time`
* `genesis_validators_root`
* `slot`
* `fork`
* `latest_block_header`
* `eth1_data`
* `eth1_deposit_index`
* `justification_bits`
* `previous_justified_checkpoint`
* `current_justified_checkpoint`
* `finalized_checkpoint`
* `latest_execution_payload_header`
* Electra+ scalar fields such as withdrawal and deposit-request state
* Fulu+ `proposer_lookahead`

Fields with dedicated delta representations **must not** also be included in
the scalar header.

For example, balances, historical summaries, and pending deposits are handled
by their own encoders.

---

## Fork handling

`BeaconStateDelta` records the [`ForkName`] associated with the transition.

Fork-specific fields are represented as `Option<T>`:

```rust
pub struct BeaconStateDelta {
    pub fork: ForkName,

    // ...

    pub previous_participation: Option<ParticipationDiff>,
    pub historical_summaries: Option<HistoricalLogDiff>,
    pub pending_deposits: Option<QueueDiff>,
}
```

This allows one delta type to represent transitions across all supported forks
while keeping fields that do not exist for a particular fork absent from the
delta.

`apply` validates these invariants.

For example:

* Altair fields cannot be present in a Phase 0 delta.
* Capella fields cannot be present before Capella.
* Electra fields cannot be present before Electra.
* Phase 0 attestation fields cannot be present after their removal.

A fork mismatch results in an error rather than silently applying the delta to
the wrong state representation.

---

## Serialization

Delta structures derive the [`rkyv::Archive`], `Serialize`, and `Deserialize`
traits.

This makes deltas suitable for archived storage and zero-copy access to the
serialized delta representation where supported by the application.

The diff algorithms themselves remain independent of the serialization layer.

A typical storage pipeline can therefore be:

```text
Beacon states
  create()
BeaconStateDelta
   rkyv
 compressed delta
 storage / network
```

The resulting byte representation can additionally be compressed using a
general-purpose compressor such as zstd.

---

## Performance model

The library is designed around **linear iteration over consensus-state components**.

For large vectors such as balances, validators, and participation flags, the diff algorithms process elements sequentially. This keeps the comparison work predictable and allows the underlying client to choose the most appropriate storage representation.

The API intentionally accepts iterators for large state components rather than requiring callers to first materialize them into contiguous buffers. A client with a tree-backed or otherwise non-contiguous state representation can expose an iterator over its existing data and let the delta algorithm perform a single sequential pass.

For clients whose state is already stored densely, these linear scans naturally benefit from CPU caching, hardware prefetching, and compiler optimizations. For tree-backed clients, the iterator interface avoids requiring a separate dense copy solely for delta generation.

This is a deliberate trade-off: the delta algorithms require an O(n) traversal of the logical state components, but they do not require those components to be physically materialized as contiguous `Vec`s beforehand.

---

## Validation and safety

`apply` returns:

```rust
Result<M, Error>
```

rather than assuming that an arbitrary delta is valid for an arbitrary state.

The application path validates:

* fork compatibility;
* fork-specific field presence;
* archived fork decoding;
* malformed delta payloads.

Individual delta encoders also use conservative representations when their
assumptions about the state transition are not satisfied.

For example, the pending-queue encoder uses strict item-boundary validation
before emitting a FIFO delta and falls back to a full replacement when the
transition cannot safely be represented as a FIFO operation.

---

## Supported consensus forks

`ForkName` currently represents:

* Phase 0
* Altair
* Bellatrix
* Capella
* Deneb
* Electra
* Fulu
* Gloas
* Heze

Fork-specific state fields are selected through the `Option<T>` fields in
`BeaconStateDelta`.

---

## Design principles

The crate is built around a few principles:

### Match the encoding to the data structure

A circular buffer should be represented as circular-buffer writes rather than
as a generic byte diff.

A sparse vector should be represented as sparse updates rather than rewriting
the entire vector.

### Prefer protocol invariants over byte comparisons

Where the consensus protocol determines when an item must be appended, the
encoder can derive that information from slots and epochs rather than scanning
the complete buffers.

### Be conservative when assumptions fail

Compact encodings are only useful if they can be reconstructed correctly.

Where a transition cannot be safely represented by a specialized encoding,
the implementation falls back to a replacement representation.

### Keep consensus clients independent

The library does not require a particular beacon-state implementation.

Clients remain responsible for their own state representation while
`eth_state_diff` provides the delta algorithms.

---

## API

The primary public API is:

* [`create`] — compute a delta from a `DiffSource`.
* [`apply`] — apply an archived delta to a `DiffTarget`.
* [`BeaconStateDelta`] — complete state-transition representation.
* [`DiffSource`] — read-only state integration trait.
* [`DiffTarget`] — mutable state integration trait.
* [`ForkName`] — consensus fork identifier.
* [`ListMutTarget`] — abstraction for mutable primitive collections.

See the [API documentation on docs.rs](https://docs.rs/eth-state-diff)
for the complete interface and individual delta algorithms.

---

## License

Licensed under either of:

* Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE)
* MIT License ([LICENSE-MIT]LICENSE-MIT)

at your option.