EIP-8189 - snap/2 - BAL-Based State Healing

Created 2026-03-06
Status Draft
Category Networking
Type Standards Track
Authors
Requires

Abstract

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.

Motivation

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.

Specification

Protocol Version

The protocol version is incremented from snap/1 to snap/2. Peers negotiate the version during the RLPx capability handshake.

Removed Messages

Message ID Reason
GetTrieNodes 0x06 Replaced by BAL-based healing
TrieNodes 0x07 Replaced by BAL-based healing

New Messages

The freed message IDs are reused for BAL exchange.

GetBlockAccessLists (0x06)

[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:

BlockAccessLists (0x07)

[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.

Unchanged Messages

Messages 0x00–0x05 (GetAccountRange, AccountRange, GetStorageRanges, StorageRanges, GetByteCodes, ByteCodes) are unchanged from snap/1.

Validation

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.

Synchronization Algorithm

snap/2 replaces trie healing with BAL-based catch-up:

  1. Download headers and identify the chain head.
  2. Pick a pivot block P sufficiently behind the chain head (e.g., HEAD-64) to reduce the likelihood of P being reorged while remaining recent enough that serving peers still hold its state in memory.
  3. Bulk download state at P via GetAccountRange, GetStorageRanges, GetByteCodes.
  4. If P becomes stale during bulk download (step 3) — i.e., serving peers no longer hold state at P — the syncing node fetches BALs for blocks P+1 through P' via GetBlockAccessLists (where P' is the new pivot), applies the diffs locally, and continues bulk download at P'.
  5. While state is being downloaded, the chain advances and the pivot switches from P to a new block P+K (typically K is 64). Because serving peers may no longer retain the state at P once the pivot advances, the syncing node fetches BALs for blocks P+1 through P+K via 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.
  6. If the pivot advances further during step 5, repeat for the newly produced blocks.
  7. Recompute and verify the state root against the latest header.

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.

Rationale

Separate Protocol vs. eth/71

This follows the established snap design philosophy. GetByteCodes was duplicated from eth into snap for the same reason:

This functionality was duplicated into snap from eth/65 to permit eth long 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.

Removing GetTrieNodes

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 ID Reuse

Message IDs are scoped to the protocol version and negotiated during the RLPx capability handshake. snap/1 peers never receive snap/2 messages.

Backwards Compatibility

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.

Security Considerations

Amplification

A GetBlockAccessLists request can trigger responses significantly larger than the request. Implementations should apply rate limiting and respect the 2 MiB soft response limit.

Unavailable Data

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.

Application Order

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

Copyright and related rights waived via CC0.