Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
b7497bc
feat(types): Beacon Chain containers under a `beacon` namespace (#1)
MegaRedHand Sep 2, 2026
bf328c6
Merge pull request #2 from lambdaclass/feat/beacon-chain-stf
MegaRedHand Sep 3, 2026
57bf591
Merge pull request #3 from lambdaclass/feat/beacon-gossip-follower
MegaRedHand Sep 4, 2026
e7dee77
Merge pull request #12 from lambdaclass/ci/split-lint-job
MegaRedHand Sep 10, 2026
8198cc9
feat(storage): unify the lean and beacon storage layer (#6)
MegaRedHand Sep 11, 2026
5a8ad41
feat(beacon): checkpoint sync for the beacon chain (#7)
MegaRedHand Sep 12, 2026
85255c1
feat(p2p): serve beacon blocks by range and by root (#8)
MegaRedHand Sep 12, 2026
4d06a75
feat(beacon): give the follower a chain, from the anchor to the tip (#9)
MegaRedHand Sep 12, 2026
76647df
perf(beacon): stop rescanning the validator registry per attester and…
MegaRedHand Sep 12, 2026
859a8ae
feat(beacon): fulu data availability sampling, custody through the av…
MegaRedHand Sep 16, 2026
71e5bd8
feat(beacon): validate execution payloads over the Engine API (#13)
MegaRedHand Sep 18, 2026
90706e1
fix(p2p): stop outbound req/resp requests crossing protocols (#18)
MegaRedHand Sep 18, 2026
0519c25
feat(blockchain): time a block's import end to end, section by sectio…
MegaRedHand Sep 18, 2026
b9e84fe
feat(beacon): follow a network described by files on disk (#15)
MegaRedHand Sep 18, 2026
db1cd4d
perf(beacon): validate an aggregate's public keys in parallel (#20)
MegaRedHand Sep 21, 2026
2bbec60
fix(beacon): rank dial candidates by custody redundancy, not presence…
MegaRedHand Sep 22, 2026
bcabd32
feat: serve the Beacon API from ethlambda beacon (#21)
MegaRedHand Sep 22, 2026
8d9dbe9
feat(beacon): add sepolia and hoodi as built-in networks (#26)
MegaRedHand Sep 22, 2026
3371698
ci: run the benchmark smoke in its own job (#30)
MegaRedHand Sep 22, 2026
9a14bae
feat(metrics): resolve import phases between half a second and 32 s (…
MegaRedHand Sep 22, 2026
92d5bc2
feat(p2p): count why peers drop us, not just that they did (#28)
MegaRedHand Sep 22, 2026
74ed2a3
perf(beacon): derive an epoch's committees once and share them (#22)
MegaRedHand Sep 23, 2026
52a23d0
perf(storage): write post-block states on a background thread (#24)
MegaRedHand Sep 23, 2026
20b94b7
feat(cli): benchmark block import by replaying a real corpus offline …
MegaRedHand Sep 23, 2026
add1cb4
perf(beacon): committee cache review fixes, in-place shuffle, head-pi…
MegaRedHand Sep 23, 2026
884b942
feat(validator): add a beacon-chain validator client (#16)
MegaRedHand Sep 23, 2026
b9139e8
feat(p2p): validate beacon gossip before propagating it (#38)
MegaRedHand Sep 24, 2026
d57e718
refactor(beacon): run every data column check in p2p, not the chain a…
MegaRedHand Sep 24, 2026
f66a7e5
CI: run jobs on the runner named by ETHLAMBDA_RUNNER
klaus993 Sep 24, 2026
7fdd779
CI: free runner disk before the Rust builds
klaus993 Sep 24, 2026
bf5753f
feat(rpc): report the preset and constants from /eth/v1/config/spec (…
MegaRedHand Sep 25, 2026
9853b22
Merge pull request #45 from lambdaclass/ci/ubicloud-runners
klaus993 Sep 25, 2026
dafeeb0
fix(beacon): leave a held block's columns to gossip until the next sl…
MegaRedHand Sep 25, 2026
1501240
feat(beacon): apply gossip aggregates to fork choice, and backbone tw…
MegaRedHand Sep 25, 2026
7efeffa
feat(p2p): validate beacon aggregates and subnet attestations before …
MegaRedHand Sep 25, 2026
fab4c10
perf(beacon): validate each BLS public key once, not on every signatu…
MegaRedHand Sep 25, 2026
eff72d5
feat(beacon): serve the validator API so ethlambda validator can atte…
pablodeymo Sep 28, 2026
3c2fe92
feat(beacon): aggregate and propose through ethlambda beacon (#40)
pablodeymo Sep 28, 2026
935e41f
fix(beacon): ask for an old held block's columns at once, not at the …
MegaRedHand Sep 28, 2026
6be5d5d
fix(beacon): let a held block's descendants wait for it, not re-impor…
MegaRedHand Sep 28, 2026
56d6a9e
fix(beacon): hold a range batch until its custody columns have known …
MegaRedHand Sep 28, 2026
e409e7e
perf(beacon): keep the registry and balances in persistent Merkle tre…
MegaRedHand Sep 29, 2026
c913728
feat(beacon): pack other nodes' attestations into produced blocks (#56)
pablodeymo Sep 29, 2026
282dd91
Merge origin/main into beacon-chain-integration
MegaRedHand Sep 29, 2026
579ceab
Merge branch 'main' into beacon-chain-integration
MegaRedHand Sep 29, 2026
aed7dd3
docs(readme): add a Docker quickstart for the beacon follower
MegaRedHand Sep 30, 2026
c79fabd
docs: update readme to run with ethrex
MegaRedHand Sep 30, 2026
c635524
Merge branch 'main' into beacon-chain-integration
MegaRedHand Sep 30, 2026
438ecef
fix(cli): make discv5 opt-in on `node` again (#637)
MegaRedHand Oct 1, 2026
cb43e98
fix(p2p): consult connection limits before any other behaviour (#640)
MegaRedHand Oct 1, 2026
125dce1
Merge branch 'main' into beacon-chain-integration
MegaRedHand Oct 2, 2026
ffac634
chore: update Cargo.lock
MegaRedHand Oct 5, 2026
17af7c3
perf(storage): borrow values on read, make has_state skip snapshot re…
MegaRedHand Oct 5, 2026
3385521
fix(p2p): send the client version as the identify agent version (#675)
MysticRyuujin Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@

- [ ] Ran `make fmt` — clean
- [ ] Ran `make lint` (clippy with `-D warnings`) — clean
- [ ] Ran `make test` (`cargo test --workspace --profile release-fast`) — all passing
- [ ] Ran `make test` (`test-consensus` plus `test-node`, at `release-fast`) — all passing
19 changes: 19 additions & 0 deletions .github/actions/free-disk/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: "Free disk space"
description: "Remove pre-installed software to free disk space for Rust builds"

runs:
using: "composite"
steps:
- name: Free up disk space
shell: bash
run: |
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /opt/ghc || true
sudo rm -rf "/usr/local/share/boost" || true
sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/share/swift || true
sudo rm -rf /opt/hostedtoolcache/CodeQL || true
sudo rm -rf /usr/local/.ghcup || true
df -h /
24 changes: 22 additions & 2 deletions .github/actions/run-fixture-tests/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
name: "Run fixture-based tests"
description: >-
Fetches the latest leanSpec test fixtures (cached by release SHA), sets up the
Rust toolchain, and runs the workspace test suite via `make test`. Assumes the
Rust toolchain, and runs a fixture-backed `make` target. Assumes the
repository has already been checked out by the caller.

inputs:
make-target:
description: >-
Which target to run. CI passes one half of the suite per job, because the
two together no longer fit a single runner's root filesystem.
required: false
default: "test"

runs:
using: "composite"
steps:
Expand Down Expand Up @@ -106,9 +114,21 @@ runs:
with:
toolchain: "1.97.1"

# Keyed by target so the two halves do not evict each other: the default
# key covers the job id, which is one value for every leg of a matrix.
- name: Setup cache
uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.make-target }}

- name: Run tests
shell: bash
run: make test
run: make ${{ inputs.make-target }}

# How close this half is to the ceiling, which is why the suite is split in
# halves at all. `always()` because the reading matters most on the failure
# it instruments, where the build is the thing that ran out.
- name: Report disk usage after the build
if: always()
shell: bash
run: df -h /
214 changes: 194 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,46 @@ concurrency:
env:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
CARGO_NET_RETRY: "10"
# Off in CI, on for local rebuilds, which is what the `release-fast` profile's
# `incremental = true` is for. An environment variable overrides the profile
# setting, so this turns it off here without touching the manifest.
#
# Incremental state is pure cost on a runner: `Swatinem/rust-cache` deletes
# `target/*/incremental` before saving, so it is never restored, and every job
# here starts from whatever the cache holds rather than from its own previous
# build. Nothing reads it, and it is comparable in size to the build output
# itself, on a filesystem that a cold workspace build has already outgrown.
CARGO_INCREMENTAL: "0"

jobs:
# `lint`, `presets` and `tooling` were one job. Split because serially they
# took 34 minutes and, between them, more disk than a runner has: a full disk
# reaches `rust-lld` through `mmap`, so it is reported as
# `ld terminated with signal 7 [Bus error]` rather than as ENOSPC.
lint:
name: Lint
runs-on: ubuntu-latest
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/free-disk

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.97.1"
components: rustfmt, clippy

- name: Setup cache
# Tools under tooling/ are separate Cargo workspaces with their own
# target dir and Cargo.lock, so they need listing explicitly or their
# builds are neither cached nor reflected in the cache key.
uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
tooling/event-monitor

- name: Check formatting
run: cargo fmt --all -- --check

- name: Cargo check
run: cargo check --locked --workspace --all-targets

# Seconds, not minutes: same fingerprints as `check` above.
- name: Clippy
run: cargo clippy --locked --workspace --all-targets -- -D warnings

Expand All @@ -55,14 +65,81 @@ jobs:
- name: Clippy (skipped spectests)
run: cargo clippy --locked --workspace --test forkchoice_spectests --test signature_spectests --test stf_spectests --test ssz_spectests -- -D warnings

# tooling/event-monitor declares its own [workspace] table, so every step
# above stops at the root workspace members and never reaches it. Its
# tests run in this job rather than in `test` because clippy has already
# compiled the test targets, and because they need none of that job's
# leanSpec fixtures.
# `--locked` so the committed Cargo.lock is actually enforced: without it
# cargo silently resolves and rewrites the lockfile in CI, and a stale or
# missing entry never fails the build.
# Two features gate code that no step in `lint` reaches, so both would rot
# silently between merges.
#
# `beacon-spec-tests` guards the beacon spec-test target, which declares it as
# a required feature so that `cargo test` skips a suite whose fixtures are a
# multi-gigabyte download. `--all-targets` in `lint` therefore never builds
# it. The `beacon-spec-tests` job below does download them and run the suite
# for real, so this job is not what proves the runners work; it is here
# because that job is by far the slowest in the workflow, and because this is
# the only place the runners are linted at `-D warnings`.
#
# `preset-minimal` is a whole second compilation, because the beacon
# containers' SSZ bounds are const-generic arguments and everything in `lint`
# builds the default (mainnet) preset only. The fixture-reading unit tests
# stay `ignore`d here, since `beacon-spec-tests` is off.
#
# `--lib`: this job downloads no fixtures, and the integration targets
# (`ssz_spectests`, `stf_spectests`) walk the leanSpec tree, which only `test`
# has. Both presets, not just the default: the spec suite's `genesis` runner
# is `#![cfg(feature = "preset-minimal")]`, so a mainnet build type-checks
# every runner except the sole coverage `beacon::genesis` has.
presets:
name: Test minimal preset
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/free-disk

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.97.1"
components: clippy

# Its own entry, keyed on the job id by the action's default. These
# builds share almost no artifacts with `lint`'s, so on one key each run
# would evict the other's.
- name: Setup cache
uses: Swatinem/rust-cache@v2

- name: Check the beacon spec-test suite compiles
run: |
cargo clippy -p ethlambda-state-transition --all-targets --features beacon-spec-tests -- -D warnings
cargo clippy -p ethlambda-state-transition --all-targets --features beacon-spec-tests,preset-minimal -- -D warnings

- name: Check the minimal preset
run: |
cargo test -p ethlambda-types --lib --features preset-minimal
cargo test -p ethlambda-state-transition --lib --features preset-minimal

# tooling/event-monitor declares its own [workspace] table, so both jobs above
# stop at the root members and never reach it. Hence its own job, and the
# `workspaces` input below pointing the cache at its target dir.
#
# `--locked` so the committed Cargo.lock is actually enforced: without it
# cargo silently resolves and rewrites the lockfile in CI, and a stale or
# missing entry never fails the build.
tooling:
name: Tooling
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.97.1"
components: rustfmt, clippy

- name: Setup cache
uses: Swatinem/rust-cache@v2
with:
workspaces: tooling/event-monitor

- name: Lint tooling
working-directory: tooling/event-monitor
run: |
Expand All @@ -73,17 +150,53 @@ jobs:
working-directory: tooling/event-monitor
run: cargo test --locked

# The workspace suite in two halves; `CONSENSUS_CRATES` in the Makefile
# defines them and says why. Both need the leanSpec fixtures and share one
# cache entry for them, so only the first leg to arrive downloads anything.
test:
name: Test
runs-on: ubuntu-latest
name: Test (${{ matrix.half }})
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
strategy:
# Which half broke is most of the triage, so report both.
fail-fast: false
matrix:
half: [consensus, node]
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/free-disk

- name: Run fixture-based tests
uses: ./.github/actions/run-fixture-tests
with:
make-target: test-${{ matrix.half }}

# Validates the benchmark harness and its JSON contract. This used to be a
# step in `Test (node)`, on the theory that the binary was already built there.
# It was not: `cargo test` never links the plain binary, and it builds with the
# dev-dependencies' features unified in (`bin/ethlambda` enables tokio's
# `test-util`), which `cargo run` leaves out. So the step rebuilt tokio and
# everything above it (libp2p, hyper, axum, the ethrex crates) into a target
# dir the test build had already brought to the disk ceiling, and the job then
# failed at random in the cache save step with ENOSPC. Here it builds only the
# binary's own graph. No fixtures: with `--mock-crypto` the synthetic benchmark
# generates its chain and reads no files.
benchmark-smoke:
name: Benchmark smoke
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/free-disk

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.97.1"

- name: Setup cache
uses: Swatinem/rust-cache@v2

# Reuses the release build from the test step; validates the benchmark
# harness end-to-end and its JSON output contract in a few seconds.
- name: Benchmark smoke (mock crypto)
run: |
cargo run --locked --profile release-fast --bin ethlambda -- benchmark synthetic --mock-crypto \
Expand Down Expand Up @@ -127,3 +240,64 @@ jobs:
fi
done
exit $status
beacon-spec-tests:
name: Beacon spec tests (${{ matrix.preset }})
runs-on: ${{ vars.ETHLAMBDA_RUNNER || 'ubuntu-latest' }}
# Far and away the longest job here: two full fixture trees to fetch and a
# release-grade build before the first case runs. Bounded well above what a
# healthy run takes, purely so a hang is reaped in tens of minutes rather
# than sitting on a runner for GitHub's six-hour default.
timeout-minutes: 120
strategy:
# Report both presets rather than cancelling the second the moment the
# first fails. They walk different fixture trees, and whether a failure is
# preset-specific or common to both is most of the triage.
fail-fast: false
matrix:
preset: [mainnet, minimal]
env:
# Read by the Makefile, which declares this `?=` and so defers to the
# environment. A run reads its own preset's tree plus `general` and nothing
# else, so fetching the other preset's would be the largest single download
# in the workflow spent on files no case opens.
CONSENSUS_SPEC_TESTS_CONFIGS: general ${{ matrix.preset }}
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/free-disk

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.97.1"

# Keyed per preset. The preset fixes the beacon containers' SSZ bounds,
# which are const-generic arguments, so the two legs share no artifacts at
# all; on one key they would evict each other on every single run.
- name: Setup cache
uses: Swatinem/rust-cache@v2
with:
key: beacon-${{ matrix.preset }}

# Deliberately not cached. The trees expand to several GiB, which against a
# repository-wide 10 GiB cache budget would evict the Rust build caches
# that actually save time here. The assets are pinned to a release and
# served from the same CDN as the runner, so re-fetching is the cheap half
# of that trade.
#
# Its own step rather than leaning on the make prerequisite, so the log
# separates download time from build-and-test time and a fixture outage is
# not reported as a test failure.
- name: Download the consensus spec test fixtures
run: make consensus-spec-tests

- name: Download the gossip validation test fixtures
run: make consensus-spec-gossip-tests

- name: Run the Beacon Chain spec tests
run: make test-beacon-${{ matrix.preset }}

# See the same step in the run-fixture-tests action.
- name: Report disk usage after the build
if: always()
run: df -h /
15 changes: 14 additions & 1 deletion .github/workflows/daily_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,32 @@ permissions:
env:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
CARGO_NET_RETRY: "10"
# See the same setting in ci.yml. This workflow runs the identical job, so it
# is under the identical disk pressure.
CARGO_INCREMENTAL: "0"

jobs:
# The same two halves ci.yml runs, and for the same reason.
test:
name: Test
name: Test (${{ matrix.half }})
runs-on: ubuntu-latest
strategy:
# A daily run exists to say what is broken, so report both halves.
fail-fast: false
matrix:
half: [consensus, node]
steps:
- uses: actions/checkout@v6

- name: Run fixture-based tests
uses: ./.github/actions/run-fixture-tests
with:
make-target: test-${{ matrix.half }}

notify:
name: Post result to Slack
# `result` is already the worst outcome across the matrix legs, so a green
# report means every half was green.
needs: test
# Run even when the test job fails or is cancelled, so we always report.
if: always()
Expand Down
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ target
# Used for generating test vectors
/leanSpec

# Beacon Chain spec test fixtures, downloaded by `make consensus-spec-tests`
/consensus-spec-tests

# The gossip validation vectors, from a newer release, downloaded by
# `make consensus-spec-gossip-tests`
/consensus-spec-tests-gossip

# Personal Claude Code overrides (not shared with team)
CLAUDE.local.md

Expand All @@ -49,3 +56,7 @@ devnet.env

# Filled-in copy of the multi-server-devnet server inventory (names hosts/ips)
devnet.inventory

# macOS Finder metadata. Added after one was committed by accident: this repo is
# developed on macOS, and a stray one is pure noise in a diff.
.DS_Store
Loading
Loading