brandon 0.1.0

Standalone Rust toolkit for Ethereum ERA/ERA1 files — read, write, verify, convert
Documentation

Brandon

Standalone Rust toolkit for Ethereum ERA/ERA1 archives.

✓ Read and stream ERA files
✓ Random-access blocks by slot
✓ Build new ERA archives
✓ Verify archive integrity
✓ Convert between archive layouts
✓ Zero-copy block iteration
✓ Works across Ethereum client implementations

$ brandon info mainnet-00000-5ec1ffb8.era1
Format:          ERA1
File size:       3.71 MiB (3891337)
Starting slot:   0
Slot range:      0..8191
Block count:     8191
State present:   no
Total entries:     32770

Entry breakdown:
  BlockAccumulator 1
  BlockBody        8192
  BlockIndex       1
  CompressedHeader 8192
  Receipts         8192
  TotalDifficulty  8192

Install

From source:

git clone https://github.com/galadd/brandon.git
cd brandon
cargo install --path .

CLI

Inspect a file

brandon info file.era1
brandon info file.era --json

Verify integrity

brandon verify file.era1
brandon verify file.era1 --manifest checksums.txt

Read blocks

# Single block by slot
brandon read file.era1 --slot 42
brandon read file.era1 --slot 42 --format raw --output block42.bin

# Stream all blocks
brandon read file.era1 --all
brandon read file.era1 --count 10

# Extract blocks to directory
brandon read file.era1 --all --output-dir ./blocks/

# Include beacon state (ERA files only)
brandon read file.era --all --state

Convert and restructure

# Rebuild indexes (fixes corrupt offsets without touching block data)
brandon convert reindex file.era1 -o fixed.era1

# Strip receipts to save disk space
brandon convert strip file.era1 -o slim.era1 --strip-receipts

# Strip everything except block headers
brandon convert strip file.era1 -o headers-only.era1 --strip-receipts --strip-bodies --strip-td

# Split into individual {slot}.snappy files
brandon convert split file.era1 --output-dir ./blocks/

Build an ERA file

# From directory of {slot}.snappy files
brandon build --blocks-dir ./compressed/ --output out.era

# With beacon state
brandon build --blocks-dir ./compressed/ --state state.snappy --state-slot 8192 --output out.era

Library

Add to Cargo.toml:

[dependencies]
brandon = "0.1"

Read a file

use brandon::{EraReader, TypedEntry};

let file = std::fs::File::open("file.era1")?;
let mut reader = EraReader::new(file);

while let Some(entry) = reader.next_entry()? {
    match entry {
        TypedEntry::Header { data } => {
            println!("block header: {} bytes", data.len());
        }
        TypedEntry::BeaconBlock { data } => {
            println!("beacon block: {} bytes", data.len());
        }
        _ => {}
    }
}

Random access by slot

use brandon::EraRandomReader;

let file = std::fs::File::open("file.era1")?;
let mut reader = EraRandomReader::new(file)?;

println!("slots: {}..{}", 
    reader.starting_slot()?,
    reader.starting_slot()? + reader.slot_count()? as u64
);

if let Some(block) = reader.read_block_at_slot(42)? {
    println!("found block: {} bytes", block.primary_data().len());
} else {
    println!("slot 42 is empty (skipped)");
}

Build an ERA file

use brandon::write::EraBuilder;
use snap::write::FrameEncoder;
use std::io::Write;

let compressed = {
    let mut enc = FrameEncoder::new(Vec::new());
    enc.write_all(&ssz_block_bytes)?;
    enc.finish()?
};

let mut builder = EraBuilder::new();
builder.add_block(0, compressed);
builder.set_state(8192, compressed_state);

let mut output = std::fs::File::create("out.era")?;
builder.build(&mut output)?;

Convert and transform

use brandon::convert::{self, strip::StripConfig};
use std::fs::File;

// Reindex a file with corrupt offsets
let input = File::open("corrupt.era1")?;
let output = File::create("fixed.era1")?;
convert::reindex::reindex(input, output)?;

// Strip receipts to save space
let input = File::open("full.era1")?;
let output = File::create("slim.era1")?;
convert::strip::strip(input, output, &StripConfig::receipts_only())?;

// ERA1 to ERA synthesis (bring your own SSZ library)
use brandon::convert::era1_to_era;
use brandon::read::Era1Block;

let input = File::open("mainnet-00000-5ec1ffb8.era1")?;
let output = File::create("synthetic.era")?;

// Buffers are reused across all blocks—zero per-block allocation
era1_to_era(input, output, |era1_block: &Era1Block, ssz_buf: &mut Vec<u8>| {
    ssz_buf.clear();
    my_ssz_lib::wrap_in_beacon_block(era1_block, ssz_buf)?;
    Ok(())
})?;

Verify a file

use brandon::verify::verify_era;

let data = std::fs::read("file.era1")?;
let result = verify_era(&data[..]);

if !result.valid {
    for err in &result.errors {
        eprintln!("error: {err}");
    }
    std::process::exit(1);
}

println!("{} blocks verified", result.block_count);

What it does

CAPABILITY DESCRIPTION
Read Stream ERA/ERA1 files, random access by slot
Write Build ERA files from compressed block data
Verify Structural validation, index integrity, manifest hashes
Convert Rebuild indexes, remove entries, extract block

Format support

FORMAT SPEC DESCRIPTION
ERA1 e2store era1 Pre-merge execution blocks
ERA e2store era Post-merge beacon chain
e2store e2store Container format

Development

# Run tests
cargo test

# Run CLI from source
cargo run -- info test.era1

# Build release binary
cargo build --release

License

MIT OR Apache-2.0