You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(claude-md): collapse storage docs into a pointer to data_storage.md
CLAUDE.md had grown a near-duplicate of docs/data_storage.md once the
BlockRoots/table-count fixes landed on this branch. Collapse the three
storage sections under Common Gotchas into a short paragraph plus a few
must-not-forget bullets, following the pattern already used for the HTTP
Servers section.
Before deleting anything, verified each CLAUDE.md claim against source and
moved what data_storage.md was missing there instead: the BlockRoots table
(key encoding, per-table section, write-path and pruning-list entries), the
constant-at-runtime facts for Metadata["config"] and the genesis validator
registry, and a stronger statement of the StateDiff config/validators
invariant naming validate_history_append.
Also fixes two inaccuracies found during verification: BlockRoots backs
get_signed_blocks_by_slot_range (BlocksByRange serving), not the RPC
by-slot endpoint, which resolves through historical_block_hashes instead;
and ChainConfig has no validator_count field, only genesis_time.
`config` is the odd one out: `init_store` writes it once at bootstrap and
228
+
nothing ever rewrites it afterward (it has a getter, `Store::config`, but no
229
+
setter). That also makes it the DB's fingerprint: `from_db_state` refuses to
230
+
resume a data directory whose persisted `genesis_time` disagrees with the
231
+
node's own config file, treating the mismatch as an empty DB (see
232
+
[Startup and Restore](#startup-and-restore)). Every other `Metadata` key is
233
+
mutated in place as the chain progresses.
234
+
199
235
### LiveChain
200
236
201
237
`slot ‖ root → parent_root`. A pure **index** for fork choice: it lets
@@ -219,9 +255,9 @@ or change predictably. Instead, `insert_state` writes:
219
255
1.**Always** a `StateDiff` keyed by the block root, linked to its parent via
220
256
`base_root` (the block's `parent_root`).
221
257
2.**Only at anchors** a full snapshot into `States`. A block is an anchor
222
-
when it crosses a `SNAPSHOT_ANCHOR_INTERVAL = 1024` slot boundary relative
258
+
when it crosses a `SNAPSHOT_ANCHOR_INTERVAL` slot boundary relative
223
259
to its parent (~68 minutes at 4-second slots). This bounds any
224
-
reconstruction walk to at most 1024 diff applications.
260
+
reconstruction walk to at most `SNAPSHOT_ANCHOR_INTERVAL` diff applications.
225
261
226
262
A `StateDiff` stores only what cannot be recovered elsewhere: the target slot,
227
263
justified/finalized checkpoints, and the justification fields
@@ -235,6 +271,16 @@ small under healthy finality). The rest is deliberately omitted:
235
271
|`latest_block_header`| The `BlockHeaders` table |
236
272
|`historical_block_hashes`| Regenerated from `base_root` + the slot gap (the state transition appends the parent root plus one zero per skipped slot, so the append is fully predictable) |
237
273
274
+
Omitting `config` and `validators` is a bet, not a fallback: a diff carries no
275
+
copy of either, so if a future state transition ever mutated one, every state
276
+
reconstructed past that point would silently pick up the ancestor snapshot's
277
+
stale value instead. The `historical_block_hashes` append is checked rather
278
+
than trusted blindly: `validate_history_append`
279
+
(`crates/storage/src/state_diff.rs`) rejects a diff whose appended hashes
280
+
don't match the expected slot gap or aren't zero-filled for skipped slots,
281
+
so a broken append surfaces at diff-creation time instead of corrupting a
282
+
later reconstruction.
283
+
238
284
Reads go through `get_state`, which tries three levels:
239
285
240
286
1. An in-memory LRU cache (`STATE_CACHE_CAPACITY = 32` states, keyed by block
@@ -298,6 +344,7 @@ sequence of independent write batches:
298
344
│
299
345
└─ 4. update_head() Metadata: head
300
346
(re-runs fork choice) (+ justified/finalized if advanced,
347
+
+ BlockRoots diff (canonical index),
301
348
+ pruning on finalization)
302
349
```
303
350
@@ -340,10 +387,10 @@ processed):
340
387
not needed for fork choice, reorg safety, or re-aggregation once outside
341
388
the window.
342
389
343
-
**Never pruned:**`BlockHeaders`, `BlockBodies`, `States`, `StateDiffs`, and
344
-
`Metadata`. Headers, bodies, and the snapshot+diff chain are the full
345
-
historical record; only the proof blobs and the fork choice index are
0 commit comments