Mopro is a toolkit for ZK app development on mobile. Mopro makes client-side proving on mobile simple.
Clone the repo and run cargo build
or cargo test
.
There are pre-built examples projects for iOS and Android here.
Mopro works by providing a static library and an interface for your app to build proofs. Before you start this tutorial you should have a zkey and wasm file generated by circom.
To get started we'll make a new rust project that builds this library. Run the following commands in your terminal:
mkdir mopro-example
cd mopro-example
cargo init --lib
This will create a new rust project in the current directory. Now we'll add some dependencies to this project. Edit your Cargo.toml
so that it looks like the following:
[package]
name = "mopro-example"
version = "0.1.0"
edition = "2021"
# We're going to build a static library named mopro_bindings
# This library name should not be changed
[lib]
crate-type = ["lib", "cdylib", "staticlib"]
name = "mopro_bindings"
# This is a script used to build the iOS static library
[[bin]]
name = "ios"
# We're going to build support for circom proofs only
[features]
default = ["mopro-ffi/circom"]
[dependencies]
mopro-ffi = { git = "https://github.com/zkmopro/mopro.git" }
uniffi = { version = "0.28", features = ["cli"] }
rust-witness = { git = "https://github.com/vimwitch/rust-witness.git" }
num-bigint = "0.4.0"
[build-dependencies]
rust-witness = { git = "https://github.com/vimwitch/rust-witness.git" }
mopro-ffi = { git = "https://github.com/zkmopro/mopro.git" }
uniffi = { version = "0.28", features = ["build"] }
Now you should copy your wasm and zkey files somewhere in the project folder. For this tutorial we'll assume you placed them in test-vectors/circom
.
Now we need to add 4 rust files. First we'll add build.rs
in the main project folder. This file should contain the following:
fn main() {
// We're going to transpile the wasm witness generators to C
// Change this to where you put your zkeys and wasm files
rust_witness::transpile::transpile_wasm("./test-vectors/circom".to_string());
// This is writing the UDL file which defines the functions exposed
// to your app. We have pre-generated this file for you
// This file must be written to ./src
std::fs::write("./src/mopro.udl", mopro_ffi::app_config::UDL).expect("Failed to write UDL");
// Finally initialize uniffi and build the scaffolding into the
// rust binary
uniffi::generate_scaffolding("./src/mopro.udl").unwrap();
}
Second we'll change the file at ./src/lib.rs
to look like the following:
use mopro_ffi::{app, WtnsFn};
// Here we're generating the C functions for a circuit named
// multiplier3.
// Your circuit name will be the name of the wasm file all lowercase
// with spaces, dashes and underscores removed
//
// e.g.
// multiplier2 -> multiplier2
// keccak_256_256_main -> keccak256256main
// aadhaar-verifier -> aadhaarverifier
rust_witness::witness!(multiplier3);
// Here we're calling a macro exported by uniffi. This macro will
// write some functions and bind them to the uniffi UDL file. These
// functions will invoke the zkey_witness_map function written below.
app!();
// This function defines a mapping between zkey filename and witness
// generator. When you make a proof the zkey filename will be passed to
// this function in order to retrieve the appropriate witness generator.
// Remember, we built the witness generator for the circuit above using
// rust_witness.
fn zkey_witness_map(name: &str) -> Result<WtnsFn, MoproError> {
match name {
"multiplier3_final.zkey" => Ok(multiplier3_witness),
_ => Err(MoproError::CircomError("Unknown circuit name".to_string())),
}
}
Finally we'll add a new file at src/bin/ios.rs
:
fn main() {
// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::ios::build();
}
and another at src/bin/android.rs
:
fn main() {
// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::android::build();
}
Now you're ready to build your static library! You should be able to run either cargo run --bin ios
or cargo run --bin android
to build the corresponding static library.