removing mopro dirs

This commit is contained in:
0xturboblitz
2024-04-05 18:51:10 -07:00
parent ca908a0387
commit 9d4782bf4d
33 changed files with 0 additions and 3346 deletions

View File

@@ -1,14 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

View File

@@ -1,32 +0,0 @@
[package]
name = "ark-zkey"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "arkzkey-util"
path = "src/bin/arkzkey_util.rs"
# XXX: Shouldn't be necessary, but this way we stay consistent with wasmer version and fix
# error[E0432]: unresolved import `wasmer` error
# (likely due to other packages)
[patch.crates-io]
# NOTE: Forked wasmer to work around memory limits
# See https://github.com/wasmerio/wasmer/commit/09c7070
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
[dependencies]
color-eyre = "0.6"
memmap2 = "0.9"
flame = "0.2"
flamer = "0.5"
ark-serialize = { version = "=0.4.1", features = ["derive"] }
ark-bn254 = { version = "=0.4.0" }
ark-groth16 = { version = "=0.4.0" }
ark-circom = { git = "https://github.com/arkworks-rs/circom-compat.git" }
ark-relations = { version = "=0.4.0" }
ark-ff = { version = "=0.4.1" }
ark-ec = { version = "=0.4.1" }

View File

@@ -1,52 +0,0 @@
# ark-zkey
Library to read `zkey` faster by serializing to `arkworks` friendly format.
See https://github.com/oskarth/mopro/issues/25 for context.
## How to use
Run the following to convert a `zkey` to an `arkzkey`. This should be done as a pre-processing step.
`cargo run --bin arkzkey-util --release -- ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.zkey`
This will generate and place an `arkzkey` file in the same directory as the original zkey.
You can also install it locally:
`cargo install --bin arkzkey-util --path . --release`
## Tests
```
cargo test multiplier2 --release -- --nocapture
cargo test keccak256 --release -- --nocapture
cargo test rsa --release -- --nocapture
```
## Benchmark (Keccak)
`cargo test keccak256 --release -- --nocapture`
```
[build] Processing zkey data...
test tests::test_keccak256_serialization_deserialization has been running for over 60 seconds
[build]Time to process zkey data: 158.753181958s
[build] Serializing proving key and constraint matrices
[build] Time to serialize proving key and constraint matrices: 42ns
[build] Writing arkzkey to: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
[build] Time to write arkzkey: 16.204274125s
Reading arkzkey from: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
Time to open arkzkey file: 51.75µs
Time to mmap arkzkey: 17.25µs
Time to deserialize proving key: 18.323550083s
Time to deserialize matrices: 46.935792ms
Time to read arkzkey: 18.3730695s
test tests::test_keccak256_serialization_deserialization ... ok
```
Vs naive:
`[build] Time to process zkey data: 158.753181958s`
**Result: 18s vs 158s**

View File

@@ -1,36 +0,0 @@
use std::env;
use std::path::{Path, PathBuf};
extern crate ark_zkey;
use ark_zkey::{convert_zkey, read_proving_key_and_matrices_from_zkey};
fn main() -> color_eyre::eyre::Result<()> {
color_eyre::install()?;
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: zkey_to_arkzkey <path_to_zkey_file>");
std::process::exit(1);
}
let zkey_path = &args[1];
let (proving_key, constraint_matrices) = read_proving_key_and_matrices_from_zkey(zkey_path)?;
let arkzkey_path = get_arkzkey_path(zkey_path);
let arkzkey_path_str = arkzkey_path
.to_str()
.ok_or_else(|| color_eyre::eyre::eyre!("Failed to convert arkzkey path to string"))?;
convert_zkey(proving_key, constraint_matrices, &arkzkey_path_str)?;
println!("Converted zkey file saved to: {}", arkzkey_path.display());
Ok(())
}
fn get_arkzkey_path(zkey_path: &str) -> PathBuf {
let path = Path::new(zkey_path);
let mut arkzkey_path = path.to_path_buf();
arkzkey_path.set_extension("arkzkey");
arkzkey_path
}

View File

@@ -1,247 +0,0 @@
use ark_bn254::{Bn254, Fr};
use ark_circom::read_zkey;
//use ark_ec::pairing::Pairing;
use ark_ff::Field;
use ark_groth16::ProvingKey;
//use ark_groth16::VerifyingKey;
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use color_eyre::eyre::{Result, WrapErr};
use memmap2::Mmap;
use std::fs::File;
//use std::io::Cursor;
//use std::io::{Read,self};
use std::io::BufReader;
use std::path::PathBuf;
use std::time::Instant;
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableMatrix<F: Field> {
pub data: Vec<Vec<(F, usize)>>,
}
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableConstraintMatrices<F: Field> {
pub num_instance_variables: usize,
pub num_witness_variables: usize,
pub num_constraints: usize,
pub a_num_non_zero: usize,
pub b_num_non_zero: usize,
pub c_num_non_zero: usize,
pub a: SerializableMatrix<F>,
pub b: SerializableMatrix<F>,
pub c: SerializableMatrix<F>,
}
impl<F: Field> From<Vec<Vec<(F, usize)>>> for SerializableMatrix<F> {
fn from(matrix: Vec<Vec<(F, usize)>>) -> Self {
SerializableMatrix { data: matrix }
}
}
impl<F: Field> From<SerializableMatrix<F>> for Vec<Vec<(F, usize)>> {
fn from(serializable_matrix: SerializableMatrix<F>) -> Self {
serializable_matrix.data
}
}
pub fn serialize_proving_key(pk: &SerializableProvingKey) -> Vec<u8> {
let mut serialized_data = Vec::new();
pk.serialize_compressed(&mut serialized_data)
.expect("Serialization failed");
serialized_data
}
pub fn deserialize_proving_key(data: Vec<u8>) -> SerializableProvingKey {
SerializableProvingKey::deserialize_compressed_unchecked(&mut &data[..])
.expect("Deserialization failed")
}
pub fn read_arkzkey(
arkzkey_path: &str,
) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {
let now = std::time::Instant::now();
let arkzkey_file_path = PathBuf::from(arkzkey_path);
let arkzkey_file = File::open(arkzkey_file_path).wrap_err("Failed to open arkzkey file")?;
println!("Time to open arkzkey file: {:?}", now.elapsed());
//let mut buf_reader = BufReader::new(arkzkey_file);
// Using mmap
let now = std::time::Instant::now();
let mmap = unsafe { Mmap::map(&arkzkey_file)? };
let mut cursor = std::io::Cursor::new(mmap);
println!("Time to mmap arkzkey: {:?}", now.elapsed());
// Was &mut buf_reader
let now = std::time::Instant::now();
let proving_key = SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize proving key")?;
println!("Time to deserialize proving key: {:?}", now.elapsed());
let now = std::time::Instant::now();
let constraint_matrices =
SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize constraint matrices")?;
println!("Time to deserialize matrices: {:?}", now.elapsed());
Ok((proving_key, constraint_matrices))
}
// TODO: Return ProvingKey<Bn254>, ConstraintMatrices<Fr>?
pub fn read_arkzkey_from_bytes(
arkzkey_bytes: &[u8],
) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
let mut cursor = std::io::Cursor::new(arkzkey_bytes);
let now = std::time::Instant::now();
let serialized_proving_key =
SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize proving key")?;
println!("Time to deserialize proving key: {:?}", now.elapsed());
let now = std::time::Instant::now();
let serialized_constraint_matrices =
SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize constraint matrices")?;
println!("Time to deserialize matrices: {:?}", now.elapsed());
// Get on right form for API
let proving_key: ProvingKey<Bn254> = serialized_proving_key.0;
let constraint_matrices: ConstraintMatrices<Fr> = ConstraintMatrices {
num_instance_variables: serialized_constraint_matrices.num_instance_variables,
num_witness_variables: serialized_constraint_matrices.num_witness_variables,
num_constraints: serialized_constraint_matrices.num_constraints,
a_num_non_zero: serialized_constraint_matrices.a_num_non_zero,
b_num_non_zero: serialized_constraint_matrices.b_num_non_zero,
c_num_non_zero: serialized_constraint_matrices.c_num_non_zero,
a: serialized_constraint_matrices.a.data,
b: serialized_constraint_matrices.b.data,
c: serialized_constraint_matrices.c.data,
};
Ok((proving_key, constraint_matrices))
}
pub fn read_proving_key_and_matrices_from_zkey(
zkey_path: &str,
) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {
println!("Reading zkey from: {}", zkey_path);
let now = Instant::now();
let zkey_file_path = PathBuf::from(zkey_path);
let zkey_file = File::open(zkey_file_path).wrap_err("Failed to open zkey file")?;
let mut buf_reader = BufReader::new(zkey_file);
let (proving_key, matrices) =
read_zkey(&mut buf_reader).wrap_err("Failed to read zkey file")?;
println!("Time to read zkey: {:?}", now.elapsed());
println!("Serializing proving key and constraint matrices");
let now = Instant::now();
let serializable_proving_key = SerializableProvingKey(proving_key);
let serializable_constrain_matrices = SerializableConstraintMatrices {
num_instance_variables: matrices.num_instance_variables,
num_witness_variables: matrices.num_witness_variables,
num_constraints: matrices.num_constraints,
a_num_non_zero: matrices.a_num_non_zero,
b_num_non_zero: matrices.b_num_non_zero,
c_num_non_zero: matrices.c_num_non_zero,
a: SerializableMatrix { data: matrices.a },
b: SerializableMatrix { data: matrices.b },
c: SerializableMatrix { data: matrices.c },
};
println!(
"Time to serialize proving key and constraint matrices: {:?}",
now.elapsed()
);
Ok((serializable_proving_key, serializable_constrain_matrices))
}
pub fn convert_zkey(
proving_key: SerializableProvingKey,
constraint_matrices: SerializableConstraintMatrices<Fr>,
arkzkey_path: &str,
) -> Result<()> {
let arkzkey_file_path = PathBuf::from(arkzkey_path);
let serialized_path = PathBuf::from(arkzkey_file_path);
let mut file =
File::create(&serialized_path).wrap_err("Failed to create serialized proving key file")?;
proving_key
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize proving key")?;
constraint_matrices
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize constraint matrices")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
fn test_circuit_serialization_deserialization(zkey_path: &str) -> Result<()> {
let arkzkey_path = zkey_path.replace(".zkey", ".arkzkey");
let (original_proving_key, original_constraint_matrices) =
read_proving_key_and_matrices_from_zkey(zkey_path)?;
println!("[build] Writing arkzkey to: {}", arkzkey_path);
let now = Instant::now();
convert_zkey(
original_proving_key.clone(),
original_constraint_matrices.clone(),
&arkzkey_path,
)?;
println!("[build] Time to write arkzkey: {:?}", now.elapsed());
println!("Reading arkzkey from: {}", arkzkey_path);
let now = Instant::now();
let (deserialized_proving_key, deserialized_constraint_matrices) =
read_arkzkey(&arkzkey_path)?;
println!("Time to read arkzkey: {:?}", now.elapsed());
assert_eq!(
original_proving_key, deserialized_proving_key,
"Original and deserialized proving keys do not match"
);
assert_eq!(
original_constraint_matrices, deserialized_constraint_matrices,
"Original and deserialized constraint matrices do not match"
);
Ok(())
}
#[test]
fn test_multiplier2_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/multiplier2/target/multiplier2_final.zkey",
)
}
#[test]
fn test_keccak256_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.zkey",
)
}
#[test]
fn test_rsa_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/rsa/target/main_final.zkey",
)
}
}

