EthCC is an Ethereum execution client written in C, in the style of Firedancer. The goal is full parity with reth: the same results, byte for byte, with less CPU and memory.
Today the execution engine implements every proof-of-stake mainnet rule set: Paris (The Merge, September 2022), Shanghai, Cancun, Prague (Pectra) and Osaka (Fusaka) with the BPO2 blob schedule. It executes real mainnet blocks from execution witnesses, reproduces reth's results byte for byte, and does so about 2× faster than reth's stateless validator on the same blocks.
| Component | Status |
|---|---|
| EVM (all Osaka opcodes, EIP-7702), 18 precompiles, tx / block rules | ✅ done |
| Hard forks Paris (The Merge), Shanghai, Cancun, Prague, Osaka (+ BPO1/BPO2): all of proof-of-stake mainnet | ✅ done |
| Merkle Patricia Trie, post-state root | ✅ done |
Stateless execution from witnesses (poc-exec) |
✅ done |
| Parallel pipeline (witness hashing, sender recovery, storage roots, prefetch) | ✅ done |
| Proof-of-work hard forks (Frontier … London) | planned |
| Storage: libmdbx with EthCC's own schema, per-block commits | ✅ done (v0.2) |
Initial-state import from a reth datadir (ec-import) |
✅ done (v0.2) |
Following the chain against its own database, checked against reth (ec-node --shadow) |
✅ done (v0.2) |
Engine API (engine_newPayloadV4, engine_forkchoiceUpdatedV3, JWT) with an in-memory block tree, undo records and reorgs below the persisted head (ec-node --engine) |
✅ done (v0.2) |
Block building (engine_getPayload), txpool |
planned |
| Staged sync, ERA import | planned |
| P2P (discv4/v5, RLPx, eth/68–69) | planned |
JSON-RPC (eth_*, debug_*, trace_*, WS/IPC) |
planned |
| Txpool and payload building | planned |
EthCC can follow mainnet behind a consensus client through the Engine API, on its own database. It cannot replace reth yet: it bootstraps its state from a reth database, catches up missing blocks from a reth node, does not build blocks, and has no P2P or user-facing JSON-RPC.
Verified on every change:
| Check | Result |
|---|---|
| execution-spec-tests v5.4.0, state tests: Paris / Shanghai / Cancun / Prague / Osaka | 1 564 / 1 745 / 16 847 / 18 869 / 19 517 — all pass |
| execution-spec-tests v5.4.0, blockchain tests: Paris / Shanghai / Cancun / Prague / Osaka (+ fork transitions) | 1 624 / 1 849 / 17 685 / 20 859 / 21 558 (+ 295 / 295) — all pass |
| execution-spec-tests v5.4.0, Engine API fixtures: Paris / Shanghai / Cancun / Prague / Osaka | 1 624 / 1 849 / 17 685 / 20 878 / 21 577 — all pass |
| 1 074 consecutive mainnet blocks vs reth (state root, every receipt, bloom, gas, requests) | 1 074 identical, 0 different |
| Differential fuzzing vs geth (goevmlab, 1 h) | 8 348 tests, 0 differences |
Speed vs reth's stateless validator on the same mainnet blocks (interleaved A/B, median of 3 runs per block; ratio = reth time / EthCC time):
| Mode | Ratio |
|---|---|
| 1 core, CPU time | 1.95× |
| 8 cores, wall clock | 1.98× |
Details: docs/results/2026-09-summary.md.
Node (v0.2): the full mainnet state imported from reth (421 M accounts, 1.66 B slots; 262 GB database) reproduces reth's state root, and the following live blocks executed against EthCC's own database match reth exactly — state root and every receipt. Details: docs/results/2026-09-v0.2-node.md.
Engine API (v0.2): all 21 577 EEST Osaka Engine API fixtures pass through the JSON-RPC layer against a database, 5 835 fork-switch scenarios pass, and EthCC follows mainnet live through the Engine API. Details: docs/results/2026-09-v0.2b-engine.md.
Requirements: Linux x86-64 with AVX2, SHA and ADX (any recent Intel/AMD CPU), clang (GCC 15.2.0 miscompiles the vendored Firedancer code), GNU make, Python 3.
git clone https://github.com/feofilaktov/EthCC.git && cd EthCC
make # debug build: ASan + UBSan, library, tests, tools
make test # unit tests
make PROFILE=release # -O3 -march=nativeconfig.mk uses ~/.local/opt/LLVM-23.1.2-Linux-X64/bin/clang if present,
otherwise clang from PATH; override with make CC=....
# Ethereum execution-spec-tests: fixtures_develop.tar.gz from the EEST v5.4.0 release
export EC_EEST_DIR=/path/to/fixtures
build/release/bin/statetest # Osaka state tests
make PROFILE=release blocktest # Osaka + BPO transition blockchain tests
# Real mainnet blocks from your own reth node (needs the debug RPC namespace)
python3 tools/fetch/fetch.py --follow 100 # fetch each new head block with its witness
build/release/bin/poc-exec ~/Dev/Ethcc-data/mainnet
build/release/bin/poc-exec --threads 8 --bench 3 ~/Dev/Ethcc-data/mainnet
# The blockchain tests again, through an EthCC database (bulk-loaded
# pre-state, a commit per block, post-state read back from the database)
make PROFILE=release blocktest-db
# ... and through the node's block tree; the Engine API fixtures through the
# JSON-RPC layer against a database
build/release/bin/blocktest --db /dev/shm/ecbt --chain
build/release/bin/enginetest --db /dev/shm/ecet --json
# All of the above (unit tests, EEST, mainnet with 1 and 8 threads)
tools/verify.sh
# Speed vs reth's stateless validator on the same blocks
tools/bench-reth/setup.sh
python3 tools/bench/ab.py ~/Dev/Ethcc-data/mainnet 3 7
# Differential fuzzing against geth (goevmlab)
tools/fuzz/run.sh 3600 4EthCC builds its database from a synced reth node (reth 2.x, default "storage v2" layout), then follows that node's chain, executing every block against its own database and checking every root against the block header.
make PROFILE=release
# one-time import: reth's database is opened read-only (reth keeps running);
# its state root must match, or no head is written
build/release/bin/ec-import --reth-db /path/to/reth/datadir/db --db ~/ethcc-db --rpc http://127.0.0.1:8545
# follow the chain two blocks behind reth, comparing every receipt too
build/release/bin/ec-node --shadow --db ~/ethcc-db --rpc http://127.0.0.1:8545 --threads 8 --receiptsThe reth node must expose the eth and debug RPC namespaces.
# EthCC's Engine API on 127.0.0.1:8552 (JWT secret file as for reth/Lighthouse);
# --backfill-rpc catches up the blocks between the database and the network
build/release/bin/ec-node --engine --db ~/ethcc-db --jwt jwt.hex --authrpc-port 8552 \
--threads 8 --backfill-rpc http://127.0.0.1:8545Two ways to feed it without giving up reth:
ec-replayreads beacon blocks from a consensus client's REST API (read-only) and sends EthCC exactly theengine_newPayloadV4/engine_forkchoiceUpdatedV3calls the consensus client would send (--followkeeps up with the head).ec-mirrorsits between the consensus client and reth: reth answers every call, and the Engine API traffic is copied to EthCC, with every status compared (DIFFlines on disagreement). Point the consensus client's--execution-endpointat ec-mirror.
build/release/bin/ec-replay --engine http://127.0.0.1:8552 --jwt jwt.hex --beacon http://127.0.0.1:5052 --follow
build/release/bin/ec-mirror --listen 127.0.0.1:8550 --jwt jwt.hex \
--primary http://127.0.0.1:8551 --secondary http://127.0.0.1:8552 --secondary-jwt ethcc-jwt.hex
``` The import
holds a read snapshot of reth's database for its whole duration, which makes
reth's database file grow while it runs.
## Layout
src/util arenas, thread pool, hex, big-stack threads, .ecfx fixture reader src/prim 256-bit integers, strict RLP, transactions / headers / blocks / receipts src/crypto keccak (Firedancer + s2n-bignum; 4-way AVX2 via XKCP), sha256, secp256k1 (libsecp256k1), P-256 and bn254 (Firedancer), RIPEMD-160, BLAKE2F, MODEXP src/state journaled world-state overlay over a pluggable backend src/evm computed-goto interpreter, calls / creates / EIP-7702, all Osaka precompiles src/trie Merkle Patricia Trie with lazy node resolution, parallel state root src/exec transaction and block rules, system calls, requests, witness backend src/db libmdbx database: schema, state view and backend, per-block commit, bulk loader src/import read-only reader of a reth datadir (Compact decoders) src/net HTTP/JSON-RPC client and server, JWT src/node Engine API: payloads, block tree of recent blocks, JSON-RPC methods src/app ec-node (engine / shadow), ec-import, ec-replay, ec-mirror, poc-exec tests unit tests; EEST runners (statetest, blocktest) tools reth fixture fetcher, verification, A/B benchmark, fuzzing, vendoring scripts deps vendored third-party code (each directory has SOURCE and LICENSE files) docs design, implementation plan, results
## Roadmap
1. **Tip-following node** — done (v0.2): libmdbx storage with EthCC's own
schema, import from a reth database, Engine API behind a consensus client
with reorgs among recent blocks, every root checked.
2. **All hard forks** from Frontier, staged sync from genesis, ERA import.
3. **P2P** (discovery, RLPx, eth wire).
4. **JSON-RPC parity** with reth (`eth_*`, `debug_*`, `trace_*`, subscriptions).
5. **Txpool and payload building**; pruning, CLI, metrics.
Progress is tracked in [issues](https://github.com/feofilaktov/EthCC/issues) and
[milestones](https://github.com/feofilaktov/EthCC/milestones).
## Contributing and security
See [CONTRIBUTING.md](CONTRIBUTING.md). Report vulnerabilities privately as
described in [SECURITY.md](SECURITY.md). Release history: [CHANGELOG.md](CHANGELOG.md).
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this project by you, as defined in the Apache-2.0 license,
shall be dual licensed as above, without any additional terms or conditions.
Third-party code under `deps/` keeps its own license (see each directory's
LICENSE / COPYING and SOURCE files): Firedancer, blst, c-kzg-4844 and libmdbx
are Apache-2.0; libsecp256k1 and yyjson are MIT; XKCP is CC0.