Skip to content
Merged
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
13 changes: 13 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,16 @@ rustflags = [
"-Ctarget-cpu=x86-64-v3",
"-Ctarget-feature=+avx2,+sse2,+ssse3,+sse4.1,+sse4.2,+bmi1,+lzcnt,+pclmulqdq",
]

# Supply-chain cooldown for dependency resolution (unstable min-publish-age,
# tracking issue rust-lang/cargo#17009): crate versions published less than
# 14 days ago are excluded when the resolver runs on a nightly cargo.
# Stable cargo ignores these tables silently, so builds from the committed
# Cargo.lock are unaffected; run `make update` to resolve under the policy.
# When the feature stabilizes, drop the [unstable] table and the nightly
# resolver pin in the Makefile: the policy then binds all resolution.
[unstable]
min-publish-age = true

[registry]
global-min-publish-age = "14 days"
44 changes: 41 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ jobs:
run: cargo fmt --all -- --check

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

- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
run: cargo clippy --locked --workspace --all-targets -- -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
Expand Down Expand Up @@ -80,6 +80,44 @@ jobs:
# harness end-to-end and its JSON output contract in a few seconds.
- name: Benchmark smoke (mock crypto)
run: |
cargo run --profile release-fast --bin ethlambda -- benchmark synthetic --mock-crypto \
cargo run --locked --profile release-fast --bin ethlambda -- benchmark synthetic --mock-crypto \
--num-validators 4 --warmup-slots 4 --iterations 3 --format json \
| jq -e '.schema_version == 1 and (.samples | length == 3)'

# Stable cargo ignores the publish-age cooldown in .cargo/config.toml, so the
# lockfile can pin too-young crates. Fail the build when either lockfile does.
# Infrastructure failures (toolchain download, a resolution that fails for
# reasons unrelated to age, e.g. a yanked upstream crate) only warn: they are
# outside the PR's control and would block every PR.
cooldown:
name: Dependency cooldown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Check lockfiles against the publish-age cooldown
run: |
if ! rustup toolchain install nightly-2026-06-21 --profile minimal; then
echo "::warning title=Publish-age cooldown check skipped::toolchain install failed"
exit 0
fi
status=0
for manifest in Cargo.toml tooling/event-monitor/Cargo.toml; do
if ! cargo +nightly-2026-06-21 update --dry-run -Z min-publish-age --manifest-path "$manifest" > cooldown.txt 2>&1; then
# Drop the index/git refresh chatter so the excerpt is the actual error; %0A = newline in annotations
msg=$(grep -v '^ *Updating ' cooldown.txt | head -20 | sed ':a;N;$!ba;s/\n/%0A/g')
echo "::warning title=Publish-age cooldown probe failed for $manifest::$msg"
continue
fi
# A cooldown-driven downgrade is annotated with the too-young version's
# publish date; downgrades for other reasons (MSRV, a tightened
# requirement) carry no such note and are not this check's business.
hits=$(grep -E '^ *Downgrading .*published' cooldown.txt || true)
if [ -n "$hits" ]; then
count=$(echo "$hits" | wc -l | tr -d ' ')
msg=$(echo "$hits" | head -20 | sed ':a;N;$!ba;s/\n/%0A/g')
echo "::error title=$manifest pins $count crate(s) younger than the publish-age cooldown::$msg"
status=1
fi
done
exit $status
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ make test # All tests + forkchoice spec tests
### Common Operations
```bash
rm -rf leanSpec && make leanSpec/fixtures # Download latest released test fixtures
make update UPDATE_ARGS="-p <crate>" # Bump deps under the 14-day publish-age cooldown (nightly resolver)
make cooldown-check # Fail if a lockfile pins crates younger than the cooldown (same as CI)
make docker-build # Build Docker image (DOCKER_TAG=local)
make run-devnet # Run local devnet with lean-quickstart
```
Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ All commits must have a verified signature.
- **Comments:** Explain *why*, not *what*. Code should be self-explanatory.
- **Error handling:** Use `Result` and `thiserror`. Avoid `.unwrap()` outside tests.
- **Dependencies:** Adding a new crate requires justification in the PR description.
Resolve version bumps with `make update` (optionally `UPDATE_ARGS="-p <crate>"`): it runs
the resolver under a 14-day publish-age cooldown (`.cargo/config.toml`) that excludes
freshly published crate versions as a supply-chain precaution. A plain `cargo update` or
`cargo add` on stable bypasses the cooldown; `make lint`/`make test` and CI build
`--locked` so an unresolved manifest change fails loudly instead of silently re-resolving.
Git dependencies have no publish age and are not covered.

