From 1fed8c6e604a058a76fe07e459ce7a470e309836 Mon Sep 17 00:00:00 2001 From: bakhtin Date: Fri, 7 Nov 2025 16:24:28 +0000 Subject: [PATCH] Actually reproducible builds Signed-off-by: bakhtin --- .github/workflows/release.yaml | 44 +++++++++---------- .github/workflows/reprotest.yml | 78 +++++++++++++++++++++++++++++++++ Makefile | 35 +++++++++++---- crates/rbuilder/Cargo.toml | 1 + docker/Dockerfile.reproducible | 18 ++++++++ rust-toolchain.toml | 1 - 6 files changed, 144 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/reprotest.yml create mode 100644 docker/Dockerfile.reproducible diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5cce5fce0..2e09ce65c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -33,7 +33,7 @@ on: jobs: extract-version: name: Extract version - runs-on: warp-ubuntu-2404-x64-16x + runs-on: warp-ubuntu-2404-x64-2x outputs: VERSION: ${{ steps.extract_version.outputs.VERSION }} steps: @@ -82,62 +82,60 @@ jobs: - ${{ github.event.inputs.features || '' }} steps: - - name: Install dependencies + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install rust run: | - sudo apt-get update - sudo apt-get install -y \ - build-essential \ - cmake \ - curl \ - git \ - libclang-dev \ - libssl-dev \ - pkg-config \ - protobuf-compiler curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - - uses: actions/checkout@v4 - - - name: Build + - name: Build reproducible binary with Docker run: | - FEATURES=${{ matrix.features }} make build - VERSION=${{ needs.extract-version.outputs.VERSION }} make build-deb + RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2) + docker build \ + --build-arg "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" \ + --build-arg "FEATURES=${{ matrix.features }}" \ + --build-arg "VERSION=${{ needs.extract-version.outputs.VERSION }}" \ + -f docker/Dockerfile.reproducible -t rbuilder:release \ + --output type=local,dest=./target . - name: Upload rbuilder artifact uses: actions/upload-artifact@v4 with: name: rbuilder-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/rbuilder + path: target/${{ matrix.configs.profile }}/rbuilder - name: Upload rbuilder-operator artifact uses: actions/upload-artifact@v4 with: name: rbuilder-operator-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/rbuilder-operator + path: target/${{ matrix.configs.profile }}/rbuilder-operator - name: Upload reth-rbuilder artifact uses: actions/upload-artifact@v4 with: name: reth-rbuilder-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/reth-rbuilder + path: target/${{ matrix.configs.profile }}/reth-rbuilder - name: Upload bid-scraper artifact uses: actions/upload-artifact@v4 with: name: bid-scraper-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/bid-scraper + path: target/${{ matrix.configs.profile }}/bid-scraper - name: Upload rbuilder-rebalancer artifact uses: actions/upload-artifact@v4 with: name: rbuilder-rebalancer-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/${{ matrix.configs.profile }}/rbuilder-rebalancer + path: target/${{ matrix.configs.profile }}/rbuilder-rebalancer - name: Upload *.deb packages uses: actions/upload-artifact@v4 with: name: deb-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}${{ matrix.features && '-' }}${{ matrix.features }} - path: target/${{ matrix.configs.target }}/debian/*.deb + path: target/debian/*.deb draft-release: diff --git a/.github/workflows/reprotest.yml b/.github/workflows/reprotest.yml new file mode 100644 index 000000000..36cd2ef7d --- /dev/null +++ b/.github/workflows/reprotest.yml @@ -0,0 +1,78 @@ +name: reproducible-build-test + +on: + workflow_dispatch: {} + schedule: + - cron: "0 1 */2 * *" + +jobs: + build: + name: build reproducible binaries + runs-on: ${{ matrix.runner }} + strategy: + matrix: + include: + - runner: warp-ubuntu-2404-x64-32x + machine: machine-1 + - runner: warp-ubuntu-2204-x64-32x + machine: machine-2 + steps: + - uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + + - name: Build reproducible binary with Docker + run: | + RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2) + docker build \ + --build-arg "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" \ + -f docker/Dockerfile.reproducible -t rbuilder:release \ + --output type=local,dest=./target . + + - name: Calculate SHA256 + id: sha256 + run: | + sha256sum target/reproducible/{rbuilder-operator,rbuilder-rebalancer} > checksums.sha256 + echo "Binaries SHA256 on ${{ matrix.machine }}: $(cat checksums.sha256)" + - name: Upload the hash + uses: actions/upload-artifact@v4 + with: + name: checksums-${{ matrix.machine }} + path: | + checksums.sha256 + retention-days: 1 + + compare: + name: compare reproducible binaries + needs: build + runs-on: ubuntu-latest + steps: + - name: Download artifacts from machine-1 + uses: actions/download-artifact@v4 + with: + name: checksums-machine-1 + path: machine-1/ + - name: Download artifacts from machine-2 + uses: actions/download-artifact@v4 + with: + name: checksums-machine-2 + path: machine-2/ + - name: Compare SHA256 hashes + run: | + echo "=== SHA256 Comparison ===" + echo "Machine 1 hashes:" + cat machine-1/checksums.sha256 + echo "Machine 2 hashes:" + cat machine-2/checksums.sha256 + + if cmp -s machine-1/checksums.sha256 machine-2/checksums.sha256; then + echo "✅ SUCCESS: Binaries are identical (reproducible build verified)" + else + echo "❌ FAILURE: Binaries differ (reproducible build failed)" + exit 1 + fi diff --git a/Makefile b/Makefile index 3193229ee..ecb9a382c 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,8 @@ ifeq ($(IS_X86_64),1) BUILD_ENV = SOURCE_DATE_EPOCH=$(SOURCE_DATE) \ RUSTFLAGS="${RUST_BUILD_FLAGS}" \ LC_ALL=${LOCALE_VAL} \ - TZ=${TZ_VAL} + TZ=${TZ_VAL} \ + JEMALLOC_OVERRIDE=/usr/lib/x86_64-linux-gnu/libjemalloc.a else # Non-x86_64: Use release profile without reproducible build flags BUILD_PROFILE = release @@ -72,7 +73,7 @@ endif .PHONY: build build: ## Build (release version) - $(BUILD_ENV) cargo build --features "$(FEATURES)" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --profile $(BUILD_PROFILE) --workspace + $(BUILD_ENV) cargo build --features "$(FEATURES) jemalloc-unprefixed" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --profile $(BUILD_PROFILE) --workspace .PHONY: build-bid-scraper build-bid-scraper: ## Build the bid-scraper binary (release version) @@ -80,11 +81,11 @@ build-bid-scraper: ## Build the bid-scraper binary (release version) .PHONY: build-rbuilder-operator build-rbuilder-operator: ## Build the rbuilder-operator binary (release version) - $(BUILD_ENV) cargo build --features "$(FEATURES)" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --bin rbuilder-operator --profile $(BUILD_PROFILE) + $(BUILD_ENV) cargo build --features "$(FEATURES) jemalloc-unprefixed" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --bin rbuilder-operator --profile $(BUILD_PROFILE) .PHONY: build-rbuilder-rebalancer build-rbuilder-rebalancer: ## Build the rbuilder-rebalancer binary (release version) - $(BUILD_ENV) cargo build --features "$(FEATURES)" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --bin rbuilder-rebalancer --profile $(BUILD_PROFILE) + $(BUILD_ENV) cargo build --features "$(FEATURES) jemalloc-unprefixed" --locked $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) --bin rbuilder-rebalancer --profile $(BUILD_PROFILE) .PHONY: build-dev build-dev: ## Build (debug version) @@ -94,8 +95,9 @@ build-dev: ## Build (debug version) docker-image-rbuilder: ## Build a rbuilder Docker image docker build --platform linux/amd64 --target rbuilder-runtime --build-arg FEATURES="$(FEATURES)" -t rbuilder -f docker/Dockerfile.rbuilder . -.PHONE: docker-image-rbuilder-operator - docker build --platform linux/amd64 --target rbuilder-runtime --build-arg FEATURES="$(FEATURES)" -t rbuilder -f docker/Dockerfile.rbuilder-operator . +.PHONY: docker-image-rbuilder-operator +docker-image-rbuilder-operator: ## Build a rbuilder-operator Docker image + docker build --platform linux/amd64 --target rbuilder-runtime --build-arg FEATURES="$(FEATURES) jemalloc-unprefixed" -t rbuilder-operator -f docker/Dockerfile.rbuilder-operator . .PHONY: docker-image-test-relay docker-image-test-relay: ## Build a test relay Docker image @@ -103,26 +105,41 @@ docker-image-test-relay: ## Build a test relay Docker image ##@ Debian Packages +# Define binary paths for smart dependencies +BID_SCRAPER_BIN := target/$(if $(BUILD_TARGET),$(BUILD_TARGET)/)$(BUILD_PROFILE)/bid-scraper +RBUILDER_OPERATOR_BIN := target/$(if $(BUILD_TARGET),$(BUILD_TARGET)/)$(BUILD_PROFILE)/rbuilder-operator +RBUILDER_REBALANCER_BIN := target/$(if $(BUILD_TARGET),$(BUILD_TARGET)/)$(BUILD_PROFILE)/rbuilder-rebalancer + .PHONY: install-cargo-deb install-cargo-deb: @command -v cargo-deb >/dev/null 2>&1 || cargo install cargo-deb@3.6.0 --locked +# Build individual binaries only if they don't exist - delegate to existing build targets +$(BID_SCRAPER_BIN): build-bid-scraper + @# Binary built by build-bid-scraper target + +$(RBUILDER_OPERATOR_BIN): build-rbuilder-operator + @# Binary built by build-rbuilder-operator target + +$(RBUILDER_REBALANCER_BIN): build-rbuilder-rebalancer + @# Binary built by build-rbuilder-rebalancer target + .PHONY: build-deb-bid-scraper -build-deb-bid-scraper: install-cargo-deb build-bid-scraper ## Build bid-scraper Debian package +build-deb-bid-scraper: install-cargo-deb $(BID_SCRAPER_BIN) ## Build bid-scraper Debian package cargo deb --profile $(BUILD_PROFILE) --no-build --no-dbgsym --no-strip \ -p bid-scraper \ $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) \ $(if $(VERSION),--deb-version "1~$(VERSION)") .PHONY: build-deb-rbuilder-operator -build-deb-rbuilder-operator: install-cargo-deb build-rbuilder-operator ## Build rbuilder-operator Debian package +build-deb-rbuilder-operator: install-cargo-deb $(RBUILDER_OPERATOR_BIN) ## Build rbuilder-operator Debian package cargo deb --profile $(BUILD_PROFILE) --no-build --no-dbgsym --no-strip \ -p rbuilder-operator \ $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) \ $(if $(VERSION),--deb-version "1~$(VERSION)") .PHONY: build-deb-rbuilder-rebalancer -build-deb-rbuilder-rebalancer: install-cargo-deb build-rbuilder-rebalancer ## Build rbuilder-rebalancer Debian package +build-deb-rbuilder-rebalancer: install-cargo-deb $(RBUILDER_REBALANCER_BIN) ## Build rbuilder-rebalancer Debian package cargo deb --profile $(BUILD_PROFILE) --no-build --no-dbgsym --no-strip \ -p rbuilder-rebalancer \ $(if $(BUILD_TARGET),--target $(BUILD_TARGET)) \ diff --git a/crates/rbuilder/Cargo.toml b/crates/rbuilder/Cargo.toml index 011e5f61d..843c3d282 100644 --- a/crates/rbuilder/Cargo.toml +++ b/crates/rbuilder/Cargo.toml @@ -154,6 +154,7 @@ criterion = { workspace = true, features = ["html_reports", "async_tokio"] } # TODO: remove? optimism = [] redact-sensitive = [] +jemalloc-unprefixed = ["tikv-jemallocator/unprefixed_malloc_on_supported_platforms"] [[bench]] name = "bench_main" diff --git a/docker/Dockerfile.reproducible b/docker/Dockerfile.reproducible new file mode 100644 index 000000000..54f513332 --- /dev/null +++ b/docker/Dockerfile.reproducible @@ -0,0 +1,18 @@ +ARG RUST_TOOLCHAIN=1.89.0 +FROM docker.io/rust:$RUST_TOOLCHAIN-trixie AS builder + +ARG FEATURES VERSION +# Switch to snapshot repository +RUN sed -i '/^# http/{N;s|^# \(http[^ ]*\)\nURIs: .*|# \1\nURIs: \1|}' /etc/apt/sources.list.d/debian.sources +RUN apt-get -o Acquire::Check-Valid-Until=false update && \ + apt-get install -y \ + libjemalloc-dev \ + libclang-dev \ + protobuf-compiler \ + cmake +WORKDIR /build +COPY . . +RUN SOURCE_DATE=1730000000 make build && make build-deb + +FROM scratch AS artifacts +COPY --from=builder /build/target/x86_64-unknown-linux-gnu/ / diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b1fa8153a..73cb934de 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,3 @@ [toolchain] channel = "stable" -version = "1.88.0" components = ["rustfmt", "clippy"]