From eeb72b69402aad57da7724d063d14499d8b9f9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:46:35 -0300 Subject: [PATCH 1/6] docs: describe ethlambda as a Beacon Chain and Lean Chain client The project's self-descriptions (README, book, CONTRIBUTING, Docker label, review prompt, infographics, CLAUDE.md) still called ethlambda a Lean consensus client only, and CLAUDE.md said it was not to be confused with a Beacon Chain client. `ethlambda beacon` now follows the Beacon Chain, so those statements are wrong. The LMD-GHOST comparison table also credited "ethlambda" with having no proposer boost or equivocation discounting, which is only true of the Lean Chain: the beacon fork choice implements both. It is now scoped to the Lean Chain. The book introduction gains the Beacon Chain part SUMMARY.md already lists. --- .github/prompts/ai-review.md | 2 +- CLAUDE.md | 5 +++-- CONTRIBUTING.md | 2 +- Dockerfile | 2 +- README.md | 8 ++++++-- book.toml | 2 +- docs/infographics/3sf-mini-infographic.html | 2 +- docs/infographics/ethlambda_architecture.html | 2 +- docs/introduction.md | 13 ++++++++----- docs/lmd_ghost.md | 16 +++++++++------- 10 files changed, 32 insertions(+), 22 deletions(-) diff --git a/.github/prompts/ai-review.md b/.github/prompts/ai-review.md index 5abef1e7f..c7cf28cdc 100644 --- a/.github/prompts/ai-review.md +++ b/.github/prompts/ai-review.md @@ -1,4 +1,4 @@ -You are a senior code reviewer for ethlambda, a minimalist Lean Ethereum consensus client written in Rust. +You are a senior code reviewer for ethlambda, a minimalist Beacon Chain and Lean Chain client written in Rust. Review this PR focusing on: - Code correctness and potential bugs diff --git a/CLAUDE.md b/CLAUDE.md index f03cd2296..a171704a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,8 @@ # ethlambda Development Guide -Development reference for ethlambda - minimalist Lean Ethereum consensus client. -Not to be confused with Ethereum consensus clients AKA Beacon Chain clients AKA Eth2 clients. +Development reference for ethlambda - minimalist Beacon Chain and Lean Chain client. +The two are different protocols sharing one binary: `ethlambda beacon` follows the +Ethereum Beacon Chain (AKA Eth2), and `ethlambda node` runs Lean consensus. ## Quick Reference diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff9c6b7d0..02955c4c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to ethlambda -Thanks for your interest in contributing to ethlambda β€” a minimalist Lean Consensus client for Ethereum, written in Rust. +Thanks for your interest in contributing to ethlambda β€” a minimalist Beacon Chain and Lean Chain client for Ethereum, written in Rust. Before diving in, we recommend reading [our introductory blog post](https://blog.lambdaclass.com/introducing-ethlambda-a-lean-consensus-client-for-ethereums-next-era/) to understand the project's goals and philosophy. diff --git a/Dockerfile b/Dockerfile index fae069bf3..55ef06559 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,7 +66,7 @@ FROM ubuntu AS runtime WORKDIR /app LABEL org.opencontainers.image.source=https://github.com/lambdaclass/ethlambda -LABEL org.opencontainers.image.description="A minimalist and fast Lean Consensus client written in Rust by LambdaClass" +LABEL org.opencontainers.image.description="A minimalist and fast Beacon Chain and Lean Chain client written in Rust by LambdaClass" LABEL org.opencontainers.image.licenses="MIT" ARG GIT_COMMIT=unknown diff --git a/README.md b/README.md index 69ff6a905..0e82f828b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # ethlambda -Minimalist, fast and modular implementation of the Lean Ethereum client written in Rust. +Minimalist, fast and modular implementation of a Beacon Chain and Lean Chain client written in Rust. 🌐 Visit our website at [**ethlambda.xyz**](https://ethlambda.xyz) to learn more about the project. @@ -316,7 +316,11 @@ Full documentation is available in the [`docs/`](./docs) directory. Please refer ## Current status -The client implements the core features of a Lean Ethereum consensus client: +ethlambda is a Beacon Chain and Lean Chain client. On the Beacon Chain +(`ethlambda beacon`), it follows a network from a checkpoint-synced anchor and serves +the standard Beacon API; see the [Beacon Chain follower](#beacon-chain-follower) +quickstart. On the Lean Chain (`ethlambda node`), it implements the core features of a +Lean consensus client: - **Networking** β€” libp2p peer connections, STATUS message handling, gossipsub for blocks and attestations - **State management** β€” genesis state generation, state transition function, block processing diff --git a/book.toml b/book.toml index a34ab2e50..df29912fb 100644 --- a/book.toml +++ b/book.toml @@ -3,7 +3,7 @@ authors = ["lambdaclass"] language = "en" src = "docs" title = "ethlambda" -description = "Minimalist, fast and modular implementation of the Lean Ethereum client written in Rust" +description = "Minimalist, fast and modular implementation of a Beacon Chain and Lean Chain client written in Rust" [rust] edition = "2024" diff --git a/docs/infographics/3sf-mini-infographic.html b/docs/infographics/3sf-mini-infographic.html index 017bef4b6..2ab46db60 100644 --- a/docs/infographics/3sf-mini-infographic.html +++ b/docs/infographics/3sf-mini-infographic.html @@ -732,7 +732,7 @@

Justification & Finalization

diff --git a/docs/infographics/ethlambda_architecture.html b/docs/infographics/ethlambda_architecture.html index 95d4ad55b..2bd2bffaf 100644 --- a/docs/infographics/ethlambda_architecture.html +++ b/docs/infographics/ethlambda_architecture.html @@ -246,7 +246,7 @@

ethlambda