View File

@@ -1,14 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

View File

@@ -1,51 +0,0 @@
[package]
name = "mopro-core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[patch.crates-io]
# NOTE: Forked wasmer to work around memory limits
# See https://github.com/wasmerio/wasmer/commit/09c7070
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
[features]
default = []
dylib = ["wasmer/dylib"]
[dependencies]
ark-circom = { git = "https://github.com/arkworks-rs/circom-compat.git" }
serde = { version = "1.0", features = ["derive"] }
ark-serialize = { version = "=0.4.1", features = ["derive"] }
num-bigint = { version = "=0.4.3", default-features = false, features = [
"rand",
] }
instant = "0.1"
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
once_cell = "1.8"
# ZKP generation
ark-ec = { version = "=0.4.1", default-features = false, features = [
"parallel",
] }
ark-crypto-primitives = { version = "=0.4.0" }
ark-std = { version = "=0.4.0", default-features = false, features = [
"parallel",
] }
ark-bn254 = { version = "=0.4.0" }
ark-groth16 = { version = "=0.4.0", default-features = false, features = [
"parallel",
] }
ark-relations = { version = "0.4", default-features = false }
ark-zkey = { path = "../ark-zkey" }
# Error handling
thiserror = "=1.0.39"
color-eyre = "=0.6.2"
criterion = "=0.3.6"
[build-dependencies]
color-eyre = "0.6"
enumset = "1.0.8"
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }

View File

@@ -1,47 +0,0 @@
# mopro-core
Core mobile Rust library. For FFI, see `mopro-ffi` which is a thin wrapper for exposing UniFFI bindings around this library.
## Overview
TBD.
## Examples
Run `cargo run --example circom`. Also see `examples/circom/README.md` for more information.
## Build dylib
Experimental support.
Turns `.wasm` file into a dynamic library (`.dylib`).
Run:
`cargo build --features dylib`
After that you'll see location of the dylib file:
```
warning: Building dylib for aarch64-apple-darwin
warning: Dylib location: /Users/user/repos/github.com/oskarth/mopro/mopro-core/target/debug/aarch64-apple-darwin/keccak256.dylib
```
Right now this is hardcoded for `rsa`.
Note that:
- It has to be built for the right architecture
- Have to run `install_name_tool` to adjust install name
- Run `codesign` to sign dylib for use on iOS
### Script
Add third argument: `dylib`:
`./scripts/update_bindings.sh device release dylib`
Note that `APPLE_SIGNING_IDENTITY` must be set.
## To use ark-zkey
Experimental support for significantly faster zkey loading. See `../ark-zkey` README for how to build arkzkey.

View File

