snap/1 is an optional RLPx capability for state synchronization. A peer that
offers snap/1 must also offer eth. The eth session supplies chain
negotiation and head updates. snap/1 supplies verifiable account, storage,
code, and trie-node data.
Wire inventory
All messages use RLP. Each request and response starts with the same request ID.
| ID | Message | Payload |
|---|---|---|
0x00 |
GetAccountRange |
[request_id, root_hash, start_hash, limit_hash, response_bytes] |
0x01 |
AccountRange |
[request_id, accounts, proof] |
0x02 |
GetStorageRanges |
[request_id, root_hash, account_hashes, start_hash, limit_hash, response_bytes] |
0x03 |
StorageRanges |
[request_id, slots, proof] |
0x04 |
GetByteCodes |
[request_id, hashes, response_bytes] |
0x05 |
ByteCodes |
[request_id, codes] |
0x06 |
GetTrieNodes |
[request_id, root_hash, paths, response_bytes] |
0x07 |
TrieNodes |
[request_id, nodes] |
GetAccountRange requests a consecutive account range in trie-key order.
AccountRange returns slim account bodies and boundary proofs.
GetStorageRanges requests slots from one or more storage tries.
StorageRanges returns slot ranges and, when needed, boundary proofs.
GetByteCodes requests contract code by code hash.
ByteCodes returns available code in request order.
GetTrieNodes requests account or storage trie nodes by compact path.
TrieNodes returns available trie nodes in request order.
Range transfer
Accounts follow trie-key order. Their wire form omits the standard empty code hash and empty storage root. The receiver restores those values before it rebuilds the state trie.
A range response must cover the requested start and the last returned item with
Merkle boundary proofs. If a whole trie fits in one response, the response
omits the proof. A server may stop at its byte or service limit. It must return
at least one item when one exists at or after the requested start, including
the first item after limit_hash when the requested interval is empty.
Storage requests may cover several accounts. A response contains full storage tries until the size limit, then at most one partial trie with a proof.
Code and trie healing
Code and trie-node responses preserve request order but omit unavailable items. They do not insert placeholders. A requester compares returned hashes or paths with its request to find gaps.
The initial download may combine ranges from several recent state roots. The
client rebuilds a near-complete trie, finds inconsistent paths, and uses
GetTrieNodes to heal them. It accepts the result only when the rebuilt state
root matches a trusted block header.
Validation and failure handling
- A server always sends a response, even when the result is empty.
- A server returns an empty response when it cannot serve the requested root.
- A receiver rejects a range with an invalid proof, an out-of-order key, or a root mismatch.
- A receiver checks each bytecode against its requested code hash.
- A receiver checks each returned trie node against the requested path and root.
- A requester must tolerate a short response and retry missing work with this peer or another peer.
Design rationale
Range transfer avoids sending every internal trie node. Boundary proofs let the
receiver detect gaps without trusting the server. The protocol remains
separate from eth because state sync needs an indexed snapshot and not every
full node chooses to maintain one.
History
snap/1 introduced range-based state sync in 2020. snap/2 is a separate,
in-progress capability that replaces trie-node healing with block access lists.