Expand description
§reth-snap-sync
snap/2 state synchronization, as specified by EIP-8189.
Instead of executing every block from genesis, a node downloads the state of a recent block from peers and verifies it against that block’s state root. snap/2 keeps that state current with EIP-7928 block access lists (BALs), which record every field each block changes, replacing snap/1’s trie-node healing.
This crate owns the synchronization logic and its progress. Requests and proof checks come from
reth-downloaders, and running the sync inside a node is left to node integration.
§How a sync runs
SnapBootstrap drives one sync from start to hand-off:
- Pick a pivot. A recent finalized block is preferred, since it cannot be reorged; otherwise the pivot sits 64 blocks behind the head by default, the EIP’s example distance. With no eligible block the sync waits.
- Download the state at the pivot. Accounts are fetched in key-order ranges, proved against the pivot’s state root. A range commits only once its contracts’ storage and code are persisted, whether supplied with it or stored beforehand.
- Advance the pivot. Peers only keep recent state, so once the pivot lags more than 96 blocks by default the sync re-anchors to a newer block. BALs of the blocks in between carry the state already downloaded forward, applied strictly in block order, and the remaining ranges download at the new root.
- Repair what BALs cannot. BALs only overwrite the fields their blocks change, so entries left stale for another reason are scheduled for repair. Once the BALs reach the pivot, each scheduled account and slot is fetched again on its own, proved against the pivot’s root.
- Hand off. Once every account is downloaded, the BALs reach the pivot and no repairs remain, the state goes to the merkle stage, which rebuilds the trie. It is accepted only when the root matches the pivot’s header.
use reth_snap_sync::SnapPivotPolicy;
let policy = SnapPivotPolicy::default();
// Without a finalized block, anchor at the EIP's example distance.
assert_eq!(policy.pivot_block(1_000, None), Some(936));
// A recent finalized block is anchored to directly.
assert_eq!(policy.pivot_block(1_000, Some(950)), Some(950));
// Stalled finality falls back to the example distance.
assert_eq!(policy.pivot_block(1_000, Some(500)), Some(936));
// A chain shorter than the head distance has no pivot yet.
assert_eq!(policy.pivot_block(4, None), None);§Design choices
- State is written in place. Downloads land directly in the hashed state tables, so there is no staging copy to move afterwards. An attempt record owns that state: every write carries the attempt and pivot it was fetched for, and responses to an older attempt or pivot are refused.
- Progress commits with its data. Each range, storage chunk or BAL commits in the same transaction as the progress it advances, so a stopped sync resumes from what the database holds.
- Nothing counts before its dependencies. An account range commits only with storage matching its accounts’ roots and code matching their hashes. Storage too large for one response is kept ahead of its range, tied to the attempt and pivot.
- BALs never skip a block. A block whose BAL no peer serves holds back every later one, and a BAL only applies on top of its parent.
- The trie is rebuilt once, at the end. The existing merkle stage builds it from the downloaded state, instead of maintaining one during the download.
- Starting over is the fallback. An attempt restarts when catch-up falls behind the BALs peers still serve, or when a reorg across its pivot cannot be repaired.
§Reorgs across the pivot
A reorg that orphans the pivot leaves the downloaded state holding the abandoned branch’s changes. The attempt keeps the headers of the last 64 blocks through its pivot, so it can find the last block both branches share and fetch the orphaned blocks’ BALs:
- Every field and storage slot those BALs changed is scheduled for repair, catch-up rewinds to the shared block, and the attempt re-anchors to a new canonical pivot.
- Applying the new branch’s BALs drops whatever they overwrite, so only entries changed on the orphaned branch alone stay scheduled.
- Once catch-up reaches the new pivot, those entries are fetched again on their own, proved against its root, and the hand-off waits until none remain.
Orphaned BALs no peer serves are waited for while the head is within the served-state window (128 blocks) of the ancestor. A reorg reaching further back than the kept headers, orphaned BALs still unserved past that window, or a reorg after the hand-off to the merkle stage starts the attempt over instead.
snap/1 synchronization is not covered: this design keeps the state current with BALs, which only snap/2 serves, rather than with snap/1’s trie-node healing.
Structs§
- Account
Coverage - How far the account key space has been downloaded.
- Account
Range Download - Downloads the account ranges an attempt still needs, one at a time in key order.
- BalState
Update - Changes one block access list makes to the downloaded state.
- Block
Access List Catch Up - Applies the lists of the canonical blocks after the applied one, one request at a time.
- Bytecode
Download - Downloads the code an account range still needs, one request at a time.
- Catch
UpProgress - How far past the pivot the downloaded state has been carried.
- Snap
Bootstrap - Drives snap synchronization until its downloaded state is ready for the trie rebuild.
- Snap
Generation - One attempt at downloading state, anchored to the pivot block it targets.
- Snap
Pivot Policy - Distance and history bounds that decide where a generation is anchored.
- Snap
Reorg - Where a reorg left an attempt: the last block both branches share and the orphaned blocks after it.
- Snap
Sync Session - One snap synchronization attempt, from the pivot it targets to the work it owns.
- Snap
Write - What a write presents to prove it belongs to the attempt owning the persisted state.
- State
Repairs - Accounts and storage slots to fetch again at the pivot, in key order.
- Storage
Chunk - Verified slots of one contract, from the slot they were requested at.
- Storage
Progress - How far the contracts of the account range being downloaded have their storage persisted.
- Storage
Range Download - Downloads the storage an account range’s contracts still need, and repaired slots again.
- Verified
Range - A verified range with the write it was fetched under.
- Verified
Snap State - Downloaded state whose trie root matched the header of the block it is anchored to.
Enums§
- Account
Range Step - What one request of an
AccountRangeDownloadproduced. - Bytecode
Step - What one request of a
BytecodeDownloadproduced. - Catch
UpStep - What one request of a
BlockAccessListCatchUpproduced. - Downloaded
Account - What the downloaded state holds for an account a list changes.
- Snap
Bootstrap Outcome - How a
SnapBootstraprun ended. - Snap
Phase - The stage a
SnapGenerationhas reached. - Snap
Sync Error - Error returned while assembling a snap state generation.
- Snap
Sync Session State - What a
SnapSyncSessionis doing. - Storage
Range Step - What one request of a
StorageRangeDownloadproduced.
Constants§
- DEFAULT_
BAL_ RESPONSE_ BYTES - Default soft response limit for block access list requests, as EIP-8189 recommends.
- DEFAULT_
CATCH_ UP_ BLOCKS - Default number of blocks asked for per request, chosen so that many average 60M gas lists
still fit under
DEFAULT_BAL_RESPONSE_BYTES. - DEFAULT_
CODE_ HASHES - Default number of code hashes asked for per request.
- DEFAULT_
RANGES_ PER_ CHECK - Default number of account ranges committed between pivot checks.
- DEFAULT_
REPAIR_ SLOTS - Default number of scheduled slots fetched again per repair batch.
- DEFAULT_
RESPONSE_ BYTES - Default soft response limit for snap requests, matching common peer limits.
- DEFAULT_
SCAN_ CHUNK - Accounts scanned between cancellation checks, bounding how long a cancelled session keeps running.
- DEFAULT_
STORAGE_ ACCOUNTS - Default number of contracts asked for per storage request.
- MAX_
HASH - Inclusive upper bound covering the full trie keyspace.
Traits§
- Snap
Account Store - Persistence for the account ranges an attempt downloads.
- Snap
Attempt Store - Persistence for the attempt that owns downloaded snap state.
- Snap
Bytecode Store - Persistence for the code an attempt’s accounts reference.
- Snap
Catch UpStore - Persistence for the block access lists an attempt applies to its downloaded state.
- Snap
State Verifier - Decides whether downloaded state can be trusted as the node’s state.
- Snap
Storage Store - Persistence for contract storage downloaded ahead of its account range.
- Snap
Sync Context - What a
SnapBootstrapneeds to know about the chain and peers it synchronizes from.