diff --git a/.github/workflows/release-reproducible.yml b/.github/workflows/release-reproducible.yml index e0e7f78aa58..9726cb77b89 100644 --- a/.github/workflows/release-reproducible.yml +++ b/.github/workflows/release-reproducible.yml @@ -40,12 +40,20 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract Rust version from Cargo.toml + id: rust_version + run: | + RUST_VERSION=$(cargo metadata --format-version 1 | jq -r '.packages[] | select(.name == "reth") | .rust_version' || echo "1") + echo "RUST_VERSION=$RUST_VERSION" >> $GITHUB_OUTPUT + - name: Build and push reproducible image uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile.reproducible push: true + build-args: | + RUST_VERSION=${{ steps.rust_version.outputs.RUST_VERSION }} tags: | ${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${{ needs.extract-version.outputs.VERSION }} ${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a647c29687..4b637889d2a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,7 @@ env: CARGO_TERM_COLOR: always DOCKER_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/reth DOCKER_OP_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/op-reth + DEB_SUPPORTED_TARGETS: x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu riscv64gc-unknown-linux-gnu jobs: dry-run: @@ -73,6 +74,10 @@ jobs: os: ubuntu-24.04 profile: maxperf allow_fail: false + - target: x86_64-unknown-linux-gnu + os: ubuntu-24.04 + profile: reproducible + allow_fail: false - target: aarch64-unknown-linux-gnu os: ubuntu-24.04 profile: maxperf @@ -119,12 +124,34 @@ jobs: echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV - name: Build Reth - run: make PROFILE=${{ matrix.configs.profile }} ${{ matrix.build.command }}-${{ matrix.configs.target }} + if: ${{ !(matrix.build.binary == 'op-reth' && matrix.configs.profile == 'reproducible') }} + run: | + if [[ "${{ matrix.build.binary }}" == "reth" && "${{ matrix.configs.profile }}" == "reproducible" ]]; then + make build-reth-reproducible + else + make PROFILE=${{ matrix.configs.profile }} ${{ matrix.build.command }}-${{ matrix.configs.target }} + fi + + - name: Build Reth deb package + if: ${{ matrix.build.binary == 'reth' && contains(env.DEB_SUPPORTED_TARGETS, matrix.configs.target) }} + run: make build-deb-${{ matrix.configs.target }} PROFILE=${{ matrix.configs.profile }} VERSION=${{ needs.extract-version.outputs.VERSION }} + - name: Move binary run: | mkdir artifacts [[ "${{ matrix.configs.target }}" == *windows* ]] && ext=".exe" - mv "target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/${{ matrix.build.binary }}${ext}" ./artifacts + + # Handle reproducible builds which always target x86_64-unknown-linux-gnu + if [[ "${{ matrix.build.binary }}" == "reth" && "${{ matrix.configs.profile }}" == "reproducible" ]]; then + mv "target/x86_64-unknown-linux-gnu/${{ matrix.configs.profile }}/${{ matrix.build.binary }}${ext}" ./artifacts + else + mv "target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/${{ matrix.build.binary }}${ext}" ./artifacts + fi + + # Move deb packages if they exist + if [[ "${{ matrix.build.binary }}" == "reth" && "${{ env.DEB_SUPPORTED_TARGETS }}" == *"${{ matrix.configs.target }}"* ]]; then + mv "target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb" ./artifacts + fi - name: Configure GPG and create artifacts env: @@ -134,9 +161,12 @@ jobs: export GPG_TTY=$(tty) echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import cd artifacts - tar -czf ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz ${{ matrix.build.binary }}* + tar -czf ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz ${{ matrix.build.binary }}*[!.deb] echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz - mv *tar.gz* .. + if [[ -f "${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb" ]]; then + echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb + fi + mv *tar.gz* *.deb* .. shell: bash - name: Upload artifact @@ -153,6 +183,20 @@ jobs: name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc + - name: Upload deb package + if: ${{ github.event.inputs.dry_run != 'true' && matrix.build.binary == 'reth' && contains(env.DEB_SUPPORTED_TARGETS, matrix.configs.target) }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb + path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb + + - name: Upload deb package signature + if: ${{ github.event.inputs.dry_run != 'true' && matrix.build.binary == 'reth' && contains(env.DEB_SUPPORTED_TARGETS, matrix.configs.target) }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb.asc + path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}-${{ matrix.configs.profile }}.deb.asc + draft-release: name: draft release runs-on: ubuntu-latest diff --git a/.github/workflows/reproducible-build.yml b/.github/workflows/reproducible-build.yml index b4a93cedaba..0f5dd2e72d8 100644 --- a/.github/workflows/reproducible-build.yml +++ b/.github/workflows/reproducible-build.yml @@ -15,24 +15,18 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: target: x86_64-unknown-linux-gnu - - name: Install cross main - run: | - cargo install cross --git https://github.com/cross-rs/cross - name: Install cargo-cache run: | cargo install cargo-cache - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - name: Build Reth run: | - make build-reproducible - mv target/x86_64-unknown-linux-gnu/release/reth reth-build-1 + make build-reth-reproducible + mv target/x86_64-unknown-linux-gnu/reproducible/reth reth-build-1 - name: Clean cache run: make clean && cargo cache -a - name: Build Reth again run: | - make build-reproducible - mv target/x86_64-unknown-linux-gnu/release/reth reth-build-2 + make build-reth-reproducible + mv target/x86_64-unknown-linux-gnu/reproducible/reth reth-build-2 - name: Compare binaries run: cmp reth-build-1 reth-build-2 diff --git a/Cargo.toml b/Cargo.toml index 369506adec1..bf419b7c1c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -324,6 +324,12 @@ inherits = "release" lto = "fat" codegen-units = 1 +[profile.reproducible] +inherits = "release" +panic = "abort" +codegen-units = 1 +incremental = false + [workspace.dependencies] # reth op-reth = { path = "crates/optimism/bin" } diff --git a/Dockerfile.reproducible b/Dockerfile.reproducible index a0d4a17b5bb..602b9b857c0 100644 --- a/Dockerfile.reproducible +++ b/Dockerfile.reproducible @@ -1,17 +1,17 @@ -# Use the Rust 1.88 image based on Debian Bookworm -FROM rust:1.88-bookworm AS builder +ARG RUST_VERSION=1 -# Install specific version of libclang-dev -RUN apt-get update && apt-get install -y libclang-dev=1:14.0-55.7~deb12u1 +FROM rust:$RUST_VERSION-bookworm AS builder + +RUN apt-get update && apt-get install -y \ + git \ + libclang-dev=1:14.0-55.7~deb12u1 # Copy the project to the container COPY ./ /app WORKDIR /app -# Build the project with the reproducible settings -RUN make build-reproducible - -RUN mv /app/target/x86_64-unknown-linux-gnu/release/reth /reth +RUN make build-reth-reproducible +RUN mv /app/target/x86_64-unknown-linux-gnu/reproducible/reth /reth # Create a minimal final image with just the binary FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a diff --git a/Makefile b/Makefile index 30f6b0aa478..8d8b0a5b3a5 100644 --- a/Makefile +++ b/Makefile @@ -64,34 +64,25 @@ install-op: ## Build and install the op-reth binary under `$(CARGO_HOME)/bin`. build: ## Build the reth binary into `target` directory. cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)" +.PHONY: build-reth +build-reth: ## Build the reth binary (alias for build target). + $(MAKE) build + # Environment variables for reproducible builds -# Initialize RUSTFLAGS -RUST_BUILD_FLAGS = -# Enable static linking to ensure reproducibility across builds -RUST_BUILD_FLAGS += --C target-feature=+crt-static -# Set the linker to use static libgcc to ensure reproducibility across builds -RUST_BUILD_FLAGS += -C link-arg=-static-libgcc -# Remove build ID from the binary to ensure reproducibility across builds -RUST_BUILD_FLAGS += -C link-arg=-Wl,--build-id=none -# Remove metadata hash from symbol names to ensure reproducible builds -RUST_BUILD_FLAGS += -C metadata='' # Set timestamp from last git commit for reproducible builds SOURCE_DATE ?= $(shell git log -1 --pretty=%ct) -# Disable incremental compilation to avoid non-deterministic artifacts -CARGO_INCREMENTAL_VAL = 0 -# Set C locale for consistent string handling and sorting -LOCALE_VAL = C -# Set UTC timezone for consistent time handling across builds -TZ_VAL = UTC - -.PHONY: build-reproducible -build-reproducible: ## Build the reth binary into `target` directory with reproducible builds. Only works for x86_64-unknown-linux-gnu currently + +# `reproducible` only supports reth on x86_64-unknown-linux-gnu +build-%-reproducible: + @if [ "$*" != "reth" ]; then \ + echo "Error: Reproducible builds are only supported for reth, not $*"; \ + exit 1; \ + fi SOURCE_DATE_EPOCH=$(SOURCE_DATE) \ - RUSTFLAGS="${RUST_BUILD_FLAGS} --remap-path-prefix $$(pwd)=." \ - CARGO_INCREMENTAL=${CARGO_INCREMENTAL_VAL} \ - LC_ALL=${LOCALE_VAL} \ - TZ=${TZ_VAL} \ - cargo build --bin reth --features "$(FEATURES)" --profile "release" --locked --target x86_64-unknown-linux-gnu + RUSTFLAGS="-C symbol-mangling-version=v0 -C strip=none -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix $$(pwd)=." \ + LC_ALL=C \ + TZ=UTC \ + cargo build --bin reth --features "$(FEATURES)" --profile "reproducible" --locked --target x86_64-unknown-linux-gnu .PHONY: build-debug build-debug: ## Build the reth binary into `target/debug` directory. @@ -155,6 +146,22 @@ op-build-x86_64-apple-darwin: op-build-aarch64-apple-darwin: $(MAKE) op-build-native-aarch64-apple-darwin +build-deb-%: + @case "$*" in \ + x86_64-unknown-linux-gnu|aarch64-unknown-linux-gnu|riscv64gc-unknown-linux-gnu) \ + echo "Building debian package for $*"; \ + ;; \ + *) \ + echo "Error: Debian packages are only supported for x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, and riscv64gc-unknown-linux-gnu, not $*"; \ + exit 1; \ + ;; \ + esac + cargo install cargo-deb@3.6.0 --locked + cargo deb --profile $(PROFILE) --no-build --no-dbgsym --no-strip \ + --target $* \ + $(if $(VERSION),--deb-version "1~$(VERSION)") \ + $(if $(VERSION),--output "target/$*/$(PROFILE)/reth-$(VERSION)-$*-$(PROFILE).deb") + # Create a `.tar.gz` containing a binary for a specific target. define tarball_release_binary cp $(CARGO_TARGET_DIR)/$(1)/$(PROFILE)/$(2) $(BIN_DIR)/$(2) diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index a590f25810b..d4e134bf48c 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -9,6 +9,20 @@ repository.workspace = true description = "Reth node implementation" default-run = "reth" +[package.metadata.deb] +maintainer = "reth team" +depends = "$auto" +section = "network" +priority = "optional" +maintainer-scripts = "../../pkg/reth/debian/" +assets = [ + "$auto", + ["../../README.md", "usr/share/doc/reth/", "644"], + ["../../LICENSE-APACHE", "usr/share/doc/reth/", "644"], + ["../../LICENSE-MIT", "usr/share/doc/reth/", "644"], +] +systemd-units = { enable = false, start = false, unit-name = "reth", unit-scripts = "../../pkg/reth/debian" } + [lints] workspace = true diff --git a/pkg/reth/debian/reth.service b/pkg/reth/debian/reth.service new file mode 100644 index 00000000000..edd78d455c0 --- /dev/null +++ b/pkg/reth/debian/reth.service @@ -0,0 +1,13 @@ +[Unit] +Description=Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol +Wants=network-online.target +After=network.target network-online.target + +[Service] +Type=exec +DynamicUser=yes +StateDirectory=reth +ExecStart=/usr/bin/reth node --datadir %S/reth --log.file.max-files 0 + +[Install] +WantedBy=multi-user.target