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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
* text=auto eol=lf
src/bindings/Nethermind.Crypto.Lean/runtimes/**/*.so filter=lfs diff=lfs merge=lfs -text
src/bindings/Nethermind.Crypto.Lean/testing/runtimes/**/native/* filter=lfs diff=lfs merge=lfs -text
src/bindings/Nethermind.Crypto.Lean/testing/lean-vectors/*.bin filter=lfs diff=lfs merge=lfs -text
45 changes: 37 additions & 8 deletions .github/workflows/build-lean.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@ jobs:
# Both builds use the same target directory, so build paths can't make them differ.
run: |
export RUSTFLAGS="${{ matrix.rustflags }} --remap-path-prefix=$CARGO_HOME=/cargo --remap-path-prefix=$GITHUB_WORKSPACE=/src"
outputs="libnethermind_lean.so examples/fixtures examples/liblean_startup_stub.so"
for build in first second; do
rm -rf target
cargo build --release --locked --lib
strip target/release/libnethermind_lean.so
if [ $build = first ]; then cp target/release/libnethermind_lean.so /tmp/first.so; fi
cargo build --release --locked --lib --example fixtures --example lean_startup_stub
(cd target/release && strip $outputs)
if [ $build = first ]; then mkdir -p /tmp/first && (cd target/release && cp $outputs /tmp/first/); fi
done
for output in $outputs; do
sha256sum "/tmp/first/$(basename $output)" "target/release/$output"
cmp "/tmp/first/$(basename $output)" "target/release/$output"
done
sha256sum /tmp/first.so target/release/libnethermind_lean.so
cmp /tmp/first.so target/release/libnethermind_lean.so

- name: Require glibc 2.35 or older
working-directory: src/ffi
Expand All @@ -68,8 +71,12 @@ jobs:
# Laid out as runtimes/<rid>/native so that consumers download every RID into runtimes/ in one step.
- name: Stage
run: |
mkdir -p staging/runtimes/linux-${{ matrix.arch }}/native
mkdir -p staging/runtimes/linux-${{ matrix.arch }}/native testing/runtimes/linux-${{ matrix.arch }}/native testing/lean-vectors
cp src/ffi/target/release/libnethermind_lean.so staging/runtimes/linux-${{ matrix.arch }}/native/
cp src/ffi/target/release/examples/fixtures src/ffi/target/release/examples/liblean_startup_stub.so \
testing/runtimes/linux-${{ matrix.arch }}/native/
# The test vectors, with the aggregated verification key they were made under.
if [ ${{ matrix.arch }} = x64 ]; then src/ffi/target/release/examples/fixtures testing/lean-vectors; fi

# A relative path: in a container job, runner.temp and github.workspace name host paths.
- name: Upload artifact
Expand All @@ -80,6 +87,14 @@ jobs:
retention-days: ${{ env.RETENTION }}
if-no-files-found: error

- name: Upload test assets
uses: actions/upload-artifact@v7
with:
name: testing-linux-${{ matrix.arch }}
path: testing
retention-days: ${{ env.RETENTION }}
if-no-files-found: error

# The .NET smoke test on the built libraries, on hardware of each architecture, before they are committed.
test:
name: Test (${{ matrix.os }})
Expand All @@ -97,7 +112,7 @@ jobs:

# Only the built libraries, never the committed ones (or their LFS pointers), may be tested.
- name: Remove the committed libraries
run: rm -rf src/bindings/Nethermind.Crypto.Lean/runtimes
run: rm -rf src/bindings/Nethermind.Crypto.Lean/runtimes src/bindings/Nethermind.Crypto.Lean/testing

- name: Download the libraries
uses: actions/download-artifact@v8
Expand All @@ -106,6 +121,13 @@ jobs:
merge-multiple: true
path: src/bindings/Nethermind.Crypto.Lean/runtimes

- name: Download the test assets
uses: actions/download-artifact@v8
with:
pattern: testing-linux-*
merge-multiple: true
path: src/bindings/Nethermind.Crypto.Lean/testing

- name: Set up .NET
uses: actions/setup-dotnet@v6

Expand Down Expand Up @@ -154,7 +176,7 @@ jobs:

# Only the built outputs, never the committed ones, may be committed.
- name: Remove the committed outputs
run: rm -rf src/bindings/Nethermind.Crypto.Lean/runtimes THIRD-PARTY-NOTICES
run: rm -rf src/bindings/Nethermind.Crypto.Lean/runtimes src/bindings/Nethermind.Crypto.Lean/testing THIRD-PARTY-NOTICES

- name: Download the libraries
uses: actions/download-artifact@v8
Expand All @@ -163,6 +185,13 @@ jobs:
merge-multiple: true
path: src/bindings/Nethermind.Crypto.Lean/runtimes

- name: Download the test assets
uses: actions/download-artifact@v8
with:
pattern: testing-linux-*
merge-multiple: true
path: src/bindings/Nethermind.Crypto.Lean/testing

- name: Download the notices
uses: actions/download-artifact@v8
with:
Expand Down
75 changes: 4 additions & 71 deletions .github/workflows/test-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,76 +100,7 @@ jobs:
# rust-toolchain.toml pins the toolchain, which rustup installs on first use.
- name: Test
working-directory: src/ffi
run: cargo test --release --locked -- --test-threads=1

# Runs the committed library, on hardware of each architecture: the release check that a cross-compiled or untested
# binary cannot pass. Nethermind's native suite supplies the tests; this repository's src/ffi supplies the fixtures and
# expectations.
native-suite:
name: Nethermind native suite (${{ matrix.rid }})
strategy:
fail-fast: false
matrix:
include:
- rid: linux-x64
runner: ubuntu-latest
- rid: linux-arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
env:
# The NethermindEth/nethermind commit whose native suite runs against the library. Bump it with any ABI or
# verification key change.
NETHERMIND_REVISION: 1e5d1fa8a071b0b329a243437d00026e1d4a72df
NATIVE_TEST_PROJECT: Nethermind.Crypto.LeanFfi.Test
LEANVM_NUM_THREADS: 1
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
lfs: true

- name: Check out Nethermind
uses: actions/checkout@v7
with:
repository: NethermindEth/nethermind
ref: ${{ env.NETHERMIND_REVISION }}
path: nethermind

# The native suite builds its fixtures from tools/lean-ffi.
- name: Replace tools/lean-ffi with src/ffi
run: |
rm -rf nethermind/tools/lean-ffi
cp -r src/ffi nethermind/tools/lean-ffi

- name: Set up .NET
uses: actions/setup-dotnet@v6
with:
global-json-file: nethermind/global.json

- name: Set up Rust
run: |
toolchain=$(sed -n 's/^channel = "\(.*\)"$/\1/p' src/ffi/rust-toolchain.toml)
test -n "$toolchain"
rustup toolchain install "$toolchain" --profile minimal

# BuildLeanFfi builds the fixtures and a host-native library; the committed library then replaces the latter.
- name: Build the native tests
working-directory: nethermind
run: dotnet build "src/Nethermind/$NATIVE_TEST_PROJECT/$NATIVE_TEST_PROJECT.csproj" -c release --disable-build-servers -m:1 -p:BuildLeanFfi=true

- name: Use the committed library
run: |
output="nethermind/src/Nethermind/artifacts/bin/$NATIVE_TEST_PROJECT/release"
test -f "$output/libnethermind_lean.so"
cp "src/bindings/Nethermind.Crypto.Lean/runtimes/${{ matrix.rid }}/native/libnethermind_lean.so" "$output/libnethermind_lean.so"
sha256sum "$output/libnethermind_lean.so"

- name: Run startup checks and verifications
working-directory: nethermind
run: >-
dotnet test --project "src/Nethermind/$NATIVE_TEST_PROJECT/$NATIVE_TEST_PROJECT.csproj" -c release --no-build -p:BuildLeanFfi=true --
--filter "FullyQualifiedName~NativeBackendStartupTests|FullyQualifiedName~NativeLeanProofVerifierTests|FullyQualifiedName~NativeBlockValidationTests"
run: cargo test --release --locked --workspace -- --test-threads=1

verify-package:
name: Verify package assets
Expand All @@ -195,11 +126,13 @@ jobs:
dotnet publish "$consumer" -c release -r "${rid%%:*}" --self-contained false -o "$out"
readelf -h "$out/libnethermind_lean.so" | grep -q "Machine:.*${rid##*:}"
test ! -e "$out/runtimes"
# The test assets reach only a consumer that opts in.
test ! -e "$out/lean-vectors" && test ! -e "$out/testing" && test ! -e "$out/fixtures"
done

publish:
name: Publish
needs: [verify-formatting, test, rust-test, native-suite, verify-package]
needs: [verify-formatting, test, rust-test, verify-package]
if: github.event_name == 'workflow_dispatch' && inputs.publish != 'No' || github.event_name == 'release'
runs-on: ubuntu-latest
permissions:
Expand Down
11 changes: 6 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ Nethermind's native EIP-8288 Lean backend (`libnethermind_lean.so`) and its NuGe

## Project structure

- [src/ffi](./src/ffi/): The Rust source of the library, a thin C ABI (`nlean_*`) over a pinned leanVM fork.
- [src/bindings](./src/bindings/): The .NET codebase. `Nethermind.Crypto.Lean` is a native-only package; `Nethermind.Crypto.Lean.Tests` smoke-tests the committed library through the `nlean_*` C API.
- [src/ffi](./src/ffi/): The Rust source of the library, a thin C ABI (`nlean_*`) over unmodified upstream leanVM pinned by git revision, and the EIP-8288 guest crates in [src/ffi/crates](./src/ffi/crates/) vendored from that revision. This repository is the only source of this code.
- [src/bindings](./src/bindings/): The .NET codebase. `Nethermind.Crypto.Lean` is a native-only package; `Nethermind.Crypto.Lean` also carries, under `testing/`, the consumers' test assets, which `buildTransitive` copies only with `NethermindCryptoLeanTestAssets=true`; `Nethermind.Crypto.Lean.Tests` tests the committed library through the `nlean_*` C API.
- [build-lean.yml](./.github/workflows/build-lean.yml): Builds [src/ffi](./src/ffi/), generates [THIRD-PARTY-NOTICES](./THIRD-PARTY-NOTICES) and opens a pull request with the resulting binaries.
- [test-publish.yml](./.github/workflows/test-publish.yml): Runs the Rust tests, and the .NET tests and Nethermind's native suite against the committed binaries, and optionally publishes on NuGet.
- [test-publish.yml](./.github/workflows/test-publish.yml): Runs the Rust tests and the .NET tests against the committed binaries, and optionally publishes the package on NuGet.

## Coding guidelines

Expand All @@ -17,8 +17,9 @@ Nethermind's native EIP-8288 Lean backend (`libnethermind_lean.so`) and its NuGe
- Add tests for new code and bug fixes.
- Use conventional commits; keep scoped and imperative.
- Keep the native binaries under `src/bindings/Nethermind.Crypto.Lean/runtimes/` and [THIRD-PARTY-NOTICES](./THIRD-PARTY-NOTICES) built from the current [src/ffi](./src/ffi/). They are updated only by [build-lean.yml](./.github/workflows/build-lean.yml) (the binaries are Git LFS objects), so do not edit or rebuild them locally.
- Version the package as `<native version>.<run number>`: the native version is the crate `version` in [Cargo.toml](./src/ffi/Cargo.toml); [build-lean.yml](./.github/workflows/build-lean.yml) copies it into `VersionPrefix` in [Directory.Build.props](./src/bindings/Directory.Build.props), and [test-publish.yml](./.github/workflows/test-publish.yml) appends the run number. The crate version stays `0.1.0` until a versioning scheme is agreed; changing it also needs the `nethermind-lean` entry in [Cargo.lock](./src/ffi/Cargo.lock).
- Keep the smoke test's expected ABI version and aggregated verification key, and the ABI version in the package `Description`, in sync with [src/ffi](./src/ffi/). On a change of either, also bump `NETHERMIND_REVISION` in [test-publish.yml](./.github/workflows/test-publish.yml) to a Nethermind commit whose native suite expects the new library.
- Version the package as `<native version>.<run number>`: the native version is the crate `version` in [Cargo.toml](./src/ffi/Cargo.toml); [build-lean.yml](./.github/workflows/build-lean.yml) copies it into `VersionPrefix` in [Directory.Build.props](./src/bindings/Directory.Build.props), and [test-publish.yml](./.github/workflows/test-publish.yml) appends the run number. The crate's minor version is the C ABI version (`0.12.x` for ABI 12); bump the patch for a guest or key change at the same ABI. Changing it also needs the `nethermind-lean` entry in [Cargo.lock](./src/ffi/Cargo.lock).
- Keep the smoke test's expected ABI version and aggregated verification key, and the ABI version in the package `Description`, in sync with [src/ffi](./src/ffi/). Consumers pin both and check them at startup; a change of either is a new package version they adopt deliberately.
- Prefer the latest versions of GitHub Actions and runners.
- Keep upstream leanVM unmodified and pinned to one revision in [src/ffi/Cargo.toml](./src/ffi/Cargo.toml). The vendored [src/ffi/crates](./src/ffi/crates/) are upstream at that revision plus EIP-8288 changes: `crates/upstream.sh diff` shows them, `crates/upstream.sh update <rev>` moves the crates and the pin together. Keep the changes as small as the guest allows.
- Keep [src/ffi/about.toml](./src/ffi/about.toml) accepting every dependency license; Build Lean generates [THIRD-PARTY-NOTICES](./THIRD-PARTY-NOTICES) from it and [src/ffi/about.hbs](./src/ffi/about.hbs).
- Keep [AGENTS.md](./AGENTS.md) in sync with the ongoing development.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Native EIP-8288 Lean backend for [Nethermind](https://github.com/NethermindEth/nethermind), packaged for NuGet as `Nethermind.Crypto.Lean`.

The library, `libnethermind_lean.so`, is built from the Rust crate in [`src/ffi`](https://github.com/nethermindeth/lean-bindings/tree/main/src/ffi), a thin C ABI over a pinned [leanVM](https://github.com/Marchhill/leanVM) fork.
The library, `libnethermind_lean.so`, is built from the Rust crate in [`src/ffi`](https://github.com/nethermindeth/lean-bindings/tree/main/src/ffi), a thin C ABI over an unmodified, pinned [leanVM](https://github.com/leanEthereum/leanVM) and the EIP-8288 aggregation guest in `src/ffi/crates` (vendored from the same revision; see its README). This repository is the source of that code: consumers take the packages, not the source.

## Package

Expand All @@ -23,12 +23,13 @@ The library, `libnethermind_lean.so`, is built from the Rust crate in [`src/ffi`

- `src/ffi`: the Rust source of the library, and the `cargo-about` configuration (`about.toml`, `about.hbs`) for `THIRD-PARTY-NOTICES`.
- `src/bindings/Nethermind.Crypto.Lean`: the package; `runtimes/*/native/libnethermind_lean.so` (Git LFS) are committed by **Build Lean**.
- `src/bindings/Nethermind.Crypto.Lean.Tests`: loads the committed library and checks its ABI version and aggregated verification key.
- `src/bindings/Nethermind.Crypto.Lean/testing`: test assets for consumers' native suites: the vectors, the `fixtures` signing/proving tool and an incompatible startup stub, all built with the library (Git LFS, committed by **Build Lean**). They ship in the package outside `runtimes/`; a test project gets them in `lean-vectors/` only with `<NethermindCryptoLeanTestAssets>true</NethermindCryptoLeanTestAssets>`, so production publishes never contain them.
- `src/bindings/Nethermind.Crypto.Lean.Tests`: loads the committed library and checks its ABI version, aggregated verification key and limits, and verifies the packaged vectors and a signature from the packaged tool.

## Workflows

- **Build Lean** (manual): builds `src/ffi` for each RID, twice, and fails unless both builds are byte-identical; generates `THIRD-PARTY-NOTICES`; runs the smoke test on the built libraries on x64 and arm64 hardware; optionally opens a pull request with them into the branch it runs on. That pull request is opened with `GITHUB_TOKEN`, which starts no workflows, so run **Test / Publish** on its branch manually.
- **Test / Publish**: on pull requests and `main`, checks formatting, runs the Rust tests, the smoke test and Nethermind's native suite (startup checks and real verifications, with fixtures from `src/ffi`) against the committed library on x64 and arm64 hardware, and verifies a consumer gets the right library per RID. On a release or a manual run, publishes to NuGet (staging or production) through trusted publishing, only after all of these pass.
- **Test / Publish**: on pull requests and `main`, checks formatting, runs the Rust tests of the whole `src/ffi` workspace and the .NET tests against the committed library on x64 and arm64 hardware, and verifies a consumer gets the right library per RID. On a release or a manual run, publishes to NuGet (staging or production) through trusted publishing, only after all of these pass.

## License

Expand Down
Loading
Loading