
Etheorem
Status — early-stage, experimental, single-developer; personal project, not an EF release. The libraries here pass the upstream consensus-spec test corpus and ship the three central SSZ theorems on a
BasicSupportedcut, but production-grade stability and a stable release line are not implied.
A Lean 4 monorepo for Ethereum consensus-spec types and SSZ (Simple Serialize) with machine-checked correctness on the verified core.
Upstream repository: https://github.com/etheorem/etheorem.
Layout
LeanSha256 ─────────────┐
├─→ SizzLean ← LeanEthCS
LeanHazmatSha256 ───────┘ (SSZ + (consensus
(FFI SHA-256) cache) containers,
Phase0…Gloas)
LeanHazmat* (FFI crypto family): Sha256 · Bls · Kzg (consumed à la carte)
LeanPoseidon (pure Poseidon2, standalone island — nothing depends on it yet)
Lake subpackages under packages/, each with its own lakefile and
independent build target:
packages/LeanSha256/— pure-Lean SHA-256 reference. NIST CAVP-validated, kernel-reducible. No FFI.packages/SizzLean/— SSZ library: spec types, serialization, deserialization, Merkleization, theSSZReprderiving handler, the cache layer, thesszUpdatemacro, plus theHashertypeclass +Sha256instance (delegating to theLeanHazmatSha256FFI binding) and the FFI ≡ spec equivalence axioms — the one layer importing both the FFI binding and the spec.packages/LeanEthCS/— Ethereum consensus-spec containers from Phase 0 through Gloas, the preset-struct macro, and thessz_staticCLI runner (eth_ssz_vector_runner, driven byscripts/run_conformance.py).packages/LeanHazmat*/— the FFI crypto family: one package per primitive family wrapping a battle-tested native library behind@[extern]. Consensus families ship today —LeanHazmatSha256(OpenSSL),LeanHazmatBls(blst),LeanHazmatKzg(c-kzg-4844), consumed à la carte. The aggregator meta-packages (LeanHazmatConsensus, …) and execution-layer families are deferred. Seehazmat-docs/.packages/LeanPoseidon/— pure-Lean Poseidon2 algebraic hash (BN254 and BLS12-381 scalar fields,t = 3): the permutation, the 2-to-1compress, and a sponge. A standalone island parallel toLeanSha256— depends on nothing in the monorepo and nothing depends on it yet. Conformance-validated by a differential test against the HorizenLabszkhashRust oracle (test-only) plus committed KATs; the kernel-/native_decide-reducible core needs no Rust. A siblingLeanPoseidonProofspackage (mathlib, standalone) provespermute = permuteRef(the shipped fast layers equal the textbook dense reference) with a clean axiom footprint.
The umbrella lakefile.toml declares no Lean libraries of its own — it
just coordinates the subpackages via [[require]] blocks
(LeanPoseidonProofs is built on its own, keeping mathlib out of the
root). Per-package publication repos will exist later; this is a
development monorepo.
Status: conformance-validated. The Layer 1 spec
(total serialize / deserialize / hashTreeRoot), the SSZRepr
typeclass + deriving handler, the FFI SHA-256 backend, the
pure-Lean Sha256Spec reference, and the cache layer
(persistent Node, Node.ofShape, cached merkle walker,
gindex-driven setManyAt, fused commit Node.commitAndHash,
closure-based pending overlay, sszUpdate macro,
SSZ.Box user surface) are all landed. Consensus containers
cover Phase 0 through Gloas, including Fulu's proposer_lookahead
and the full Gloas ePBS BeaconState (nine EIP-7732 fields plus
the supporting Builder / ExecutionPayloadBid types).
Conformance pinned at consensus-spec-tests
v1.6.0-beta.0
in scripts/run_conformance.py. The universal proof set
(decode_encode, serialize_injective, encode_size_le_max
over SSZType.Supported) and the AVX-512 SIMD inner loop for
sha256BatchCombine remain as planned follow-ups; see
packages/SizzLean/docs/PLAN.md
for the staged roadmap.
Documents
Per-subpackage design docs live next to the code they describe:
packages/SizzLean/docs/ARCHITECTURE.md— the SSZ library's binding design (SSZTypeuniverse,SSZReprtypeclass + deriving, cached Merkle tree, FFI SHA-256, trust boundary, module layout).packages/SizzLean/docs/PLAN.md— SizzLean's stage-by-stage deliverables and acceptance.packages/SizzLean/docs/OPTIMISATION.md— implementation-level companion to ARCHITECTURE.md §6: the cache layer's data structures, how each Phase 17 sub-stage is wired, and the bench-gating story.packages/SizzLean/docs/research/— background research (pre-research.md,cache-research.md).packages/<Pkg>/README.md— per-subpackage READMEs.
Repo-wide docs:
docs/monorepo-arch.md— how the monorepo is laid out: the three-subpackage shape, which lakefiles are TOML vs procedural, where the FFI C shim lives, the LeanSha256 standalone mirror, and the naming / dep / build conventions.CLAUDE.md— style and discipline conventions, project-wide.CONTRIBUTING.md— PR / issue workflow, toolchain setup, code-style pointers.SECURITY.md— vulnerability-disclosure policy.CODE_OF_CONDUCT.md— community guidelines.
Prerequisites
On a fresh machine you need four things before lake build will
work. The Lean toolchain and just itself aren't installed by
the project — they're external tools the recipes assume.
-
elan(the Lean toolchain manager — provideslake/lean). The version inlean-toolchainis installed on first use.curl https://elan.lean-lang.org/elan-init.sh -sSf | sh -
just(task runner — every workflow below is wrapped in ajustrecipe;just doctorwon't run untiljustitself is installed). Install via your platform's package manager:brew install just(macOS),cargo install just(anywhere with Rust), or see https://just.systems for distro packages. -
OpenSSL 3.x +
pkg-config(system-level build deps for the SHA-256 FFI shim — see Native dependencies below for the per-platform one-liners). The Justfile'sdoctor-nativerecipe pinpoints what's missing. -
python3+uv(only forofficial-ssz-vector-tests*). Runjust setup-pythononce to create.venv/and install the harness deps.
Verify everything in one shot:
just doctor # checks elan/lake/lean + pkg-config/OpenSSL + python3/uv
just doctor prints actionable platform-specific install hints
if anything's missing. The CI runs the slimmer just doctor-native
gate (build-time native deps only — the Lean toolchain is
installed by leanprover/lean-action later in the workflow).
Build
Toolchain pinned in lean-toolchain (elan picks it up).
# From the repo root — common targets by name:
lake build LeanSha256
lake build SizzLean
lake build LeanEthCS
lake build LeanPoseidon # standalone Poseidon2 island (fires its anchor KAT)
# Test suites (per package, run on demand):
lake build LeanSha256Tests
lake build SizzLeanTests
lake build LeanEthCSTests
lake build LeanPoseidonTests # committed Poseidon2 KATs (no Rust)
lake exe poseidon_fuzz # Poseidon2 differential test vs the zkhash oracle (needs cargo)
just test-poseidon-proofs # mathlib equivalence proof permute = permuteRef (standalone; fetches olean cache)
# Bench + profile executables:
lake build ssz_bench # microbench grid, S1–S7 (see SizzLeanBench.lean)
lake build ssz_profile # phase-by-phase profile of one workload
# ssz_static / ssz_generic CLI driver (consumed by scripts/run_conformance.py):
lake build eth_ssz_vector_runner
# Or build a single subpackage in isolation:
cd packages/SizzLean && lake build
The repo's Justfile wraps the common workflows
(just build, just test, just bench,
just official-ssz-vector-tests-static, …) — see just --list
for the full set.
CI runs lake build for each named library on the pinned
toolchain via leanprover/lean-action.
Native dependencies
The FFI SHA-256 shim (packages/LeanHazmatSha256/csrc/sha256_shim.c,
used by SizzLean's hash path) links against OpenSSL's libcrypto,
discovered via pkg-config (Debian/Ubuntu fallback baked in). The Lake
build expects:
-
Linux (Debian/Ubuntu, including CI):
libssl-devfor the headers (/usr/include/openssl/evp.h) and the versionedlibcrypto.so.3shared library, pluspkg-config. Install via:sudo apt-get install libssl-dev pkg-config -
macOS:
openssl@3+pkg-configvia Homebrew (thepkg-configdiscovery handles the keg-only include/lib paths).
Run just doctor-native to verify the build-time native deps
(cc, git, pkg-config, OpenSSL 3.x).
Vendored crypto (the LeanHazmat BLS / KZG families). LeanHazmatBls
(blst) and LeanHazmatKzg (c-kzg-4844) wrap vendored native libraries,
fetched at pinned tags by just vendor-bls / just vendor-kzg into
gitignored vendor/ trees before lake build (never git submodules; see
hazmat-docs/ARCHITECTURE.md §6). just build runs both vendor steps for you. The C / C++ compilers are invoked
through the Lean toolchain's cc wrapper — no separate configuration
required.
Conformance harness
scripts/run_conformance.py drives the Lean eth_ssz_vector_runner CLI against
ethereum/consensus-spec-tests release archives. Default mode runs
the ssz_generic suite (type-agnostic SSZ tests for uints,
basic_vector, bitvector, bitlist, boolean, containers),
with a --limit N subset cap by default.
# One-time: create `.venv/` with the harness deps (cramjam + PyYAML).
# Wraps `uv venv` + `uv pip install -r scripts/requirements.txt`.
just setup-python
# Default: ssz_generic subset (5 cases per handler/suite)
.venv/bin/python scripts/run_conformance.py
# Full ssz_generic sweep
.venv/bin/python scripts/run_conformance.py --all
# Switch to ssz_static (per-fork consensus types)
.venv/bin/python scripts/run_conformance.py --suite static
# Single-shape focus
.venv/bin/python scripts/run_conformance.py --include 'generic:uints/*'
Current dispatch coverage
Numbers below are against consensus-spec-tests v1.6.0-beta.0
(the tag pinned in scripts/run_conformance.py). Both presets
green across every fork Phase 0 → Fulu — Gloas and EIP-7805 test
vectors exist in the corpus but aren't yet in the CLI dispatch
table or the harness's FORKS list.
ssz_generic: 2188 / 2188 in-scope cases pass across all handlers (uints, basic_vector, bitvector, bitlist, boolean, containers). 292 progressive-container cases are deliberately out of scope. The test-only structs (SingleFieldTestStruct,SmallTestStruct,FixedTestStruct,VarTestStruct,ComplexTestStruct,BitsStruct) have their SSZ shapes hardcoded inpackages/LeanEthCS/LeanEthCS/Cli/Main.lean.ssz_static --config mainnet --all: 1585 / 1585 cases pass across every fork Phase 0 → Fulu.ssz_static --config minimal --all: 38991 / 38991 cases pass across every fork Phase 0 → Fulu — including variable-size composites (Attestation,BeaconBlockBody,BeaconState) and all fork deltas (Altair / Bellatrix / Capella / Deneb / Electra / Fulu).- One-command full sweep:
just official-ssz-vector-tests-alldrives generic + static-mainnet + static-minimal end-to-end.