From 632ef6f64353da4efb43b6729b3dc0e6d41f159c Mon Sep 17 00:00:00 2001 From: David Rusu Date: Sun, 7 Jun 2026 11:00:45 -0400 Subject: [PATCH] fix: host -fPIC Linux archives from the fork instead of building from source Building rapidsnark from source in build.rs required cmake/nasm/gmp on every runner, which the self-hosted CI hosts lack. Instead, host pre-built -fPIC static archives as a fork GitHub release and download them like before. - build.rs: reverted to the download-based approach (the PIC archives are built on glibc 2.35 so they carry no __isoc23 references; no compat shim needed). - download_rapidsnark.sh: Linux x86_64/arm64 now download the -fPIC rebuilds from this fork's `rapidsnark-pic-*` release; macOS/iOS/Android keep using the upstream iden3 archives (which work fine). - Add .github/workflows/build-pic-archives.yml: builds the -fPIC archives (rapidsnark + GMP) inside a glibc-2.35 container and publishes/updates the release. Trigger by pushing a `rapidsnark-pic-*` tag or via workflow_dispatch. Verified locally (glibc-2.35 container): the produced archives link into a -shared cdylib with rust-lld. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build-pic-archives.yml | 108 +++++++++++++++++++++++ crates/build.rs | 92 ------------------- crates/download_rapidsnark.sh | 33 ++++--- 3 files changed, 127 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/build-pic-archives.yml diff --git a/.github/workflows/build-pic-archives.yml b/.github/workflows/build-pic-archives.yml new file mode 100644 index 0000000..5c7a494 --- /dev/null +++ b/.github/workflows/build-pic-archives.yml @@ -0,0 +1,108 @@ +name: Build PIC archives + +# Builds position-independent (-fPIC) static archives of rapidsnark + GMP for +# glibc-Linux and publishes them as a GitHub release. The upstream iden3 Linux +# archives are non-PIC and built against a newer glibc, so they cannot be linked +# into a shared library (e.g. a downstream cdylib). crates/download_rapidsnark.sh +# consumes the release produced here for the Linux targets. +# +# Trigger by pushing a tag like `rapidsnark-pic-v0.0.8`, or manually via the +# "Run workflow" button (workflow_dispatch). + +on: + workflow_dispatch: + inputs: + rapidsnark_version: + description: "iden3 rapidsnark tag to build" + default: "v0.0.8" + push: + tags: + - "rapidsnark-pic-*" + +permissions: + contents: write + +env: + RAPIDSNARK_VERSION: ${{ github.event.inputs.rapidsnark_version || 'v0.0.8' }} + GMP_VERSION: "6.3.0" + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + slug: rapidsnark-linux-x86_64-pic + # Build inside a glibc-2.35 image so the archives stay compatible with older + # glibc hosts (avoids the __isoc23_* / newer-GLIBCXX symbol requirements). + runs-on: ubuntu-latest + container: ubuntu:22.04 + steps: + - name: Install build dependencies + run: | + apt-get update + apt-get install -y build-essential cmake nasm m4 xz-utils zip git curl ca-certificates libgmp-dev + + - name: Build GMP (static, -fPIC) + run: | + curl -fsSL -o gmp.tar.xz "https://ftpmirror.gnu.org/gmp/gmp-${GMP_VERSION}.tar.xz" + tar xf gmp.tar.xz + cd "gmp-${GMP_VERSION}" + ./configure --enable-static --disable-shared --with-pic --prefix="$PWD/../gmp-install" + make -j"$(nproc)" + make install + + - name: Build rapidsnark (static, -fPIC) + run: | + git clone --depth 1 --branch "$RAPIDSNARK_VERSION" https://github.com/iden3/rapidsnark.git rs + cd rs + git submodule update --init --depth 1 depends/ffiasm depends/json + mkdir bp && cd bp + cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DUSE_ASM=ON \ + -DUSE_OPENMP=OFF \ + -DUSE_LOGGER=ON + make -j"$(nproc)" rapidsnarkStatic fr fq + + - name: Package archives + run: | + NAME="${{ matrix.slug }}-${RAPIDSNARK_VERSION}" + mkdir -p "pkg/$NAME/lib" + cp rs/bp/src/librapidsnark.a rs/bp/src/libfr.a rs/bp/src/libfq.a \ + gmp-install/lib/libgmp.a "pkg/$NAME/lib/" + (cd pkg && zip -r "../$NAME.zip" "$NAME") + echo "--- contents ---" + unzip -l "$NAME.zip" + echo "isoc23 refs (expect 0): $(nm "pkg/$NAME/lib/librapidsnark.a" | grep -c isoc23 || true)" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.slug }} + path: ${{ matrix.slug }}-*.zip + if-no-files-found: error + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: Publish / update release + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="rapidsnark-pic-${RAPIDSNARK_VERSION}" + ls -la dist/ + gh release create "$TAG" --repo "$GITHUB_REPOSITORY" \ + --title "rapidsnark ${RAPIDSNARK_VERSION} (-fPIC Linux archives)" \ + --notes "Position-independent static archives of rapidsnark ${RAPIDSNARK_VERSION} and GMP ${GMP_VERSION}, built on glibc 2.35 for Linux. Consumed by crates/download_rapidsnark.sh for the Linux targets." \ + || echo "Release $TAG already exists; updating assets." + gh release upload "$TAG" dist/*.zip --repo "$GITHUB_REPOSITORY" --clobber diff --git a/crates/build.rs b/crates/build.rs index 7026905..39819cc 100644 --- a/crates/build.rs +++ b/crates/build.rs @@ -4,8 +4,6 @@ use std::path::Path; use std::process::Command; const RAPIDSNARK_DOWNLOAD_SCRIPT: &str = include_str!("./download_rapidsnark.sh"); -const RAPIDSNARK_GIT: &str = "https://github.com/iden3/rapidsnark.git"; -const RAPIDSNARK_TAG: &str = "v0.0.8"; fn main() { let target = env::var("TARGET").unwrap(); @@ -15,16 +13,6 @@ fn main() { // See: https://github.com/zkmopro/chkstk_stub chkstk_stub::build(); - // On glibc-Linux with static linking the prebuilt iden3 archives don't work: - // they are non-PIC (cannot be linked into a downstream cdylib such as the - // node's C bindings) and are built against a newer glibc (undefined - // __isoc23_strtoll/ull on older hosts). Build rapidsnark from source with - // -fPIC against the host toolchain instead, which resolves both problems. - if build_from_source_applies() { - build_rapidsnark_from_source(&out_dir); - return; - } - // Try to list contents of the target directory let rapidsnark_path = Path::new(&out_dir).join(Path::new("rapidsnark")); // If the rapidsnark repo is not downloaded, download it @@ -85,86 +73,6 @@ fn main() { println!("cargo:rustc-link-lib={thread_lib}"); } -fn build_from_source_applies() -> bool { - is_static_rapidsnark() - && env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") - && env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") -} - -/// Build rapidsnark's static archives from source with position-independent code. -/// -/// Requires `git`, `cmake`, `make`, `nasm`, a C++ compiler, and the GMP/libsodium -/// development headers to be available on the build host. GMP is linked from the -/// system as a (PIC) shared library. -fn build_rapidsnark_from_source(out_dir: &str) { - let src = Path::new(out_dir).join("rapidsnark-src"); - if !src.join("CMakeLists.txt").exists() { - // Clean any partial checkout so the clone can succeed. - let _ = fs::remove_dir_all(&src); - run(Command::new("git").args([ - "clone", - "--depth", - "1", - "--branch", - RAPIDSNARK_TAG, - RAPIDSNARK_GIT, - src.to_str().unwrap(), - ])); - } - // Only the field-element codegen (ffiasm) and JSON header are needed to build - // the prover/verifier static libraries. - run(Command::new("git").current_dir(&src).args([ - "submodule", - "update", - "--init", - "--depth", - "1", - "depends/ffiasm", - "depends/json", - ])); - - let build = src.join("build_pic"); - fs::create_dir_all(&build).expect("Failed to create rapidsnark build dir"); - run(Command::new("cmake").current_dir(&build).args([ - "..", - "-DCMAKE_BUILD_TYPE=Release", - "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", - "-DUSE_ASM=ON", - "-DUSE_OPENMP=OFF", - "-DUSE_LOGGER=ON", - ])); - - let jobs = std::thread::available_parallelism() - .map(|n| n.get()) - .unwrap_or(4) - .to_string(); - run(Command::new("make").current_dir(&build).args([ - "-j", - &jobs, - "rapidsnarkStatic", - "fr", - "fq", - ])); - - let lib_dir = build.join("src"); - println!("cargo:rustc-link-search=native={}", lib_dir.display()); - println!("cargo:rustc-link-lib=static=rapidsnark"); - println!("cargo:rustc-link-lib=static=fr"); - println!("cargo:rustc-link-lib=static=fq"); - // GMP is provided by the system as a PIC shared library. - println!("cargo:rustc-link-lib=dylib=gmp"); - println!("cargo:rustc-link-lib=stdc++"); - println!("cargo:rustc-link-lib=pthread"); -} - -fn run(cmd: &mut Command) { - eprintln!("rapidsnark build: running {cmd:?}"); - let status = cmd - .status() - .unwrap_or_else(|e| panic!("Failed to spawn {cmd:?}: {e}")); - assert!(status.success(), "Command failed ({status}): {cmd:?}"); -} - fn is_static_rapidsnark() -> bool { env::var_os("CARGO_FEATURE_STATIC_RAPIDSNARK").is_some() } diff --git a/crates/download_rapidsnark.sh b/crates/download_rapidsnark.sh index dc9950e..d64ee87 100755 --- a/crates/download_rapidsnark.sh +++ b/crates/download_rapidsnark.sh @@ -14,27 +14,32 @@ if [ -z "$TARGET" ]; then exit 1 fi -# Pinned iden3 rapidsnark release. Bump this to update the prebuilt artifacts. +# Pinned rapidsnark release. Bump this to update the prebuilt artifacts. VERSION="v0.0.8" -BASE_URL="https://github.com/iden3/rapidsnark/releases/download/$VERSION" +# Upstream iden3 prebuilt archives (used for macOS / iOS / Android). +IDEN3_BASE="https://github.com/iden3/rapidsnark/releases/download/$VERSION" +# The iden3 Linux archives are non-PIC and built against a newer glibc, so they +# cannot be linked into a shared library (e.g. a downstream cdylib) on the build +# hosts. The Logos fork hosts -fPIC rebuilds of the Linux archives instead. +FORK_BASE="https://github.com/logos-blockchain/logos-blockchain-rust-rapidsnark/releases/download/rapidsnark-pic-$VERSION" BUILD_DIR="$OUT_DIR/rapidsnark" mkdir -p "$BUILD_DIR" arch=$(echo "$TARGET" | cut -d'-' -f1) -# Map the rust target triple to the iden3 release asset slug. +# Map the rust target triple to the release asset slug and its hosting base URL. case "$TARGET" in - x86_64-*-linux-*) asset="rapidsnark-linux-x86_64-$VERSION" ;; - aarch64-*-linux-gnu*) asset="rapidsnark-linux-arm64-$VERSION" ;; - aarch64-linux-android) asset="rapidsnark-android-arm64-$VERSION" ;; - x86_64-linux-android) asset="rapidsnark-android-x86_64-$VERSION" ;; - aarch64-apple-darwin) asset="rapidsnark-macOS-arm64-$VERSION" ;; - x86_64-apple-darwin) asset="rapidsnark-macOS-x86_64-$VERSION" ;; - aarch64-apple-ios-sim|x86_64-apple-ios) asset="rapidsnark-iOS-Simulator-$VERSION" ;; - aarch64-apple-ios) asset="rapidsnark-iOS-$VERSION" ;; + x86_64-*-linux-*) asset="rapidsnark-linux-x86_64-pic-$VERSION"; base_url="$FORK_BASE" ;; + aarch64-*-linux-gnu*) asset="rapidsnark-linux-arm64-pic-$VERSION"; base_url="$FORK_BASE" ;; + aarch64-linux-android) asset="rapidsnark-android-arm64-$VERSION"; base_url="$IDEN3_BASE" ;; + x86_64-linux-android) asset="rapidsnark-android-x86_64-$VERSION"; base_url="$IDEN3_BASE" ;; + aarch64-apple-darwin) asset="rapidsnark-macOS-arm64-$VERSION"; base_url="$IDEN3_BASE" ;; + x86_64-apple-darwin) asset="rapidsnark-macOS-x86_64-$VERSION"; base_url="$IDEN3_BASE" ;; + aarch64-apple-ios-sim|x86_64-apple-ios) asset="rapidsnark-iOS-Simulator-$VERSION"; base_url="$IDEN3_BASE" ;; + aarch64-apple-ios) asset="rapidsnark-iOS-$VERSION"; base_url="$IDEN3_BASE" ;; *) - echo "Unsupported TARGET: $TARGET (no iden3 rapidsnark $VERSION asset mapping)" + echo "Unsupported TARGET: $TARGET (no rapidsnark $VERSION asset mapping)" exit 1 ;; esac @@ -42,8 +47,8 @@ esac zip_file="$BUILD_DIR/$asset.zip" echo "Downloading $asset.zip ..." -if ! curl -fL -o "$zip_file" "$BASE_URL/$asset.zip"; then - echo "Failed to download $BASE_URL/$asset.zip" +if ! curl -fL -o "$zip_file" "$base_url/$asset.zip"; then + echo "Failed to download $base_url/$asset.zip" exit 1 fi