@@ -1,95 +0,0 @@
use color_eyre::eyre::Result;
use std::env;
use std::path::PathBuf;
fn prepare_env(zkey_path: String, wasm_path: String, arkzkey_path: String) -> Result<()> {
let project_dir = env::var("CARGO_MANIFEST_DIR")?;
let zkey_file = PathBuf::from(&project_dir).join(zkey_path);
let wasm_file = PathBuf::from(&project_dir).join(wasm_path);
let arkzkey_file = PathBuf::from(&project_dir).join(arkzkey_path);
// TODO: Right now emitting as warnings for visibility, figure out better way to do this?
println!("cargo:warning=zkey_file: {}", zkey_file.display());
println!("cargo:warning=wasm_file: {}", wasm_file.display());
println!("cargo:warning=arkzkey_file: {}", arkzkey_file.display());
// Set BUILD_RS_ZKEY_FILE and BUILD_RS_WASM_FILE env var
println!("cargo:rustc-env=BUILD_RS_ZKEY_FILE={}", zkey_file.display());
println!("cargo:rustc-env=BUILD_RS_WASM_FILE={}", wasm_file.display());
println!(
"cargo:rustc-env=BUILD_RS_ARKZKEY_FILE={}",
arkzkey_file.display()
);
Ok(())
}
#[cfg(feature = "dylib")]
fn build_dylib(wasm_path: String, dylib_name: String) -> Result<()> {
use std::path::Path;
use std::{fs, str::FromStr};
use color_eyre::eyre::eyre;
use enumset::enum_set;
use enumset::EnumSet;
use wasmer::Cranelift;
use wasmer::Dylib;
use wasmer::Target;
use wasmer::{Module, Store, Triple};
let out_dir = env::var("OUT_DIR")?;
let project_dir = env::var("CARGO_MANIFEST_DIR")?;
let build_mode = env::var("PROFILE")?;
let target_arch = env::var("TARGET")?;
let out_dir = Path::new(&out_dir).to_path_buf();
let wasm_file = Path::new(&wasm_path).to_path_buf();
let dylib_file = out_dir.join(&dylib_name);
let final_dir = PathBuf::from(&project_dir)
.join("target")
.join(&target_arch)
.join(build_mode);
// if dylib_file.exists() {
// return Ok(());
// }
// Create a WASM engine for the target that can compile
let triple = Triple::from_str(&target_arch).map_err(|e| eyre!(e))?;
let cpu_features = enum_set!();
let target = Target::new(triple, cpu_features);
let engine = Dylib::new(Cranelift::default()).target(target).engine();
println!("cargo:warning=Building dylib for {}", target_arch);
// Compile the WASM module
let store = Store::new(&engine);
let module = Module::from_file(&store, &wasm_file).unwrap();
module.serialize_to_file(&dylib_file).unwrap();
assert!(dylib_file.exists());
// Copy dylib to a more predictable path
fs::create_dir_all(&final_dir)?;
let final_path = final_dir.join(dylib_name);
fs::copy(&dylib_file, &final_path)?;
println!("cargo:warning=Dylib location: {}", final_path.display());
Ok(())
}
fn main() -> Result<()> {
// TODO: build_circuit function to builds all related artifacts, instead of doing this externally
let dir = "../../circuits";
let circuit = "proof_of_passport";
let zkey_path = format!("{}/build/{}_final.zkey", dir, circuit);
let wasm_path = format!("{}/build/{}_js/{}.wasm", dir, circuit, circuit);
// TODO: Need to modify script for this
let arkzkey_path = format!("{}/build/{}_final.arkzkey", dir, circuit);
println!("cargo:warning=arkzkey_path: {}", arkzkey_path);
prepare_env(zkey_path, wasm_path, arkzkey_path)?;
Ok(())
}

View File

@@ -1,8 +0,0 @@
pub mod middleware;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MoproError {
#[error("CircomError: {0}")]
CircomError(String),
}

View File

