changed 2 days ago
Published Linked with GitHub

EraE specification

Specification

The format can be summarized with the following expression:

​​​​   eraE := Version | CompressedHeader+ | CompressedBody+ | CompressedReceipts+ | Proofs+ | TotalDifficulty* | other-entries* | Accumulator? | BlockIndex

Each basic element is its own e2store entry:

​​​​   Version            = { type: 0x3265, data: nil }
​​​​   CompressedHeader   = { type: 0x03,   data: snappyFramed(rlp(header)) }
​​​​   CompressedBody     = { type: 0x04,   data: snappyFramed(rlp(body)) }
​​​​   CompressedSlimReceipts = { type: 0x08,   data: snappyFramed(rlp([tx-type, post-state-or-status, cumulative-gas, logs])) }
​​​​   TotalDifficulty    = { type: 0x06,   data: uint256(header.total_difficulty) }
​​​​   Proof              = { type: 0x09    data: snappyFramed(rlp([proof-type, ssz(BlockProofHistoricalHashesAccumulator) | ssz(BlockProofHistoricalRoots) | ssz(BlockProofHistoricalSummaries)]))}
​​​​   AccumulatorRoot    = { type: 0x07,   data: hash_tree_root(List(HeaderRecord, 8192)) }
​​​​   Index              = { type: 0x3267, data: index }

A few notes on individual elements:

  • CompressedReceipts is optional and a different format than the consensus EIP-2718 format to optimize internal use in clients.
  • Proof is optional but if included, provides the coresponding proof for each CompressedHeader in the file corresponding to the Portal Network proofs specification[1]. It's possible to have multiple proof types in the same file at fork boundaries.
  • TotalDifficulty is optional and little-endian encoded.
  • AccumulatorRoot is optional and only defined for epochs with pre-merge data.
  • HeaderRecord is defined in the Portal Network specification[2].
  • other-entries is a placeholder for other potential objects to be added to EraE.

BlockIndex stores relative offsets to the components of each block entry. This allows O(1) access to all components of a block. The format is:

​​​​   block-index := starting-number | indexes | indexes | indexes ... | component-count | count

Where indexes represents the index of each block component:

​​​​   indexes := header-index | body-index | receipts-index | difficulty-index? | proof-index?

The value component-count is the number of indexes stored by indexes. It should be in the range of 3-5, depending on whether TotalyDifficulty and Proofs are present

All values in the block index are little-endian uint64.

starting-number is the first block number in the archive. Every index is a defined relative to index's location in the file. The total number of block entries in the file is recorded in count.

Due to the accumulator size limit of 8192, the maximum number of blocks in an Era batch is also 8192. This is also the value of SLOTS_PER_HISTORICAL_ROOT[3] on the Beacon chain, so it is nice to align on the value.

Merge transition

There are some small differences between pre-merge and post-merge eraE files:

  • TotalDifficulty should only be encoded for eraE files pre-merge. For the epoch where the merge occurs, fill all remaining post-merge blocks with the final total difficulty of the chain.
  • AccumulatorRoot should only be encoded for eraE files pre-merge. For the epoch where the merge occurs, compute the root for an incomplete epoch where only pre-merge blocks are recorded in the accumulator.

  1. https://github.com/ethereum/portal-network-specs/blob/master/history/history-network.md#block-header ↩︎

  2. https://github.com/ethereum/portal-network-specs/blob/master/history/history-network.md#the-historical-hashes-accumulator ↩︎

  3. https://github.com/ethereum/consensus-specs/blob/44ae6e661d9beac383f4a1f33be74259bae93c85/presets/mainnet/phase0.yaml#L42 ↩︎

Select a repo