### Review Process

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ ENV NO_DEFAULT_FEATURES=$NO_DEFAULT_FEATURES
ARG LOCKED="--locked"
ENV LOCKED=$LOCKED

RUN cargo chef cook --profile $BUILD_PROFILE $NO_DEFAULT_FEATURES --features "$FEATURES" --recipe-path recipe.json
RUN cargo chef cook --profile $BUILD_PROFILE $NO_DEFAULT_FEATURES --features "$FEATURES" $LOCKED --recipe-path recipe.json

# Build application
# Include .git so vergen-git2 can extract version info (branch, commit SHA)
Expand Down
36 changes: 33 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help fmt lint bench docker-build shadow-build shadow-docker-build run-devnet test docs docs-deps docs-serve
.PHONY: help fmt lint bench update cooldown-check docker-build shadow-build shadow-docker-build run-devnet test docs docs-deps docs-serve

help: ## πŸ“š Show help for each of the Makefile recipes
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Expand All @@ -7,12 +7,42 @@ fmt: ## 🎨 Format all code using rustfmt
cargo fmt --all

lint: ## πŸ” Run clippy on all workspace crates
cargo clippy --workspace --all-targets -- -D warnings
cargo clippy --locked --workspace --all-targets -- -D warnings

test: leanSpec/fixtures ## πŸ§ͺ Run all tests
# release-fast: release-grade opt-level to avoid stack overflows during
# signature verification/aggregation, without paying for LTO on every rebuild
cargo test --workspace --profile release-fast
cargo test --locked --workspace --profile release-fast

# Used ONLY to resolve dependency updates: min-publish-age (.cargo/config.toml)
# is nightly-only, everything else runs on the stable toolchain pinned in
# rust-toolchain.toml.
RESOLVER_TOOLCHAIN := nightly-2026-06-21

# Versions published less than 14 days ago are excluded from resolution.
# Resolution done on stable (`cargo add`, plain `cargo update`) is NOT covered;
# this target is the intended path for routine updates. Git dependencies have
# no publish age and are refreshed WITHOUT any cooldown: review their lockfile
# rev changes manually.
update: ## πŸ“¦ Update dependencies under the publish-age cooldown (UPDATE_ARGS="-p foo")
rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal > /dev/null && \
cargo +$(RESOLVER_TOOLCHAIN) update -Z min-publish-age $(UPDATE_ARGS)

# Stable cargo ignores the cooldown, so a lockfile can pin too-young crates;
# same check as the CI `cooldown` job, without touching the files. A cooldown
# downgrade is annotated with the too-young version's publish date; downgrades
# for other reasons carry no such note and are not flagged.
cooldown-check: ## πŸ”Ž Fail if a lockfile pins crates younger than the publish-age cooldown
@rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal > /dev/null && \
status=0; \
for manifest in Cargo.toml tooling/event-monitor/Cargo.toml; do \
if ! out=$$(cargo +$(RESOLVER_TOOLCHAIN) update --dry-run -Z min-publish-age --manifest-path $$manifest 2>&1); then \
echo "WARNING: publish-age cooldown probe failed for $$manifest:"; echo "$$out" | grep -v "^ *Updating " | head -20; continue; \
fi; \
hits=$$(echo "$$out" | grep -E "^ *Downgrading .*published" || true); \
if [ -n "$$hits" ]; then echo "ERROR: $$manifest pins crates younger than the publish-age cooldown:"; echo "$$hits"; status=1; fi; \
done; \
exit $$status

BENCH_ARGS ?= synthetic --mock-crypto

Expand Down
Loading