@@ -1,860 +0,0 @@
use self::{
serialization::{SerializableInputs, SerializableProof, SerializableProvingKey},
utils::{assert_paths_exists, bytes_to_bits},
};
use crate::MoproError;
use std::collections::HashMap;
//use std::io::Cursor;
use std::sync::Mutex;
use std::time::Instant;
use ark_bn254::{Bn254, Fr};
use ark_circom::{
CircomBuilder,
CircomCircuit,
CircomConfig,
CircomReduction,
WitnessCalculator, //read_zkey,
};
use ark_crypto_primitives::snark::SNARK;
use ark_groth16::{prepare_verifying_key, Groth16, ProvingKey};
use ark_std::UniformRand;
use ark_relations::r1cs::ConstraintMatrices;
use ark_std::rand::thread_rng;
use color_eyre::Result;
use core::include_bytes;
use num_bigint::BigInt;
use once_cell::sync::{Lazy, OnceCell};
use wasmer::{Module, Store};
use ark_zkey::read_arkzkey_from_bytes; //SerializableConstraintMatrices
#[cfg(feature = "dylib")]
use {
std::{env, path::Path},
wasmer::Dylib,
};
pub mod serialization;
pub mod utils;
type GrothBn = Groth16<Bn254>;
type CircuitInputs = HashMap<String, Vec<BigInt>>;
// TODO: Split up this namespace a bit, right now quite a lot of things going on
pub struct CircomState {
builder: Option<CircomBuilder<Bn254>>,
circuit: Option<CircomCircuit<Bn254>>,
params: Option<ProvingKey<Bn254>>,
}
impl Default for CircomState {
fn default() -> Self {
Self::new()
}
}
// NOTE: A lot of the contents of this file is inspired by github.com/worldcoin/semaphore-rs
// TODO: Replace printlns with logging
//const ZKEY_BYTES: &[u8] = include_bytes!(env!("BUILD_RS_ZKEY_FILE"));
const ARKZKEY_BYTES: &[u8] = include_bytes!(env!("BUILD_RS_ARKZKEY_FILE"));
// static ZKEY: Lazy<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> = Lazy::new(|| {
// let mut reader = Cursor::new(ZKEY_BYTES);
// read_zkey(&mut reader).expect("Failed to read zkey")
// });
static ARKZKEY: Lazy<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> = Lazy::new(|| {
//let mut reader = Cursor::new(ARKZKEY_BYTES);
// TODO: Use reader? More flexible; unclear if perf diff
read_arkzkey_from_bytes(ARKZKEY_BYTES).expect("Failed to read arkzkey")
});
const WASM: &[u8] = include_bytes!(env!("BUILD_RS_WASM_FILE"));
/// `WITNESS_CALCULATOR` is a lazily initialized, thread-safe singleton of type `WitnessCalculator`.
/// `OnceCell` ensures that the initialization occurs exactly once, and `Mutex` allows safe shared
/// access from multiple threads.
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
/// Initializes the `WITNESS_CALCULATOR` singleton with a `WitnessCalculator` instance created from
/// a specified dylib file (WASM circuit). Also initialize `ZKEY`.
#[cfg(feature = "dylib")]
pub fn initialize(dylib_path: &Path) {
println!("Initializing dylib: {:?}", dylib_path);
WITNESS_CALCULATOR
.set(from_dylib(dylib_path))
.expect("Failed to set WITNESS_CALCULATOR");
// Initialize ARKZKEY
// TODO: Speed this up even more
let now = std::time::Instant::now();
Lazy::force(&ARKZKEY);
println!("Initializing arkzkey took: {:.2?}", now.elapsed());
}
#[cfg(not(feature = "dylib"))]
pub fn initialize() {
println!("Initializing library with arkzkey");
// Initialize ARKZKEY
// TODO: Speed this up even more!
let now = std::time::Instant::now();
Lazy::force(&ARKZKEY);
println!("Initializing arkzkey took: {:.2?}", now.elapsed());
}
/// Creates a `WitnessCalculator` instance from a dylib file.
#[cfg(feature = "dylib")]
fn from_dylib(path: &Path) -> Mutex<WitnessCalculator> {
let store = Store::new(&Dylib::headless().engine());
let module = unsafe {
Module::deserialize_from_file(&store, path).expect("Failed to load dylib module")
};
let result =
WitnessCalculator::from_module(module).expect("Failed to create WitnessCalculator");
Mutex::new(result)
}
// #[must_use]
// pub fn zkey() -> &'static (ProvingKey<Bn254>, ConstraintMatrices<Fr>) {
// &ZKEY
// }
// Experimental
#[must_use]
pub fn arkzkey() -> &'static (ProvingKey<Bn254>, ConstraintMatrices<Fr>) {
&ARKZKEY
}
/// Provides access to the `WITNESS_CALCULATOR` singleton, initializing it if necessary.
/// It expects the path to the dylib file to be set in the `CIRCUIT_WASM_DYLIB` environment variable.
#[cfg(feature = "dylib")]
#[must_use]
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
let var_name = "CIRCUIT_WASM_DYLIB";
WITNESS_CALCULATOR.get_or_init(|| {
let path = env::var(var_name).unwrap_or_else(|_| {
panic!(
"Mopro circuit WASM Dylib not initialized. \
Please set {} environment variable to the path of the dylib file",
var_name
)
});
from_dylib(Path::new(&path))
})
}
#[cfg(not(feature = "dylib"))]
#[must_use]
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
WITNESS_CALCULATOR.get_or_init(|| {
let store = Store::default();
let module = Module::from_binary(&store, WASM).expect("WASM should be valid");
let result =
WitnessCalculator::from_module(module).expect("Failed to create WitnessCalculator");
Mutex::new(result)
})
}
pub fn generate_proof2(
inputs: CircuitInputs,
) -> Result<(SerializableProof, SerializableInputs), MoproError> {
let mut rng = thread_rng();
let rng = &mut rng;
let r = ark_bn254::Fr::rand(rng);
let s = ark_bn254::Fr::rand(rng);
println!("Generating proof 2");
let now = std::time::Instant::now();
let full_assignment = witness_calculator()
.lock()
.expect("Failed to lock witness calculator")
.calculate_witness_element::<Bn254, _>(inputs, false)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
println!("Witness generation took: {:.2?}", now.elapsed());
let now = std::time::Instant::now();
//let zkey = zkey();
let zkey = arkzkey();
println!("Loading arkzkey took: {:.2?}", now.elapsed());
let public_inputs = full_assignment.as_slice()[1..zkey.1.num_instance_variables].to_vec();
let now = std::time::Instant::now();
let ark_proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices(
&zkey.0,
r,
s,
&zkey.1,
zkey.1.num_instance_variables,
zkey.1.num_constraints,
full_assignment.as_slice(),
);
let proof = ark_proof.map_err(|e| MoproError::CircomError(e.to_string()))?;
println!("proof generation took: {:.2?}", now.elapsed());
// TODO: Add SerializableInputs(inputs)))
Ok((SerializableProof(proof), SerializableInputs(public_inputs)))
}
pub fn verify_proof2(
serialized_proof: SerializableProof,
serialized_inputs: SerializableInputs,
) -> Result<bool, MoproError> {
let start = Instant::now();
let zkey = arkzkey();
let pvk = prepare_verifying_key(&zkey.0.vk);
let proof_verified =
GrothBn::verify_with_processed_vk(&pvk, &serialized_inputs.0, &serialized_proof.0)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
let verification_duration = start.elapsed();
println!("Verification time 2: {:?}", verification_duration);
Ok(proof_verified)
}
impl CircomState {
pub fn new() -> Self {
Self {
builder: None,
circuit: None,
params: None,
}
}
pub fn setup(
&mut self,
wasm_path: &str,
r1cs_path: &str,
) -> Result<SerializableProvingKey, MoproError> {
assert_paths_exists(wasm_path, r1cs_path)?;
println!("Setup");
let start = Instant::now();
// Load the WASM and R1CS for witness and proof generation
let cfg = self.load_config(wasm_path, r1cs_path)?;
// Create an empty instance for setup
self.builder = Some(CircomBuilder::new(cfg));
// Run a trusted setup using the rng in the state
let params = self.run_trusted_setup()?;
self.params = Some(params.clone());
let setup_duration = start.elapsed();
println!("Setup time: {:?}", setup_duration);
Ok(SerializableProvingKey(params))
}
// NOTE: Consider generate_proof<T: Into<BigInt>> API
// XXX: BigInt might present problems for UniFFI
pub fn generate_proof(
&mut self,
inputs: CircuitInputs,
) -> Result<(SerializableProof, SerializableInputs), MoproError> {
let start = Instant::now();
println!("Generating proof");
let mut rng = thread_rng();
let builder = self.builder.as_mut().ok_or(MoproError::CircomError(
"Builder has not been set up".to_string(),
))?;
// Insert our inputs as key value pairs
for (key, values) in &inputs {
for value in values {
builder.push_input(&key, value.clone());
}
}
// Clone the builder, then build the circuit
let circom = builder
.clone()
.build()
.map_err(|e| MoproError::CircomError(e.to_string()))?;
// Update the circuit in self
self.circuit = Some(circom.clone());
let params = self.params.as_ref().ok_or(MoproError::CircomError(
"Parameters have not been set up".to_string(),
))?;
let inputs = circom.get_public_inputs().ok_or(MoproError::CircomError(
"Failed to get public inputs".to_string(),
))?;
let proof = GrothBn::prove(params, circom.clone(), &mut rng)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
let proof_duration = start.elapsed();
println!("Proof generation time: {:?}", proof_duration);
Ok((SerializableProof(proof), SerializableInputs(inputs)))
}
pub fn verify_proof(
&self,
serialized_proof: SerializableProof,
serialized_inputs: SerializableInputs,
) -> Result<bool, MoproError> {
let start = Instant::now();
println!("Verifying proof");
let params = self.params.as_ref().ok_or(MoproError::CircomError(
"Parameters have not been set up".to_string(),
))?;
let pvk =
GrothBn::process_vk(&params.vk).map_err(|e| MoproError::CircomError(e.to_string()))?;
let proof_verified =
GrothBn::verify_with_processed_vk(&pvk, &serialized_inputs.0, &serialized_proof.0)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
let verification_duration = start.elapsed();
println!("Verification time: {:?}", verification_duration);
Ok(proof_verified)
}
fn load_config(
&self,
wasm_path: &str,
r1cs_path: &str,
) -> Result<CircomConfig<Bn254>, MoproError> {
CircomConfig::<Bn254>::new(wasm_path, r1cs_path)
.map_err(|e| MoproError::CircomError(e.to_string()))
}
fn run_trusted_setup(&mut self) -> Result<ProvingKey<Bn254>, MoproError> {
let circom_setup = self
.builder
.as_mut()
.ok_or(MoproError::CircomError(
"Builder has not been set up".to_string(),
))?
.setup();
let mut rng = thread_rng();
GrothBn::generate_random_parameters_with_reduction(circom_setup, &mut rng)
.map_err(|e| MoproError::CircomError(e.to_string()))
}
}
// Helper function for Keccak256 example
pub fn bytes_to_circuit_inputs(bytes: &[u8]) -> CircuitInputs {
let bits = bytes_to_bits(bytes);
let big_int_bits = bits
.into_iter()
.map(|bit| BigInt::from(bit as u8))
.collect();
let mut inputs = HashMap::new();
inputs.insert("in".to_string(), big_int_bits);
inputs
}
pub fn strings_to_circuit_inputs(strings: &[&str]) -> Vec<BigInt> {
strings
.iter()
.map(|&value| BigInt::parse_bytes(value.as_bytes(), 10).unwrap())
.collect()
}
pub fn bytes_to_circuit_outputs(bytes: &[u8]) -> SerializableInputs {
let bits = bytes_to_bits(bytes);
let field_bits = bits.into_iter().map(|bit| Fr::from(bit as u8)).collect();
SerializableInputs(field_bits)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_setup_prove_verify_simple() {
let wasm_path = "./examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm";
let r1cs_path = "./examples/circom/multiplier2/target/multiplier2.r1cs";
// Instantiate CircomState
let mut circom_state = CircomState::new();
// Setup
let setup_res = circom_state.setup(wasm_path, r1cs_path);
assert!(setup_res.is_ok());
let _serialized_pk = setup_res.unwrap();
// Deserialize the proving key and inputs if necessary
// Prepare inputs
let mut inputs = HashMap::new();
let a = 3;
let b = 5;
let c = a * b;
inputs.insert("a".to_string(), vec![BigInt::from(a)]);
inputs.insert("b".to_string(), vec![BigInt::from(b)]);
// output = [public output c, public input a]
let expected_output = vec![Fr::from(c), Fr::from(a)];
let serialized_outputs = SerializableInputs(expected_output);
// Proof generation
let generate_proof_res = circom_state.generate_proof(inputs);
// Check and print the error if there is one
if let Err(e) = &generate_proof_res {
println!("Error: {:?}", e);
}
assert!(generate_proof_res.is_ok());
let (serialized_proof, serialized_inputs) = generate_proof_res.unwrap();
// Check output
assert_eq!(serialized_inputs, serialized_outputs);
// Proof verification
let verify_res = circom_state.verify_proof(serialized_proof, serialized_inputs);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap()); // Verifying that the proof was indeed verified
}
#[test]
fn test_setup_prove_verify_keccak() {
let wasm_path =
"./examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm";
let r1cs_path = "./examples/circom/keccak256/target/keccak256_256_test.r1cs";
// Instantiate CircomState
let mut circom_state = CircomState::new();
// Setup
let setup_res = circom_state.setup(wasm_path, r1cs_path);
assert!(setup_res.is_ok());
let _serialized_pk = setup_res.unwrap();
// Deserialize the proving key and inputs if necessary
// Prepare inputs
let input_vec = vec![
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
// Expected output
let expected_output_vec = vec![
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
];
let inputs = bytes_to_circuit_inputs(&input_vec);
let serialized_outputs = bytes_to_circuit_outputs(&expected_output_vec);
// Proof generation
let generate_proof_res = circom_state.generate_proof(inputs);
// Check and print the error if there is one
if let Err(e) = &generate_proof_res {
println!("Error: {:?}", e);
}
assert!(generate_proof_res.is_ok());
let (serialized_proof, serialized_inputs) = generate_proof_res.unwrap();
// Check output
assert_eq!(serialized_inputs, serialized_outputs);
// Proof verification
let verify_res = circom_state.verify_proof(serialized_proof, serialized_inputs);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap()); // Verifying that the proof was indeed verified
}
#[test]
fn test_setup_error() {
// Arrange: Create a new CircomState instance
let mut circom_state = CircomState::new();
let wasm_path = "badpath/multiplier2.wasm";
let r1cs_path = "badpath/multiplier2.r1cs";
// Act: Call the setup method
let result = circom_state.setup(wasm_path, r1cs_path);
// Assert: Check that the method returns an error
assert!(result.is_err());
}
#[cfg(feature = "dylib")]
#[test]
fn test_dylib_init_and_generate_witness() {
// Assumes that the dylib file has been built and is in the following location
let dylib_path = "target/debug/aarch64-apple-darwin/keccak256.dylib";
// Initialize libray
initialize(Path::new(&dylib_path));
let input_vec = vec![
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
let inputs = bytes_to_circuit_inputs(&input_vec);
let now = std::time::Instant::now();
let full_assignment = witness_calculator()
.lock()
.expect("Failed to lock witness calculator")
.calculate_witness_element::<Bn254, _>(inputs, false)
.map_err(|e| MoproError::CircomError(e.to_string()));
println!("Witness generation took: {:.2?}", now.elapsed());
assert!(full_assignment.is_ok());
}
#[test]
fn test_generate_proof2() {
// XXX: This can be done better
#[cfg(feature = "dylib")]
{
// Assumes that the dylib file has been built and is in the following location
let dylib_path = "target/debug/aarch64-apple-darwin/keccak256.dylib";
// Initialize libray
initialize(Path::new(&dylib_path));
}
let input_vec = vec![
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
let expected_output_vec = vec![
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
];
let inputs = bytes_to_circuit_inputs(&input_vec);
let serialized_outputs = bytes_to_circuit_outputs(&expected_output_vec);
let generate_proof_res = generate_proof2(inputs);
let (serialized_proof, serialized_inputs) = generate_proof_res.unwrap();
assert_eq!(serialized_inputs, serialized_outputs);
// Proof verification
let verify_res = verify_proof2(serialized_proof, serialized_inputs);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap()); // Verifying that the proof was indeed verified
}
#[ignore = "ignore for ci"]
#[test]
fn test_setup_prove_rsa() {
let wasm_path = "./examples/circom/rsa/target/main_js/main.wasm";
let r1cs_path = "./examples/circom/rsa/target/main.r1cs";
// Instantiate CircomState
let mut circom_state = CircomState::new();
// Setup
let setup_res = circom_state.setup(wasm_path, r1cs_path);
assert!(setup_res.is_ok());
let _serialized_pk = setup_res.unwrap();
// Deserialize the proving key and inputs if necessary
// Prepare inputs
let signature = [
"3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493",
];
let modulus = [
"13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",
];
let base_message = [
"18114495772705111902",
"2254271930739856077",
"2068851770",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
];
let mut inputs: HashMap<String, Vec<BigInt>> = HashMap::new();
inputs.insert(
"signature".to_string(),
strings_to_circuit_inputs(&signature),
);
inputs.insert("modulus".to_string(), strings_to_circuit_inputs(&modulus));
inputs.insert(
"base_message".to_string(),
strings_to_circuit_inputs(&base_message),
);
// Proof generation
let generate_proof_res = circom_state.generate_proof(inputs);
// Check and print the error if there is one
if let Err(e) = &generate_proof_res {
println!("Error: {:?}", e);
}
assert!(generate_proof_res.is_ok());
let (serialized_proof, serialized_inputs) = generate_proof_res.unwrap();
// Proof verification
let verify_res = circom_state.verify_proof(serialized_proof, serialized_inputs);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap()); // Verifying that the proof was indeed verified
}
#[ignore = "ignore for ci"]
#[test]
fn test_setup_prove_rsa2() {
// Prepare inputs
let signature = [
"3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493",
];
let modulus = [
"13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",
];
let base_message = [
"18114495772705111902",
"2254271930739856077",
"2068851770",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
];
let mut inputs: HashMap<String, Vec<BigInt>> = HashMap::new();
inputs.insert(
"signature".to_string(),
strings_to_circuit_inputs(&signature),
);
inputs.insert("modulus".to_string(), strings_to_circuit_inputs(&modulus));
inputs.insert(
"base_message".to_string(),
strings_to_circuit_inputs(&base_message),
);
// Proof generation
let generate_proof_res = generate_proof2(inputs);
// Check and print the error if there is one
if let Err(e) = &generate_proof_res {
println!("Error: {:?}", e);
}
assert!(generate_proof_res.is_ok());
let (serialized_proof, serialized_inputs) = generate_proof_res.unwrap();
// Proof verification
let verify_res = verify_proof2(serialized_proof, serialized_inputs);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap()); // Verifying that the proof was indeed verified
}
}

