This EIP upgrades the snap protocol from version 1 to version 2, removing GetTrieNodes (0x06) / TrieNodes (0x07) and replacing them with GetBlockAccessLists (0x06) / BlockAccessLists (0x07) to enable BAL-based state healing during snap sync.
EIP-7928 introduces block-level access lists (BALs) that capture all state changes per block, committed to block headers via block-access-list-hash. With BALs available, the snap sync healing phase — which iteratively fetches individual Merkle trie nodes via GetTrieNodes to resolve state inconsistencies — can be replaced entirely.
Instead of discovering and fetching trie nodes across many round trips, a syncing node downloads BALs for the blocks that advanced during sync and applies state diffs sequentially. Each BAL is verified against its header commitment. The set of blocks to catch up is known upfront, eliminating iterative discovery.
The protocol version is incremented from snap/1 to snap/2. Peers negotiate the version during the RLPx capability handshake.
| Message | ID | Reason |
|---|---|---|
GetTrieNodes |
0x06 | Replaced by BAL-based healing |
TrieNodes |
0x07 | Replaced by BAL-based healing |
The freed message IDs are reused for BAL exchange.
[request-id: P, [blockhash₁: B_32, blockhash₂: B_32, ...]]
Request BALs for the given block hashes. The number of hashes per request is subject to implementation-defined limits.
BALs are only available for blocks after EIP-7928 activation and within the retention period defined therein.
Notes:
[request-id: P, [block-access-list₁, block-access-list₂, ...]]
Response to GetBlockAccessLists. Each element corresponds positionally to a block hash from the request. Empty BALs (zero-length byte slice) are returned for blocks where the BAL is unavailable.
The recommended soft limit for BlockAccessLists responses is 2 MiB.
Messages 0x00–0x05 (GetAccountRange, AccountRange, GetStorageRanges, StorageRanges, GetByteCodes, ByteCodes) are unchanged from snap/1.
Received BALs must be validated by computing keccak256(rlp.encode(bal)) and comparing against the block-access-list-hash in the corresponding block header. See EIP-7928 for the BAL encoding format.
snap/2 replaces trie healing with BAL-based catch-up:
GetAccountRange, GetStorageRanges, GetByteCodes.GetBlockAccessLists (where P' is the new pivot), applies the diffs locally, and continues bulk download at P'.GetBlockAccessLists, applies the state diffs locally, and immediately uses P+K as the new sync target. Each BAL is verified against its header hash before application.If the chain reorgs past the pivot block P, let W be the common ancestor of the old and new canonical chains. The syncing node collects BALs for blocks W+1 through P on the old fork, identifies state entries mutated on the old fork but not on the new fork, deletes those entries locally, and re-fetches them via GetAccountRange / GetStorageRanges. It then applies BALs from W+1 forward on the new canonical chain and continues sync with a new pivot. If the required orphaned BALs are unavailable, the node must discard state and restart sync.
This follows the established snap design philosophy. GetByteCodes was duplicated from eth into snap for the same reason:
This functionality was duplicated into
snapfrometh/65to permitethlong term to become a chain maintenance protocol only and move synchronization primitives out into satellite protocols only.
Synchronization primitives belong in satellite protocols. snap is optional and independently versioned; duplicating BAL exchange here avoids coupling sync functionality to eth.
BAL-based healing makes trie node fetching for state reconciliation unnecessary. Removing these messages avoids maintaining two healing mechanisms and frees message IDs for reuse.
Message IDs are scoped to the protocol version and negotiated during the RLPx capability handshake. snap/1 peers never receive snap/2 messages.
This EIP requires rolling out a new protocol version, snap/2. Older clients continue using snap/1. snap/2 is only meaningful for post-Amsterdam blocks, since the block-access-list-hash header field (EIP-7928) is absent for earlier blocks.
For a node synchronizing data, if both snap/1 and snap/2 are supported, it should use either snap/1 or snap/2 for state sync; running both simultaneously is not recommended. Once the synchronization phase is complete, the node can serve requests for both protocols.
A GetBlockAccessLists request can trigger responses significantly larger than the request. Implementations should apply rate limiting and respect the 2 MiB soft response limit.
Peers returning empty entries for available blocks may be misbehaving or may have pruned data legitimately. Implementations should track peer reliability and deprioritize unreliable peers.
BALs must be applied in strict block order with each BAL hash verified before application. Incorrect ordering or wrong-fork BALs produce an invalid state root, detected during the final state root verification.
Copyright and related rights waived via CC0.