Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 59 additions & 0 deletions .github/workflows/hive.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Hive

# Runs on manual dispatch and on every pull request. The suite is slow, but
# running it automatically ensures it actually runs (a label-gated job tends to
# never get triggered).
on:
workflow_dispatch:
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
hive:
name: Run hive tests
runs-on: ubuntu-latest
# hive itself imposes no timeout; cap the job so a hang can't run forever.
timeout-minutes: 180
env:
# `docker-local` -> `cross` bind-mounts this into the build container.
LEAN_REPO_ROOT: ${{ github.workspace }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive

# Needed to build the hive binary (`go build .`).
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
cache: false

# Needed by `docker-local` to cross-build the x86_64 client binary.
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross --rev 7b24b6e9f6834a3ac31d3dad1e2ff24c1b66f7cf

# hive launches client containers; restart docker to settle iptables
# rules on the runner (same workaround the zeam hive workflow uses).
- name: Restart docker
run: sudo systemctl restart docker

- name: Run hive tests
run: make test-hive

# Persist hive's per-suite JSON results and `details/` failure logs, which
# otherwise die with the runner. `always()` keeps them even when a step
# fails. Needed to debug failures (e.g. deadline/connection errors that
# only surface on CI).
- name: Upload hive logs
if: always()
uses: actions/upload-artifact@v4
with:
name: hive-logs
path: lean_client/hive/workspace/logs/
retention-days: 1
if-no-files-found: warn
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ lean_client/target/*
lean_client/tests/mainnet
lean_client/bin
lean_client/spec/
lean_client/hive/
target/*
*.local*

Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ docker-local:
.PHONY: release
release:
$(MAKE) -C lean_client release

.PHONY: test-hive
test-hive:
$(MAKE) -C lean_client test-hive
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,27 @@ leanEthereum Consensus Client written in Rust using Grandine's libraries.
```

After a minute all the nodes should be synced up and see each other

## Running Hive tests

Runs the [Hive](https://github.com/ethereum/hive) lean simulator against our
`grandine_lean` client. From the repo root:

```bash
make test-hive
```

Requires a **Go toolchain**, **Docker** (running, usable without `sudo`), and
**Rust + [`cross`](https://github.com/cross-rs/cross)**.

Pin a hive version (defaults to latest `master`):

```bash
make test-hive HIVE_VERSION=<full-commit-hash>
```

Remove the hive clone and other build artifacts:

```bash
make clean
```
79 changes: 77 additions & 2 deletions lean_client/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ CURRENT_DEVNET := devnet-5
# Repo root that cross bind-mounts into its build container (see
# `lean_client/Cross.toml`). Auto-derived locally; CI overrides via env.
LEAN_REPO_ROOT ?= $(shell git rev-parse --show-toplevel)
# ethereum/hive ref to build. Defaults to latest master, resolved at clone time.
# Override with `make test-hive HIVE_VERSION=<commit>` to pin a specific commit.
HIVE_VERSION ?= master
# Devnet variant of the grandine lean hive client to test. Selects the client
# definition (`simulators/lean/clients/<HIVE_DEVNET>.yaml`) and image
# (`grandine_lean_<HIVE_DEVNET>`). Note: hive uses `devnet5` (no hyphen).
HIVE_DEVNET ?= devnet5

# Temporary variable, which constructs `--tag` arguments, to be passed into
# docker build command. It is used as intermediatery step, not as config
Expand Down Expand Up @@ -81,18 +88,23 @@ build:
clean:
cargo clean
rm -rf ./bin
rm -rf ./hive

CLIENT_SOURCES := $(shell find . \
\( -path './target' -o -path './hive' -o -path './spec' -o -path './bin' \) -prune \
-o \( -name '*.rs' -o -name 'Cargo.toml' \) -print) Cargo.lock

.PHONY: x86_64-unknown-linux-gnu
x86_64-unknown-linux-gnu: ./target/x86_64-unknown-linux-gnu/release/lean_client

./target/x86_64-unknown-linux-gnu/release/lean_client:
./target/x86_64-unknown-linux-gnu/release/lean_client: $(CLIENT_SOURCES)
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C target-cpu=x86-64-v3" \
LEAN_REPO_ROOT=$(LEAN_REPO_ROOT) cross build --bin lean_client --target x86_64-unknown-linux-gnu --profile release

.PHONY: aarch64-unknown-linux-gnu
aarch64-unknown-linux-gnu: ./target/aarch64-unknown-linux-gnu/release/lean_client

./target/aarch64-unknown-linux-gnu/release/lean_client:
./target/aarch64-unknown-linux-gnu/release/lean_client: $(CLIENT_SOURCES)
LEAN_REPO_ROOT=$(LEAN_REPO_ROOT) cross build --bin lean_client --target aarch64-unknown-linux-gnu --profile release

.PHONY: release
Expand Down Expand Up @@ -126,6 +138,69 @@ docker-local: ./target/x86_64-unknown-linux-gnu/release/lean_client
$(DOCKER_TAGS) \
.

### Hive
.PHONY: test-hive
test-hive: hive/hive docker-local
@cd hive && \
./hive --sim lean \
--client-file simulators/lean/clients/$(HIVE_DEVNET).yaml \
--client grandine_lean_$(HIVE_DEVNET) \
--docker.output \
--docker.nocache="hive/clients/grandine_lean_$(HIVE_DEVNET)" \
--results-root ./workspace/logs; \
hive_status=$$?; \
logs=./workspace/logs; \
suites=$$(ls $$logs/*.json 2>/dev/null | grep -v '/hive\.json$$' || true); \
if [ -z "$$suites" ]; then \
echo "ERROR: hive produced no suite results - treating as an infrastructure/script failure."; \
exit $$hive_status; \
fi; \
if ! command -v jq >/dev/null 2>&1; then \
echo "jq not found; skipping summary. Raw results under $$logs"; \
exit 0; \
fi; \
summary=$$( \
echo "==================== Hive test summary ===================="; \
total=0; failed_total=0; \
for f in $$suites; do \
name=$$(jq -r '.name' "$$f"); \
n=$$(jq '.testCases | length' "$$f"); \
nf=$$(jq '[.testCases[] | select(.summaryResult.pass | not)] | length' "$$f"); \
total=$$((total + n)); failed_total=$$((failed_total + nf)); \
if [ "$$nf" -eq 0 ]; then \
printf '%s: %d/%d passed\n' "$$name" "$$n" "$$n"; \
else \
printf '%s: %d/%d passed (%d failed)\n' "$$name" "$$((n - nf))" "$$n" "$$nf"; \
jq -r '.testCases[] | select(.summaryResult.pass | not) | " FAIL: \(.name)"' "$$f"; \
fi; \
done; \
echo "-----------------------------------------------------------"; \
printf 'Total: %d/%d passed, %d failed\n' "$$((total - failed_total))" "$$total" "$$failed_total"; \
echo "Full failure details: lean_client/hive/workspace/logs/details/"; \
echo "===========================================================" \
); \
echo; \
printf '%s\n' "$$summary"; \
if [ -n "$$GITHUB_STEP_SUMMARY" ]; then \
{ echo '```'; printf '%s\n' "$$summary"; echo '```'; } >> "$$GITHUB_STEP_SUMMARY"; \
fi

hive/hive: hive/.hive-ref-$(HIVE_VERSION)
@echo "==> Building hive binary (go build)..."
@cd hive && go build .
@echo "==> Built hive binary at hive/hive ($$(cd hive && git rev-parse --short HEAD))"

hive/.hive-ref-$(HIVE_VERSION):
@echo "==> Fetching ethereum/hive at ref '$(HIVE_VERSION)'..."
@rm -rf hive
@mkdir -p hive
@cd hive && git init -q && \
git remote add origin https://github.com/ethereum/hive.git && \
git fetch --depth 1 -q origin $(HIVE_VERSION) && \
git switch -q --detach FETCH_HEAD
@echo "==> Checked out hive at $$(cd hive && git rev-parse --short HEAD)"
@touch $@

### Shadow-simulator support
# `rust/patch/quinn-udp` is a vendored fallback-only build of quinn-udp so QUIC
# runs under the Shadow simulator (which does not emulate sendmsg cmsg / GRO /
Expand Down