From a2ccb0cdc03f89234d919ef0407d42da9fe3fc7a Mon Sep 17 00:00:00 2001 From: Moven T <60170228+moven0831@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:12:19 +0800 Subject: [PATCH 1/3] fix: replace dead ci-keys.zkmopro.org links (#725) * fix(docs): replace dead ci-keys.zkmopro.org download links ci-keys.zkmopro.org stopped resolving, so following the setup guide or the EthTaipei workshop hit "curl: (6) Could not resolve host". Point at the fixtures committed in this repo and in rust-rapidsnark instead, pinned to a tag or commit so they cannot drift. The versioned 0.1 and 0.2 copies carried the same dead host, so they are updated too. keccak256.json becomes circuit-registry's keccak256/input.json, which is byte-identical to the input the workshop already pastes inline. Also note that rapidsnark has no x86_64-apple-ios prebuilt, since rust-rapidsnark 0.1.4 dropped that target, and building it now needs RAPIDSNARK_LIB_DIR. Finally, cover the rapidsnark feature in cli_template_tests. Scaffolded projects leave it off, so nothing in CI compiled rust-rapidsnark or fetched its prebuilt libraries, which is why a download endpoint could break for users without CI noticing. Runs on both macos-latest and ubuntu-latest. Refs #724 Co-Authored-By: Claude Opus 5 (1M context) * fix(ci): point rapidsnark guard error at circom.rs The grep guard's error message named write_toml.rs, which only emits the # CIRCOM_DEPENDENCIES marker. The circom-prover line the sed pattern matches comes from the DEPENDENCIES const in cli/src/init/circom.rs. Co-Authored-By: Claude Fable 5 --- .github/workflows/build-and-test.yml | 12 ++++++++++++ docs/blog/2025-03-27-ethtaipei-workshop.md | 6 +++--- docs/docs/adapters/circom.md | 1 + docs/docs/sdk/react-native.md | 2 +- docs/docs/setup/rust-setup.md | 4 ++-- .../version-0.1/setup/react-native-setup.md | 2 +- docs/versioned_docs/version-0.2/setup/rust-setup.md | 4 ++-- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 1cf1305a8..66260e922 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -132,6 +132,18 @@ jobs: export CLASSPATH="$PWD/jna-5.13.0.jar" cargo test --all --all-features + - name: Build with the rapidsnark feature + if: matrix.adapter == 'circom' + working-directory: ${{ runner.temp }}/mopro-example-circom + run: | + sed -i.bak 's/^circom-prover = "0.1"$/circom-prover = { version = "0.1", features = ["rapidsnark"] }/' Cargo.toml + rm Cargo.toml.bak + grep -q 'features = \["rapidsnark"\]' Cargo.toml || { + echo "::error::failed to enable the rapidsnark feature; the circom-prover entry in cli/src/init/circom.rs may have changed" + exit 1 + } + cargo build + cli_build_ios_prepare: needs: cli_build strategy: diff --git a/docs/blog/2025-03-27-ethtaipei-workshop.md b/docs/blog/2025-03-27-ethtaipei-workshop.md index 2dd5afd03..de483fc82 100644 --- a/docs/blog/2025-03-27-ethtaipei-workshop.md +++ b/docs/blog/2025-03-27-ethtaipei-workshop.md @@ -236,8 +236,8 @@ Alternatively, you can watch this video to see how to run the app. This section explains how to update circuits with alternative witness generators and corresponding zkey files. We use the [Keccak256 circuit](https://github.com/zkmopro/circuit-registry/blob/main/keccak256/keccak256_256_test.circom) as a reference example here. 1. Add wasm and zkey file in the `test-vectors/circom` folder - - wasm: https://ci-keys.zkmopro.org/keccak256_256_test.wasm - - zkey: https://ci-keys.zkmopro.org/keccak256_256_test_final.zkey + - wasm: https://raw.githubusercontent.com/zkmopro/rust-rapidsnark/v0.1.4/tests/test-vectors/keccak256_256_test.wasm + - zkey: https://raw.githubusercontent.com/zkmopro/rust-rapidsnark/v0.1.4/tests/test-vectors/keccak256_256_test_final.zkey 2. In `src/lib.rs` file, update the circuit's witness generator function definition. ```diff @@ -315,7 +315,7 @@ This section explains how to update circuits with alternative witness generators + val zkeyPath = getFilePathFromAssets("keccak256_256_test_final.zkey") ``` -- Update circuit inputs: https://ci-keys.zkmopro.org/keccak256.json +- Update circuit inputs: https://raw.githubusercontent.com/zkmopro/circuit-registry/aab2ee1290ecd5d2b7dff985520bd1f3ce8a9750/keccak256/input.json - **iOS:**
diff --git a/docs/docs/adapters/circom.md b/docs/docs/adapters/circom.md index 2ead42cce..5212309ed 100644 --- a/docs/docs/adapters/circom.md +++ b/docs/docs/adapters/circom.md @@ -302,6 +302,7 @@ Mopro now supports 2 Circom provers. You can find more information in [the blog - `rust-rapidsnark` is based on the original C++ implementation of [rapidsnark](https://github.com/iden3/rapidsnark), with the binary wrapped and integrated in Rust. - Activate `rapidsnark` Feature for both `[dependencies]` and `[build-dependencies]` +- No prebuilt covers `x86_64-apple-ios`. To build for the Intel Mac simulator, point `RAPIDSNARK_LIB_DIR` at your own build of those libraries. ```toml [dependencies] diff --git a/docs/docs/sdk/react-native.md b/docs/docs/sdk/react-native.md index 012bf21fb..47b454b34 100644 --- a/docs/docs/sdk/react-native.md +++ b/docs/docs/sdk/react-native.md @@ -117,7 +117,7 @@ To learn how to read a .zkey file from an app, please refer to the [`loadAssets` :::warning The default bindings are built specifically for the `multiplier2` circom circuit. If you'd like to update the circuit or switch to a different proving scheme, please refer to the [How to Build the Package](#how-to-build-the-package) section.
Circuit source code: https://github.com/zkmopro/circuit-registry/tree/main/multiplier2
-Example .zkey file for the circuit: http://ci-keys.zkmopro.org/multiplier2_final.zkey
+Example .zkey file for the circuit: https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2_final.zkey
::: And in `index.js`, for example, replace this with diff --git a/docs/docs/setup/rust-setup.md b/docs/docs/setup/rust-setup.md index d7ab82b9f..6e82888d9 100644 --- a/docs/docs/setup/rust-setup.md +++ b/docs/docs/setup/rust-setup.md @@ -171,8 +171,8 @@ fn main() { Learn more about `.wasm` files in [Circom documentation](https://docs.circom.io/getting-started/compiling-circuits/).
Here are the example WASM and Zkey files to be downloaded. -- http://ci-keys.zkmopro.org/multiplier2.wasm -- http://ci-keys.zkmopro.org/multiplier2_final.zkey +- [multiplier2.wasm](https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2.wasm) +- [multiplier2_final.zkey](https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2_final.zkey) ::: diff --git a/docs/versioned_docs/version-0.1/setup/react-native-setup.md b/docs/versioned_docs/version-0.1/setup/react-native-setup.md index a198436ad..ef9b4b8c7 100644 --- a/docs/versioned_docs/version-0.1/setup/react-native-setup.md +++ b/docs/versioned_docs/version-0.1/setup/react-native-setup.md @@ -487,7 +487,7 @@ export default function HomeScreen() { const [proof, setProof] = useState(""); async function genProof(): Promise { const asset = Asset.fromURI( - "https://ci-keys.zkmopro.org/multiplier2_final.zkey" + "https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2_final.zkey" ); const newFileName = "multiplier2_final.zkey"; const newFilePath = `${FileSystem.documentDirectory}${newFileName}`; diff --git a/docs/versioned_docs/version-0.2/setup/rust-setup.md b/docs/versioned_docs/version-0.2/setup/rust-setup.md index b4c2cd597..0f805aef3 100644 --- a/docs/versioned_docs/version-0.2/setup/rust-setup.md +++ b/docs/versioned_docs/version-0.2/setup/rust-setup.md @@ -154,8 +154,8 @@ fn main() { Learn more about `.wasm` files in [Circom documentation](https://docs.circom.io/getting-started/compiling-circuits/).
Here are the example WASM and Zkey files to be downloaded. -- http://ci-keys.zkmopro.org/multiplier2.wasm -- http://ci-keys.zkmopro.org/multiplier2_final.zkey +- [multiplier2.wasm](https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2.wasm) +- [multiplier2_final.zkey](https://raw.githubusercontent.com/zkmopro/mopro/mopro-cli-v0.3.7/cli/src/template/init/test-vectors/circom/multiplier2_final.zkey) ::: From f3a1c6546348db33491af69a1d0a702e2dcdf206 Mon Sep 17 00:00:00 2001 From: bl0n <63465718+bl0nnn@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:28:15 +0200 Subject: [PATCH 2/3] fix: remove unused ubrn WASM scaffolding (#727) * fix: remove unused ubrn WASM scaffolding * correctly formatted rust code --- cli/src/build/react_native_noir.rs | 3 +++ cli/src/create/react_native.rs | 22 ++++------------------ mopro-ffi/src/app_config/react_native.rs | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/cli/src/build/react_native_noir.rs b/cli/src/build/react_native_noir.rs index ef465287b..c1c5a7e6f 100644 --- a/cli/src/build/react_native_noir.rs +++ b/cli/src/build/react_native_noir.rs @@ -18,6 +18,7 @@ use anyhow::Context; use mopro_ffi::app_config::android::AndroidBindingsParams; use mopro_ffi::app_config::constants::Mode; use mopro_ffi::app_config::project_name_from_toml; +use mopro_ffi::app_config::react_native::remove_unrequested_wasm_stubs; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -77,6 +78,8 @@ pub fn build( bindgen_lib.display() ); } + + remove_unrequested_wasm_stubs(bindings_dir)?; Ok(()) } diff --git a/cli/src/create/react_native.rs b/cli/src/create/react_native.rs index d53d112a6..60bdb4ae9 100644 --- a/cli/src/create/react_native.rs +++ b/cli/src/create/react_native.rs @@ -7,7 +7,9 @@ use crate::style::print_green_bold; use anyhow::{Context, Error, Result}; use mopro_ffi::app_config::constants::{REACT_NATIVE_APP_DIR, REACT_NATIVE_BINDINGS_DIR}; -use mopro_ffi::app_config::react_native::patch_gradle_properties_architectures; +use mopro_ffi::app_config::react_native::{ + patch_gradle_properties_architectures, remove_unrequested_wasm_stubs, +}; use std::{fs, path::PathBuf}; pub struct ReactNative; @@ -39,7 +41,7 @@ impl Create for ReactNative { react_native_bindings_dir.as_ref().unwrap(), &mopro_module_dir, )?; - remove_stale_web_entrypoint(&mopro_module_dir)?; + remove_unrequested_wasm_stubs(&mopro_module_dir)?; let assets_dir = target_dir.join("assets/keys"); fs::remove_dir_all(&assets_dir)?; @@ -87,19 +89,3 @@ impl Create for ReactNative { print_footer_message(); } } - -/// The downloaded `zkmopro/react-native-app` scaffold ships a static -/// `src/index.web.ts` (for optional web/wasm support) that imports from -/// `./generated/wasm-bindgen/index.js` and `index_bg.wasm`. `mopro build` never -/// generates that `generated/wasm-bindgen` directory — React Native builds only -/// target iOS/Android — so the file is always a dangling reference. Since -/// `copy_dir` only overwrites files present in the built bindings dir, it can't -/// remove this pre-existing one; left in place, it breaks `npm install`'s -/// `prepare: bob build` step (`tsc` fails to resolve the missing module). -fn remove_stale_web_entrypoint(mopro_module_dir: &std::path::Path) -> Result<()> { - let index_web_ts = mopro_module_dir.join("src").join("index.web.ts"); - if index_web_ts.exists() { - fs::remove_file(&index_web_ts)?; - } - Ok(()) -} diff --git a/mopro-ffi/src/app_config/react_native.rs b/mopro-ffi/src/app_config/react_native.rs index 2ff51f146..8c22c4ebe 100644 --- a/mopro-ffi/src/app_config/react_native.rs +++ b/mopro-ffi/src/app_config/react_native.rs @@ -318,3 +318,23 @@ pub fn patch_gradle_properties_architectures( .with_context(|| format!("Failed to patch {:?}", gradle_properties_path))?; Ok(()) } + +/// Remove leftover WASM scaffolding that `uniffi-bindgen-react-native generate all` +/// writes into the React Native bindings dir (`src/index.web.ts` and `rust_modules/wasm/`). +/// +/// Mopro's React Native path only targets iOS/Android. `generate all` with no +/// platform still emits ubrn's web templates, but never runs wasm-pack, so +/// `src/generated/wasm-bindgen/` is missing. Left in place, `src/index.web.ts` +/// is a dangling import and `bob build` / `tsc` fails. +pub fn remove_unrequested_wasm_stubs(bindings_dir: &Path) -> anyhow::Result<()> { + let index_web_file = bindings_dir.join("src").join("index.web.ts"); + if index_web_file.exists() { + fs::remove_file(&index_web_file)?; + } + + let wasm_crate = bindings_dir.join("rust_modules").join("wasm"); + if wasm_crate.exists() { + fs::remove_dir_all(&wasm_crate)?; + } + Ok(()) +} From 1dd120bdc2c9b3c85774601fb3846e64da6b9ff1 Mon Sep 17 00:00:00 2001 From: bl0n <63465718+bl0nnn@users.noreply.github.com> Date: Sat, 5 Sep 2026 04:21:19 +0200 Subject: [PATCH 3/3] Fix: keep all Android ABIs in jniLibs for Noir React Native (#726) --- cli/src/build/react_native_noir.rs | 79 ++++++++++++++++++++---- mopro-ffi/src/app_config/react_native.rs | 2 +- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/cli/src/build/react_native_noir.rs b/cli/src/build/react_native_noir.rs index c1c5a7e6f..edbf2870f 100644 --- a/cli/src/build/react_native_noir.rs +++ b/cli/src/build/react_native_noir.rs @@ -18,7 +18,7 @@ use anyhow::Context; use mopro_ffi::app_config::android::AndroidBindingsParams; use mopro_ffi::app_config::constants::Mode; use mopro_ffi::app_config::project_name_from_toml; -use mopro_ffi::app_config::react_native::remove_unrequested_wasm_stubs; +use mopro_ffi::app_config::react_native::{android_abi_for_triple, remove_unrequested_wasm_stubs}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -34,20 +34,44 @@ pub fn build( mode: Mode, params: &AndroidBindingsParams, ) -> anyhow::Result<()> { + let bindgen_arch = android_arch_strings + .first() + .context("No Android architectures provided for binding generation")?; + patch_ubrn_config_for_noir(bindings_dir, params)?; + let lib_name = format!( + "lib{}.so", + project_name_from_toml(project_dir) + .context("Failed to get project name from Cargo.toml")? + ); + + let target_dir = project_dir.join("target"); + let jni_libs = bindings_dir.join("android/src/main/jniLibs"); + + // `ubrn build android` wipes jniLibs on every invocation, and Noir needs one + // invocation per arch (each has its own BB_LIB_DIR and Zig linker), so the + // last arch would be the only survivor. --no-jnilibs avoids this. `jni_libs_next` + // is used to install each arch's library from target dir before replacing live + // jniLibs dir with it, creating a single tree. + let jni_libs_next = bindings_dir.join("android/src/main/jniLibs_next"); + + if jni_libs_next.exists() { + fs::remove_dir_all(&jni_libs_next).context("Failed to clear jniLibs staging")?; + } + for arch in android_arch_strings { let env = params .arch_overrides .get(arch) .map(|config| config.extra_env.clone()) .unwrap_or_default(); - build_android_once(mode, arch, bindings_dir, &env)?; + build_android_once(mode, arch, bindings_dir, &target_dir, &env)?; + install_jni_lib(arch, mode, &target_dir, &jni_libs_next, &lib_name)?; } - let bindgen_arch = android_arch_strings - .first() - .context("No Android architectures provided for binding generation")?; + publish_jni_libs(&jni_libs_next, &jni_libs)?; + let bindgen_env = params .arch_overrides .get(bindgen_arch) @@ -87,6 +111,7 @@ fn build_android_once( mode: Mode, target_string: &str, bindings_dir: &Path, + target_dir: &Path, env: &[(String, String)], ) -> anyhow::Result<()> { let mut args = vec![ @@ -94,6 +119,7 @@ fn build_android_once( "android".to_string(), "--targets".to_string(), target_string.to_string(), + "--no-jniLibs".to_string(), ]; if mode == Mode::Release { args.push("--release".to_string()); @@ -104,6 +130,8 @@ fn build_android_once( for (key, value) in env { cmd.env(key, value); } + cmd.env("CARGO_TARGET_DIR", target_dir); + let status = cmd.status().expect("failed to build react native bindings"); if !status.success() { anyhow::bail!("Failed to build react native bindings for {target_string}"); @@ -141,7 +169,7 @@ fn build_ndk_linked_lib_for_bindgen( for (key, value) in extra_env { cmd.env(key, value); } - cmd.env("CARGO_BUILD_TARGET_DIR", &bindgen_target) + cmd.env("CARGO_TARGET_DIR", &bindgen_target) .env("CARGO_BUILD_TARGET", arch) .env("CARGO_NDK_OUTPUT_PATH", bindgen_target.join("jniLibs")); @@ -152,11 +180,7 @@ fn build_ndk_linked_lib_for_bindgen( anyhow::bail!("cargo ndk build (bindgen lib) failed for {arch}"); } - let profile_dir = if mode == Mode::Release { - "release" - } else { - "debug" - }; + let profile_dir = mode.as_str(); let out_lib_path = bindgen_target.join(arch).join(profile_dir).join(&lib_name); if !out_lib_path.exists() { anyhow::bail!( @@ -220,3 +244,36 @@ fn patch_ubrn_config_for_noir( fs::write(&config_path, contents).context("Failed to write ubrn.config.yaml")?; Ok(()) } + +/// Copy the built library into the temporary sibling directory `jniLibs_next/`. +fn install_jni_lib( + arch: &str, + mode: Mode, + target_dir: &Path, + jni_libs: &Path, + lib_name: &str, +) -> anyhow::Result<()> { + let abi = android_abi_for_triple(arch) + .with_context(|| format!("No Android ABI known for target triple {arch}"))?; + let src = target_dir.join(arch).join(mode.as_str()).join(lib_name); + let dst_dir = jni_libs.join(abi); + + fs::create_dir_all(&dst_dir).context("Failed to create jniLibs directory")?; + fs::copy(&src, dst_dir.join(lib_name)).with_context(|| { + format!( + "Failed to copy {} into {}", + src.display(), + dst_dir.display() + ) + })?; + Ok(()) +} + +///publish the temporary sibling directory `jniLibs_next/` to the live `jniLibs/` directory. +fn publish_jni_libs(staging: &Path, live: &Path) -> anyhow::Result<()> { + if live.exists() { + fs::remove_dir_all(live).context("Failed to replace jniLibs")?; + } + fs::rename(staging, live).context("Failed to publish jniLibs")?; + Ok(()) +} diff --git a/mopro-ffi/src/app_config/react_native.rs b/mopro-ffi/src/app_config/react_native.rs index 8c22c4ebe..d63e07f24 100644 --- a/mopro-ffi/src/app_config/react_native.rs +++ b/mopro-ffi/src/app_config/react_native.rs @@ -243,7 +243,7 @@ get_filename_component(UNIFFI_BINDGEN_PATH \"${UNIFFI_BINDGEN_PATH}\" DIRECTORY) Ok(()) } -fn android_abi_for_triple(triple: &str) -> Option<&'static str> { +pub fn android_abi_for_triple(triple: &str) -> Option<&'static str> { match triple { "aarch64-linux-android" => Some(ARCH_ARM_64_V8), "armv7-linux-androideabi" => Some(ARCH_ARM_V7_ABI),