Files
zeekayandHanzo Dev 1030ab79cd Build against cevm 0feba60: blocks Go's live VM accepts
cevm builds the 86-byte extra Go builds (the fee window and empty
predicate results) and refuses other extras once the chain is running,
refuses a difficulty other than 1, and neither builds nor pools a
transaction carrying warp or stake-weight predicates. Go's plugin
handler accepts the blocks it builds.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 10:56:38 -07:00

313 lines
16 KiB
Docker

# syntax=docker/dockerfile:1.26@sha256:ecfaec9ed6d810b56388c508f4121597bfbba70d41a6dfeee4d8cad5f295fc32
#
# lux-cpp/node, compiled and tested from source. The build context is this
# repository; every other source is fetched at a commit pinned below:
#
# luxcrypto luxfi/crypto `make dist`: libluxcrypto.a, the ML-DSA-65 and
# ML-KEM-768 of the post-quantum peer handshake, as a Go c-archive
# deps AWS-LC, cevm's Conan dependencies, the consensus engine and cevm
# test every test target, run by ctest (`--target test`)
# binaries luxd, lux-join, and cevm — the EVM plugin luxd runs its chain in
# runtime gcr.io/distroless/cc-debian12:nonroot and the binaries
#
# What is not pinned by commit resolves when the image is built: Debian's
# packages, Conan's Python dependencies, the intx, nlohmann_json and cmake
# packages from ConanCenter, and blst v0.3.14, which lux-crypto's kzg build
# clones by tag for its headers.
#
# Private repositories are read with the BuildKit secret GH_READ_TOKEN, a GitHub
# token that can read them. It is never an ARG and never a file. Only the two
# steps that fetch mount it, and they run git and nothing else: git takes it as
# configuration from their environment, and each fails if the token is anywhere
# in the filesystem it leaves.
#
# Every Debian here is 12, the runtime's. A binary carries the glibc symbol
# versions it was linked against; linked against a newer glibc it builds, it
# pushes, and it dies on first exec in distroless-debian12.
# ── pins ────────────────────────────────────────────────────────────────────
# Full commit SHAs, each checked to be on the branch or tag its fetch line
# names, spelled in full: a bare `main` would also match a tag called main. GitHub serves any commit it still holds by SHA, including ones no
# branch reaches any more and ones that only exist in a fork.
ARG LUX_CPP_CONSENSUS=9928599fc95d92c54b759468a351af907ab29aed
# cevm main, the tree lux-cpp/node's tests pass on and its own image builds
# against. Not the integrate/fixes/fix-* branches: those are unmerged GPU-EVM
# work, and this is the CPU build.
ARG LUX_CPP_CEVM=0feba60c0e04c25a4ef069bc1897ad91adbb8fba
# The evmc commit cevm's submodule names, fetched where the submodule goes.
ARG LUX_CPP_EVMC=7cb4b0ca30df7b036d1a582196af5e9a1455a47d
# The tree lux-cpp/node's last green image built. The commit after it on main
# adds bcc to CMake's algorithm list and not to the Conan recipe's exports, so
# the package it makes cannot configure.
ARG LUX_CPP_CRYPTO=87e59776b97e9e13f1f07ee4deb129f9cda8cff8
ARG LUX_CPP_BLST=0bf3bb96c630d7451b8549d1ad95074997a5a5e3
ARG LUX_CPP_ZAP=2a37db5a574f73d972c8c22e5d79b6b24dcbe637
ARG LUX_GPU_KERNELS=76cdda14b381f77cc42d81c094460bbce6bcb3d1
# lux-crypto's CMake FetchContent dependencies, at the commits its tags name.
ARG LUX_CPP_INTX=0306b495820a5259563e5055f0a8b726b9ae4e0b
ARG LUX_CPP_EVMMAX=9af2f9ac9b45fef6d547e59a06ea091f64966700
ARG LUX_CPP_PQCLEAN=826eff8124946a005c189eaf2aba5d2a3df8329a
ARG LUX_CPP_ED25519_DONNA=805dc05f47d3dfaa574cea16fd78321d63b6b17d
ARG LUX_CPP_BLAKE3_REFERENCE=38173020aa1f643c904f8f0f0340e16f31f01cfc
ARG LUXFI_C_KZG_4844=9763b8f10ea45221f383e6f6500b78eb4e4ad748
ARG LUXFI_SR25519_CRUST=0cb4dc4d96e20c4b3dd6d520a43544ea55921ec4
# The last luxfi/crypto whose go directive golang:1.26.8 builds. main after it
# moves the directive to 1.27.1 and changes no Go source.
ARG LUXFI_CRYPTO=923186d4163bf15acf537f96b679a1f7f7915a64
ARG AWS_LC=7b627926398b6bb786296132b22dbb37bab7649f
# ── fetch ───────────────────────────────────────────────────────────────────
# Sourced by every step that fetches. It holds no secret.
FROM scratch AS scripts
COPY <<'EOF' /fetch.sh
# auth: read access to GitHub for the rest of this RUN, from GH_READ_TOKEN.
# git reads it as configuration from its environment (GIT_CONFIG_COUNT, see
# git-config(1)), so no file records it — not .git/config, not ~/.gitconfig,
# not a layer. It is sent to https://github.com/ and to no other host.
auth() {
if [ -r /run/secrets/GH_READ_TOKEN ]; then
token=$(tr -d '[:space:]' < /run/secrets/GH_READ_TOKEN)
else
token=
fi
if [ -z "$token" ]; then
echo "node: the build secret GH_READ_TOKEN is missing. It is a GitHub token that can read the private lux-cpp, lux-gpu and luxfi repositories this build fetches; pass it as --secret id=GH_READ_TOKEN." >&2
exit 1
fi
header=$(printf 'x-access-token:%s' "$token" | base64 -w0)
export GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=http.https://github.com/.extraheader \
GIT_CONFIG_VALUE_0="AUTHORIZATION: basic $header"
}
# sealed: refuses the layer if the token, bare or as the header, is in any file
# on this filesystem, /run/secrets aside, where the secret itself is mounted. A
# file that cannot be read refuses it too: unread is not clean.
sealed() {
set --
for d in /* /.[!.]* /run/*; do
case $d in
/proc | /sys | /dev | /run | /run/secrets) ;;
*) [ -e "$d" ] && [ ! -L "$d" ] && set -- "$@" "$d" ;;
esac
done
found=0
grep -rqF -D skip -e "$token" -e "$header" -- "$@" || found=$?
case $found in
0) echo "node: the GitHub token was written into this layer; it is refused." >&2
exit 1 ;;
1) ;;
*) echo "node: could not read every file this layer holds; it is refused." >&2
exit 1 ;;
esac
}
# fetch <owner/repo> <refs/heads/… or refs/tags/…> <sha> <dir>: the one
# commit, by its full SHA, with no history, after proving it is on that branch
# or tag. The proof reads commits only, in a repository of its own that is then
# removed.
fetch() {
repo=$1 ref=$2 sha=$3 dir=$4
if ! printf '%s' "$sha" | grep -Eqx '[0-9a-f]{40}'; then
echo "node: $repo is pinned to '$sha', which is not a full commit SHA." >&2
exit 1
fi
case $ref in
refs/heads/?* | refs/tags/?*) ;;
*) echo "node: $repo is checked against '$ref', which is not a full branch or tag ref." >&2
exit 1 ;;
esac
log=$(mktemp -d)
git init -q --bare "$log"
git -C "$log" remote add origin "https://github.com/$repo"
git -C "$log" fetch -q --filter=tree:0 origin "$ref"
if ! git -C "$log" rev-list FETCH_HEAD | grep -qx "$sha"; then
echo "node: $repo $sha is not on $ref." >&2
exit 1
fi
rm -rf "$log"
git init -q "$dir"
git -C "$dir" remote add origin "https://github.com/$repo"
git -C "$dir" fetch -q --depth 1 origin "$sha"
git -C "$dir" checkout -q --detach FETCH_HEAD
test "$(git -C "$dir" rev-parse HEAD)" = "$sha"
}
EOF
# ── luxcrypto ───────────────────────────────────────────────────────────────
# A Go c-archive, so Go builds it and the C++ stage takes the archive. Public
# sources and modules: this stage is given no token.
FROM golang:1.26.8-bookworm@sha256:a688600ca24f8a4d3ca77f95b0dd40704a9fc787c826660eb7ba0b641b8b175d AS luxcrypto
COPY --from=scripts /fetch.sh /usr/local/lib/fetch.sh
ARG LUXFI_CRYPTO
RUN <<'EOF'
set -eu
. /usr/local/lib/fetch.sh
fetch luxfi/crypto refs/heads/main "$LUXFI_CRYPTO" /src/lux/crypto
cd /src/lux/crypto
go mod download github.com/luxfi/accel
make dist
test -s dist/libluxcrypto.a
EOF
# ── builder ─────────────────────────────────────────────────────────────────
FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake ninja-build git ca-certificates go-md2man golang-go perl \
nlohmann-json3-dev python3 python3-pip python3-venv \
&& rm -rf /var/lib/apt/lists/*
# cevm resolves intx, blst, lux-crypto and lux-zap-core through Conan.
RUN python3 -m venv /opt/conan && /opt/conan/bin/pip install --no-cache-dir conan==2.32.0
ENV PATH=/opt/conan/bin:$PATH
COPY --from=scripts /fetch.sh /usr/local/lib/fetch.sh
# AWS-LC: the TLS 1.3 + X25519MLKEM768 the peer link runs on. Static, built once.
ARG AWS_LC
RUN <<'EOF'
set -eu
. /usr/local/lib/fetch.sh
fetch aws/aws-lc refs/tags/v1.65.0 "$AWS_LC" /src/aws-lc
cmake -S /src/aws-lc -B /src/aws-lc-build -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF
cmake --build /src/aws-lc-build --target ssl crypto
EOF
# What Conan builds from. lux-crypto's CMake would clone seven more private
# repositories while it configures, so they are fetched here instead, into
# /src/deps under their FetchContent names, and the next step points FetchContent
# at them. evmc goes where cevm's submodule does; cevm refuses to configure
# without it.
ARG LUX_CPP_CEVM LUX_CPP_EVMC LUX_CPP_CRYPTO LUX_CPP_BLST LUX_CPP_ZAP \
LUX_CPP_INTX LUX_CPP_EVMMAX LUX_CPP_PQCLEAN LUX_CPP_ED25519_DONNA \
LUX_CPP_BLAKE3_REFERENCE LUXFI_C_KZG_4844 LUXFI_SR25519_CRUST
RUN --mount=type=secret,id=GH_READ_TOKEN <<'EOF'
set -eu
. /usr/local/lib/fetch.sh
auth
fetch lux-cpp/cevm refs/heads/main "$LUX_CPP_CEVM" /src/luxcpp/cevm
fetch lux-cpp/evmc refs/heads/main "$LUX_CPP_EVMC" /src/luxcpp/cevm/evmc
fetch lux-cpp/crypto refs/heads/main "$LUX_CPP_CRYPTO" /src/luxcpp/crypto
fetch lux-cpp/blst refs/heads/main "$LUX_CPP_BLST" /src/luxcpp/blst
fetch lux-cpp/zap-cpp-core refs/heads/main "$LUX_CPP_ZAP" /src/luxcpp/zap-cpp-core
fetch lux-cpp/intx refs/tags/v0.15.0 "$LUX_CPP_INTX" /src/deps/luxcpp_intx
fetch lux-cpp/evmmax refs/tags/v0.21.0 "$LUX_CPP_EVMMAX" /src/deps/luxcpp_evmmax
fetch lux-cpp/pqclean refs/tags/v1.0.0 "$LUX_CPP_PQCLEAN" /src/deps/luxcpp_pqclean
fetch lux-cpp/ed25519-donna refs/tags/v1.0.0 "$LUX_CPP_ED25519_DONNA" /src/deps/luxcpp_ed25519_donna
fetch lux-cpp/blake3-reference refs/tags/v1.5.0 "$LUX_CPP_BLAKE3_REFERENCE" /src/deps/luxcpp_blake3_reference
fetch luxfi/c-kzg-4844 refs/tags/v2.1.7 "$LUXFI_C_KZG_4844" /src/deps/luxfi_c_kzg_4844
fetch luxfi/sr25519-crust refs/tags/v0.2.0 "$LUXFI_SR25519_CRUST" /src/deps/sr25519_crust
sealed
EOF
# cevm's dependencies, with no token. The lux packages are on no Conan remote,
# so they are exported from their checkouts first. FetchContent takes each
# dependency from /src/deps (FETCHCONTENT_SOURCE_DIR_<NAME>) instead of cloning
# it, and a dependency not fetched there fails rather than being cloned. blst is built portable, choosing ADX at run time, so the binary does not
# depend on the CPU of the node that happened to build it.
RUN <<'EOF'
set -eu
deps="'FETCHCONTENT_FULLY_DISCONNECTED': {'value': 'ON', 'cache': True, 'type': 'BOOL'}, "
for d in /src/deps/*; do
n=$(basename "$d" | tr '[:lower:]' '[:upper:]')
deps="$deps'FETCHCONTENT_SOURCE_DIR_$n': '$d', "
done
conan profile detect --force
printf 'core.download:parallel=1\n' >> "$(conan config home)/global.conf"
conan export /src/luxcpp/crypto
conan export /src/luxcpp/blst
conan export /src/luxcpp/zap-cpp-core
conan install /src/luxcpp/cevm \
-pr /src/luxcpp/cevm/.github/conan/manylinux-relax.profile \
-s build_type=Release -s compiler.cppstd=gnu20 \
-o 'bls/*:portable=True' \
-c "lux-crypto/*:tools.cmake.cmaketoolchain:extra_variables={$deps}" \
--output-folder=/src/cevm-conan --build=missing
EOF
# The engine under the node. gpu-kernels goes where cevm's CMakeLists
# name it, lux-private/gpu-kernels four levels above lib/evm: it carries the
# 0x100 precompile's 256-bit arithmetic and the aivm oracle.
ARG LUX_CPP_CONSENSUS LUX_GPU_KERNELS
RUN --mount=type=secret,id=GH_READ_TOKEN <<'EOF'
set -eu
. /usr/local/lib/fetch.sh
auth
fetch lux-cpp/consensus refs/heads/main "$LUX_CPP_CONSENSUS" /src/lux-cpp/consensus
fetch lux-gpu/gpu-kernels refs/heads/main "$LUX_GPU_KERNELS" /src/lux-private/gpu-kernels
sealed
EOF
# Where the node looks for it: ../../lux/crypto/dist from its source directory,
# /src/lux-cpp/node.
COPY --from=luxcrypto /src/lux/crypto/dist /src/lux/crypto/dist
COPY . /src/lux-cpp/node
# One configure for both the tests and the binaries, so what is tested is what
# ships: Release, the same flags, the same toolchain.
RUN cmake -S /src/lux-cpp/node -B /src/build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=/src/cevm-conan/build/Release/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
-DCONSENSUS_DIR=/src/lux-cpp/consensus \
-DCEVM_DIR=/src/luxcpp/cevm \
-DLUXCPP_ROOT=/src/luxcpp \
-DZAP_DIR=/src/luxcpp/zap-cpp-core/include \
-DAWSLC_SRC=/src/aws-lc \
-DAWSLC_BUILD_DIR=/src/aws-lc-build \
-DBLST_ISA=portable
# ── test ────────────────────────────────────────────────────────────────────
# This repository's tests and the consensus engine's. A failure fails the build,
# so an image of a tree whose tests fail cannot be made from this file. cevm's
# tests are cevm's: they are registered here because it is a subdirectory, and
# built and run by its own build.
FROM builder AS test
RUN cmake --build /src/build && \
ctest --test-dir /src/build -L node --output-on-failure && \
ctest --test-dir /src/build/consensus-build --output-on-failure
# ── binaries ────────────────────────────────────────────────────────────────
# Then two checks before anything ships: the binaries need no library the
# runtime lacks (a Conan .so, or a system libssl in place of AWS-LC, would pass
# here and fail on first exec there), and luxd runs. --publish on a throwaway
# directory makes an ML-DSA-65 identity through libluxcrypto's Go runtime and a
# BLS key, and prints the committee line the BLS key signs.
FROM builder AS binaries
RUN <<'EOF'
set -eu
cmake --build /src/build --target luxd lux-join cevm-bin
mkdir -p /out/bin /out/libexec/lux
strip -o /out/bin/luxd /src/build/luxd
strip -o /out/bin/lux-join /src/build/lux-join
strip -o /out/libexec/lux/cevm "$(find /src/build -type f -name cevm -perm -u+x | head -n1)"
for b in bin/luxd bin/lux-join libexec/lux/cevm; do
needs=$(readelf -d "/out/$b" | sed -n 's/.*(NEEDED).*\[\(.*\)\]$/\1/p')
test -n "$needs"
extra=$(printf '%s\n' "$needs" | grep -Evx 'lib(c|m|pthread|dl|rt|resolv|gcc_s|stdc\+\+|gomp)\.so\.[0-9]+|ld-linux-(x86-64|aarch64)\.so\.[0-9]+' || true)
if [ -n "$extra" ]; then
echo "node: $b links $extra, which gcr.io/distroless/cc-debian12 does not carry." >&2
exit 1
fi
done
k=$(mktemp -d)
/out/bin/luxd --data "$k" --publish > "$k.line"
test -s "$k.line"
rm -rf "$k" "$k.line"
EOF
# ── runtime ─────────────────────────────────────────────────────────────────
# cc carries exactly the C and C++ runtimes the node links, and no shell,
# package manager or fetcher: an image that holds a validator's key holds
# nothing that can be told to fetch and run.
FROM gcr.io/distroless/cc-debian12:nonroot@sha256:9dac0a79194e45a7da0158a9c6da57b217585af0786db3845d1f0ec1a0dd182f
COPY --from=binaries /out/ /usr/local/
# RPC, and the base port the vote mesh listens on.
EXPOSE 9730 9731
USER nonroot
ENTRYPOINT ["/usr/local/bin/luxd"]