View File

@@ -1,106 +0,0 @@
use ark_bn254::Bn254;
use ark_ec::pairing::Pairing;
use ark_groth16::{Proof, ProvingKey};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use color_eyre::Result;
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
pub struct SerializableProof(pub Proof<Bn254>);
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableInputs(pub Vec<<Bn254 as Pairing>::ScalarField>);
pub fn serialize_proof(proof: &SerializableProof) -> Vec<u8> {
let mut serialized_data = Vec::new();
proof
.serialize_uncompressed(&mut serialized_data)
.expect("Serialization failed");
serialized_data
}
pub fn deserialize_proof(data: Vec<u8>) -> SerializableProof {
SerializableProof::deserialize_uncompressed(&mut &data[..]).expect("Deserialization failed")
}
pub fn serialize_proving_key(pk: &SerializableProvingKey) -> Vec<u8> {
let mut serialized_data = Vec::new();
pk.serialize_uncompressed(&mut serialized_data)
.expect("Serialization failed");
serialized_data
}
pub fn deserialize_proving_key(data: Vec<u8>) -> SerializableProvingKey {
SerializableProvingKey::deserialize_uncompressed(&mut &data[..])
.expect("Deserialization failed")
}
pub fn serialize_inputs(inputs: &SerializableInputs) -> Vec<u8> {
let mut serialized_data = Vec::new();
inputs
.serialize_uncompressed(&mut serialized_data)
.expect("Serialization failed");
serialized_data
}
pub fn deserialize_inputs(data: Vec<u8>) -> SerializableInputs {
SerializableInputs::deserialize_uncompressed(&mut &data[..]).expect("Deserialization failed")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::middleware::circom::serialization::SerializableProvingKey;
use crate::middleware::circom::utils::assert_paths_exists;
use crate::MoproError;
use ark_bn254::Bn254;
use ark_circom::{CircomBuilder, CircomConfig};
use ark_groth16::Groth16;
use ark_std::rand::thread_rng;
use color_eyre::Result;
type GrothBn = Groth16<Bn254>;
fn generate_serializable_proving_key(
wasm_path: &str,
r1cs_path: &str,
) -> Result<SerializableProvingKey, MoproError> {
assert_paths_exists(wasm_path, r1cs_path)?;
let cfg = CircomConfig::<Bn254>::new(wasm_path, r1cs_path)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
let builder = CircomBuilder::new(cfg);
let circom = builder.setup();
let mut rng = thread_rng();
let raw_params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)
.map_err(|e| MoproError::CircomError(e.to_string()))?;
Ok(SerializableProvingKey(raw_params))
}
#[test]
fn test_serialization_deserialization() {
let wasm_path = "./examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm";
let r1cs_path = "./examples/circom/multiplier2/target/multiplier2.r1cs";
// Generate a serializable proving key for testing
let serializable_pk = generate_serializable_proving_key(wasm_path, r1cs_path)
.expect("Failed to generate serializable proving key");
// Serialize
let serialized_data = serialize_proving_key(&serializable_pk);
// Deserialize
let deserialized_pk = deserialize_proving_key(serialized_data);
// Assert that the original and deserialized ProvingKeys are the same
assert_eq!(
serializable_pk.0, deserialized_pk.0,
"Original and deserialized proving keys do not match"
);
}
}

