MoPro Integration Guide
What is MoPro?
MoPro (Mobile Proving) is a framework that brings zero-knowledge proof capabilities to mobile platforms. It provides cross-platform support for iOS, Android, and Web with multiple ZK backends including Circom, and Noir.
In Deimos, MoPro is used to run benchmarks on actual mobile devices, measuring real-world performance of ZK proof generation and verification.
Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Kotlin/Swift │───▶│ Rust FFI │───▶│ ZK Circuits │ │ (Mobile App) │ │ (UniFFI) │ │ (Circom/Noir) │ └─────────────────┘ └──────────────────┘ └─────────────────┘
The architecture consists of three layers:
- Mobile App Layer: Kotlin (Android) or Swift (iOS) UI and benchmarking logic
- FFI Layer: Rust code with UniFFI bindings for type-safe cross-language communication
- Circuit Layer: Compiled ZK circuits (Circom or Noir)
Directory Structure
Each MoPro implementation follows a consistent pattern:
moPro/
├── mopro-sha256/ # SHA-256 implementation
│ ├── src/
│ │ └── lib.rs # Rust library with ZK proof functions
│ ├── android/ # Android application
│ │ └── app/src/main/java/ # Kotlin integration code
│ ├── ios/ # iOS application (if created)
│ ├── MoproAndroidBindings/ # Generated FFI bindings
│ ├── test-vectors/ # Test data for circuits
│ ├── Cargo.toml # Rust dependencies
│ └── README.md # Setup instructions
│
├── mopro-keccack256/ # Keccak-256 implementation
│ └── (same structure)
│
└── mopro-example-app/ # Example implementation
└── (same structure)Key directories in each MoPro implementation:
- src/: Rust library with ZK proof functions
- android/: Android application with Kotlin integration
- MoproAndroidBindings/: Generated FFI bindings
- test-vectors/: Test data for circuits
- Cargo.toml: Rust dependencies
Setting Up MoPro
Prerequisites
- Rust toolchain installed
- MoPro CLI tool installed
- Android Studio (for Android) or Xcode (for iOS)
- Compiled circuit files (r1cs, wasm, zkey)
Install MoPro CLI
git clone https://github.com/zkmopro/mopro
cd mopro/cli
cargo install --path .Initialize Project
cd benchmarking-suite/moPro/mopro-sha256
# Initialize MoPro configuration
mopro init
# This creates mopro-config.toml with project settingsBuild Native Bindings
# Build bindings for all configured platforms
mopro build
# This compiles Rust code and generates FFI bindingsCreate Platform Templates
# Generate platform-specific templates
mopro create
# This creates android/, ios/, or web/ directories with starter codeUpdate Bindings
# After making changes to Rust code
mopro update
# This refreshes bindings in all platform directoriesRust Implementation
Basic Structure (src/lib.rs)
use mopro_ffi::app; // Define your app with MoPro macro app!(); // The macro automatically generates these functions: // - generate_circom_proof() // - verify_circom_proof() // - generate_halo2_proof() // - verify_halo2_proof() // - generate_noir_proof() // - verify_noir_proof()
Custom Functions
You can add custom functions using the #[uniffi::export] attribute:
#[uniffi::export]
fn mopro_hello_world() -> String {
"Hello, World!".to_string()
}
#[uniffi::export]
fn custom_benchmark_function(input: String) -> Result<String, String> {
// Your custom logic here
Ok("Result".to_string())
}Cargo Configuration
[package]
name = "mopro-sha256"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["lib", "cdylib", "staticlib"]
[dependencies]
mopro-ffi = { version = "0.1", features = ["circom"] }
uniffi = "0.29.0"
[features]
default = ["mopro-ffi/circom"]
# Optional: "mopro-ffi/halo2", "mopro-ffi/noir"Android Integration
Opening the Project
# Open in Android Studio
open android -a Android\ Studio
# Or build from command line
cd android
./gradlew buildKotlin Usage Example
import uniffi.mopro.generateCircomProof
import uniffi.mopro.verifyCircomProof
import uniffi.mopro.ProofLib
// Prepare input JSON
val input = """
{
"in": [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
}
""".trimIndent()
// Generate proof
val proofResult = generateCircomProof(
zkeyPath = "/path/to/circuit.zkey",
circuitInputs = input,
proofLib = ProofLib.ARKWORKS
)
// Verify proof
val isValid = verifyCircomProof(
zkeyPath = "/path/to/circuit.zkey",
proof = proofResult,
proofLib = ProofLib.ARKWORKS
)
println("Proof valid: $isValid")Benchmarking Implementation
// Measure proving time
val startTime = System.currentTimeMillis()
val proof = generateCircomProof(zkeyPath, input, ProofLib.ARKWORKS)
val provingTime = System.currentTimeMillis() - startTime
// Measure memory usage
val runtime = Runtime.getRuntime()
val memoryBefore = runtime.totalMemory() - runtime.freeMemory()
// ... generate proof ...
val memoryAfter = runtime.totalMemory() - runtime.freeMemory()
val memoryUsed = memoryAfter - memoryBefore
// Store results
val benchmarkResult = BenchmarkResult(
provingTime = provingTime,
verificationTime = verificationTime,
memoryUsed = memoryUsed,
proofSize = proof.length
)iOS Integration
Opening the Project
# Open in Xcode
open ios/MoproApp.xcodeproj
# Or build from command line
cd ios
xcodebuild -project MoproApp.xcodeproj -scheme MoproApp buildSwift Usage Example
import mopro
// Prepare input
let input = """
{
"in": [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
}
"""
// Generate proof
let proof = try generateCircomProof(
zkeyPath: "/path/to/circuit.zkey",
circuitInputs: input,
proofLib: .arkworks
)
// Verify proof
let isValid = try verifyCircomProof(
zkeyPath: "/path/to/circuit.zkey",
proof: proof,
proofLib: .arkworks
)
print("Proof valid: \(isValid)")Benchmarking Features
Metrics Collected
- Proving Time: Duration of proof generation in milliseconds
- Verification Time: Duration of proof verification in milliseconds
- Memory Usage: Peak RAM consumption during operations in MB
- Proof Size: Size of generated proof in bytes
- Success Rate: Percentage of successful proof generations
- Device Information: Model, OS version, CPU, RAM
Single Run Testing
Test individual proof generation with detailed metrics display. Useful for debugging and understanding performance characteristics.
Batch Processing
Run multiple proof generations automatically and collect statistics:
- Average proving time
- Minimum and maximum times
- Standard deviation
- Success rate
- Memory usage patterns
Framework Comparison
Compare performance across different proving backends (Arkworks, Rapidsnark) side-by-side.
Real-time Monitoring
Live display of performance metrics during proof generation with progress indicators.
Circuit Integration
Adding Circuit Files
Place your compiled circuit files in the appropriate directory:
mopro-sha256/
├── circuits/
│ ├── sha256.wasm # Witness generator
│ ├── sha256.zkey # Proving key
│ └── verification_key.json # Verification key
└── test-vectors/
└── input.json # Test inputsConfiguring Circuit Paths
Update your app to reference the circuit files. On Android, place files in assets/ directory. On iOS, add files to the Xcode project bundle.
Loading Circuit Files
// Android - Load from assets
val zkeyPath = context.assets.open("sha256.zkey").use { input ->
val file = File(context.filesDir, "sha256.zkey")
file.outputStream().use { output ->
input.copyTo(output)
}
file.absolutePath
}
// iOS - Load from bundle
guard let zkeyPath = Bundle.main.path(
forResource: "sha256",
ofType: "zkey"
) else {
fatalError("Circuit file not found")
}Testing
Rust Tests
# Run all tests
cargo test
# Run specific test
cargo test test_circom
# Run with verbose output
cargo test -- --nocapture
# Run tests with release optimizations
cargo test --releaseAndroid Tests
cd android
# Run unit tests
./gradlew test
# Run instrumented tests on device
./gradlew connectedAndroidTestiOS Tests
cd ios
# Run tests
xcodebuild test -project MoproApp.xcodeproj -scheme MoproAppPerformance Optimization
Rust Optimization
- Always build with
--releaseflag for production - Enable LTO (Link Time Optimization) in Cargo.toml
- Use appropriate optimization level (opt-level = 3)
- Profile with cargo flamegraph to identify bottlenecks
Mobile Optimization
- Test on physical devices, not emulators
- Disable debug logging in production builds
- Use background threads for proof generation
- Implement proper memory management
- Cache circuit files to avoid repeated loading
Circuit Optimization
- Use ZK-friendly hash functions when possible (Poseidon, MiMC)
- Minimize constraint count in circuits
- Use appropriate Powers of Tau size (not too large)
- Consider using Rapidsnark for faster proving
Common Issues
UniFFI Version Mismatch
Issue: Build fails with UniFFI errors
Solution: Ensure you're using UniFFI version 0.29.0 as specified in Cargo.toml. This version is pinned for compatibility.
Circuit File Not Found
Issue: App crashes when trying to load circuit files
Solution: Verify circuit files are properly included in assets (Android) or bundle (iOS). Check file paths are correct.
Out of Memory
Issue: App crashes during proof generation
Solution: Large circuits may exceed mobile device memory. Use smaller circuits or implement memory-efficient proving strategies. Test on devices with adequate RAM.
Slow Performance
Issue: Proof generation takes too long
Solution: This is expected for large circuits on mobile. Use release builds, test on physical devices, and consider using ZK-friendly hash functions.
Use Cases
Privacy-Preserving Authentication
- Prove knowledge of password without revealing it
- Zero-knowledge identity verification
- Private credential systems
Blockchain Integration
- Mobile wallet proof generation
- Layer 2 scaling solutions
- Private transaction validation
Performance Research
- Mobile ZK proof benchmarking
- Cross-platform performance analysis
- Resource optimization studies
Resources
- MoPro Documentation: https://zkmopro.org
- MoPro GitHub: https://github.com/zkmopro/mopro
- UniFFI Guide: https://mozilla.github.io/uniffi-rs
- Circom Documentation: https://docs.circom.io
- Community Telegram: @zkmopro
- Community Twitter: @zkmopro
Example Projects
Check out the example implementations in the repository:
- mopro-sha256: SHA-256 hash benchmarking app
- mopro-keccack256: Keccak-256 hash benchmarking app
- mopro-example-app: Basic example with all features