v10| A collection of utility functions for Ethereum data storage formats. | | ——————————————————————- |
[CONSIDERED FOR DEPRECATION] This library is being considered for deprecation. If you find it useful, please let us know!
@ethereumjs/e2store provides utilities for working with Ethereum data storage formats, including E2HS, Era1, and Era files. These formats are commonly used for storing historical blockchain data, beacon states, and block data in an efficient, provable, and standardized way.
To obtain the latest version, simply require the project using npm:
npm install @ethereumjs/e2store
All helpers are re-exported from the root level and deep imports are not necessary. So an import can be done like this:
import { formatEntry } from "@ethereumjs/e2store"
E2HS is a format for storing historical blockchain data along with proofs. It provides efficient access to block headers, bodies, and receipts, and can be used to bootstrap a Portal History Network DB.
import { formatE2HS } from "@ethereumjs/e2store"
// Format data into E2HS format
// data is an array of block tuple data and an epoch index
const e2hs = await formatE2HS(data)
// ./examples/e2hs.ts#L30-L32
const tuples = readTuplesFromE2HS(e2hsFile)
const tuple = (await tuples.next()).value
// ./examples/e2hs.ts#L47-L49
const targetIndex = 1234
const tupleAtIndex = await readE2HSTupleAtIndex(e2hsFile, targetIndex)
console.log('tuple at index 1234', tupleAtIndex)
getBlockIndex returns the index of the block in the e2hs file:
readBlockIndex returns the starting number and offsets of the blocks in the e2hs file:
// ./examples/e2hs.ts#L18-L28
const blockIndex = getBlockIndex(e2hsFile)
console.log('blockIndex', {
type: blockIndex.type,
count: blockIndex.count,
recordStart: blockIndex.recordStart,
data: blockIndex.data.length + ' bytes',
})
const { startingNumber, offsets } = readBlockIndex(blockIndex.data, blockIndex.count)
console.log('startingNumber', startingNumber)
console.log('offsets', offsets.length)
decompressE2HSTuple decompresses a block tuple:
parseEH2SBlockTuple parses a block tuple:
// ./examples/e2hs.ts#L35-L39
const decompressedTuple = await decompressE2HSTuple(tuple!)
console.log('decompressedTuple', decompressedTuple)
const parsedTuple = parseEH2SBlockTuple(decompressedTuple)
console.log('parsedTuple', parsedTuple)
Era1 files store historical data in epochs of 8192 blocks, making it efficient to access large ranges of historical data. Era1 block tuples contain a header, body, receipts, and total difficulty. The data can be verified by reconstructing the epoch accumulator, and validating again the accumulator root, also contained in the era1 file.
Export history from an EthereumJS client DB in epochs of 8192 blocks as Era1 files:
import { exportEpochAsEra1 } from "@ethereumjs/e2store"
const dataDir = PATH_TO_ETHEREUMJS_CLIENT_DB
const epoch = 0
// Generates ${dataDir}/era1/epoch-0.era1
await exportEpochAsEra1(epoch, dataDir)
readERA1 returns an async iterator of block tuples (header + body + receipts + totalDifficulty):
// ./examples/era1.ts#L48-L50
const tuples = await readERA1(era1File)
const tupleEntry = await tuples.next()
console.log('tupleEntry', tupleEntry.value!)
validateERA1 validates the era1 file by reconstructing the epoch accumulator and validating the accumulator root:
// ./examples/era1.ts#L57-L59
const valid = await validateERA1(era1File)
console.log('valid', valid)
getBlockIndex returns the index of the block in the era1 file:
readBlockIndex returns the starting number and offsets of the blocks in the era1 file:
readOtherEntries returns the accumulator root and other entries of the era1 file:
getHeaderRecords returns the header records of the era1 file:
// ./examples/era1.ts#L21-L45
const blockIndex = getBlockIndex(era1File)
console.log('blockIndex', {
type: blockIndex.type,
count: blockIndex.count,
recordStart: blockIndex.recordStart,
data: blockIndex.data.length + ' bytes',
})
const { startingNumber, offsets } = readBlockIndex(blockIndex.data, blockIndex.count)
console.log('startingNumber', startingNumber)
console.log('offsets', offsets.length)
const { accumulatorRoot, otherEntries } = await readOtherEntries(era1File)
console.log('accumulatorRoot', bytesToHex(accumulatorRoot))
console.log('otherEntries', otherEntries.length)
const headerRecords = await getHeaderRecords(era1File)
const epochAccumulator = EpochAccumulator.encode(headerRecords)
const epochAccumulatorRoot = EpochAccumulator.merkleRoot(headerRecords)
console.log('epochAccumulator', epochAccumulator.length + ' bytes')
console.log('epochAccumulatorRoot', bytesToHex(epochAccumulatorRoot))
console.log(
'Reconstructed root matches encoded root',
bytesToHex(epochAccumulatorRoot) === bytesToHex(accumulatorRoot),
parseBlockTuple parses a block tuple:
blockFromTuple converts a block tuple to a block:
// ./examples/era1.ts#L53-L58
console.log('parsed tuple', tuple)
const block = blockFromTuple(tuple)
console.log('block', block)
const valid = await validateERA1(era1File)
Era files are used to store beacon chain data, including beacon states and blocks.
readBeaconState reads the beacon state from an era file:
// ./examples/era.ts#L18-L24
// Read beacon state from era file
const state = await readBeaconState(eraFile)
console.log('beaconState', {
slot: state.slot,
validators: state.validators.length,
})
readBeaconBlock reads a specific beacon block from an era file:
// ./examples/era.ts#L25-L30
// Read a specific beacon block
const block = await readBeaconBlock(eraFile, 0)
console.log('beaconBlock', {
slot: block.message.slot,
proposer_index: block.message.proposer_index,
})
readBlocksFromEra returns an async iterator of beacon blocks:
// ./examples/era.ts#L32-L37
// Read blocks from era file
const blocks = readBlocksFromEra(eraFile)
const firstBlock = (await blocks.next()).value!
console.log(`Block`, firstBlock)
readSlotIndex reads the slot index from an era file:
// ./examples/era.ts#L10-L16
// Read slot index from era file
const slotIndex = readSlotIndex(eraFile)
console.log('slotIndex', {
startSlot: slotIndex.startSlot,
recordStart: slotIndex.recordStart,
slotOffsets: slotIndex.slotOffsets.length + ' slots',
})
The EthereumJS GitHub organization and its repositories are managed by members of the former Ethereum Foundation JavaScript team and the broader Ethereum community. If you want to join for work or carry out improvements on the libraries see the developer docs for an overview of current standards and tools and review our code of conduct.