-
Minimalist Lean Ethereum Consensus Client in Rust
+
Minimalist Beacon Chain and Lean Chain Client in Rust
Rust diff --git a/docs/introduction.md b/docs/introduction.md index c9ac04d48..01145be79 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -4,21 +4,24 @@ # Introduction -**ethlambda** is a minimalist, fast and modular implementation of the Lean Ethereum -consensus client, written in Rust. +**ethlambda** is a minimalist, fast and modular implementation of a Beacon Chain and +Lean Chain client, written in Rust. This book collects the design notes and operator-facing references for ethlambda. -It is split into four parts: +It is split into five parts: - **Design** describes the shape of a running node: the [architecture](./architecture.md) of its actors, workers and shared storage, and how they are wired together at startup. -- **Consensus** explains how the chain advances: the +- **Consensus** explains how the Lean Chain advances: the [slot and interval structure](./slots_and_intervals.md) that schedules every validator duty, the [3SF-mini](./3sf_mini.md) justification and finalization rules, and the [LMD-GHOST](./lmd_ghost.md) fork choice algorithm. These documents are implementation-agnostic; ethlambda-specific behaviour is called out in blockquotes. +- **Beacon Chain** covers the Ethereum Beacon Chain side: its + [state transition](./beacon_stf.md), the [mainnet wire](./beacon_wire.md), + and the [execution layer pairing](./beacon_engine.md). - **Operations** documents observable surfaces of a running node: [Prometheus metrics](./metrics.md), [checkpoint sync](./checkpoint_sync.md), and the [fork choice visualization](./fork_choice_visualization.md) served @@ -48,7 +51,7 @@ into the rendered output: ## Related projects -ethlambda is one of several Lean Ethereum consensus clients under active development. +On the Lean Chain, ethlambda is one of several clients under active development. For comparison and cross-client testing: - [zeam](https://github.com/blockblaz/zeam) (Zig) diff --git a/docs/lmd_ghost.md b/docs/lmd_ghost.md index 9319f04d6..6c77ad84d 100644 --- a/docs/lmd_ghost.md +++ b/docs/lmd_ghost.md @@ -684,10 +684,12 @@ at specific intervals: ### Differences from the Ethereum Beacon Chain -ethlambda is a lean consensus client with several simplifications compared to the -Ethereum Beacon Chain: +Lean consensus simplifies several parts of the Ethereum Beacon Chain's fork choice. This +section describes ethlambda's Lean Chain (`ethlambda node`); its Beacon Chain follower +(`ethlambda beacon`) runs the Beacon Chain's own fork choice, proposer boost and +equivocation discounting included. -| Aspect | ethlambda | Ethereum Beacon Chain | +| Aspect | Lean Chain | Ethereum Beacon Chain | |--------|-----------|----------------------| | **Vote weight** | Equal: 1 vote per validator | Proportional to effective balance (up to 32 ETH) | | **Proposer boost** | None | Yes: newly proposed blocks get temporary bonus weight | @@ -697,18 +699,18 @@ Ethereum Beacon Chain: | **Slot duration** | 4 seconds (configurable) | 12 seconds | **No proposer boost.** The Beacon Chain adds a "proposer boost", a temporary weight bonus -given to newly proposed blocks to prevent balancing attacks. ethlambda does not implement -this. Instead, proposer fairness is handled through the two-stage attestation pipeline +given to newly proposed blocks to prevent balancing attacks. The Lean Chain has no such +boost. Instead, proposer fairness is handled through the two-stage attestation pipeline (the proposer's own attestation enters as "new", not "known"). **No balance weighting.** In the Beacon Chain, a validator with 32 ETH of effective balance -has more fork choice weight than one with 16 ETH. In ethlambda, every validator has exactly +has more fork choice weight than one with 16 ETH. On the Lean Chain, every validator has exactly equal weight (1 vote = 1 unit of weight), simplifying the algorithm and analysis. **No equivocation discounting.** The Beacon Chain's fork choice detects validators who equivocate (attest to conflicting blocks in the same slot) and excludes their weight. This addresses the "nothing at stake" problem where validators can costlessly vote for multiple -forks. ethlambda does not implement this in its fork choice. +forks. The Lean Chain's fork choice does not do this. ### Key Files From 9c6d0a691f3c3d8845c6e24d946ab56e14f7e2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:58:45 -0300 Subject: [PATCH 2/6] docs: scope Lean-only explanations to the Lean Chain Several pages still spoke of "ethlambda" or "the node" where they meant the Lean Chain alone, and some of those statements are false for the Beacon Chain side: the LMD-GHOST notes say every validator has equal weight, 3SF-mini is described as ethlambda's finality gadget, and the architecture page describes only the Lean node without saying so. The "In ethlambda" notes in the Consensus pages become "In ethlambda's Lean Chain", and the architecture, benchmarking, metrics and fork choice visualization pages say which chain they describe. The Lean content is unchanged. --- README.md | 4 ++-- docs/3sf_mini.md | 17 +++++++++-------- docs/architecture.md | 6 ++++++ docs/benchmarking.md | 12 ++++++------ docs/fork_choice_visualization.md | 4 +++- docs/introduction.md | 3 ++- docs/lmd_ghost.md | 18 ++++++++++-------- docs/metrics.md | 4 ++-- docs/slots_and_intervals.md | 8 ++++---- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 0e82f828b..0eee80381 100644 --- a/README.md +++ b/README.md @@ -184,8 +184,8 @@ cargo build --release #### `ethlambda node` β€” the Lean consensus chain -This is what the rest of this README is about, and it is the default: a bare -flag list with no sub-command still runs the node, so existing scripts and +The [devnet section](#running-in-a-devnet) below runs this sub-command, and it +is the default: a bare flag list with no sub-command still runs the node, so existing scripts and Docker entrypoints keep working unchanged. It needs a genesis config, a validator registry, a bootnode list and this diff --git a/docs/3sf_mini.md b/docs/3sf_mini.md index 4f2aae7f3..b85936019 100644 --- a/docs/3sf_mini.md +++ b/docs/3sf_mini.md @@ -1,8 +1,9 @@ # 3SF-mini: Justification & Finalization -ethlambda uses **3SF-mini** (Three-Stage Finality, minimal version) for justification -and finalization. Unlike the Ethereum Beacon Chain's epoch-based Casper FFG, 3SF-mini -operates at the **slot level**: any slot can be justified, not just epoch boundaries. +ethlambda's Lean Chain (`ethlambda node`) uses **3SF-mini** (Three-Stage Finality, +minimal version) for justification and finalization. Unlike the Ethereum Beacon Chain's +epoch-based Casper FFG, which `ethlambda beacon` follows, 3SF-mini operates at the +**slot level**: any slot can be justified, not just epoch boundaries. ## Quick Example: Three Slots to Finality @@ -78,7 +79,7 @@ A checkpoint becomes **justified** when at least two-thirds of validators attest The threshold is computed as: `3 Γ— vote_count >= 2 Γ— validator_count` -> **In ethlambda:** Justification and finalization are processed inside +> **In ethlambda's Lean Chain:** Justification and finalization are processed inside > `process_attestations()` in `crates/blockchain/state_transition/src/lib.rs`, > called from `process_block()`. The supermajority check is > `3 * vote_count >= 2 * validator_count`. @@ -98,7 +99,7 @@ finalized slot. This is the novel part of 3SF-mini. A slot is **justifiable** if `delta = slot - finalized_slot` matches any rule: -> **In ethlambda:** The function `slot_is_justifiable_after(slot, finalized_slot)` in +> **In ethlambda's Lean Chain:** The function `slot_is_justifiable_after(slot, finalized_slot)` in > `crates/blockchain/state_transition/src/lib.rs` implements this check. It uses > `isqrt()` for perfect square detection and the identity `4n(n+1) + 1 = (2n+1)Β²` > for pronic number detection. @@ -155,7 +156,7 @@ whose target is the **next justifiable slot**. In other words, there must be **n justifiable slots between source and target**: the two must be consecutive entries in the justifiability schedule. -> **In ethlambda:** The `try_finalize()` function iterates over slots between +> **In ethlambda's Lean Chain:** The `try_finalize()` function iterates over slots between > source and target and calls `slot_is_justifiable_after` on each. If any slot > is justifiable, finalization fails (source and target aren't consecutive). > The check uses `original_finalized_slot` (the finalized slot at the start of @@ -304,7 +305,7 @@ When finalization advances, the following cleanup occurs: - Gossip signatures and aggregation proofs for finalized blocks are cleaned up - Future fork choice runs start from the finalized slot's successor -> **In ethlambda:** The `justified_slots` bitlist uses relative indexing (index 0 = +> **In ethlambda's Lean Chain:** The `justified_slots` bitlist uses relative indexing (index 0 = > `finalized_slot + 1`). When finalization advances, `shift_window()` in > `crates/blockchain/state_transition/src/justified_slots_ops.rs` drops the > now-finalized prefix. The attestation target is also walked back to the nearest @@ -342,7 +343,7 @@ behind the head. The attestation **target** is derived by walking back from the toward the safe target (max 3 steps), then to the nearest justifiable slot. See [Safe Target Selection](lmd_ghost.md#safe-target-selection) for details. -> **In ethlambda:** `get_attestation_target()` in `crates/blockchain/src/store.rs` +> **In ethlambda's Lean Chain:** `get_attestation_target()` in `crates/blockchain/src/store.rs` > implements this walk-back. `JUSTIFICATION_LOOKBACK_SLOTS = 3` provides a liveness > guarantee: even if the safe target is stuck, the target eventually advances once > the head moves far enough ahead. diff --git a/docs/architecture.md b/docs/architecture.md index 7f889d4ec..9a4df60e0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -12,6 +12,12 @@ genservers: each owns its state, and the only way in is a message. Everything else hangs off those two: an aggregation worker on a blocking thread, the libp2p swarm loop on its own task, and the axum servers that expose the node to the outside world. +This page describes the Lean Chain node, `ethlambda node`. `ethlambda beacon` runs the +same two actors over its own `Store`, but its chain actor (`BlockChain::spawn_beacon`) has +no validator keys, no aggregator role and no duties for the sync gate to hold back: it +imports blocks and runs the Beacon Chain's fork choice. [The mainnet wire](./beacon_wire.md) +and [Beacon Chain state transition](./beacon_stf.md) cover that side. + > Note: for what a genserver is, read [this blogpost on the `spawned` crate](https://blog.lambdaclass.com/introducing-spawned-erlang-style-actors-for-rust/). ```text diff --git a/docs/benchmarking.md b/docs/benchmarking.md index 7a69b858a..ae50db389 100644 --- a/docs/benchmarking.md +++ b/docs/benchmarking.md @@ -1,12 +1,12 @@ # Benchmarking -`ethlambda benchmark` measures two things the node does in production, each -against a reproducible offline workload, with no devnet running: +`ethlambda benchmark` measures two things the node does in production, one per +chain, each against a reproducible offline workload, with no devnet running: -- **`synthetic`**: block building, the way the node performs it when it - proposes, on a synthetic in-memory chain built for the run. -- **`import`**: block import, replaying a corpus of real blocks through the - node's own import path. See [Import workload](#import-workload) below. +- **`synthetic`**: Lean Chain block building, the way `ethlambda node` performs + it when it proposes, on a synthetic in-memory chain built for the run. +- **`import`**: Beacon Chain block import, replaying a corpus of real blocks + through `ethlambda beacon`'s own import path. See [Import workload](#import-workload) below. Both are otherwise only observable through the Prometheus histograms a live node exports. Those are noisy, depend on whatever the network happened to be diff --git a/docs/fork_choice_visualization.md b/docs/fork_choice_visualization.md index f30928cf7..985d0a118 100644 --- a/docs/fork_choice_visualization.md +++ b/docs/fork_choice_visualization.md @@ -9,7 +9,9 @@ A browser-based real-time visualization of the LMD GHOST fork choice tree, serve | `GET /lean/v0/fork_choice/ui` | Interactive D3.js visualization page | | `GET /lean/v0/fork_choice` | JSON snapshot of the fork choice tree | -Both endpoints are served on the API port (`--api-port`, default `5052`). +Both endpoints are served on the API port (`--api-port`, default `5052`), by `ethlambda node` +only: they show the Lean Chain's fork choice tree, and on `ethlambda beacon` every `/lean/v0` +path returns 404. ## Quick Start diff --git a/docs/introduction.md b/docs/introduction.md index 01145be79..512e749a1 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -27,7 +27,8 @@ It is split into five parts: and the [fork choice visualization](./fork_choice_visualization.md) served by the API. - **Development** collects notes for contributors, starting with the - [spec deviations](./spec_deviations.md) where ethlambda departs from leanSpec. + [spec deviations](./spec_deviations.md) where ethlambda departs from leanSpec (Lean + Chain) or consensus-specs (Beacon Chain). For build and contribution instructions, see the [`README`](https://github.com/lambdaclass/ethlambda/blob/main/README.md) and diff --git a/docs/lmd_ghost.md b/docs/lmd_ghost.md index 6c77ad84d..3d767ab03 100644 --- a/docs/lmd_ghost.md +++ b/docs/lmd_ghost.md @@ -5,8 +5,10 @@ fork choice algorithm works. LMD-GHOST is the fork choice rule used by Ethereum' and its derivatives. Each validator's **latest attestation** is their single active vote, and the algorithm follows the heaviest branch at every fork. -This document is implementation-agnostic, with ethlambda-specific details called out in -blockquotes marked **"In ethlambda"**. +This document is implementation-agnostic, with details of ethlambda's Lean Chain +(`ethlambda node`) called out in blockquotes marked **"In ethlambda's Lean Chain"**. +ethlambda's Beacon Chain follower (`ethlambda beacon`) runs the consensus-specs fork +choice instead; see [its section of the Beacon Chain state transition](./beacon_stf.md#fork-choice). > Much of the conceptual framing in this document is inspired by Ben Edgington's > [Eth2 Book](https://eth2book.info/), particularly the @@ -176,7 +178,7 @@ the chain. | Attestations | Latest message per validator: validator_index β†’ attestation | | Min score | Minimum weight for a branch to be considered (0 = follow any branch; higher = conservative) | -> **In ethlambda:** The function is `compute_lmd_ghost_head()` in +> **In ethlambda's Lean Chain:** The function is `compute_lmd_ghost_head()` in > `crates/blockchain/fork_choice/src/lib.rs`. The block tree comes from > the `LiveChain` storage index, and `min_score` is 0 for head selection > or ⌈2V/3βŒ‰ for safe target computation. @@ -207,7 +209,7 @@ effective balance is added instead. β”” start_root (not weighted, used as the descent origin) ``` -> **In ethlambda:** All validators have equal weight (+1 per vote). The Ethereum +> **In ethlambda's Lean Chain:** All validators have equal weight (+1 per vote). The Ethereum > Beacon Chain instead weights votes by effective balance (up to 2048 ETH). Then, **greedily descend.** Starting from the start root, at each node pick the child @@ -399,7 +401,7 @@ finality gadget (e.g., Casper FFG in the Ethereum Beacon Chain, or [3SF-mini](3s β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -> **In ethlambda:** The finality gadget is [3SF-mini](3sf_mini.md), which operates at +> **In ethlambda's Lean Chain:** The finality gadget is [3SF-mini](3sf_mini.md), which operates at > the slot level rather than epoch boundaries. The two layers interact: LMD-GHOST runs its greedy descent **starting from the latest @@ -422,7 +424,7 @@ tree.** Without finality, fork choice would need to consider every block since g and the tree would grow without bound. With finality, all blocks at or before the finalized checkpoint can be discarded from the fork choice's working set. -> **In ethlambda:** The `LiveChain` index (the in-memory block tree used by fork choice) +> **In ethlambda's Lean Chain:** The `LiveChain` index (the in-memory block tree used by fork choice) > is pruned every time finalization advances, keeping it bounded to only the non-finalized > portion of the chain. @@ -455,7 +457,7 @@ designated moments. This ensures all validators operate on a consistent view. fixed points ``` -> **In ethlambda:** The two stages are called "new" and "known" attestations, held in +> **In ethlambda's Lean Chain:** The two stages are called "new" and "known" attestations, held in > the in-memory `new_payloads` and `known_payloads` buffers of the `Store` respectively. > Promotion happens at tick intervals 0 (if proposing) and 4 (end of slot). @@ -576,7 +578,7 @@ Reorgs are normal during transient network conditions but should be rare in stab operation. They cannot cross a finalization boundary: once a block is finalized, it is permanently part of the canonical chain. -> **In ethlambda:** Reorgs are detected by checking whether the old and new heads +> **In ethlambda's Lean Chain:** Reorgs are detected by checking whether the old and new heads > share a common prefix, and tracked via Prometheus metrics > (`lean_fork_choice_reorgs_total`). diff --git a/docs/metrics.md b/docs/metrics.md index 662e4f18e..b36138578 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -2,9 +2,9 @@ We collect various metrics and serve them via a Prometheus-compatible HTTP endpoint at `http://:/metrics` (default: `http://127.0.0.1:5054/metrics`). -A ready-to-use Grafana + Prometheus monitoring stack with pre-configured [leanMetrics](https://github.com/leanEthereum/leanMetrics) dashboards is available in [lean-quickstart](https://github.com/blockblaz/lean-quickstart). +For the Lean Chain, a ready-to-use Grafana + Prometheus monitoring stack with pre-configured [leanMetrics](https://github.com/leanEthereum/leanMetrics) dashboards is available in [lean-quickstart](https://github.com/blockblaz/lean-quickstart). -The exposed metrics follow [the leanMetrics specification](https://github.com/leanEthereum/leanMetrics/blob/2719baad8351c9ad5eaf3c8621f33fcec20a1dc7/metrics.md), with some metrics not yet implemented. We have a full list of implemented metrics below, with a checkbox indicating whether each metric is currently supported or not. +The Lean Chain's metrics follow [the leanMetrics specification](https://github.com/leanEthereum/leanMetrics/blob/2719baad8351c9ad5eaf3c8621f33fcec20a1dc7/metrics.md), with some metrics not yet implemented. The Beacon Chain side has no such specification: its own metrics are among the [Custom Metrics](#custom-metrics-non-leanmetrics) (the "Beacon" sections and Data Column Sidecars), and the validator client's are under [Validator client](#validator-client). We have a full list of implemented metrics below, with a checkbox indicating whether each metric is currently supported or not. ## Node Info Metrics diff --git a/docs/slots_and_intervals.md b/docs/slots_and_intervals.md index e658b3784..6b3a24a0f 100644 --- a/docs/slots_and_intervals.md +++ b/docs/slots_and_intervals.md @@ -33,7 +33,7 @@ knows which duty is due. The order is a dependency chain, since each interval co what the previous one produced. A duty that overruns its interval is not rescheduled: it lands late, and the slot moves on without it. -> **In ethlambda:** the intervals are the `SlotInterval` variants in +> **In ethlambda's Lean Chain:** the intervals are the `SlotInterval` variants in > `crates/blockchain/src/lib.rs`. `INTERVALS_PER_SLOT` is fixed in > `crates/common/types/src/constants.rs` (each interval carries a distinct duty, so > the count is not a knob), while the slot duration is read from the network's config @@ -59,7 +59,7 @@ Genesis occupies slot 0, so proposals start at slot 1, and nothing forces a slot filled: a proposer that is offline or too slow leaves an empty slot, and the next block simply points its parent root at an older block. -> **In ethlambda:** block proposal is merged into the previous slot's head-update +> **In ethlambda's Lean Chain:** block proposal is merged into the previous slot's head-update > interval: the proposer advances its store to the next slot, builds the block there, > and holds publication until the slot boundary. That buys the build one extra interval > of headroom and leaves no actor work at the block-proposal tick itself. @@ -71,7 +71,7 @@ append to it a `(source, target)` [finality vote](./3sf_mini.md#recap-attestatio These votes are in aggregation subnets and are imported by aggregators. Aggregators verify the votes in their subnet and store them for later aggregation. -> **In ethlambda:** a validator's subnet is `validator_index % attestation_committee_count`, +> **In ethlambda's Lean Chain:** a validator's subnet is `validator_index % attestation_committee_count`, > and a node only aggregates for subnets it subscribed to at startup. Aggregation is also > gated on the aggregator role, seeded by `--is-aggregator` and flippable at runtime > through the admin API. A chain whose validators all decline the role still gossips votes @@ -89,7 +89,7 @@ into one proof is the heaviest recurring computation in the client. It is also w block affordable, since a block carrying raw votes would need one full XMSS signature per voter, quickly going over the network bandwidth limit. -> **In ethlambda:** the proofs run on an off-thread worker so the blockchain actor's +> **In ethlambda's Lean Chain:** the proofs run on an off-thread worker so the blockchain actor's > message loop stays responsive, and a session may start up to `EARLY_AGGREGATION_WINDOW` > before the interval boundary once two thirds of the signatures are in. At the session's > soft deadline the actor stops handing out new jobs, but a proof already in flight From dcdb8e3f44fc11b09d94544eee420bd471ac8c1b Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Wed, 28 Oct 2026 14:01:51 -0300 Subject: [PATCH 3/6] docs: document the release binary, the validator client and the beacon node's duties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a quickstart for running the beacon node from a release binary, with the platform requirements, the macOS quarantine note and the SIGINT-only shutdown caveat for systemd. Add an `ethlambda validator` section with its flags and the no-slashing-protection warning. Point the Docker quickstart at the `latest` release image instead of the hand-published `beacon` tag. Correct the beacon node description, which claimed it publishes nothing and joins no subnets, and the PlatΓ₯berget paragraph, which said the validator endpoints refuse gloas. Drop the shipped state-diffs roadmap item and scope the execution-client item to the Lean Chain. Mark Docker and yq as prerequisites of the devnet and image targets only. --- README.md | 143 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 117 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0eee80381..4d9d26780 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,17 @@ Follows Ethereum mainnet from a checkpoint-synced anchor and serves the standard Beacon API on port 5052, paired with an [ethrex](https://github.com/lambdaclass/ethrex) execution client that validates each block's execution payload. Both run from pre-built images -(`ghcr.io/lambdaclass/ethlambda:beacon` and `ghcr.io/lambdaclass/ethrex`), so -only [Docker](https://www.docker.com/get-started) is needed. +(`ghcr.io/lambdaclass/ethlambda:latest` and `ghcr.io/lambdaclass/ethrex`), so +only [Docker](https://www.docker.com/get-started) is needed. To run the release +binary without Docker, see +[Running from a release binary](#running-from-a-release-binary) below. > **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for > mainnet (1 TB recommended; see its > [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). ```sh -docker pull ghcr.io/lambdaclass/ethlambda:beacon +docker pull ghcr.io/lambdaclass/ethlambda:latest docker pull ghcr.io/lambdaclass/ethrex mkdir -p beacon-data ethrex-data @@ -55,7 +57,7 @@ docker run -d --name ethlambda-beacon --network ethereum --stop-signal SIGINT \ -p 127.0.0.1:5052:5052 \ -v "$PWD/beacon-data:/data" \ -v "$PWD/jwt.hex:/jwt.hex:ro" \ - ghcr.io/lambdaclass/ethlambda:beacon beacon \ + ghcr.io/lambdaclass/ethlambda:latest beacon \ --network mainnet \ --checkpoint-sync-url https://beaconstate.ethstaker.cc \ --node-key /data/node-key \ @@ -76,7 +78,7 @@ answers attestation data, aggregation and block production requests with `503`: ```sh -docker pull ghcr.io/lambdaclass/ethlambda:beacon +docker pull ghcr.io/lambdaclass/ethlambda:latest mkdir -p beacon-data # A persisted key keeps the node's identity, and so its custody set, stable across restarts @@ -89,7 +91,7 @@ docker run -d --name ethlambda-beacon --stop-signal SIGINT \ -p 9000:9000/udp -p 9001:9001/udp -p 9001:9001/tcp \ -p 127.0.0.1:5052:5052 \ -v "$PWD/beacon-data:/data" \ - ghcr.io/lambdaclass/ethlambda:beacon beacon \ + ghcr.io/lambdaclass/ethlambda:latest beacon \ --network mainnet \ --checkpoint-sync-url https://beaconstate.ethstaker.cc \ --node-key /data/node-key \ @@ -135,10 +137,57 @@ provider. See the remaining flags. `--network plataberget` (the Glamsterdam testnet) is built in as well. It has -run gloas since its `GLOAS_FORK_EPOCH`, which the follower follows: a gloas -checkpoint anchor is accepted, and payload envelopes are fetched and imported. -It stays a follower, so the validator-client endpoints still refuse a gloas -epoch. +run gloas since its `GLOAS_FORK_EPOCH`, which the node follows: a gloas +checkpoint anchor is accepted, payload envelopes are fetched and imported, and +the validator-client endpoints serve gloas duties, the payload envelope and the +payload timeliness vote included. + +### Running from a release binary + +Every release attaches binaries for Linux (x86_64, aarch64) and macOS (Apple +silicon); see [Releases](#releases) below. This is the beacon node from the +quickstart above, run from one of them, with ethrex already listening on +`127.0.0.1:8551`: + +```sh +# Pick the asset for your platform: ethlambda-linux-x86_64, ethlambda-linux-aarch64 or ethlambda-macos-aarch64 +curl -LO https://github.com/lambdaclass/ethlambda/releases/latest/download/ethlambda-linux-x86_64 +chmod +x ethlambda-linux-x86_64 && mv ethlambda-linux-x86_64 ethlambda +./ethlambda --version + +mkdir -p beacon-data +# A persisted key keeps the node's identity, and so its custody set, stable across restarts +openssl rand -hex 32 > beacon-data/node-key +# The secret both clients authenticate the Engine API with; pass ethrex the same file +openssl rand -hex 32 > jwt.hex + +./ethlambda beacon \ + --network mainnet \ + --checkpoint-sync-url https://beaconstate.ethstaker.cc \ + --node-key beacon-data/node-key \ + --data-dir beacon-data/db \ + --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ + --execution-endpoint http://127.0.0.1:8551 \ + --execution-jwt-secret jwt.hex +``` + +Drop the two `--execution-*` flags to follow the chain without an execution +client, with the limits described above. The Beacon API binds `127.0.0.1:5052` +by default, so `--http-address` is only needed to reach it from another host. +`--discovery.advertise-ip` still applies: without it the node advertises +`0.0.0.0`. Stop the node with `Ctrl+C`; it shuts down gracefully on SIGINT +only, so a systemd unit needs `KillSignal=SIGINT`. + +- `ethlambda-linux-x86_64` targets x86-64-v3, so it needs a CPU with AVX2 + (Haswell or later), and glibc 2.35 or newer. +- The macOS binary is not notarized. A browser download is quarantined, which + `xattr -d com.apple.quarantine ` lifts; a `curl` download is not. +- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify a + binary's provenance attestation. + +The same binary runs [`ethlambda node`](#ethlambda-node--the-lean-consensus-chain) +and [`ethlambda validator`](#ethlambda-validator--the-validator-client); their +flags are below. ### Lean consensus devnet @@ -151,8 +200,9 @@ To run a local lean devnet with ethlambda, follow the instructions on the - [Rust](https://rust-lang.org/tools/install) - [Git](https://git-scm.com/install) -- [Docker](https://www.docker.com/get-started) -- [yq](https://github.com/mikefarah/yq#install) +- For `make run-devnet` and `make docker-build` only: + [Docker](https://www.docker.com/get-started) and + [yq](https://github.com/mikefarah/yq#install) ### Building and testing @@ -173,14 +223,16 @@ Run `make help` or take a look at our [`Makefile`](./Makefile) for other useful ### Running the node -The binary follows one of two chains, chosen by a sub-command: +The binary carries both chains and the validator client as sub-commands: ```sh cargo build --release -./target/release/ethlambda [flags] +./target/release/ethlambda [flags] ``` -[`docs/cli.md`](./docs/cli.md) is the full flag reference for both. +[`docs/cli.md`](./docs/cli.md) is the full flag reference for every sub-command, +`benchmark` and `replay-era` included. A +[release binary](#running-from-a-release-binary) takes the same arguments. #### `ethlambda node` β€” the Lean consensus chain @@ -217,13 +269,16 @@ under `lean-quickstart/local-devnet/genesis/`. See #### `ethlambda beacon` β€” the Ethereum Beacon Chain -A beacon chain follower. It anchors at a finalized checkpoint fetched from a -Beacon API, then follows the chain to its tip: blocks arriving over gossip and -range sync are imported through fork choice, and it custodies its slice of the -fulu data column matrix. What it stores it serves back, to peers over +A beacon node. It anchors at a finalized checkpoint fetched from a Beacon API, +then follows the chain to its tip: blocks arriving over gossip and range sync +are imported through fork choice, and it custodies its slice of the fulu data +column matrix. What it stores it serves back, to peers over `beacon_blocks_by_{range,root}` and to anyone over the standard Beacon API on -`--api-port`. It has no validator duties: it publishes nothing and subscribes to -no attestation or sync committee subnet. +`--api-port`. It performs no validator duties of its own: those belong to +[`ethlambda validator`](#ethlambda-validator--the-validator-client), which it +serves over that API once paired with an execution client, joining the +attestation and sync committee subnets its validators ask for and publishing +what they sign. ```sh ./target/release/ethlambda beacon \ @@ -260,6 +315,42 @@ See [`docs/beacon_wire.md`](./docs/beacon_wire.md) for what goes on the wire, chosen and verified, and [`docs/rpc.md`](./docs/rpc.md) for the Beacon API endpoints. +#### `ethlambda validator` β€” the validator client + +A separate process that holds the keys and performs the duties: attestations, +block proposals, aggregation, sync committee messages and, from gloas on, the +payload envelope and the payload timeliness vote. It talks to a beacon node over +the standard Beacon API, so it works against `ethlambda beacon` (run with the +`--execution-*` flags) or any other conformant beacon node. + +> **Warning:** the validator client keeps no slashing-protection record. Do not +> run the same keys in another client while this one runs, and treat a restart +> with the same care as a manual key move. Read +> [Spec deviations](./docs/spec_deviations.md#the-validator-client-keeps-no-slashing-protection-record) +> before using keys that hold real stake. The client repeats this warning at +> startup. + +```sh +./target/release/ethlambda validator \ + --beacon-nodes http://127.0.0.1:5052 \ + --validators-dir ./validators \ + --secrets-dir ./secrets \ + --suggested-fee-recipient 0x +``` + +| Flag | Meaning | +|---|---| +| `--beacon-nodes` | Base URLs of the beacon nodes to use, comma-separated or repeated. Tried in order, so the order is a preference | +| `--validators-dir` | Directory of EIP-2335 keystores and `validator_definitions.yml`. Without that file, keystores in the Lighthouse layout (`<0xpubkey>/voting-keystore.json`) are discovered and the file is written | +| `--secrets-dir` | One password file per keystore, named after the validator's public key | +| `--suggested-fee-recipient` | Execution address for block rewards, `0x`-prefixed. Optional, but without it the beacon node picks an address that is not yours | +| `--graffiti` | Text for proposed blocks, at most 32 bytes | +| `--enable-keymanager` | Serve the keymanager API on `--keymanager-port` (default `5062`). Off by default, since it mutates key material | + +Metrics are served on `--metrics-port` (default `5064`). See +[`docs/cli.md`](./docs/cli.md#validator-flags) for the remaining flags and what +the client does each slot. + ### Running in a devnet To run a local devnet with multiple clients using [lean-quickstart](https://github.com/blockblaz/lean-quickstart): @@ -318,9 +409,10 @@ Full documentation is available in the [`docs/`](./docs) directory. Please refer ethlambda is a Beacon Chain and Lean Chain client. On the Beacon Chain (`ethlambda beacon`), it follows a network from a checkpoint-synced anchor and serves -the standard Beacon API; see the [Beacon Chain follower](#beacon-chain-follower) -quickstart. On the Lean Chain (`ethlambda node`), it implements the core features of a -Lean consensus client: +the standard Beacon API; paired with an execution client it validates payloads and +serves the [validator client](#ethlambda-validator--the-validator-client). See the +[Beacon Chain follower](#beacon-chain-follower) quickstart. On the Lean Chain +(`ethlambda node`), it implements the core features of a Lean consensus client: - **Networking** β€” libp2p peer connections, STATUS message handling, gossipsub for blocks and attestations - **State management** β€” genesis state generation, state transition function, block processing @@ -373,9 +465,8 @@ We wrote a [blogpost](https://blog.lambdaclass.com/ethlambda-devnet-5-and-beyond Some features we are looking to implement in the near future, in order of priority: - [Optimize block building](https://github.com/lambdaclass/ethlambda/issues/465) -- [Use state-diffs for storing states in the database](https://github.com/lambdaclass/ethlambda/issues/238) - [Prototype Goldfish + RLMD GHOST + BFT β€” devnet-6](https://github.com/lambdaclass/ethlambda/pull/434) -- [Integrate with execution clients](https://github.com/lambdaclass/ethlambda/pull/367), in particular [ethrex](https://github.com/lambdaclass/ethrex) β€” devnet-7 +- Integrate the Lean Chain with execution clients, in particular [ethrex](https://github.com/lambdaclass/ethrex) β€” devnet-7 ([first prototype](https://github.com/lambdaclass/ethlambda/pull/367)). The beacon node already pairs with one over the Engine API; see [`docs/beacon_engine.md`](./docs/beacon_engine.md) - Replace libp2p with the experimental [ethp2p](https://github.com/ethp2p/ethp2p), which we are porting to Rust - [Add a guest program and ZK proving of the STF](https://github.com/lambdaclass/ethlambda/issues/156) - Rewrite the STF in the concrete programming language to enable formal verification From 95d5c8b8f8f08160f8257f25d517001a5fb59383 Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Wed, 28 Oct 2026 15:51:11 -0300 Subject: [PATCH 4/6] Put the release-binary quickstart first and run ethrex alongside it Reorder the Quickstart so the beacon node runs from the release binaries first, with the ethrex binary downloaded and started next to it, and move the Docker setup below as its own section. Capitalize Gloas in prose and note that slashing protection is a work in progress. Addresses the review comments on #716. --- README.md | 218 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index 4d9d26780..0ec6c546e 100644 --- a/README.md +++ b/README.md @@ -15,16 +15,113 @@ Minimalist, fast and modular implementation of a Beacon Chain and Lean Chain cli Follows Ethereum mainnet from a checkpoint-synced anchor and serves the standard Beacon API on port 5052, paired with an [ethrex](https://github.com/lambdaclass/ethrex) execution client that validates -each block's execution payload. Both run from pre-built images -(`ghcr.io/lambdaclass/ethlambda:latest` and `ghcr.io/lambdaclass/ethrex`), so -only [Docker](https://www.docker.com/get-started) is needed. To run the release -binary without Docker, see -[Running from a release binary](#running-from-a-release-binary) below. +each block's execution payload. Both ship release binaries for Linux (x86_64, +aarch64) and macOS (Apple silicon): ethlambda's are listed under +[Releases](#releases) below and ethrex's on +[its releases page](https://github.com/lambdaclass/ethrex/releases/latest). +To run both from pre-built Docker images instead, see +[Running with Docker](#running-with-docker) below. > **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for > mainnet (1 TB recommended; see its > [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). +```sh +# Pick the assets for your platform: linux-x86_64, linux-aarch64 or macos-aarch64 +curl -LO https://github.com/lambdaclass/ethlambda/releases/latest/download/ethlambda-linux-x86_64 +curl -LO https://github.com/lambdaclass/ethrex/releases/latest/download/ethrex-linux-x86_64 +chmod +x ethlambda-linux-x86_64 ethrex-linux-x86_64 +mv ethlambda-linux-x86_64 ethlambda && mv ethrex-linux-x86_64 ethrex +./ethlambda --version && ./ethrex --version + +mkdir -p beacon-data ethrex-data +# A persisted key keeps the node's identity, and so its custody set, stable across restarts +openssl rand -hex 32 > beacon-data/node-key +# The secret both clients authenticate the Engine API with +openssl rand -hex 32 > jwt.hex + +# In one terminal: ethrex, serving the Engine API on 127.0.0.1:8551 +./ethrex \ + --network mainnet \ + --datadir ethrex-data \ + --authrpc.jwtsecret jwt.hex + +# In another: the beacon node +./ethlambda beacon \ + --network mainnet \ + --checkpoint-sync-url https://beaconstate.ethstaker.cc \ + --node-key beacon-data/node-key \ + --data-dir beacon-data/db \ + --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ + --execution-endpoint http://127.0.0.1:8551 \ + --execution-jwt-secret jwt.hex +``` + +`--discovery.advertise-ip` is the address published in the node's ENR. Without +it the node advertises `0.0.0.0` and logs a warning. It still dials out and +follows the chain, but peers cannot reach it until discovery learns the address +from their replies. The Beacon API binds `127.0.0.1:5052` by default, so +`--http-address` is only needed to reach it from another host. Stop the node +with `Ctrl+C`; it shuts down gracefully on SIGINT only, so a systemd unit needs +`KillSignal=SIGINT`. + +- `ethlambda-linux-x86_64` targets x86-64-v3, so it needs a CPU with AVX2 + (Haswell or later), and glibc 2.35 or newer. +- The macOS binaries are not notarized. A browser download is quarantined, which + `xattr -d com.apple.quarantine ` lifts; a `curl` download is not. +- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify an + ethlambda binary's provenance attestation. + +To run the consensus layer only, drop ethrex, the JWT secret and the two +`--execution-*` flags. The node then follows the chain without validating +execution payloads. It cannot serve a validator client, since it answers +attestation data, aggregation and block production requests with `503`. + +The first start downloads the finalized state (several hundred MB) and logs +`Beacon checkpoint sync complete`, then a few minutes of +`Block parent missing, storing as pending` before `Block imported successfully` +lines start. Later starts resume from `beacon-data/db`. + +ethrex logs `No messages from the consensus layer` until the beacon node's +first fork choice update, then starts snap sync. Until that finishes, which +takes hours on mainnet, ethrex answers each payload `SYNCING` and the beacon +node imports blocks optimistically. + +Check progress from another terminal: + +```sh +curl -s localhost:5052/eth/v1/node/syncing +``` + +The node has caught up once `sync_distance` (the chain's current slot minus +`head_slot`) is near 0. Don't rely on `is_syncing` yet: it currently reads +`false` during catch-up as well. + +For Sepolia or Hoodi, change `--network mainnet` to `--network sepolia` or +`--network hoodi` in both the ethrex and the beacon node arguments, and point +`--checkpoint-sync-url` at that network's provider. See +[`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below for +the remaining flags. + +`--network plataberget` (the Glamsterdam testnet) is built in as well. It has +run Gloas since its `GLOAS_FORK_EPOCH`, which the node follows: a Gloas +checkpoint anchor is accepted, payload envelopes are fetched and imported, and +the validator-client endpoints serve Gloas duties, the payload envelope and the +payload timeliness vote included. + +The same binary runs [`ethlambda node`](#ethlambda-node--the-lean-consensus-chain) +and [`ethlambda validator`](#ethlambda-validator--the-validator-client); their +flags are below. + +### Running with Docker + +The same two nodes from pre-built images (`ghcr.io/lambdaclass/ethlambda:latest` +and `ghcr.io/lambdaclass/ethrex`), so only +[Docker](https://www.docker.com/get-started) is needed. Follow the logs with +`docker logs -f ethlambda-beacon` and `docker logs -f ethrex`. For another +network, change `--network mainnet` in both containers' arguments, not Docker's +own `--network ethereum`. + ```sh docker pull ghcr.io/lambdaclass/ethlambda:latest docker pull ghcr.io/lambdaclass/ethrex @@ -34,7 +131,7 @@ mkdir -p beacon-data ethrex-data openssl rand -hex 32 > beacon-data/node-key # The secret both clients authenticate the Engine API with openssl rand -hex 32 > jwt.hex -# This host's public address, published in the node's ENR (see below) +# This host's public address, published in the node's ENR (see above) PUBLIC_IP=$(curl -s https://ifconfig.me) # A private network, so the beacon node reaches ethrex's Engine API by name @@ -71,11 +168,8 @@ docker run -d --name ethlambda-beacon --network ethereum --stop-signal SIGINT \
Running without an execution client -To run the consensus layer only, drop ethrex, the shared network, the JWT secret -and the two `--execution-*` flags. The node then follows the chain without -validating execution payloads. It cannot serve a validator client, since it -answers attestation data, aggregation and block production requests with -`503`: +The consensus layer only, as described above: no ethrex, no shared network, no +JWT secret and no `--execution-*` flags. ```sh docker pull ghcr.io/lambdaclass/ethlambda:latest @@ -83,7 +177,7 @@ docker pull ghcr.io/lambdaclass/ethlambda:latest mkdir -p beacon-data # A persisted key keeps the node's identity, and so its custody set, stable across restarts openssl rand -hex 32 > beacon-data/node-key -# This host's public address, published in the node's ENR (see below) +# This host's public address, published in the node's ENR (see above) PUBLIC_IP=$(curl -s https://ifconfig.me) # The node shuts down gracefully on SIGINT only, so this makes `docker stop` flush its state @@ -102,93 +196,6 @@ docker run -d --name ethlambda-beacon --stop-signal SIGINT \
-`--discovery.advertise-ip` is the address published in the node's ENR. Behind -Docker's port mapping the node cannot see its own public address, so without -the flag it advertises `0.0.0.0` and logs a warning. It still dials out and -follows the chain, but peers cannot reach it until discovery learns the address -from their replies. - -Follow the logs with `docker logs -f ethlambda-beacon`. The first start -downloads the finalized state (several hundred MB) and logs -`Beacon checkpoint sync complete`, then a few minutes of -`Block parent missing, storing as pending` before `Block imported successfully` -lines start. Later starts resume from `beacon-data/db`. - -ethrex logs `No messages from the consensus layer` until the beacon node's -first fork choice update, then starts snap sync (`docker logs -f ethrex`). -Until that finishes, which takes hours on mainnet, ethrex answers each payload -`SYNCING` and the beacon node imports blocks optimistically. - -Check progress from another terminal: - -```sh -curl -s localhost:5052/eth/v1/node/syncing -``` - -The node has caught up once `sync_distance` (the chain's current slot minus -`head_slot`) is near 0. Don't rely on `is_syncing` yet: it currently reads -`false` during catch-up as well. - -For Sepolia or Hoodi, change `--network mainnet` to `--network sepolia` or -`--network hoodi` in both the ethrex and the beacon node arguments (not Docker's -own `--network ethereum`), and point `--checkpoint-sync-url` at that network's -provider. See -[`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below for -the remaining flags. - -`--network plataberget` (the Glamsterdam testnet) is built in as well. It has -run gloas since its `GLOAS_FORK_EPOCH`, which the node follows: a gloas -checkpoint anchor is accepted, payload envelopes are fetched and imported, and -the validator-client endpoints serve gloas duties, the payload envelope and the -payload timeliness vote included. - -### Running from a release binary - -Every release attaches binaries for Linux (x86_64, aarch64) and macOS (Apple -silicon); see [Releases](#releases) below. This is the beacon node from the -quickstart above, run from one of them, with ethrex already listening on -`127.0.0.1:8551`: - -```sh -# Pick the asset for your platform: ethlambda-linux-x86_64, ethlambda-linux-aarch64 or ethlambda-macos-aarch64 -curl -LO https://github.com/lambdaclass/ethlambda/releases/latest/download/ethlambda-linux-x86_64 -chmod +x ethlambda-linux-x86_64 && mv ethlambda-linux-x86_64 ethlambda -./ethlambda --version - -mkdir -p beacon-data -# A persisted key keeps the node's identity, and so its custody set, stable across restarts -openssl rand -hex 32 > beacon-data/node-key -# The secret both clients authenticate the Engine API with; pass ethrex the same file -openssl rand -hex 32 > jwt.hex - -./ethlambda beacon \ - --network mainnet \ - --checkpoint-sync-url https://beaconstate.ethstaker.cc \ - --node-key beacon-data/node-key \ - --data-dir beacon-data/db \ - --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ - --execution-endpoint http://127.0.0.1:8551 \ - --execution-jwt-secret jwt.hex -``` - -Drop the two `--execution-*` flags to follow the chain without an execution -client, with the limits described above. The Beacon API binds `127.0.0.1:5052` -by default, so `--http-address` is only needed to reach it from another host. -`--discovery.advertise-ip` still applies: without it the node advertises -`0.0.0.0`. Stop the node with `Ctrl+C`; it shuts down gracefully on SIGINT -only, so a systemd unit needs `KillSignal=SIGINT`. - -- `ethlambda-linux-x86_64` targets x86-64-v3, so it needs a CPU with AVX2 - (Haswell or later), and glibc 2.35 or newer. -- The macOS binary is not notarized. A browser download is quarantined, which - `xattr -d com.apple.quarantine ` lifts; a `curl` download is not. -- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify a - binary's provenance attestation. - -The same binary runs [`ethlambda node`](#ethlambda-node--the-lean-consensus-chain) -and [`ethlambda validator`](#ethlambda-validator--the-validator-client); their -flags are below. - ### Lean consensus devnet To run a local lean devnet with ethlambda, follow the instructions on the @@ -231,8 +238,8 @@ cargo build --release ``` [`docs/cli.md`](./docs/cli.md) is the full flag reference for every sub-command, -`benchmark` and `replay-era` included. A -[release binary](#running-from-a-release-binary) takes the same arguments. +`benchmark` and `replay-era` included. The +[quickstart](#beacon-chain-follower) runs a release binary with the same arguments. #### `ethlambda node` β€” the Lean consensus chain @@ -318,14 +325,15 @@ endpoints. #### `ethlambda validator` β€” the validator client A separate process that holds the keys and performs the duties: attestations, -block proposals, aggregation, sync committee messages and, from gloas on, the +block proposals, aggregation, sync committee messages and, from Gloas on, the payload envelope and the payload timeliness vote. It talks to a beacon node over the standard Beacon API, so it works against `ethlambda beacon` (run with the `--execution-*` flags) or any other conformant beacon node. -> **Warning:** the validator client keeps no slashing-protection record. Do not -> run the same keys in another client while this one runs, and treat a restart -> with the same care as a manual key move. Read +> **Warning:** the validator client keeps no slashing-protection record yet. +> Slashing protection is a work in progress and will be added soon. Until then, +> do not run the same keys in another client while this one runs, and treat a +> restart with the same care as a manual key move. Read > [Spec deviations](./docs/spec_deviations.md#the-validator-client-keeps-no-slashing-protection-record) > before using keys that hold real stake. The client repeats this warning at > startup. From bc1379aa784624b008a992c4f7a5a66be14ba505 Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Wed, 28 Oct 2026 15:59:18 -0300 Subject: [PATCH 5/6] Make the quickstart a standalone binary and split ethrex into its own section The quickstart now runs the beacon node alone from the release binary, without an execution client, and says what that mode cannot do. Pairing with ethrex, from its own release binary, gets a section of its own, and the Docker setup moves under it as a sub-section. --- README.md | 117 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 0ec6c546e..b3aa70df1 100644 --- a/README.md +++ b/README.md @@ -13,48 +13,30 @@ Minimalist, fast and modular implementation of a Beacon Chain and Lean Chain cli ### Beacon Chain follower Follows Ethereum mainnet from a checkpoint-synced anchor and serves the -standard Beacon API on port 5052, paired with an -[ethrex](https://github.com/lambdaclass/ethrex) execution client that validates -each block's execution payload. Both ship release binaries for Linux (x86_64, -aarch64) and macOS (Apple silicon): ethlambda's are listed under -[Releases](#releases) below and ethrex's on -[its releases page](https://github.com/lambdaclass/ethrex/releases/latest). -To run both from pre-built Docker images instead, see -[Running with Docker](#running-with-docker) below. - -> **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for -> mainnet (1 TB recommended; see its -> [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). +standard Beacon API on port 5052. Every release attaches binaries for Linux +(x86_64, aarch64) and macOS (Apple silicon); see [Releases](#releases) below. +On its own the node follows the chain without validating execution payloads +and cannot serve a validator client, since it answers attestation data, +aggregation and block production requests with `503`; for both, pair it with +an execution client as described in +[Running with the ethrex execution client](#running-with-the-ethrex-execution-client). ```sh -# Pick the assets for your platform: linux-x86_64, linux-aarch64 or macos-aarch64 +# Pick the asset for your platform: ethlambda-linux-x86_64, ethlambda-linux-aarch64 or ethlambda-macos-aarch64 curl -LO https://github.com/lambdaclass/ethlambda/releases/latest/download/ethlambda-linux-x86_64 -curl -LO https://github.com/lambdaclass/ethrex/releases/latest/download/ethrex-linux-x86_64 -chmod +x ethlambda-linux-x86_64 ethrex-linux-x86_64 -mv ethlambda-linux-x86_64 ethlambda && mv ethrex-linux-x86_64 ethrex -./ethlambda --version && ./ethrex --version +chmod +x ethlambda-linux-x86_64 && mv ethlambda-linux-x86_64 ethlambda +./ethlambda --version -mkdir -p beacon-data ethrex-data +mkdir -p beacon-data # A persisted key keeps the node's identity, and so its custody set, stable across restarts openssl rand -hex 32 > beacon-data/node-key -# The secret both clients authenticate the Engine API with -openssl rand -hex 32 > jwt.hex -# In one terminal: ethrex, serving the Engine API on 127.0.0.1:8551 -./ethrex \ - --network mainnet \ - --datadir ethrex-data \ - --authrpc.jwtsecret jwt.hex - -# In another: the beacon node ./ethlambda beacon \ --network mainnet \ --checkpoint-sync-url https://beaconstate.ethstaker.cc \ --node-key beacon-data/node-key \ --data-dir beacon-data/db \ - --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ - --execution-endpoint http://127.0.0.1:8551 \ - --execution-jwt-secret jwt.hex + --discovery.advertise-ip "$(curl -s https://ifconfig.me)" ``` `--discovery.advertise-ip` is the address published in the node's ENR. Without @@ -67,26 +49,16 @@ with `Ctrl+C`; it shuts down gracefully on SIGINT only, so a systemd unit needs - `ethlambda-linux-x86_64` targets x86-64-v3, so it needs a CPU with AVX2 (Haswell or later), and glibc 2.35 or newer. -- The macOS binaries are not notarized. A browser download is quarantined, which +- The macOS binary is not notarized. A browser download is quarantined, which `xattr -d com.apple.quarantine ` lifts; a `curl` download is not. -- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify an - ethlambda binary's provenance attestation. - -To run the consensus layer only, drop ethrex, the JWT secret and the two -`--execution-*` flags. The node then follows the chain without validating -execution payloads. It cannot serve a validator client, since it answers -attestation data, aggregation and block production requests with `503`. +- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify a + binary's provenance attestation. The first start downloads the finalized state (several hundred MB) and logs `Beacon checkpoint sync complete`, then a few minutes of `Block parent missing, storing as pending` before `Block imported successfully` lines start. Later starts resume from `beacon-data/db`. -ethrex logs `No messages from the consensus layer` until the beacon node's -first fork choice update, then starts snap sync. Until that finishes, which -takes hours on mainnet, ethrex answers each payload `SYNCING` and the beacon -node imports blocks optimistically. - Check progress from another terminal: ```sh @@ -98,10 +70,9 @@ The node has caught up once `sync_distance` (the chain's current slot minus `false` during catch-up as well. For Sepolia or Hoodi, change `--network mainnet` to `--network sepolia` or -`--network hoodi` in both the ethrex and the beacon node arguments, and point -`--checkpoint-sync-url` at that network's provider. See -[`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below for -the remaining flags. +`--network hoodi` and point `--checkpoint-sync-url` at that network's provider. +See [`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below +for the remaining flags. `--network plataberget` (the Glamsterdam testnet) is built in as well. It has run Gloas since its `GLOAS_FORK_EPOCH`, which the node follows: a Gloas @@ -113,7 +84,53 @@ The same binary runs [`ethlambda node`](#ethlambda-node--the-lean-consensus-chai and [`ethlambda validator`](#ethlambda-validator--the-validator-client); their flags are below. -### Running with Docker +### Running with the ethrex execution client + +Paired with an [ethrex](https://github.com/lambdaclass/ethrex) execution +client, the beacon node validates each block's execution payload and can serve +a [validator client](#ethlambda-validator--the-validator-client). The two talk +over the Engine API, authenticated with a shared JWT secret. ethrex ships +release binaries for the same three platforms on +[its releases page](https://github.com/lambdaclass/ethrex/releases/latest). + +> **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for +> mainnet (1 TB recommended; see its +> [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). + +```sh +# Pick the asset for your platform: ethrex-linux-x86_64, ethrex-linux-aarch64 or ethrex-macos-aarch64 +curl -LO https://github.com/lambdaclass/ethrex/releases/latest/download/ethrex-linux-x86_64 +chmod +x ethrex-linux-x86_64 && mv ethrex-linux-x86_64 ethrex +./ethrex --version + +mkdir -p ethrex-data +# The secret both clients authenticate the Engine API with +openssl rand -hex 32 > jwt.hex + +# In one terminal: ethrex, serving the Engine API on 127.0.0.1:8551 +./ethrex \ + --network mainnet \ + --datadir ethrex-data \ + --authrpc.jwtsecret jwt.hex + +# In another: the beacon node from the quickstart, pointed at ethrex +./ethlambda beacon \ + --network mainnet \ + --checkpoint-sync-url https://beaconstate.ethstaker.cc \ + --node-key beacon-data/node-key \ + --data-dir beacon-data/db \ + --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ + --execution-endpoint http://127.0.0.1:8551 \ + --execution-jwt-secret jwt.hex +``` + +ethrex logs `No messages from the consensus layer` until the beacon node's +first fork choice update, then starts snap sync. Until that finishes, which +takes hours on mainnet, ethrex answers each payload `SYNCING` and the beacon +node imports blocks optimistically. For another network, change +`--network mainnet` in both the ethrex and the beacon node arguments. + +#### Running with Docker The same two nodes from pre-built images (`ghcr.io/lambdaclass/ethlambda:latest` and `ghcr.io/lambdaclass/ethrex`), so only @@ -168,8 +185,8 @@ docker run -d --name ethlambda-beacon --network ethereum --stop-signal SIGINT \
Running without an execution client -The consensus layer only, as described above: no ethrex, no shared network, no -JWT secret and no `--execution-*` flags. +The standalone node from the quickstart: no ethrex, no shared network, no JWT +secret and no `--execution-*` flags. ```sh docker pull ghcr.io/lambdaclass/ethlambda:latest From bac988afb38261353b7b99aeb119af225988d031 Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Wed, 28 Oct 2026 16:25:43 -0300 Subject: [PATCH 6/6] Run ethrex inside the Beacon Chain quickstart again, with a paragraph on skipping it The quickstart downloads both release binaries and starts ethrex before the beacon node, keeping the Engine API and JWT sentence from the section it replaces, and a paragraph says what running without ethrex skips (payload validation) and loses (the validator client). The Docker setup moves up to a section of its own under the quickstart. --- README.md | 119 ++++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index b3aa70df1..2b3457da7 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,49 @@ Minimalist, fast and modular implementation of a Beacon Chain and Lean Chain cli ### Beacon Chain follower Follows Ethereum mainnet from a checkpoint-synced anchor and serves the -standard Beacon API on port 5052. Every release attaches binaries for Linux -(x86_64, aarch64) and macOS (Apple silicon); see [Releases](#releases) below. -On its own the node follows the chain without validating execution payloads -and cannot serve a validator client, since it answers attestation data, -aggregation and block production requests with `503`; for both, pair it with -an execution client as described in -[Running with the ethrex execution client](#running-with-the-ethrex-execution-client). +standard Beacon API on port 5052, paired with an +[ethrex](https://github.com/lambdaclass/ethrex) execution client that validates +each block's execution payload. The two talk over the Engine API, authenticated +with a shared JWT secret. Both ship release binaries for Linux (x86_64, +aarch64) and macOS (Apple silicon): ethlambda's are listed under +[Releases](#releases) below and ethrex's on +[its releases page](https://github.com/lambdaclass/ethrex/releases/latest). +To run both from pre-built Docker images instead, see +[Running with Docker](#running-with-docker) below. + +> **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for +> mainnet (1 TB recommended; see its +> [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). ```sh -# Pick the asset for your platform: ethlambda-linux-x86_64, ethlambda-linux-aarch64 or ethlambda-macos-aarch64 +# Pick the assets for your platform: linux-x86_64, linux-aarch64 or macos-aarch64 curl -LO https://github.com/lambdaclass/ethlambda/releases/latest/download/ethlambda-linux-x86_64 -chmod +x ethlambda-linux-x86_64 && mv ethlambda-linux-x86_64 ethlambda -./ethlambda --version +curl -LO https://github.com/lambdaclass/ethrex/releases/latest/download/ethrex-linux-x86_64 +chmod +x ethlambda-linux-x86_64 ethrex-linux-x86_64 +mv ethlambda-linux-x86_64 ethlambda && mv ethrex-linux-x86_64 ethrex +./ethlambda --version && ./ethrex --version -mkdir -p beacon-data +mkdir -p beacon-data ethrex-data # A persisted key keeps the node's identity, and so its custody set, stable across restarts openssl rand -hex 32 > beacon-data/node-key +# The secret both clients authenticate the Engine API with +openssl rand -hex 32 > jwt.hex +# In one terminal: ethrex, serving the Engine API on 127.0.0.1:8551 +./ethrex \ + --network mainnet \ + --datadir ethrex-data \ + --authrpc.jwtsecret jwt.hex + +# In another: the beacon node, pointed at ethrex ./ethlambda beacon \ --network mainnet \ --checkpoint-sync-url https://beaconstate.ethstaker.cc \ --node-key beacon-data/node-key \ --data-dir beacon-data/db \ - --discovery.advertise-ip "$(curl -s https://ifconfig.me)" + --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ + --execution-endpoint http://127.0.0.1:8551 \ + --execution-jwt-secret jwt.hex ``` `--discovery.advertise-ip` is the address published in the node's ENR. Without @@ -49,16 +68,27 @@ with `Ctrl+C`; it shuts down gracefully on SIGINT only, so a systemd unit needs - `ethlambda-linux-x86_64` targets x86-64-v3, so it needs a CPU with AVX2 (Haswell or later), and glibc 2.35 or newer. -- The macOS binary is not notarized. A browser download is quarantined, which +- The macOS binaries are not notarized. A browser download is quarantined, which `xattr -d com.apple.quarantine ` lifts; a `curl` download is not. -- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify a - binary's provenance attestation. +- [`RELEASE.md`](./RELEASE.md#verifying-a-release) shows how to verify an + ethlambda binary's provenance attestation. + +To skip payload validation, run the node without ethrex: drop it, the JWT +secret and the two `--execution-*` flags. The node then follows the chain and +serves the Beacon API without validating execution payloads, but it cannot +serve a validator client, since it answers attestation data, aggregation and +block production requests with `503`. The first start downloads the finalized state (several hundred MB) and logs `Beacon checkpoint sync complete`, then a few minutes of `Block parent missing, storing as pending` before `Block imported successfully` lines start. Later starts resume from `beacon-data/db`. +ethrex logs `No messages from the consensus layer` until the beacon node's +first fork choice update, then starts snap sync. Until that finishes, which +takes hours on mainnet, ethrex answers each payload `SYNCING` and the beacon +node imports blocks optimistically. + Check progress from another terminal: ```sh @@ -70,9 +100,10 @@ The node has caught up once `sync_distance` (the chain's current slot minus `false` during catch-up as well. For Sepolia or Hoodi, change `--network mainnet` to `--network sepolia` or -`--network hoodi` and point `--checkpoint-sync-url` at that network's provider. -See [`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below -for the remaining flags. +`--network hoodi` in both the ethrex and the beacon node arguments, and point +`--checkpoint-sync-url` at that network's provider. See +[`ethlambda beacon`](#ethlambda-beacon--the-ethereum-beacon-chain) below for +the remaining flags. `--network plataberget` (the Glamsterdam testnet) is built in as well. It has run Gloas since its `GLOAS_FORK_EPOCH`, which the node follows: a Gloas @@ -84,53 +115,7 @@ The same binary runs [`ethlambda node`](#ethlambda-node--the-lean-consensus-chai and [`ethlambda validator`](#ethlambda-validator--the-validator-client); their flags are below. -### Running with the ethrex execution client - -Paired with an [ethrex](https://github.com/lambdaclass/ethrex) execution -client, the beacon node validates each block's execution payload and can serve -a [validator client](#ethlambda-validator--the-validator-client). The two talk -over the Engine API, authenticated with a shared JWT secret. ethrex ships -release binaries for the same three platforms on -[its releases page](https://github.com/lambdaclass/ethrex/releases/latest). - -> **Warning:** this uses a lot of disk. ethrex needs at least 500 GB for -> mainnet (1 TB recommended; see its -> [hardware requirements](https://docs.ethrex.xyz/getting-started/hardware_requirements.html)). - -```sh -# Pick the asset for your platform: ethrex-linux-x86_64, ethrex-linux-aarch64 or ethrex-macos-aarch64 -curl -LO https://github.com/lambdaclass/ethrex/releases/latest/download/ethrex-linux-x86_64 -chmod +x ethrex-linux-x86_64 && mv ethrex-linux-x86_64 ethrex -./ethrex --version - -mkdir -p ethrex-data -# The secret both clients authenticate the Engine API with -openssl rand -hex 32 > jwt.hex - -# In one terminal: ethrex, serving the Engine API on 127.0.0.1:8551 -./ethrex \ - --network mainnet \ - --datadir ethrex-data \ - --authrpc.jwtsecret jwt.hex - -# In another: the beacon node from the quickstart, pointed at ethrex -./ethlambda beacon \ - --network mainnet \ - --checkpoint-sync-url https://beaconstate.ethstaker.cc \ - --node-key beacon-data/node-key \ - --data-dir beacon-data/db \ - --discovery.advertise-ip "$(curl -s https://ifconfig.me)" \ - --execution-endpoint http://127.0.0.1:8551 \ - --execution-jwt-secret jwt.hex -``` - -ethrex logs `No messages from the consensus layer` until the beacon node's -first fork choice update, then starts snap sync. Until that finishes, which -takes hours on mainnet, ethrex answers each payload `SYNCING` and the beacon -node imports blocks optimistically. For another network, change -`--network mainnet` in both the ethrex and the beacon node arguments. - -#### Running with Docker +### Running with Docker The same two nodes from pre-built images (`ghcr.io/lambdaclass/ethlambda:latest` and `ghcr.io/lambdaclass/ethrex`), so only @@ -185,8 +170,8 @@ docker run -d --name ethlambda-beacon --network ethereum --stop-signal SIGINT \
Running without an execution client -The standalone node from the quickstart: no ethrex, no shared network, no JWT -secret and no `--execution-*` flags. +The node without ethrex, as described above: no shared network, no JWT secret +and no `--execution-*` flags. ```sh docker pull ghcr.io/lambdaclass/ethlambda:latest