View File

@@ -1,33 +0,0 @@
use crate::MoproError;
use std::path::Path;
pub fn assert_paths_exists(wasm_path: &str, r1cs_path: &str) -> Result<(), MoproError> {
// Check that the files exist - ark-circom should probably do this instead and not panic
if !Path::new(wasm_path).exists() {
return Err(MoproError::CircomError(format!(
"Path does not exist: {}",
wasm_path
)));
}
if !Path::new(r1cs_path).exists() {
return Err(MoproError::CircomError(format!(
"Path does not exist: {}",
r1cs_path
)));
};
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
let mut bits = Vec::new();
for &byte in bytes {
for j in 0..8 {
let bit = (byte >> j) & 1;
bits.push(bit == 1);
}
}
bits
}

View File

@@ -1 +0,0 @@
pub mod circom;

View File

@@ -1,18 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# kotlin generated file
jniLibs/
src/uniffi/mopro/

View File

@@ -1,47 +0,0 @@
[package]
name = "mopro-ffi"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["lib", "cdylib", "staticlib"]
name = "mopro_ffi"
[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"
[features]
default = []
# If we enable dylib here it should be enabled in mopro-core as well
dylib = ["mopro-core/dylib"]
[patch.crates-io]
# NOTE: Forked wasmer to work around memory limits
# See https://github.com/wasmerio/wasmer/commit/09c7070
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
[dependencies]
mopro-core = { path = "../mopro-core" }
uniffi = { version = "0.25", features = ["cli"] }
serde = { version = "1", features = ["derive"] }
bincode = "1"
ark-serialize = { version = "=0.4.1", features = ["derive"] }
num-bigint = { version = "=0.4.3", default-features = false, features = [
"rand",
] }
# Error handling
thiserror = "=1.0.39"
color-eyre = "=0.6.2"
criterion = "=0.3.6"
[build-dependencies]
uniffi = { version = "0.25", features = ["build"] }
[dev-dependencies]
uniffi = { version = "0.25", features = ["bindgen-tests"] }
ark-bn254 = { version = "=0.4.0" }

View File

@@ -1,16 +0,0 @@
TARGETS = x86_64-apple-ios aarch64-apple-ios aarch64-apple-ios-sim
BUILD_TYPES = debug release
all: $(BUILD_TYPES)
debug: $(TARGETS)
for target in $(TARGETS); do \
cargo build --target $$target; \
done
release:
for target in $(TARGETS); do \
cargo build --release --target $$target; \
done
.PHONY: all $(BUILD_TYPES) $(TARGETS)

View File

@@ -1,48 +0,0 @@
# mopro-ffi
Thin wrapper around `mopro-core`, exposes UniFFI bindings to be used by `rust-ios`, etc.
## Overview
TBD.
## Development
### Prerequisites
1. Ensure you have Rust installed
2. Add platform targets `rustup target add x86_64-apple-ios aarch64-apple-ios aarch64-apple-ios-sim`
3. Install `uniffi-bindgen` locally with `cargo install --bin uniffi-bindgen --path .`
4. In order to locally run the bindings tests, you will need
* Kotlin:
* `kotlinc`, the [Kotlin command-line compiler](https://kotlinlang.org/docs/command-line.html).
* `ktlint`, the [Kotlin linter used to format the generated bindings](https://ktlint.github.io/).
* The [Java Native Access](https://github.com/java-native-access/jna#download) JAR downloaded and its path
added to your `$CLASSPATH` environment variable.
* Swift:
* `swift` and `swiftc`, the [Swift command-line tools](https://swift.org/download/).
* The Swift `Foundation` package.
### Platforms supported
Currently iOS is the main target, but Android will soon follow. PRs welcome.
### Building
Run `make` to build debug and release static libraries for supported platforms.
### Generate UniFFI bindings
The following command generates Swift bindings:
`uniffi-bindgen generate src/mopro.udl --language swift --out-dir target/SwiftBindings`
## Test bindings
To test bindings:
`cargo test --test test_generated_bindings`
To test bindings in release mode without warning:
`cargo test --test test_generated_bindings --release 2>/dev/null`

View File

@@ -1,3 +0,0 @@
fn main() {
uniffi::generate_scaffolding("src/mopro.udl").expect("Building the UDL file failed");
}

View File

@@ -1,295 +0,0 @@
use mopro_core::middleware::circom;
use mopro_core::MoproError;
use num_bigint::BigInt;
use std::collections::HashMap;
use std::path::Path;
use std::str::FromStr;
use std::sync::RwLock;
#[derive(Debug)]
pub enum FFIError {
MoproError(mopro_core::MoproError),
SerializationError(String),
}
#[derive(Debug, Clone)]
pub struct GenerateProofResult {
pub proof: Vec<u8>,
pub inputs: Vec<u8>,
}
// NOTE: Make UniFFI and Rust happy, can maybe do some renaming here
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
pub struct SetupResult {
pub provingKey: Vec<u8>,
}
// pub inputs: Vec<u8>,
impl From<mopro_core::MoproError> for FFIError {
fn from(error: mopro_core::MoproError) -> Self {
FFIError::MoproError(error)
}
}
pub struct MoproCircom {
state: RwLock<circom::CircomState>,
}
impl Default for MoproCircom {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(feature = "dylib"))]
pub fn initialize_mopro() -> Result<(), MoproError> {
// TODO: Error handle / panic?
circom::initialize();
Ok(())
}
#[cfg(feature = "dylib")]
pub fn initialize_mopro() -> Result<(), MoproError> {
println!("need to use dylib to init!");
panic!("need to use dylib to init!");
}
#[cfg(feature = "dylib")]
pub fn initialize_mopro_dylib(dylib_path: String) -> Result<(), MoproError> {
// TODO: Error handle / panic?
let dylib_path = Path::new(dylib_path.as_str());
circom::initialize(dylib_path);
Ok(())
}
#[cfg(not(feature = "dylib"))]
pub fn initialize_mopro_dylib(dylib_path: String) -> Result<(), MoproError> {
println!("dylib feature not enabled!");
panic!("dylib feature not enabled!");
}
pub fn generate_proof2(
inputs: HashMap<String, Vec<String>>,
) -> Result<GenerateProofResult, MoproError> {
// Convert inputs to BigInt
let bigint_inputs = inputs
.into_iter()
.map(|(k, v)| {
(
k,
v.into_iter()
.map(|i| BigInt::from_str(&i).unwrap())
.collect(),
)
})
.collect();
let (proof, inputs) = circom::generate_proof2(bigint_inputs)?;
let serialized_proof = circom::serialization::serialize_proof(&proof);
let serialized_inputs = circom::serialization::serialize_inputs(&inputs);
Ok(GenerateProofResult {
proof: serialized_proof,
inputs: serialized_inputs,
})
}
pub fn verify_proof2(proof: Vec<u8>, public_input: Vec<u8>) -> Result<bool, MoproError> {
let deserialized_proof = circom::serialization::deserialize_proof(proof);
let deserialized_public_input = circom::serialization::deserialize_inputs(public_input);
let is_valid = circom::verify_proof2(deserialized_proof, deserialized_public_input)?;
Ok(is_valid)
}
// TODO: Use FFIError::SerializationError instead
impl MoproCircom {
pub fn new() -> Self {
Self {
state: RwLock::new(circom::CircomState::new()),
}
}
pub fn setup(&self, wasm_path: String, r1cs_path: String) -> Result<SetupResult, MoproError> {
let mut state_guard = self.state.write().unwrap();
let pk = state_guard.setup(wasm_path.as_str(), r1cs_path.as_str())?;
Ok(SetupResult {
provingKey: circom::serialization::serialize_proving_key(&pk),
})
}
// inputs: circom::serialization::serialize_inputs(&inputs),
pub fn generate_proof(
&self,
inputs: HashMap<String, Vec<String>>,
) -> Result<GenerateProofResult, MoproError> {
let mut state_guard = self.state.write().unwrap();
// Convert inputs to BigInt
let bigint_inputs = inputs
.into_iter()
.map(|(k, v)| {
(
k,
v.into_iter()
.map(|i| BigInt::from_str(&i).unwrap())
.collect(),
)
})
.collect();
let (proof, inputs) = state_guard.generate_proof(bigint_inputs)?;
Ok(GenerateProofResult {
proof: circom::serialization::serialize_proof(&proof),
inputs: circom::serialization::serialize_inputs(&inputs),
})
}
pub fn verify_proof(&self, proof: Vec<u8>, public_input: Vec<u8>) -> Result<bool, MoproError> {
let state_guard = self.state.read().unwrap();
let deserialized_proof = circom::serialization::deserialize_proof(proof);
let deserialized_public_input = circom::serialization::deserialize_inputs(public_input);
let is_valid = state_guard.verify_proof(deserialized_proof, deserialized_public_input)?;
Ok(is_valid)
}
}
fn add(a: u32, b: u32) -> u32 {
a + b
}
fn hello() -> String {
"Hello World from Rust".to_string()
}
// TODO: Remove me
// UniFFI expects String type
// See https://mozilla.github.io/uniffi-rs/udl/builtin_types.html
// fn run_example(wasm_path: String, r1cs_path: String) -> Result<(), MoproError> {
// circom::run_example(wasm_path.as_str(), r1cs_path.as_str())
// }
uniffi::include_scaffolding!("mopro");
#[cfg(test)]
mod tests {
use super::*;
use ark_bn254::Fr;
use num_bigint::BigUint;
fn bytes_to_circuit_inputs(input_vec: &Vec<u8>) -> HashMap<String, Vec<String>> {
let bits = circom::utils::bytes_to_bits(&input_vec);
let converted_vec: Vec<String> = bits
.into_iter()
.map(|bit| (bit as i32).to_string())
.collect();
let mut inputs = HashMap::new();
inputs.insert("in".to_string(), converted_vec);
inputs
}
fn bytes_to_circuit_outputs(bytes: &[u8]) -> Vec<u8> {
let bits = circom::utils::bytes_to_bits(bytes);
let field_bits = bits.into_iter().map(|bit| Fr::from(bit as u8)).collect();
let circom_outputs = circom::serialization::SerializableInputs(field_bits);
circom::serialization::serialize_inputs(&circom_outputs)
}
#[test]
fn add_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
#[test]
fn test_end_to_end() -> Result<(), MoproError> {
// Paths to your wasm and r1cs files
let wasm_path =
"./../mopro-core/examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm";
let r1cs_path = "./../mopro-core/examples/circom/multiplier2/target/multiplier2.r1cs";
// Create a new MoproCircom instance
let mopro_circom = MoproCircom::new();
// Step 1: Setup
let setup_result = mopro_circom.setup(wasm_path.to_string(), r1cs_path.to_string())?;
assert!(setup_result.provingKey.len() > 0);
let mut inputs = HashMap::new();
let a = BigUint::from_str(
"21888242871839275222246405745257275088548364400416034343698204186575808495616",
)
.unwrap();
let b = BigUint::from(1u8);
let c = a.clone() * b.clone();
inputs.insert("a".to_string(), vec![a.to_string()]);
inputs.insert("b".to_string(), vec![b.to_string()]);
// output = [public output c, public input a]
let expected_output = vec![Fr::from(c), Fr::from(a)];
let circom_outputs = circom::serialization::SerializableInputs(expected_output);
let serialized_outputs = circom::serialization::serialize_inputs(&circom_outputs);
// Step 2: Generate Proof
let generate_proof_result = mopro_circom.generate_proof(inputs)?;
let serialized_proof = generate_proof_result.proof;
let serialized_inputs = generate_proof_result.inputs;
assert!(serialized_proof.len() > 0);
assert_eq!(serialized_inputs, serialized_outputs);
// Step 3: Verify Proof
let is_valid = mopro_circom.verify_proof(serialized_proof, serialized_inputs)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_end_to_end_keccak() -> Result<(), MoproError> {
// Paths to your wasm and r1cs files
let wasm_path =
"./../mopro-core/examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm";
let r1cs_path = "./../mopro-core/examples/circom/keccak256/target/keccak256_256_test.r1cs";
// Create a new MoproCircom instance
let mopro_circom = MoproCircom::new();
// Step 1: Setup
let setup_result = mopro_circom.setup(wasm_path.to_string(), r1cs_path.to_string())?;
assert!(setup_result.provingKey.len() > 0);
// Prepare inputs
let input_vec = vec![
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
// Expected output
let expected_output_vec = vec![
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
];
let inputs = bytes_to_circuit_inputs(&input_vec);
let serialized_outputs = bytes_to_circuit_outputs(&expected_output_vec);
// Step 2: Generate Proof
let generate_proof_result = mopro_circom.generate_proof(inputs)?;
let serialized_proof = generate_proof_result.proof;
let serialized_inputs = generate_proof_result.inputs;
assert!(serialized_proof.len() > 0);
assert_eq!(serialized_inputs, serialized_outputs);
// Step 3: Verify Proof
let is_valid = mopro_circom.verify_proof(serialized_proof, serialized_inputs)?;
assert!(is_valid);
Ok(())
}
}

View File

@@ -1,43 +0,0 @@
namespace mopro {
u32 add(u32 a, u32 b);
string hello();
[Throws=MoproError]
void initialize_mopro();
[Throws=MoproError]
void initialize_mopro_dylib(string dylib_path);
[Throws=MoproError]
GenerateProofResult generate_proof2(record<string, sequence<string>> circuit_inputs);
[Throws=MoproError]
boolean verify_proof2(bytes proof, bytes public_input);
};
dictionary SetupResult {
bytes provingKey;
};
dictionary GenerateProofResult {
bytes proof;
bytes inputs;
};
[Error]
enum MoproError {
"CircomError",
};
interface MoproCircom {
constructor();
[Throws=MoproError]
SetupResult setup(string wasm_path, string r1cs_path);
[Throws=MoproError]
GenerateProofResult generate_proof(record<string, sequence<string>> circuit_inputs);
[Throws=MoproError]
boolean verify_proof(bytes proof, bytes public_input);
};

View File

@@ -1,21 +0,0 @@
import uniffi.mopro.*
var wasmPath = "../mopro-core/examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm"
var r1csPath = "../mopro-core/examples/circom/multiplier2/target/multiplier2.r1cs"
try {
var moproCircom = MoproCircom()
var setupResult = moproCircom.setup(wasmPath, r1csPath)
assert(setupResult.provingKey.size > 0) { "Proving key should not be empty" }
val inputs = mutableMapOf<String, List<String>>()
inputs["a"] = listOf("3")
inputs["b"] = listOf("5")
var generateProofResult = moproCircom.generateProof(inputs)
assert(generateProofResult.proof.size > 0) { "Proof is empty" }
var isValid = moproCircom.verifyProof(generateProofResult.proof, generateProofResult.inputs)
assert(isValid) { "Proof is invalid" }
} catch (e: Exception) {
println(e)
}

View File

@@ -1,65 +0,0 @@
import mopro
import Foundation
let moproCircom = MoproCircom()
let wasmPath = "./../../../../mopro-core/examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm"
let r1csPath = "./../../../../mopro-core/examples/circom/multiplier2/target/multiplier2.r1cs"
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}
do {
// Setup
let setupResult = try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath)
assert(!setupResult.provingKey.isEmpty, "Proving key should not be empty")
// Prepare inputs
var inputs = [String: [String]]()
let a = 3
let b = 5
let c = a*b
inputs["a"] = [String(a)]
inputs["b"] = [String(b)]
// Expected outputs
let outputs: [String] = [String(c), String(a)]
let expectedOutput: [UInt8] = serializeOutputs(outputs)
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Verify Proof
assert(Data(expectedOutput) == generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}

View File

@@ -1,279 +0,0 @@
import uniffi.mopro.*
var wasmPath =
"../mopro-core/examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm"
var r1csPath = "../mopro-core/examples/circom/keccak256/target/keccak256_256_test.r1cs"
try {
var moproCircom = MoproCircom()
var setupResult = moproCircom.setup(wasmPath, r1csPath)
assert(setupResult.provingKey.size > 0) { "Proving key should not be empty" }
val inputs = mutableMapOf<String, List<String>>()
inputs["in"] =
listOf(
"0",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"1",
"0",
"1",
"0",
"0",
"1",
"1",
"0",
"1",
"1",
"0",
"0",
"1",
"1",
"1",
"0",
"0",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0"
)
var generateProofResult = moproCircom.generateProof(inputs)
assert(generateProofResult.proof.size > 0) { "Proof is empty" }
var isValid = moproCircom.verifyProof(generateProofResult.proof, generateProofResult.inputs)
assert(isValid) { "Proof is invalid" }
} catch (e: Exception) {
println(e)
}

View File

@@ -1,82 +0,0 @@
import mopro
import Foundation
let moproCircom = MoproCircom()
let wasmPath = "./../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm"
let r1csPath = "./../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test.r1cs"
// Helper function to convert bytes to bits
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}
do {
// Setup
let setupResult = try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath)
assert(!setupResult.provingKey.isEmpty, "Proving key should not be empty")
// Prepare inputs
let inputVec: [UInt8] = [
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
]
let bits = bytesToBits(bytes: inputVec)
var inputs = [String: [String]]()
inputs["in"] = bits
// Expected outputs
let outputVec: [UInt8] = [
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
]
let outputBits: [String] = bytesToBits(bytes: outputVec)
let expectedOutput: [UInt8] = serializeOutputs(outputBits)
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Verify Proof
assert(Data(expectedOutput) == generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}

View File

@@ -1,273 +0,0 @@
import uniffi.mopro.*
try {
initializeMopro()
val inputs = mutableMapOf<String, List<String>>()
inputs["in"] =
listOf(
"0",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"1",
"0",
"1",
"0",
"0",
"1",
"1",
"0",
"1",
"1",
"0",
"0",
"1",
"1",
"1",
"0",
"0",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0"
)
var generateProofResult = generateProof2(inputs)
assert(generateProofResult.proof.size > 0) { "Proof is empty" }
var isValid = verifyProof2(generateProofResult.proof, generateProofResult.inputs)
assert(isValid) { "Proof is invalid" }
} catch (e: Exception) {
println(e)
}

View File

@@ -1,86 +0,0 @@
import Foundation
import mopro
//let moproCircom = MoproCircom()
// Using zkey and generate_proof2
// let wasmPath = "./../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm"
// let r1csPath = "./../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test.r1cs"
// Helper function to convert bytes to bits
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}
do {
// // Setup
// let setupResult = try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath)
// assert(!setupResult.provingKey.isEmpty, "Proving key should not be empty")
// Prepare inputs
let inputVec: [UInt8] = [
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
]
let bits = bytesToBits(bytes: inputVec)
var inputs = [String: [String]]()
inputs["in"] = bits
// Expected outputs
let outputVec: [UInt8] = [
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
]
let outputBits: [String] = bytesToBits(bytes: outputVec)
let expectedOutput: [UInt8] = serializeOutputs(outputBits)
// // Generate Proof
let generateProofResult = try generateProof2(circuitInputs: inputs)
// let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// // Verify Proof
assert(Data(expectedOutput) == generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
let isValid = try verifyProof2(
proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}

View File

@@ -1,115 +0,0 @@
import uniffi.mopro.*;
var wasmPath = "../mopro-core/examples/circom/rsa/target/main_js/main.wasm"
var r1csPath = "../mopro-core/examples/circom/rsa/target/main.r1cs"
try {
var moproCircom = MoproCircom()
var setupResult = moproCircom.setup(wasmPath, r1csPath)
assert(setupResult.provingKey.size > 0) { "Proving key should not be empty"}
val inputs = mutableMapOf<String, List<String>>()
inputs["signature"] = listOf("3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493")
inputs["modulus"] = listOf("13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",)
inputs["base_message"] = listOf("18114495772705111902",
"2254271930739856077",
"2068851770",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",)
var generateProofResult = moproCircom.generateProof(inputs)
assert(generateProofResult.proof.size > 0) { "Proof is empty"}
var isValid = moproCircom.verifyProof(generateProofResult.proof, generateProofResult.inputs)
assert(isValid) { "Proof is invalid"}
} catch (e: Exception) {
println(e);
}

View File

@@ -1,174 +0,0 @@
import mopro
import Foundation
let moproCircom = MoproCircom()
let wasmPath = "./../../../../mopro-core/examples/circom/rsa/target/main_js/main.wasm"
let r1csPath = "./../../../../mopro-core/examples/circom/rsa/target/main.r1cs"
// Helper function to convert bytes to bits
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}
do {
// Setup
let setupResult = try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath)
assert(!setupResult.provingKey.isEmpty, "Proving key should not be empty")
// Prepare inputs
let signature: [String] = [
"3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493",
]
let modulus: [String] = [
"13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",
]
let base_message: [String] = [
"18114495772705111902",
"2254271930739856077",
"2068851770",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
]
var inputs = [String: [String]]()
inputs["signature"] = signature;
inputs["modulus"] = modulus;
inputs["base_message"] = base_message;
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Verifying the Proof
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}

View File

@@ -1,167 +0,0 @@
import mopro
import Foundation
// Helper function to convert bytes to bits
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}
do {
// Initialize
try initializeMopro()
// Prepare inputs
let signature: [String] = [
"3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493",
]
let modulus: [String] = [
"13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",
]
let base_message: [String] = [
"18114495772705111902",
"2254271930739856077",
"2068851770",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
]
var inputs = [String: [String]]()
inputs["signature"] = signature;
inputs["modulus"] = modulus;
inputs["base_message"] = base_message;
// Generate Proof
let generateProofResult = try generateProof2(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Verifying the Proof
let isValid = try verifyProof2(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}

View File

@@ -1,13 +0,0 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_mopro.swift",
"tests/bindings/test_mopro.kts",
// "tests/bindings/test_mopro.rb",
// "tests/bindings/test_mopro.py",
"tests/bindings/test_mopro_keccak.swift",
// "tests/bindings/test_mopro_keccak.kts", // FIXME: java.lang.OutOfMemoryError: Java heap space
"tests/bindings/test_mopro_keccak2.swift",
"tests/bindings/test_mopro_keccak2.kts",
"tests/bindings/test_mopro_rsa.swift",
// "tests/bindings/test_mopro_rsa.kts", // FIXME: java.lang.OutOfMemoryError: Java heap space
// "tests/bindings/test_mopro_rsa2.swift",
);

View File

@@ -1,3 +0,0 @@
fn main() {
uniffi::uniffi_bindgen_main()
}

View File

@@ -1,2 +0,0 @@
[bindings.swift]
module_name = "mopro"