From 2451f0be5b1f1b157182ebca2717e8f640f9dc96 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 17 Sep 2023 10:43:22 +0900 Subject: [PATCH 01/29] r1cs_parser --- Cargo.toml | 3 + src/frontend/circom/mod.rs | 114 +++++++++++ src/frontend/circom/r1cs_reader.rs | 250 +++++++++++++++++++++++ src/frontend/circom/test_folder/toy.r1cs | Bin 0 -> 220 bytes src/frontend/mod.rs | 1 + 5 files changed, 368 insertions(+) create mode 100644 src/frontend/circom/mod.rs create mode 100644 src/frontend/circom/r1cs_reader.rs create mode 100644 src/frontend/circom/test_folder/toy.r1cs diff --git a/Cargo.toml b/Cargo.toml index a8f99d54..95bc3319 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } thiserror = "1.0" rayon = "1.7.0" +byteorder = "=1.4.3" +ark-bn254 = { version = "=0.4.0" } +hex = "=0.4.3" # tmp imports for espresso's sumcheck ark-serialize = "0.4.2" diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs new file mode 100644 index 00000000..c95899bc --- /dev/null +++ b/src/frontend/circom/mod.rs @@ -0,0 +1,114 @@ +use std::fs::File; +use std::io::BufReader; +use std::error::Error; +use std::path::PathBuf; +use ark_bn254::{Bn254, Fr}; +use ark_ff::PrimeField; +use ark_ff::biginteger::BigInt; +use ark_ec::pairing::Pairing; +mod r1cs_reader; + +pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); +pub type ConstraintVec = Vec<(usize, ::ScalarField)>; + +pub fn bigint_to_bn254_scalar(bigint: BigInt<4>) -> Option { + Fr::from_bigint(bigint) +} + +pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints { + let convert_vec = |vec: ConstraintVec| -> ConstraintVec { + vec.into_iter() + .filter_map(|(index, bigint)| { + match bigint_to_bn254_scalar(bigint.into()) { + Some(scalar) => Some((index, scalar)), + None => None + } + }) + .collect() + }; + + (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) +} + +pub fn extract_constraints_from_r1cs(filename: &str) -> Result>, Box> { + let current_dir = std::env::current_dir()?; + let filepath: PathBuf = [current_dir.to_str().unwrap(), filename].iter().collect(); + let file = File::open(filepath)?; + let reader = BufReader::new(file); + + let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; + let r1cs = r1cs_reader::R1CS::::from(r1cs_file); + Ok(r1cs.constraints) +} + +pub fn convert_to_folding_r1cs(constraints: Vec>) -> crate::ccs::r1cs::R1CS { + // let mut a_matrix = Vec::new(); + // let mut b_matrix = Vec::new(); + // let mut c_matrix = Vec::new(); + let mut a_matrix: Vec> = Vec::new(); + let mut b_matrix: Vec> = Vec::new(); + let mut c_matrix: Vec> = Vec::new(); + + let n_rows = constraints.len(); + + for (ai, bi, ci) in constraints { + a_matrix.push(ai.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + b_matrix.push(bi.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + } + + let l = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); + let n_cols = l; + + let A = crate::utils::vec::SparseMatrix:: { + n_rows, + n_cols, + coeffs: a_matrix, + }; + let B = crate::utils::vec::SparseMatrix:: { + n_rows, + n_cols, + coeffs: b_matrix, + }; + let C = crate::utils::vec::SparseMatrix:: { + n_rows, + n_cols, + coeffs: c_matrix, + }; + + crate::ccs::r1cs::R1CS:: { + l, + A, + B, + C, + } +} + +#[test] +fn from_circom() { + let filename = "src/toy.r1cs"; + + match extract_constraints_from_r1cs(filename) { + Ok(constraints) => { + println!("Original Constraints:"); + for constraint in &constraints { + println!("{:?}", constraint); + } + + println!("Converted Constraints:"); + let converted_constraints: Vec> = constraints.iter().map(|constraint| { + convert_constraints_bigint_to_scalar(constraint.clone()) + }).collect(); + + for constraint in &converted_constraints { + println!("{:?}", constraint); + } + + let folding_r1cs = convert_to_folding_r1cs(converted_constraints); + println!("Folding R1CS: {:?}", folding_r1cs); + }, + Err(e) => { + eprintln!("Error while extracting constraints: {:?}", e); + } + } +} diff --git a/src/frontend/circom/r1cs_reader.rs b/src/frontend/circom/r1cs_reader.rs new file mode 100644 index 00000000..975e4fdf --- /dev/null +++ b/src/frontend/circom/r1cs_reader.rs @@ -0,0 +1,250 @@ +//! R1CS circom file reader +//! Copied from +//! Spec: +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io::{Error, ErrorKind}; + +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, SerializationError, SerializationError::IoError}; +use ark_std::io::{Read, Seek, SeekFrom}; + +use std::collections::HashMap; + +type IoResult = Result; + +pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); +pub type ConstraintVec = Vec<(usize, ::ScalarField)>; + +#[derive(Clone, Debug)] +pub struct R1CS { + pub num_inputs: usize, + pub num_aux: usize, + pub num_variables: usize, + pub constraints: Vec>, + pub wire_mapping: Option>, +} + +impl From> for R1CS { + fn from(file: R1CSFile) -> Self { + let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize; + let num_variables = file.header.n_wires as usize; + let num_aux = num_variables - num_inputs; + R1CS { + num_aux, + num_inputs, + num_variables, + constraints: file.constraints, + wire_mapping: Some(file.wire_mapping.iter().map(|e| *e as usize).collect()), + } + } +} + +pub struct R1CSFile { + pub version: u32, + pub header: Header, + pub constraints: Vec>, + pub wire_mapping: Vec, +} + +impl R1CSFile { + /// reader must implement the Seek trait, for example with a Cursor + /// + /// ```rust,ignore + /// let reader = BufReader::new(Cursor::new(&data[..])); + /// ``` + pub fn new(mut reader: R) -> IoResult> { + let mut magic = [0u8; 4]; + reader.read_exact(&mut magic)?; + if magic != [0x72, 0x31, 0x63, 0x73] { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "Invalid magic number", + ))); + } + + let version = reader.read_u32::()?; + if version != 1 { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "Unsupported version", + ))); + } + + let num_sections = reader.read_u32::()?; + + // todo: handle sec_size correctly + // section type -> file offset + let mut sec_offsets = HashMap::::new(); + let mut sec_sizes = HashMap::::new(); + + // get file offset of each section + for _ in 0..num_sections { + let sec_type = reader.read_u32::()?; + let sec_size = reader.read_u64::()?; + let offset = reader.stream_position()?; + sec_offsets.insert(sec_type, offset); + sec_sizes.insert(sec_type, sec_size); + reader.seek(SeekFrom::Current(sec_size as i64))?; + } + + let header_type = 1; + let constraint_type = 2; + let wire2label_type = 3; + + let header_offset = sec_offsets.get(&header_type).ok_or_else(|| { + Error::new( + ErrorKind::InvalidData, + "No section offset for header type found", + ) + }); + + reader.seek(SeekFrom::Start(*header_offset?))?; + + let header_size = sec_sizes.get(&header_type).ok_or_else(|| { + Error::new( + ErrorKind::InvalidData, + "No section size for header type found", + ) + }); + + let header = Header::new(&mut reader, *header_size?)?; + + let constraint_offset = sec_offsets.get(&constraint_type).ok_or_else(|| { + Error::new( + ErrorKind::InvalidData, + "No section offset for constraint type found", + ) + }); + + reader.seek(SeekFrom::Start(*constraint_offset?))?; + + let constraints = read_constraints::<&mut R, E>(&mut reader, &header)?; + + let wire2label_offset = sec_offsets.get(&wire2label_type).ok_or_else(|| { + Error::new( + ErrorKind::InvalidData, + "No section offset for wire2label type found", + ) + }); + + reader.seek(SeekFrom::Start(*wire2label_offset?))?; + + let wire2label_size = sec_sizes.get(&wire2label_type).ok_or_else(|| { + Error::new( + ErrorKind::InvalidData, + "No section size for wire2label type found", + ) + }); + + let wire_mapping = read_map(&mut reader, *wire2label_size?, &header)?; + + Ok(R1CSFile { + version, + header, + constraints, + wire_mapping, + }) + } +} + +pub struct Header { + pub field_size: u32, + pub prime_size: Vec, + pub n_wires: u32, + pub n_pub_out: u32, + pub n_pub_in: u32, + pub n_prv_in: u32, + pub n_labels: u64, + pub n_constraints: u32, +} + +impl Header { + fn new(mut reader: R, size: u64) -> IoResult
{ + let field_size = reader.read_u32::()?; + if field_size != 32 { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "This parser only supports 32-byte fields", + ))); + } + + if size != 32 + field_size as u64 { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "Invalid header section size", + ))); + } + + let mut prime_size = vec![0u8; field_size as usize]; + reader.read_exact(&mut prime_size)?; + + if prime_size + != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") + .unwrap() + { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "This parser only supports bn256", + ))); + } + + Ok(Header { + field_size, + prime_size, + n_wires: reader.read_u32::()?, + n_pub_out: reader.read_u32::()?, + n_pub_in: reader.read_u32::()?, + n_prv_in: reader.read_u32::()?, + n_labels: reader.read_u64::()?, + n_constraints: reader.read_u32::()?, + }) + } +} + +fn read_constraint_vec(mut reader: R) -> IoResult> { + let n_vec = reader.read_u32::()? as usize; + let mut vec = Vec::with_capacity(n_vec); + for _ in 0..n_vec { + vec.push(( + reader.read_u32::()? as usize, + E::ScalarField::deserialize_uncompressed(&mut reader)?, + )); + } + Ok(vec) +} + +fn read_constraints( + mut reader: R, + header: &Header, +) -> IoResult>> { + // todo check section size + let mut vec = Vec::with_capacity(header.n_constraints as usize); + for _ in 0..header.n_constraints { + vec.push(( + read_constraint_vec::<&mut R, E>(&mut reader)?, + read_constraint_vec::<&mut R, E>(&mut reader)?, + read_constraint_vec::<&mut R, E>(&mut reader)?, + )); + } + Ok(vec) +} + +fn read_map(mut reader: R, size: u64, header: &Header) -> IoResult> { + if size != header.n_wires as u64 * 8 { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "Invalid map section size", + ))); + } + let mut vec = Vec::with_capacity(header.n_wires as usize); + for _ in 0..header.n_wires { + vec.push(reader.read_u64::()?); + } + if vec[0] != 0 { + return Err(IoError(Error::new( + ErrorKind::InvalidData, + "Wire 0 should always be mapped to 0", + ))); + } + Ok(vec) +} \ No newline at end of file diff --git a/src/frontend/circom/test_folder/toy.r1cs b/src/frontend/circom/test_folder/toy.r1cs new file mode 100644 index 0000000000000000000000000000000000000000..f55206bcd50fc38b7751bc51de76a2f240494e09 GIT binary patch literal 220 zcmXRiOfF_*U|?VdVkRIC0b*nT5(CKt!H3CTA39Gg*jefE!dN3VqOozCYrqc81%?lc z{89`+>c0Q~{~n(jkU0)O3<3&34CLcA6XYh49uNir5FZ3Ud=Og##6bcubs)EZ00217 BFdqN_ literal 0 HcmV?d00001 diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 3222a46c..a6ab5532 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -1 +1,2 @@ pub mod arkworks; +pub mod circom; \ No newline at end of file From 90bbb332bf7553c5967a9a3e19da369b14a325b8 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:45:26 +0900 Subject: [PATCH 02/29] z vector calculation --- Cargo.toml | 17 +- src/frontend/circom/mod.rs | 44 +- src/frontend/circom/witness/circom.rs | 165 ++++++ src/frontend/circom/witness/memory.rs | 273 ++++++++++ src/frontend/circom/witness/mod.rs | 24 + .../circom/witness/witness_calculator.rs | 499 ++++++++++++++++++ 6 files changed, 1015 insertions(+), 7 deletions(-) create mode 100644 src/frontend/circom/witness/circom.rs create mode 100644 src/frontend/circom/witness/memory.rs create mode 100644 src/frontend/circom/witness/mod.rs create mode 100644 src/frontend/circom/witness/witness_calculator.rs diff --git a/Cargo.toml b/Cargo.toml index 95bc3319..0e0b3260 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,18 @@ byteorder = "=1.4.3" ark-bn254 = { version = "=0.4.0" } hex = "=0.4.3" +# WASM operations +wasmer = { version = "=2.3.0", default-features = false } +fnv = { version = "=1.0.7", default-features = false } +num = { version = "=0.4.0" } +num-traits = { version = "=0.2.15", default-features = false } +num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] } + +# error handling +color-eyre = "=0.6.2" +criterion = "=0.3.6" +cfg-if = "=1.0.0" + # tmp imports for espresso's sumcheck ark-serialize = "0.4.2" espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} @@ -27,11 +39,14 @@ espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", pack ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0"} ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["crh"] } +serde_json = "=1.0.94" [features] -default = ["parallel", "nova", "hypernova"] +default = ["parallel", "nova", "hypernova", "wasmer/default", "circom-2"] hypernova=[] nova=[] +wasm = ["wasmer/js-default"] +circom-2 = [] parallel = [ "ark-std/parallel", diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index c95899bc..6e8d18ca 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -4,14 +4,18 @@ use std::error::Error; use std::path::PathBuf; use ark_bn254::{Bn254, Fr}; use ark_ff::PrimeField; -use ark_ff::biginteger::BigInt; +use ark_ff::biginteger; use ark_ec::pairing::Pairing; +use num_bigint::BigInt; + mod r1cs_reader; +mod witness; + pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, ::ScalarField)>; -pub fn bigint_to_bn254_scalar(bigint: BigInt<4>) -> Option { +pub fn bigint_to_bn254_scalar(bigint: biginteger::BigInt<4>) -> Option { Fr::from_bigint(bigint) } @@ -42,9 +46,6 @@ pub fn extract_constraints_from_r1cs(filename: &str) -> Result>) -> crate::ccs::r1cs::R1CS { - // let mut a_matrix = Vec::new(); - // let mut b_matrix = Vec::new(); - // let mut c_matrix = Vec::new(); let mut a_matrix: Vec> = Vec::new(); let mut b_matrix: Vec> = Vec::new(); let mut c_matrix: Vec> = Vec::new(); @@ -84,6 +85,28 @@ pub fn convert_to_folding_r1cs(constraints: Vec>) -> crate::c } } +pub fn calculate_witness)>>(inputs: I) -> Result<(), Box> { + let current_dir = std::env::current_dir()?; + let wasm_path = current_dir.join("src").join("toy.wasm"); + + let mut calculator = witness::WitnessCalculator::new(wasm_path)?; + + match calculator.calculate_witness(inputs, true) { + Ok(witness) => { + println!("Witness as BigInt:"); + for w in &witness { + println!("{:?}", w); + } + }, + Err(e) => { + eprintln!("Error while calculating witness: {:?}", e); + return Err(Box::::from(format!("Witness calculation failed: {}", e))); + } + } + + Ok(()) +} + #[test] fn from_circom() { let filename = "src/toy.r1cs"; @@ -111,4 +134,13 @@ fn from_circom() { eprintln!("Error while extracting constraints: {:?}", e); } } -} + + let inputs = vec![ + ("step_in".to_string(), vec![BigInt::from(10)]), + ("adder".to_string(), vec![BigInt::from(2)]) + ]; + + if let Err(e) = calculate_witness(inputs) { + eprintln!("Failed to calculate the witness: {:?}", e); + } +} \ No newline at end of file diff --git a/src/frontend/circom/witness/circom.rs b/src/frontend/circom/witness/circom.rs new file mode 100644 index 00000000..f9f5aedd --- /dev/null +++ b/src/frontend/circom/witness/circom.rs @@ -0,0 +1,165 @@ +use color_eyre::Result; +use wasmer::{Function, Instance, Value}; + +#[derive(Clone, Debug)] +pub struct Wasm(Instance); + +pub trait CircomBase { + fn init(&self, sanity_check: bool) -> Result<()>; + fn func(&self, name: &str) -> &Function; + fn get_ptr_witness_buffer(&self) -> Result; + fn get_ptr_witness(&self, w: u32) -> Result; + fn get_n_vars(&self) -> Result; + fn get_signal_offset32( + &self, + p_sig_offset: u32, + component: u32, + hash_msb: u32, + hash_lsb: u32, + ) -> Result<()>; + fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()>; + fn get_u32(&self, name: &str) -> Result; + // Only exists natively in Circom2, hardcoded for Circom + fn get_version(&self) -> Result; +} + +pub trait Circom { + fn get_fr_len(&self) -> Result; + fn get_ptr_raw_prime(&self) -> Result; +} + +pub trait Circom2 { + fn get_field_num_len32(&self) -> Result; + fn get_raw_prime(&self) -> Result<()>; + fn read_shared_rw_memory(&self, i: u32) -> Result; + fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()>; + fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()>; + fn get_witness(&self, i: u32) -> Result<()>; + fn get_witness_size(&self) -> Result; +} + +impl Circom for Wasm { + fn get_fr_len(&self) -> Result { + self.get_u32("getFrLen") + } + + fn get_ptr_raw_prime(&self) -> Result { + self.get_u32("getPRawPrime") + } +} + +#[cfg(feature = "circom-2")] +impl Circom2 for Wasm { + fn get_field_num_len32(&self) -> Result { + self.get_u32("getFieldNumLen32") + } + + fn get_raw_prime(&self) -> Result<()> { + let func = self.func("getRawPrime"); + func.call(&[])?; + Ok(()) + } + + fn read_shared_rw_memory(&self, i: u32) -> Result { + let func = self.func("readSharedRWMemory"); + let result = func.call(&[i.into()])?; + Ok(result[0].unwrap_i32() as u32) + } + + fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()> { + let func = self.func("writeSharedRWMemory"); + func.call(&[i.into(), v.into()])?; + Ok(()) + } + + fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()> { + let func = self.func("setInputSignal"); + func.call(&[hmsb.into(), hlsb.into(), pos.into()])?; + Ok(()) + } + + fn get_witness(&self, i: u32) -> Result<()> { + let func = self.func("getWitness"); + func.call(&[i.into()])?; + Ok(()) + } + + fn get_witness_size(&self) -> Result { + self.get_u32("getWitnessSize") + } +} + +impl CircomBase for Wasm { + fn init(&self, sanity_check: bool) -> Result<()> { + let func = self.func("init"); + func.call(&[Value::I32(sanity_check as i32)])?; + Ok(()) + } + + fn get_ptr_witness_buffer(&self) -> Result { + self.get_u32("getWitnessBuffer") + } + + fn get_ptr_witness(&self, w: u32) -> Result { + let func = self.func("getPWitness"); + let res = func.call(&[w.into()])?; + + Ok(res[0].unwrap_i32() as u32) + } + + fn get_n_vars(&self) -> Result { + self.get_u32("getNVars") + } + + fn get_signal_offset32( + &self, + p_sig_offset: u32, + component: u32, + hash_msb: u32, + hash_lsb: u32, + ) -> Result<()> { + let func = self.func("getSignalOffset32"); + func.call(&[ + p_sig_offset.into(), + component.into(), + hash_msb.into(), + hash_lsb.into(), + ])?; + + Ok(()) + } + + fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()> { + let func = self.func("setSignal"); + func.call(&[c_idx.into(), component.into(), signal.into(), p_val.into()])?; + + Ok(()) + } + + // Default to version 1 if it isn't explicitly defined + fn get_version(&self) -> Result { + match self.0.exports.get_function("getVersion") { + Ok(func) => Ok(func.call(&[])?[0].unwrap_i32() as u32), + Err(_) => Ok(1), + } + } + + fn get_u32(&self, name: &str) -> Result { + let func = self.func(name); + let result = func.call(&[])?; + Ok(result[0].unwrap_i32() as u32) + } + + fn func(&self, name: &str) -> &Function { + self.0 + .exports + .get_function(name) + .unwrap_or_else(|_| panic!("function {} not found", name)) + } +} + +impl Wasm { + pub fn new(instance: Instance) -> Self { + Self(instance) + } +} diff --git a/src/frontend/circom/witness/memory.rs b/src/frontend/circom/witness/memory.rs new file mode 100644 index 00000000..1298b411 --- /dev/null +++ b/src/frontend/circom/witness/memory.rs @@ -0,0 +1,273 @@ +//! Safe-ish interface for reading and writing specific types to the WASM runtime's memory +use ark_serialize::CanonicalDeserialize; +use num_traits::ToPrimitive; +use wasmer::{Memory, MemoryView}; + +// TODO: Decide whether we want Ark here or if it should use a generic BigInt package +use ark_bn254::FrConfig; +use ark_ff::MontConfig; +use ark_ff::{BigInteger, BigInteger256, Zero}; + +use num_bigint::{BigInt, BigUint}; + +use color_eyre::Result; +use std::str::FromStr; +use std::{convert::TryFrom, ops::Deref}; + +#[derive(Clone, Debug)] +pub struct SafeMemory { + pub memory: Memory, + pub prime: BigInt, + + short_max: BigInt, + short_min: BigInt, + r_inv: BigInt, + n32: usize, +} + +impl Deref for SafeMemory { + type Target = Memory; + + fn deref(&self) -> &Self::Target { + &self.memory + } +} + +impl SafeMemory { + /// Creates a new SafeMemory + pub fn new(memory: Memory, n32: usize, prime: BigInt) -> Self { + // TODO: Figure out a better way to calculate these + let short_max = BigInt::from(0x8000_0000u64); + let short_min = BigInt::from_biguint( + num_bigint::Sign::NoSign, + BigUint::try_from(FrConfig::MODULUS).unwrap(), + ) - &short_max; + let r_inv = BigInt::from_str( + "9915499612839321149637521777990102151350674507940716049588462388200839649614", + ) + .unwrap(); + + Self { + memory, + prime, + + short_max, + short_min, + r_inv, + n32, + } + } + + /// Gets an immutable view to the memory in 32 byte chunks + pub fn view(&self) -> MemoryView { + self.memory.view() + } + + /// Returns the next free position in the memory + pub fn free_pos(&self) -> u32 { + self.view()[0].get() + } + + /// Sets the next free position in the memory + pub fn set_free_pos(&mut self, ptr: u32) { + self.write_u32(0, ptr); + } + + /// Allocates a U32 in memory + pub fn alloc_u32(&mut self) -> u32 { + let p = self.free_pos(); + self.set_free_pos(p + 8); + p + } + + /// Writes a u32 to the specified memory offset + pub fn write_u32(&mut self, ptr: usize, num: u32) { + let buf = unsafe { self.memory.data_unchecked_mut() }; + buf[ptr..ptr + std::mem::size_of::()].copy_from_slice(&num.to_le_bytes()); + } + + /// Reads a u32 from the specified memory offset + pub fn read_u32(&self, ptr: usize) -> u32 { + let buf = unsafe { self.memory.data_unchecked() }; + + let mut bytes = [0; 4]; + bytes.copy_from_slice(&buf[ptr..ptr + std::mem::size_of::()]); + + u32::from_le_bytes(bytes) + } + + /// Allocates `self.n32 * 4 + 8` bytes in the memory + pub fn alloc_fr(&mut self) -> u32 { + let p = self.free_pos(); + self.set_free_pos(p + self.n32 as u32 * 4 + 8); + p + } + + /// Writes a Field Element to memory at the specified offset, truncating + /// to smaller u32 types if needed and adjusting the sign via 2s complement + pub fn write_fr(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + if fr < &self.short_max && fr > &self.short_min { + if fr >= &BigInt::zero() { + self.write_short_positive(ptr, fr)?; + } else { + self.write_short_negative(ptr, fr)?; + } + } else { + self.write_long_normal(ptr, fr)?; + } + + Ok(()) + } + + /// Reads a Field Element from the memory at the specified offset + pub fn read_fr(&self, ptr: usize) -> Result { + let view = self.memory.view::(); + + let res = if view[ptr + 4 + 3].get() & 0x80 != 0 { + let mut num = self.read_big(ptr + 8, self.n32)?; + if view[ptr + 4 + 3].get() & 0x40 != 0 { + num = (num * &self.r_inv) % &self.prime + } + num + } else if view[ptr + 3].get() & 0x40 != 0 { + let mut num = self.read_u32(ptr).into(); + // handle small negative + num -= BigInt::from(0x100000000i64); + num + } else { + self.read_u32(ptr).into() + }; + + Ok(res) + } + + fn write_short_positive(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + let num = fr.to_i32().expect("not a short positive"); + self.write_u32(ptr, num as u32); + self.write_u32(ptr + 4, 0); + Ok(()) + } + + fn write_short_negative(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + // 2s complement + let num = fr - &self.short_min; + let num = num - &self.short_max; + let num = num + BigInt::from(0x0001_0000_0000i64); + + let num = num + .to_u32() + .expect("could not cast as u32 (should never happen)"); + + self.write_u32(ptr, num); + self.write_u32(ptr + 4, 0); + Ok(()) + } + + fn write_long_normal(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + self.write_u32(ptr, 0); + self.write_u32(ptr + 4, i32::MIN as u32); // 0x80000000 + self.write_big(ptr + 8, fr)?; + Ok(()) + } + + fn write_big(&self, ptr: usize, num: &BigInt) -> Result<()> { + let buf = unsafe { self.memory.data_unchecked_mut() }; + + // TODO: How do we handle negative bignums? + let (_, num) = num.clone().into_parts(); + let num = BigInteger256::try_from(num).unwrap(); + + let bytes = num.to_bytes_le(); + let len = bytes.len(); + buf[ptr..ptr + len].copy_from_slice(&bytes); + + Ok(()) + } + + /// Reads `num_bytes * 32` from the specified memory offset in a Big Integer + pub fn read_big(&self, ptr: usize, num_bytes: usize) -> Result { + let buf = unsafe { self.memory.data_unchecked() }; + let buf = &buf[ptr..ptr + num_bytes * 32]; + + // TODO: Is there a better way to read big integers? + let big = BigInteger256::deserialize_uncompressed(buf).unwrap(); + let big = BigUint::try_from(big).unwrap(); + Ok(big.into()) + } +} + +// TODO: Figure out how to read / write numbers > u32 +// circom-witness-calculator: Wasm + Memory -> expose BigInts so that they can be consumed by any proof system +// ark-circom: +// 1. can read zkey +// 2. can generate witness from inputs +// 3. can generate proofs +// 4. can serialize proofs in the desired format +#[cfg(test)] +mod tests { + use super::*; + use num_traits::ToPrimitive; + use std::str::FromStr; + use wasmer::{MemoryType, Store}; + + fn new() -> SafeMemory { + SafeMemory::new( + Memory::new(&Store::default(), MemoryType::new(1, None, false)).unwrap(), + 2, + BigInt::from_str( + "21888242871839275222246405745257275088548364400416034343698204186575808495617", + ) + .unwrap(), + ) + } + + #[test] + fn i32_bounds() { + let mem = new(); + let i32_max = i32::MAX as i64 + 1; + assert_eq!(mem.short_min.to_i64().unwrap(), -i32_max); + assert_eq!(mem.short_max.to_i64().unwrap(), i32_max); + } + + #[test] + fn read_write_32() { + let mut mem = new(); + let num = u32::MAX; + + let inp = mem.read_u32(0); + assert_eq!(inp, 0); + + mem.write_u32(0, num); + let inp = mem.read_u32(0); + assert_eq!(inp, num); + } + + #[test] + fn read_write_fr_small_positive() { + read_write_fr(BigInt::from(1_000_000)); + } + + #[test] + fn read_write_fr_small_negative() { + read_write_fr(BigInt::from(-1_000_000)); + } + + #[test] + fn read_write_fr_big_positive() { + read_write_fr(BigInt::from(500000000000i64)); + } + + // TODO: How should this be handled? + #[test] + #[ignore] + fn read_write_fr_big_negative() { + read_write_fr(BigInt::from_str("-500000000000").unwrap()) + } + + fn read_write_fr(num: BigInt) { + let mut mem = new(); + mem.write_fr(0, &num).unwrap(); + let res = mem.read_fr(0).unwrap(); + assert_eq!(res, num); + } +} diff --git a/src/frontend/circom/witness/mod.rs b/src/frontend/circom/witness/mod.rs new file mode 100644 index 00000000..51708aa7 --- /dev/null +++ b/src/frontend/circom/witness/mod.rs @@ -0,0 +1,24 @@ +mod witness_calculator; +pub use witness_calculator::WitnessCalculator; + +mod memory; +pub(super) use memory::SafeMemory; + +mod circom; +pub(super) use circom::{CircomBase, Wasm}; + +#[cfg(feature = "circom-2")] +pub(super) use circom::Circom2; + +pub(super) use circom::Circom; + +use fnv::FnvHasher; +use std::hash::Hasher; + +pub(crate) fn fnv(inp: &str) -> (u32, u32) { + let mut hasher = FnvHasher::default(); + hasher.write(inp.as_bytes()); + let h = hasher.finish(); + + ((h >> 32) as u32, h as u32) +} diff --git a/src/frontend/circom/witness/witness_calculator.rs b/src/frontend/circom/witness/witness_calculator.rs new file mode 100644 index 00000000..49582b9f --- /dev/null +++ b/src/frontend/circom/witness/witness_calculator.rs @@ -0,0 +1,499 @@ +use super::{fnv, CircomBase, SafeMemory, Wasm}; +use color_eyre::Result; +use num_bigint::BigInt; +use num_traits::Zero; +use std::cell::Cell; +use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, RuntimeError, Store}; + +#[cfg(feature = "circom-2")] +use num::ToPrimitive; + +#[cfg(feature = "circom-2")] +use super::Circom2; + +use super::Circom; + +#[derive(Clone, Debug)] +pub struct WitnessCalculator { + pub instance: Wasm, + pub memory: SafeMemory, + pub n64: u32, + pub circom_version: u32, +} + +// Error type to signal end of execution. +// From https://docs.wasmer.io/integrations/examples/exit-early +#[derive(thiserror::Error, Debug, Clone, Copy)] +#[error("{0}")] +struct ExitCode(u32); + +#[cfg(feature = "circom-2")] +fn from_array32(arr: Vec) -> BigInt { + let mut res = BigInt::zero(); + let radix = BigInt::from(0x100000000u64); + for &val in arr.iter() { + res = res * &radix + BigInt::from(val); + } + res +} + +#[cfg(feature = "circom-2")] +fn to_array32(s: &BigInt, size: usize) -> Vec { + let mut res = vec![0; size]; + let mut rem = s.clone(); + let radix = BigInt::from(0x100000000u64); + let mut c = size; + while !rem.is_zero() { + c -= 1; + res[c] = (&rem % &radix).to_u32().unwrap(); + rem /= &radix; + } + + res +} + +impl WitnessCalculator { + pub fn new(path: impl AsRef) -> Result { + Self::from_file(path) + } + + pub fn from_file(path: impl AsRef) -> Result { + let store = Store::default(); + let module = Module::from_file(&store, path)?; + Self::from_module(module) + } + + pub fn from_module(module: Module) -> Result { + let store = module.store(); + + // Set up the memory + let memory = Memory::new(store, MemoryType::new(2000, None, false)).unwrap(); + let import_object = imports! { + "env" => { + "memory" => memory.clone(), + }, + // Host function callbacks from the WASM + "runtime" => { + "error" => runtime::error(store), + "logSetSignal" => runtime::log_signal(store), + "logGetSignal" => runtime::log_signal(store), + "logFinishComponent" => runtime::log_component(store), + "logStartComponent" => runtime::log_component(store), + "log" => runtime::log_component(store), + "exceptionHandler" => runtime::exception_handler(store), + "showSharedRWMemory" => runtime::show_memory(store), + "printErrorMessage" => runtime::print_error_message(store), + "writeBufferMessage" => runtime::write_buffer_message(store), + } + }; + let instance = Wasm::new(Instance::new(&module, &import_object)?); + + let version = instance.get_version().unwrap_or(1); + + // Circom 2 feature flag with version 2 + #[cfg(feature = "circom-2")] + fn new_circom2(instance: Wasm, memory: Memory, version: u32) -> Result { + let n32 = instance.get_field_num_len32()?; + let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); + instance.get_raw_prime()?; + let mut arr = vec![0; n32 as usize]; + for i in 0..n32 { + let res = instance.read_shared_rw_memory(i)?; + arr[(n32 as usize) - (i as usize) - 1] = res; + } + let prime = from_array32(arr); + + let n64 = ((prime.bits() - 1) / 64 + 1) as u32; + safe_memory.prime = prime; + + Ok(WitnessCalculator { + instance, + memory: safe_memory, + n64, + circom_version: version, + }) + } + + fn new_circom1(instance: Wasm, memory: Memory, version: u32) -> Result { + // Fallback to Circom 1 behavior + let n32 = (instance.get_fr_len()? >> 2) - 2; + let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); + let ptr = instance.get_ptr_raw_prime()?; + let prime = safe_memory.read_big(ptr as usize, n32 as usize)?; + + let n64 = ((prime.bits() - 1) / 64 + 1) as u32; + safe_memory.prime = prime; + + Ok(WitnessCalculator { + instance, + memory: safe_memory, + n64, + circom_version: version, + }) + } + + // Three possibilities: + // a) Circom 2 feature flag enabled, WASM runtime version 2 + // b) Circom 2 feature flag enabled, WASM runtime version 1 + // c) Circom 1 default behavior + // + // Once Circom 2 support is more stable, feature flag can be removed + + cfg_if::cfg_if! { + if #[cfg(feature = "circom-2")] { + match version { + 2 => new_circom2(instance, memory, version), + 1 => new_circom1(instance, memory, version), + _ => panic!("Unknown Circom version") + } + } else { + new_circom1(instance, memory, version) + } + } + } + + pub fn calculate_witness)>>( + &mut self, + inputs: I, + sanity_check: bool, + ) -> Result> { + self.instance.init(sanity_check)?; + + cfg_if::cfg_if! { + if #[cfg(feature = "circom-2")] { + match self.circom_version { + 2 => self.calculate_witness_circom2(inputs, sanity_check), + 1 => self.calculate_witness_circom1(inputs, sanity_check), + _ => panic!("Unknown Circom version") + } + } else { + self.calculate_witness_circom1(inputs, sanity_check) + } + } + } + + // Circom 1 default behavior + fn calculate_witness_circom1)>>( + &mut self, + inputs: I, + sanity_check: bool, + ) -> Result> { + self.instance.init(sanity_check)?; + + let old_mem_free_pos = self.memory.free_pos(); + let p_sig_offset = self.memory.alloc_u32(); + let p_fr = self.memory.alloc_fr(); + + // allocate the inputs + for (name, values) in inputs.into_iter() { + let (msb, lsb) = fnv(&name); + + self.instance + .get_signal_offset32(p_sig_offset, 0, msb, lsb)?; + + let sig_offset = self.memory.read_u32(p_sig_offset as usize) as usize; + + for (i, value) in values.into_iter().enumerate() { + self.memory.write_fr(p_fr as usize, &value)?; + self.instance + .set_signal(0, 0, (sig_offset + i) as u32, p_fr)?; + } + } + + let mut w = Vec::new(); + + let n_vars = self.instance.get_n_vars()?; + for i in 0..n_vars { + let ptr = self.instance.get_ptr_witness(i)? as usize; + let el = self.memory.read_fr(ptr)?; + w.push(el); + } + + self.memory.set_free_pos(old_mem_free_pos); + + Ok(w) + } + + // Circom 2 feature flag with version 2 + #[cfg(feature = "circom-2")] + fn calculate_witness_circom2)>>( + &mut self, + inputs: I, + sanity_check: bool, + ) -> Result> { + self.instance.init(sanity_check)?; + + let n32 = self.instance.get_field_num_len32()?; + + // allocate the inputs + for (name, values) in inputs.into_iter() { + let (msb, lsb) = fnv(&name); + + for (i, value) in values.into_iter().enumerate() { + let f_arr = to_array32(&value, n32 as usize); + for j in 0..n32 { + self.instance + .write_shared_rw_memory(j, f_arr[(n32 as usize) - 1 - (j as usize)])?; + } + self.instance.set_input_signal(msb, lsb, i as u32)?; + } + } + + let mut w = Vec::new(); + + let witness_size = self.instance.get_witness_size()?; + for i in 0..witness_size { + self.instance.get_witness(i)?; + let mut arr = vec![0; n32 as usize]; + for j in 0..n32 { + arr[(n32 as usize) - 1 - (j as usize)] = self.instance.read_shared_rw_memory(j)?; + } + w.push(from_array32(arr)); + } + + Ok(w) + } + + pub fn calculate_witness_element< + E: ark_ec::pairing::Pairing, + I: IntoIterator)>, + >( + &mut self, + inputs: I, + sanity_check: bool, + ) -> Result> { + use ark_ff::PrimeField; + let witness = self.calculate_witness(inputs, sanity_check)?; + let modulus = ::MODULUS; + + // convert it to field elements + use num_traits::Signed; + let witness = witness + .into_iter() + .map(|w| { + let w = if w.sign() == num_bigint::Sign::Minus { + // Need to negate the witness element if negative + modulus.into() - w.abs().to_biguint().unwrap() + } else { + w.to_biguint().unwrap() + }; + E::ScalarField::from(w) + }) + .collect::>(); + + Ok(witness) + } + + pub fn get_witness_buffer(&self) -> Result> { + let ptr = self.instance.get_ptr_witness_buffer()? as usize; + + let view = self.memory.memory.view::(); + + let len = self.instance.get_n_vars()? * self.n64 * 8; + let arr = view[ptr..ptr + len as usize] + .iter() + .map(Cell::get) + .collect::>(); + + Ok(arr) + } +} + +// callback hooks for debugging +mod runtime { + use super::*; + + pub fn error(store: &Store) -> Function { + #[allow(unused)] + #[allow(clippy::many_single_char_names)] + fn func(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) -> Result<(), RuntimeError> { + // NOTE: We can also get more information why it is failing, see p2str etc here: + // https://github.com/iden3/circom_runtime/blob/master/js/witness_calculator.js#L52-L64 + println!("runtime error, exiting early: {a} {b} {c} {d} {e} {f}",); + Err(RuntimeError::user(Box::new(ExitCode(1)))) + } + Function::new_native(store, func) + } + + // Circom 2.0 + pub fn exception_handler(store: &Store) -> Function { + #[allow(unused)] + fn func(a: i32) {} + Function::new_native(store, func) + } + + // Circom 2.0 + pub fn show_memory(store: &Store) -> Function { + #[allow(unused)] + fn func() {} + Function::new_native(store, func) + } + + // Circom 2.0 + pub fn print_error_message(store: &Store) -> Function { + #[allow(unused)] + fn func() {} + Function::new_native(store, func) + } + + // Circom 2.0 + pub fn write_buffer_message(store: &Store) -> Function { + #[allow(unused)] + fn func() {} + Function::new_native(store, func) + } + + pub fn log_signal(store: &Store) -> Function { + #[allow(unused)] + fn func(a: i32, b: i32) {} + Function::new_native(store, func) + } + + pub fn log_component(store: &Store) -> Function { + #[allow(unused)] + fn func(a: i32) {} + Function::new_native(store, func) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, path::PathBuf}; + + struct TestCase<'a> { + circuit_path: &'a str, + inputs_path: &'a str, + n_vars: u32, + n64: u32, + witness: &'a [&'a str], + } + + pub fn root_path(p: &str) -> String { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push(p); + path.to_string_lossy().to_string() + } + + #[test] + fn multiplier_1() { + run_test(TestCase { + circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), + inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(), + n_vars: 4, + n64: 4, + witness: &["1", "33", "3", "11"], + }); + } + + #[test] + fn multiplier_2() { + run_test(TestCase { + circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), + inputs_path: root_path("test-vectors/mycircuit-input2.json").as_str(), + n_vars: 4, + n64: 4, + witness: &[ + "1", + "21888242871839275222246405745257275088548364400416034343698204186575672693159", + "21888242871839275222246405745257275088548364400416034343698204186575796149939", + "11", + ], + }); + } + + #[test] + fn multiplier_3() { + run_test(TestCase { + circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), + inputs_path: root_path("test-vectors/mycircuit-input3.json").as_str(), + n_vars: 4, + n64: 4, + witness: &[ + "1", + "21888242871839275222246405745257275088548364400416034343698204186575808493616", + "10944121435919637611123202872628637544274182200208017171849102093287904246808", + "2", + ], + }); + } + + #[test] + fn safe_multipler() { + let witness = + std::fs::read_to_string(root_path("test-vectors/safe-circuit-witness.json")).unwrap(); + let witness: Vec = serde_json::from_str(&witness).unwrap(); + let witness = &witness.iter().map(|x| x.as_ref()).collect::>(); + run_test(TestCase { + circuit_path: root_path("test-vectors/circuit2.wasm").as_str(), + inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(), + n_vars: 132, // 128 + 4 + n64: 4, + witness, + }); + } + + #[test] + fn smt_verifier() { + let witness = + std::fs::read_to_string(root_path("test-vectors/smtverifier10-witness.json")).unwrap(); + let witness: Vec = serde_json::from_str(&witness).unwrap(); + let witness = &witness.iter().map(|x| x.as_ref()).collect::>(); + + run_test(TestCase { + circuit_path: root_path("test-vectors/smtverifier10.wasm").as_str(), + inputs_path: root_path("test-vectors/smtverifier10-input.json").as_str(), + n_vars: 4794, + n64: 4, + witness, + }); + } + + use serde_json::Value; + use std::str::FromStr; + + fn value_to_bigint(v: Value) -> BigInt { + match v { + Value::String(inner) => BigInt::from_str(&inner).unwrap(), + Value::Number(inner) => BigInt::from(inner.as_u64().expect("not a u32")), + _ => panic!("unsupported type"), + } + } + + fn run_test(case: TestCase) { + let mut wtns = WitnessCalculator::new(case.circuit_path).unwrap(); + assert_eq!( + wtns.memory.prime.to_str_radix(16), + "30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase() + ); + assert_eq!({ wtns.instance.get_n_vars().unwrap() }, case.n_vars); + assert_eq!({ wtns.n64 }, case.n64); + + let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap(); + let inputs: std::collections::HashMap = + serde_json::from_str(&inputs_str).unwrap(); + + let inputs = inputs + .iter() + .map(|(key, value)| { + let res = match value { + Value::String(inner) => { + vec![BigInt::from_str(inner).unwrap()] + } + Value::Number(inner) => { + vec![BigInt::from(inner.as_u64().expect("not a u32"))] + } + Value::Array(inner) => inner.iter().cloned().map(value_to_bigint).collect(), + _ => panic!(), + }; + + (key.clone(), res) + }) + .collect::>(); + + let res = wtns.calculate_witness(inputs, false).unwrap(); + for (r, w) in res.iter().zip(case.witness) { + assert_eq!(r, &BigInt::from_str(w).unwrap()); + } + } +} From da1851fa72b19f5485f5407ce5e001ea5bfc9bc4 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 17 Sep 2023 20:23:08 +0900 Subject: [PATCH 03/29] test function done --- Cargo.toml | 2 + src/frontend/circom/mod.rs | 130 ++++++++++-------- .../circom/test_folder/mycircuit.wasm | Bin 0 -> 30625 bytes src/frontend/circom/test_folder/toy.r1cs | Bin 220 -> 264 bytes src/frontend/circom/test_folder/toy.wasm | Bin 0 -> 34279 bytes .../test_folder/toy_vitalik_example.r1cs | Bin 0 -> 464 bytes 6 files changed, 75 insertions(+), 57 deletions(-) create mode 100644 src/frontend/circom/test_folder/mycircuit.wasm create mode 100644 src/frontend/circom/test_folder/toy.wasm create mode 100644 src/frontend/circom/test_folder/toy_vitalik_example.r1cs diff --git a/Cargo.toml b/Cargo.toml index 0e0b3260..3ded1b0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] color-eyre = "=0.6.2" criterion = "=0.3.6" cfg-if = "=1.0.0" +anyhow = "1.0" + # tmp imports for espresso's sumcheck ark-serialize = "0.4.2" diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 6e8d18ca..39c483b6 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -2,16 +2,18 @@ use std::fs::File; use std::io::BufReader; use std::error::Error; use std::path::PathBuf; + +use num_bigint::BigInt; +use color_eyre::Result; + use ark_bn254::{Bn254, Fr}; use ark_ff::PrimeField; use ark_ff::biginteger; use ark_ec::pairing::Pairing; -use num_bigint::BigInt; mod r1cs_reader; mod witness; - pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, ::ScalarField)>; @@ -34,10 +36,8 @@ pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) } -pub fn extract_constraints_from_r1cs(filename: &str) -> Result>, Box> { - let current_dir = std::env::current_dir()?; - let filepath: PathBuf = [current_dir.to_str().unwrap(), filename].iter().collect(); - let file = File::open(filepath)?; +pub fn extract_constraints_from_r1cs(filename: &PathBuf) -> Result>, Box> { + let file = File::open(filename)?; let reader = BufReader::new(file); let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; @@ -85,62 +85,78 @@ pub fn convert_to_folding_r1cs(constraints: Vec>) -> crate::c } } -pub fn calculate_witness)>>(inputs: I) -> Result<(), Box> { - let current_dir = std::env::current_dir()?; - let wasm_path = current_dir.join("src").join("toy.wasm"); - - let mut calculator = witness::WitnessCalculator::new(wasm_path)?; - - match calculator.calculate_witness(inputs, true) { - Ok(witness) => { - println!("Witness as BigInt:"); - for w in &witness { - println!("{:?}", w); - } - }, - Err(e) => { - eprintln!("Error while calculating witness: {:?}", e); - return Err(Box::::from(format!("Witness calculation failed: {}", e))); - } - } - - Ok(()) +pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { + let mut calculator = witness::WitnessCalculator::new(wasm_filepath.clone())?; + calculator.calculate_witness(inputs, true) } -#[test] -fn from_circom() { - let filename = "src/toy.r1cs"; +fn bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result, Box> { + let big_uint = value.to_biguint().ok_or_else(|| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt is negative")))?; + Ok(ark_ff::BigInt::<4>::try_from(big_uint).map_err(|_| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt conversion failed")))?) +} - match extract_constraints_from_r1cs(filename) { - Ok(constraints) => { - println!("Original Constraints:"); - for constraint in &constraints { - println!("{:?}", constraint); +pub fn circom_to_folding_r1cs_and_z( + constraints: Vec>, + witness: &Vec, +) -> Result<(crate::ccs::r1cs::R1CS, Vec), Box> { + let folding_r1cs = convert_to_folding_r1cs(constraints); + + let z: Vec = witness + .iter() + .filter_map(|big_int| { + match bigint_to_ark_bigint(big_int) { + Ok(ark_big_int) => bigint_to_bn254_scalar(ark_big_int.into()), + Err(_) => None } + }) + .collect(); - println!("Converted Constraints:"); - let converted_constraints: Vec> = constraints.iter().map(|constraint| { - convert_constraints_bigint_to_scalar(constraint.clone()) - }).collect(); - - for constraint in &converted_constraints { - println!("{:?}", constraint); - } + Ok((folding_r1cs, z)) +} - let folding_r1cs = convert_to_folding_r1cs(converted_constraints); - println!("Folding R1CS: {:?}", folding_r1cs); - }, - Err(e) => { - eprintln!("Error while extracting constraints: {:?}", e); - } +#[cfg(test)] +mod tests { + use ark_bn254::Bn254; + use num_bigint::BigInt; + + use super::Constraints; + use super::calculate_witness; + use super::circom_to_folding_r1cs_and_z; + use super::convert_constraints_bigint_to_scalar; + use super::extract_constraints_from_r1cs; + + #[test] + fn test_circom_to_folding_conversion() { + let current_dir = std::env::current_dir().unwrap(); + + let r1cs_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.r1cs"); + let wasm_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.wasm"); + + assert!(r1cs_filepath.exists(), "R1CS filepath does not exist."); + assert!(wasm_filepath.exists(), "WASM filepath does not exist."); + + let constraints = extract_constraints_from_r1cs(&r1cs_filepath).expect("Failed to extract constraints"); + assert!(!constraints.is_empty(), "No constraints were extracted."); + + let converted_constraints: Vec> = constraints + .iter() + .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) + .collect(); + assert_eq!(constraints.len(), converted_constraints.len(), "Converted constraints count doesn't match the original."); + + let inputs = vec![ + ("step_in".to_string(), vec![BigInt::from(10)]), + ("adder".to_string(), vec![BigInt::from(2)]), + ]; + + let witness = calculate_witness(&wasm_filepath, inputs).expect("Failed to calculate the witness"); + assert!(!witness.is_empty(), "Witness calculation resulted in an empty vector."); + + let (r1cs, z) = circom_to_folding_r1cs_and_z(converted_constraints, &witness) + .expect("Failed to convert circom R1CS to folding R1CS"); + assert!(!z.is_empty(), "The z vector is empty."); + + r1cs.check_relation(&z).expect("R1CS relation check failed"); } - let inputs = vec![ - ("step_in".to_string(), vec![BigInt::from(10)]), - ("adder".to_string(), vec![BigInt::from(2)]) - ]; - - if let Err(e) = calculate_witness(inputs) { - eprintln!("Failed to calculate the witness: {:?}", e); - } -} \ No newline at end of file +} diff --git a/src/frontend/circom/test_folder/mycircuit.wasm b/src/frontend/circom/test_folder/mycircuit.wasm new file mode 100644 index 0000000000000000000000000000000000000000..4a28f0c11c19939b3d70aae10d311b9d7078fb1a GIT binary patch literal 30625 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 literal 0 HcmV?d00001 diff --git a/src/frontend/circom/test_folder/toy.r1cs b/src/frontend/circom/test_folder/toy.r1cs index f55206bcd50fc38b7751bc51de76a2f240494e09..8db3126d2f00f35fa87a3b5d4c4d4202ea7bf20c 100644 GIT binary patch literal 264 zcmXRiOfF_*U|?VdVkRK20AdgTiGlb)@L}@Tht3lVc2;`4FxH5TXl&f(8n8oif#Jg< zzZ3(QUJ#8BfaIZS2%6^rHAn%X2d{b-h#IijAoD?d5CE}3eo_EpWB_#o%x_>dAb9}V Co-JYk delta 94 zcmeBRy2GeiWSCsc$iTqB48%-890J7103^mZQC67)#Q6UI|NDt0K8hg4AWa|)0w6vJ LzzmX@_)s1IIgJZR diff --git a/src/frontend/circom/test_folder/toy.wasm b/src/frontend/circom/test_folder/toy.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5498f5cbdb0e0916da7f3e681ac8fdbe850042fe GIT binary patch literal 34279 zcmeHwdypK*dEfNR>~r=pxA)-Q6SGS|1W1Co7XXUp19KoiQUpK{;6tQD0f)N8UM$w zPz+b)ihGj~xeTg&<+5~IMLLBqUuKti*2||Q>9m9rE(3Pi;zA`HR+8LO6Riu4PPJaq zD{Bjtl}@$UczV9ESgW+8tV8NsX;mAYL#yoA{FG);hilV4$Op@Y>WGsd|{+A?KVzFcrx0sw@jLDKL z$6|_t4A!3%i=|TOSd2Ldykt!3%iu4k=d$@+p-)3No6TnQY%Zt9*a{fM6b6xhT-M`t zE`=Nz0@vCyOGvDe+uM}F9`9(iP zdbOX!5=i}oT5VK2(rdYPrSnW<&TwNBp?;*&n3i!=RCOfFr!1BR1K;Ouz7)~z2;|(QB<%>qhZ@gERr7|2 z&kO1S)Ewioa>g$qfmsRA7R)_4tOQREt42=_E5Vb)662-3#{WpkJ74>&p4$Hit>CgD z?Uf-IF0YpqLpGGh5k;CxY3l2SGG~0jrKm)KUbqA(F!^3NLzGN_k|7Gv!X-d~f9#b9 zh%ykM3=pMAlwyEVB+3|3#sZWvF6!IbPsl#4-mtWqyNSR2RiAQiP>DBf<=hM&9iuaN zRE^Hy(KtGTNAc(k9{r;;cwmUmz|nZ~ZS61etZ*56TIv&cQaMKON#*!uQeCwztU9(= zVQ@-!ityZOIf>lJi*(B$Tsl~)<%B`pymZ-6ABPi>`%t=%Te6%E)a}v}236!0%O#Cb?W8u`}gNKrC16gh4WRC3ZF#5~3IjU;JeBWA@$%!`fK z*VBk&<^E_RTHUj^D@YimYNRDnT-8XDCN^zWY}&low0&aJ_J>T%I~yZpx)fJMfWOdc zS+UjfVypFut=1p18t;X-huV)OhCVBXJ}-v8PYiv32zn0U&Jc)MF^GAw>-UL4><@v+ z;dn3vM_vp^pBRq*5FA{w$&g}wV#WGHig6KVLn8KvMC6q{>{P~!J*xe`K9;#WCzWLU z!)VTa>dvvxmyjJdB-z6`=Yh;e1aaPGk;SDmL#DBkf(FY61hEiPBTl0UJo?dyy(PC& zsq?7ylr&=07w~G)jy9_{$;TtNT5ZA*2@55y5mK~7$;~Colv4sA3~NkttHSn{2!oBy zFw*%bE2>&ZBoellt0Z}M7|F1=u=?i04wN-9d^A#vtTuUDdh`|+qm9jwE%ISyq(lir zs7I3b&QYa$fu0hoxQWjeH`imVoE0H!4NaqjHIiZ#tx;=Ksa`7fwg(0dJ?xP%geoNY zgC|NDBPCR!mz-�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 literal 0 HcmV?d00001 diff --git a/src/frontend/circom/test_folder/toy_vitalik_example.r1cs b/src/frontend/circom/test_folder/toy_vitalik_example.r1cs new file mode 100644 index 0000000000000000000000000000000000000000..a2a6ce5e208ba386921dd2f134d898d4f983f557 GIT binary patch literal 464 zcmXRiOfF_*U|?VdVkRKA0C9i-M1lA~@L}@Tht3lVc2;`4FxH5TXl&f(8n8oif#Jg< zzZ3(QI;b{?4lE={o&_RK&^%(*f!s&PUXZ__YVg`iqFE51U?D+vIY7k~fHaVg*IrhL h8n7EddO>^;0J#U`b`1~*2|)bSkTf*R7yxm6NzMQO literal 0 HcmV?d00001 From 1c2e949412967e3b9fc8a094a7778f98ff364ccc Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 17 Sep 2023 20:41:11 +0900 Subject: [PATCH 04/29] improved --- Cargo.toml | 16 ++++--- src/frontend/circom/mod.rs | 39 +++++++++--------- .../{mycircuit.wasm => other_example.wasm} | Bin ...alik_example.r1cs => vitalik_example.r1cs} | Bin 4 files changed, 26 insertions(+), 29 deletions(-) rename src/frontend/circom/test_folder/{mycircuit.wasm => other_example.wasm} (100%) rename src/frontend/circom/test_folder/{toy_vitalik_example.r1cs => vitalik_example.r1cs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 3ded1b0d..56e60742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,17 @@ ark-std = "^0.4.0" ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge"] } ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } -thiserror = "1.0" rayon = "1.7.0" byteorder = "=1.4.3" ark-bn254 = { version = "=0.4.0" } hex = "=0.4.3" -# WASM operations +# tmp imports for espresso's sumcheck +ark-serialize = "0.4.2" +espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} +espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", package="transcript"} + +# wasm operations for Circom's witness calculation wasmer = { version = "=2.3.0", default-features = false } fnv = { version = "=1.0.7", default-features = false } num = { version = "=0.4.0" } @@ -29,13 +33,7 @@ color-eyre = "=0.6.2" criterion = "=0.3.6" cfg-if = "=1.0.0" anyhow = "1.0" - - -# tmp imports for espresso's sumcheck -ark-serialize = "0.4.2" -espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} -espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", package="transcript"} - +thiserror = "1.0" [dev-dependencies] ark-pallas = {version="0.4.0", features=["r1cs"]} diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 39c483b6..9482b266 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -7,8 +7,7 @@ use num_bigint::BigInt; use color_eyre::Result; use ark_bn254::{Bn254, Fr}; -use ark_ff::PrimeField; -use ark_ff::biginteger; +use ark_ff::{PrimeField, biginteger}; use ark_ec::pairing::Pairing; mod r1cs_reader; @@ -131,32 +130,32 @@ mod tests { let r1cs_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.r1cs"); let wasm_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.wasm"); - - assert!(r1cs_filepath.exists(), "R1CS filepath does not exist."); - assert!(wasm_filepath.exists(), "WASM filepath does not exist."); - - let constraints = extract_constraints_from_r1cs(&r1cs_filepath).expect("Failed to extract constraints"); - assert!(!constraints.is_empty(), "No constraints were extracted."); - + + assert!(r1cs_filepath.exists()); + assert!(wasm_filepath.exists()); + + let constraints = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); + assert!(!constraints.is_empty()); + let converted_constraints: Vec> = constraints .iter() .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) .collect(); - assert_eq!(constraints.len(), converted_constraints.len(), "Converted constraints count doesn't match the original."); - + assert_eq!(constraints.len(), converted_constraints.len()); + let inputs = vec![ ("step_in".to_string(), vec![BigInt::from(10)]), ("adder".to_string(), vec![BigInt::from(2)]), ]; - - let witness = calculate_witness(&wasm_filepath, inputs).expect("Failed to calculate the witness"); - assert!(!witness.is_empty(), "Witness calculation resulted in an empty vector."); - + + let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); + assert!(!witness.is_empty()); + let (r1cs, z) = circom_to_folding_r1cs_and_z(converted_constraints, &witness) - .expect("Failed to convert circom R1CS to folding R1CS"); - assert!(!z.is_empty(), "The z vector is empty."); - - r1cs.check_relation(&z).expect("R1CS relation check failed"); - } + .expect("Error"); + assert!(!z.is_empty()); + + r1cs.check_relation(&z).expect("Error"); + } } diff --git a/src/frontend/circom/test_folder/mycircuit.wasm b/src/frontend/circom/test_folder/other_example.wasm similarity index 100% rename from src/frontend/circom/test_folder/mycircuit.wasm rename to src/frontend/circom/test_folder/other_example.wasm diff --git a/src/frontend/circom/test_folder/toy_vitalik_example.r1cs b/src/frontend/circom/test_folder/vitalik_example.r1cs similarity index 100% rename from src/frontend/circom/test_folder/toy_vitalik_example.r1cs rename to src/frontend/circom/test_folder/vitalik_example.r1cs From 84b66b9e4b7bdcfb242d340c80e856c349536afd Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 17 Sep 2023 23:51:36 +0900 Subject: [PATCH 05/29] Brushuped --- src/frontend/circom/mod.rs | 48 +++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 9482b266..af8f878a 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -1,30 +1,26 @@ -use std::fs::File; -use std::io::BufReader; -use std::error::Error; -use std::path::PathBuf; +use std::{fs::File, io::BufReader, path::PathBuf, error::Error}; use num_bigint::BigInt; use color_eyre::Result; use ark_bn254::{Bn254, Fr}; -use ark_ff::{PrimeField, biginteger}; +use ark_ff::PrimeField; use ark_ec::pairing::Pairing; +use crate::ccs::r1cs::R1CS; +use crate::utils::vec::SparseMatrix; + mod r1cs_reader; mod witness; pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, ::ScalarField)>; -pub fn bigint_to_bn254_scalar(bigint: biginteger::BigInt<4>) -> Option { - Fr::from_bigint(bigint) -} - pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints { let convert_vec = |vec: ConstraintVec| -> ConstraintVec { vec.into_iter() .filter_map(|(index, bigint)| { - match bigint_to_bn254_scalar(bigint.into()) { + match Fr::from_bigint(bigint.into()) { Some(scalar) => Some((index, scalar)), None => None } @@ -35,8 +31,8 @@ pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) } -pub fn extract_constraints_from_r1cs(filename: &PathBuf) -> Result>, Box> { - let file = File::open(filename)?; +pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result>, Box> { + let file = File::open(r1cs_filepath)?; let reader = BufReader::new(file); let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; @@ -44,7 +40,7 @@ pub fn extract_constraints_from_r1cs(filename: &PathBuf) -> Result>) -> crate::ccs::r1cs::R1CS { +pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> R1CS { let mut a_matrix: Vec> = Vec::new(); let mut b_matrix: Vec> = Vec::new(); let mut c_matrix: Vec> = Vec::new(); @@ -60,23 +56,23 @@ pub fn convert_to_folding_r1cs(constraints: Vec>) -> crate::c let l = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); let n_cols = l; - let A = crate::utils::vec::SparseMatrix:: { + let A = SparseMatrix:: { n_rows, n_cols, coeffs: a_matrix, }; - let B = crate::utils::vec::SparseMatrix:: { + let B = SparseMatrix:: { n_rows, n_cols, coeffs: b_matrix, }; - let C = crate::utils::vec::SparseMatrix:: { + let C = SparseMatrix:: { n_rows, n_cols, coeffs: c_matrix, }; - crate::ccs::r1cs::R1CS:: { + R1CS:: { l, A, B, @@ -89,28 +85,28 @@ pub fn calculate_witness)>>(wasm_fil calculator.calculate_witness(inputs, true) } -fn bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result, Box> { +fn num_bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result, Box> { let big_uint = value.to_biguint().ok_or_else(|| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt is negative")))?; Ok(ark_ff::BigInt::<4>::try_from(big_uint).map_err(|_| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt conversion failed")))?) } -pub fn circom_to_folding_r1cs_and_z( +pub fn circom_to_folding_schemes_r1cs_and_z( constraints: Vec>, witness: &Vec, -) -> Result<(crate::ccs::r1cs::R1CS, Vec), Box> { - let folding_r1cs = convert_to_folding_r1cs(constraints); +) -> Result<(R1CS, Vec), Box> { + let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints); let z: Vec = witness .iter() .filter_map(|big_int| { - match bigint_to_ark_bigint(big_int) { - Ok(ark_big_int) => bigint_to_bn254_scalar(ark_big_int.into()), + match num_bigint_to_ark_bigint(big_int) { + Ok(ark_big_int) => Fr::from_bigint(ark_big_int.into()), Err(_) => None } }) .collect(); - Ok((folding_r1cs, z)) + Ok((folding_schemes_r1cs, z)) } #[cfg(test)] @@ -120,7 +116,7 @@ mod tests { use super::Constraints; use super::calculate_witness; - use super::circom_to_folding_r1cs_and_z; + use super::circom_to_folding_schemes_r1cs_and_z; use super::convert_constraints_bigint_to_scalar; use super::extract_constraints_from_r1cs; @@ -151,7 +147,7 @@ mod tests { let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); assert!(!witness.is_empty()); - let (r1cs, z) = circom_to_folding_r1cs_and_z(converted_constraints, &witness) + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness) .expect("Error"); assert!(!z.is_empty()); From c9c2fdfc4efde8d8d40d6413ab73d57d966c41e2 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 18 Sep 2023 00:47:12 +0900 Subject: [PATCH 06/29] add comment --- src/frontend/circom/mod.rs | 14 ++++++++++---- .../circom/test_folder/other_example.wasm | Bin 30625 -> 0 bytes src/frontend/circom/test_folder/vitalik_35.r1cs | Bin 0 -> 540 bytes src/frontend/circom/test_folder/vitalik_35.wasm | Bin 0 -> 34475 bytes .../circom/test_folder/vitalik_example.r1cs | Bin 464 -> 456 bytes .../circom/test_folder/vitalik_example.wasm | Bin 0 -> 34395 bytes 6 files changed, 10 insertions(+), 4 deletions(-) delete mode 100644 src/frontend/circom/test_folder/other_example.wasm create mode 100644 src/frontend/circom/test_folder/vitalik_35.r1cs create mode 100644 src/frontend/circom/test_folder/vitalik_35.wasm create mode 100644 src/frontend/circom/test_folder/vitalik_example.wasm diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index af8f878a..4aa64f41 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -16,6 +16,7 @@ mod witness; pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, ::ScalarField)>; +// Convert the BigInt constraints to Bn254's ScalarField constraints. pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints { let convert_vec = |vec: ConstraintVec| -> ConstraintVec { vec.into_iter() @@ -31,6 +32,7 @@ pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) } +// Extract R1CS constraints from the provided R1CS file path. pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result>, Box> { let file = File::open(r1cs_filepath)?; let reader = BufReader::new(file); @@ -40,6 +42,7 @@ pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result>) -> R1CS { let mut a_matrix: Vec> = Vec::new(); let mut b_matrix: Vec> = Vec::new(); @@ -80,16 +83,19 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> } } +// Calculate the witness given the WASM filepath and inputs. pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { let mut calculator = witness::WitnessCalculator::new(wasm_filepath.clone())?; calculator.calculate_witness(inputs, true) } +// Helper function to convert `num_bigint::BigInt` to `ark_ff::BigInt`. fn num_bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result, Box> { let big_uint = value.to_biguint().ok_or_else(|| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt is negative")))?; Ok(ark_ff::BigInt::<4>::try_from(big_uint).map_err(|_| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt conversion failed")))?) } +// Convert R1CS constraints and witness from Circom format to folding-schemes R1CS and z format. pub fn circom_to_folding_schemes_r1cs_and_z( constraints: Vec>, witness: &Vec, @@ -124,8 +130,8 @@ mod tests { fn test_circom_to_folding_conversion() { let current_dir = std::env::current_dir().unwrap(); - let r1cs_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.r1cs"); - let wasm_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("toy.wasm"); + let r1cs_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("vitalik_35.r1cs"); + let wasm_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("vitalik_35.wasm"); assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); @@ -140,8 +146,8 @@ mod tests { assert_eq!(constraints.len(), converted_constraints.len()); let inputs = vec![ - ("step_in".to_string(), vec![BigInt::from(10)]), - ("adder".to_string(), vec![BigInt::from(2)]), + ("step_in".to_string(), vec![BigInt::from(3)]), + // ("adder".to_string(), vec![BigInt::from(2)]), ]; let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); diff --git a/src/frontend/circom/test_folder/other_example.wasm b/src/frontend/circom/test_folder/other_example.wasm deleted file mode 100644 index 4a28f0c11c19939b3d70aae10d311b9d7078fb1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30625 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 diff --git a/src/frontend/circom/test_folder/vitalik_35.r1cs b/src/frontend/circom/test_folder/vitalik_35.r1cs new file mode 100644 index 0000000000000000000000000000000000000000..d1d827a64097acb0feaca8daf14d3c769bac2bc9 GIT binary patch literal 540 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 KD@(z6TbWTDEmjSzKaiJ0pD@kstiPoh?r&_P* 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 F{{wfsChPzJ literal 0 HcmV?d00001 From 1e36b6598683915a5f4557792bc2c3231844699c Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 18 Sep 2023 07:35:58 +0900 Subject: [PATCH 07/29] Added description of test_circuit --- src/frontend/circom/mod.rs | 25 +++++++++++++++--- .../{vitalik_35.r1cs => test_circuit.r1cs} | Bin 540 -> 540 bytes .../{vitalik_35.wasm => test_circuit.wasm} | Bin src/frontend/circom/test_folder/toy.r1cs | Bin 264 -> 0 bytes src/frontend/circom/test_folder/toy.wasm | Bin 34279 -> 0 bytes .../circom/test_folder/vitalik_example.r1cs | Bin 456 -> 0 bytes .../circom/test_folder/vitalik_example.wasm | Bin 34395 -> 0 bytes 7 files changed, 22 insertions(+), 3 deletions(-) rename src/frontend/circom/test_folder/{vitalik_35.r1cs => test_circuit.r1cs} (88%) rename src/frontend/circom/test_folder/{vitalik_35.wasm => test_circuit.wasm} (100%) delete mode 100644 src/frontend/circom/test_folder/toy.r1cs delete mode 100644 src/frontend/circom/test_folder/toy.wasm delete mode 100644 src/frontend/circom/test_folder/vitalik_example.r1cs delete mode 100644 src/frontend/circom/test_folder/vitalik_example.wasm diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 4aa64f41..1a1ad994 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -130,8 +130,25 @@ mod tests { fn test_circom_to_folding_conversion() { let current_dir = std::env::current_dir().unwrap(); - let r1cs_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("vitalik_35.r1cs"); - let wasm_filepath = current_dir.join("src").join("frontend").join("circom").join("test_folder").join("vitalik_35.wasm"); + let base_path = current_dir.join("src/frontend/circom/test_folder"); + let r1cs_filepath = base_path.join("test_circuit.r1cs"); + let wasm_filepath = base_path.join("test_circuit.wasm"); + + /* + This is the test_circuit.cirom file. + When step_in equals 3, it will pass the test function. + + template Example () { + signal input step_in; + signal output step_out; + signal temp; + + temp <== step_in * step_in; + step_out <== temp * step_in + step_in + 5; + step_out === 35; + } + component main = Example(); + */ assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); @@ -146,8 +163,10 @@ mod tests { assert_eq!(constraints.len(), converted_constraints.len()); let inputs = vec![ + // success ("step_in".to_string(), vec![BigInt::from(3)]), - // ("adder".to_string(), vec![BigInt::from(2)]), + // fail + // ("step_in".to_string(), vec![BigInt::from(6)]), ]; let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); diff --git a/src/frontend/circom/test_folder/vitalik_35.r1cs b/src/frontend/circom/test_folder/test_circuit.r1cs similarity index 88% rename from src/frontend/circom/test_folder/vitalik_35.r1cs rename to src/frontend/circom/test_folder/test_circuit.r1cs index d1d827a64097acb0feaca8daf14d3c769bac2bc9..8c475f0f7491f84950ab9ac9fbcba986306a2b86 100644 GIT binary patch delta 17 XcmbQkGKXcu9Y$6LAYh#QkkJ|dEVl%N delta 17 XcmbQkGKXcu9Y$701|XRHkkJ|dEV~4R diff --git a/src/frontend/circom/test_folder/vitalik_35.wasm b/src/frontend/circom/test_folder/test_circuit.wasm similarity index 100% rename from src/frontend/circom/test_folder/vitalik_35.wasm rename to src/frontend/circom/test_folder/test_circuit.wasm diff --git a/src/frontend/circom/test_folder/toy.r1cs b/src/frontend/circom/test_folder/toy.r1cs deleted file mode 100644 index 8db3126d2f00f35fa87a3b5d4c4d4202ea7bf20c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264 zcmXRiOfF_*U|?VdVkRK20AdgTiGlb)@L}@Tht3lVc2;`4FxH5TXl&f(8n8oif#Jg< zzZ3(QUJ#8BfaIZS2%6^rHAn%X2d{b-h#IijAoD?d5CE}3eo_EpWB_#o%x_>dAb9}V Co-JYk diff --git a/src/frontend/circom/test_folder/toy.wasm b/src/frontend/circom/test_folder/toy.wasm deleted file mode 100644 index 5498f5cbdb0e0916da7f3e681ac8fdbe850042fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34279 zcmeHwdypK*dEfNR>~r=pxA)-Q6SGS|1W1Co7XXUp19KoiQUpK{;6tQD0f)N8UM$w zPz+b)ihGj~xeTg&<+5~IMLLBqUuKti*2||Q>9m9rE(3Pi;zA`HR+8LO6Riu4PPJaq zD{Bjtl}@$UczV9ESgW+8tV8NsX;mAYL#yoA{FG);hilV4$Op@Y>WGsd|{+A?KVzFcrx0sw@jLDKL z$6|_t4A!3%i=|TOSd2Ldykt!3%iu4k=d$@+p-)3No6TnQY%Zt9*a{fM6b6xhT-M`t zE`=Nz0@vCyOGvDe+uM}F9`9(iP zdbOX!5=i}oT5VK2(rdYPrSnW<&TwNBp?;*&n3i!=RCOfFr!1BR1K;Ouz7)~z2;|(QB<%>qhZ@gERr7|2 z&kO1S)Ewioa>g$qfmsRA7R)_4tOQREt42=_E5Vb)662-3#{WpkJ74>&p4$Hit>CgD z?Uf-IF0YpqLpGGh5k;CxY3l2SGG~0jrKm)KUbqA(F!^3NLzGN_k|7Gv!X-d~f9#b9 zh%ykM3=pMAlwyEVB+3|3#sZWvF6!IbPsl#4-mtWqyNSR2RiAQiP>DBf<=hM&9iuaN zRE^Hy(KtGTNAc(k9{r;;cwmUmz|nZ~ZS61etZ*56TIv&cQaMKON#*!uQeCwztU9(= zVQ@-!ityZOIf>lJi*(B$Tsl~)<%B`pymZ-6ABPi>`%t=%Te6%E)a}v}236!0%O#Cb?W8u`}gNKrC16gh4WRC3ZF#5~3IjU;JeBWA@$%!`fK z*VBk&<^E_RTHUj^D@YimYNRDnT-8XDCN^zWY}&low0&aJ_J>T%I~yZpx)fJMfWOdc zS+UjfVypFut=1p18t;X-huV)OhCVBXJ}-v8PYiv32zn0U&Jc)MF^GAw>-UL4><@v+ z;dn3vM_vp^pBRq*5FA{w$&g}wV#WGHig6KVLn8KvMC6q{>{P~!J*xe`K9;#WCzWLU z!)VTa>dvvxmyjJdB-z6`=Yh;e1aaPGk;SDmL#DBkf(FY61hEiPBTl0UJo?dyy(PC& zsq?7ylr&=07w~G)jy9_{$;TtNT5ZA*2@55y5mK~7$;~Colv4sA3~NkttHSn{2!oBy zFw*%bE2>&ZBoellt0Z}M7|F1=u=?i04wN-9d^A#vtTuUDdh`|+qm9jwE%ISyq(lir zs7I3b&QYa$fu0hoxQWjeH`imVoE0H!4NaqjHIiZ#tx;=Ksa`7fwg(0dJ?xP%geoNY zgC|NDBPCR!mz-�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 diff --git a/src/frontend/circom/test_folder/vitalik_example.r1cs b/src/frontend/circom/test_folder/vitalik_example.r1cs deleted file mode 100644 index 6bef95eff80a15fc22d0abc6e0fb14ca493327d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456 zcmXRiOfF_*U|?VdVkRKA0C9i-M1lA~@L}@Tht3lVc2;`4FxH5TXl&f(8n8oif#Jg< zzZ3(QI;b{?4lE={9%Kg)5Hycibs+ZAVQ~WsQw9JYJW0L) diff --git a/src/frontend/circom/test_folder/vitalik_example.wasm b/src/frontend/circom/test_folder/vitalik_example.wasm deleted file mode 100644 index 96033edfcb765c42502e10c75287b12f75d49a1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34395 zcmeHwd5|2}d0)Top0nrZojo{vV9_%KM1UlSy#P=&5A=cnNf7`+KqN)#0QMLFvpdJ` z%mCO`k-*B3L@81JP(_ZN*a@MiT=XBLW2aauC34vtTXB?1Tv1Z46f02@sGRsn?1WX3 zQ}j_re&6e3`g*eqhy_=(z6TbWTDEmjSzKaiJ0pD@kstiPoh?r&_P* zm9>S+N~hXvJUiD|tW{c4)*pw5pxT?4|SPD-p!@ z#pabW7w1})#nZ2xsMMRSbxBi}mn0@LMNw2WCQ0!`G8V%f|4WiAu~;&RTTD(c#$-vB zV=+ZR2J26X#ZswsEXJG!UNR>2W$>5NbJ={Z(5E4s&1N%tHkVUlYz2&B3WLahTh`-s zE`=V?K!O*&TqZ%@LR>gOxcoQA~Hb5~BaU>s>) zN9NEseZiDn4Xdra!7im+G)TfyhJ6_WdjC_q@qoi&QoUFct>CmshUbK$&@r! zPk;Tl*wTZN!3=4yA{|ofQ1XzHw9@oLif)C9hm@i=E*a7+&&O{`E2dkwqBafkUZr?S zJxq7qz9;RwbSfV+rM%X?P>i!=RCOfFXDpTm1K$^Iz7)~z2;|(WB<%>qhZ-+NRr7|2 z&nMNxs5!=E<&0lO0<#jJEtq?9SP7mSR*jwJOoVDbZUhA5c;B|{XTg-d_}|2QBI z5M>}h86ZlLD8&G!NR%<6j0GrTT-3LDBf<=hM&9iuaN zRE^Hy(KtGTNAc(k9{r;;cwmUmz|nZ~ZS7C;tZ)^2TIv&cQaMKON#*!eQeCwztU9(= zVQ@-!ityZOIf>lJi*(B$Tsl~)<%B`pymZ-6pM(>U`%t=%Te6%E)a}v}236!0%O#Cb?W8u`}gNKrC16gh4WRC3ZF#5~3IjU;JeBWA@$%!`fK z*VBk&<^E_RTHUj^D@YimYNRDnT-8XDCN^zWY}&low0&aJ_J>T%I~yZpx)fJMfWOdc zS+UjfVypFut=1p18t;X7h1!oMhCVBXJ}-v8PYiv32zn0Uo)CyxF^GAw>-UL4><@v+ z;dnR%M_vp^pBRq*5FA{wiI8G_V#WGHig6KVLL&BuMC6q{>Qu&yJ+A$~K9;#UE0tvY z!)VTa>dvw+myjJdB-z6`=Yh;e1aaPGk;SDmL#DBkf(FY61hEiPBTl0UJo?dyy(PC( zsq?7ylr&=07w~G)j<%{c$;TtNTW!J+2@55y5mK~7$*m>Ilv4sA3~NktyTbOC2!oBS zFw*%bE2>&ZBoellt0Z}M7|F1=u=>`*4wN-9d^A#vtTuT^dh`|+qm8YQE%ISyq(lir zs7I3b&QYa$fu0hoxP{Lax7K5 z^prC#meU&HN8#B^&fcJ5IMD+%O(;yS8Z$yXw?Jm1r!bj25~jEM7^;ZX@1uWCt%UZp`0mb4I-4oFziqNR}TdKOZUjP#Er#tJ0U8jgfDtUxlY+(=l!3M7wdCuLfWH)!0Z93bKCXC4Vp zqqdSPA65<<@~JE{6jNPt^2wom83_xFza%T_HJLcdSRR#2v6#8ZN-@n)5QWKN=1o>o zHA6B~9l)437;Z5=W^Ty1>@3CcFF`eeVkDOKL5hT_9BZNWgvpjm397}SnaVN*o`t{| zA8;i9?(?VErk+WG0sPN>T}rIwQG(Tii%2o9c2Y z%?qbyH}JP|{gjb5rIV`=AT^u(3bLh5wp2=UE-6||R!NEU(X2enN-0w+rMY}5^MnQ7O|O}V7t0^OyOX5Iu~0Oh8vtL6sK!bMMjk}+>6 z&{Dz;luZRN-Bd~mb5pg2P3mz77{fynO6Ub#QC8Knh6VvMSWn)-=vGhL0V54*z&0C$ zNkQRJR?1>6obn;AjhH;{mbKE{BE-s5meQS~;7mn!DE_)8isHB~}qT|&21$`~=E zC!uHpDy1xoayc1I1Rxot!d=z0hk4^dn6!}1n^-L5WD0T#+%!~opOYEtI+?W*5F~R0 za|SfhLG9=Im~xd5xpO6%&sAt1V#ron{yGHZ3B0wU5+bS?t0*YnU7_w$RWH?E!R=D9 zI@m31FW+{l880$NjeU58H1^>U(%6SbNMj!!V}vwDMy^IaJd7?L zs)I)|hzF)+ZfPGLA&q@_gf#Zy5z^R)M@VBI9wCi=c!V_e;b9OSyF42C@YvPGBkACg z3F2X`KwEz6!y}}z508+>K0HDi`|t>9?876Zu@8@s#y&iD5gr)nxfb@}v8Rhi#=%1m z;^A$_K0HDi`|t>9?876Zu@8@s#y&hk8vF1FY3##e58+|0U0D|P;qh=658c6IAc%*x zkRIV5A&q@_gf#Zy5z^R)M@VBI9wCi=c!V_e;qfrxf$?s%e@t}o7;x|?2JyfY*)8q! zkC4VbJVF}#@Ca$_!y}}z508+>K0HDi`|y|`JgkMo2>+Pr;!$+)7z^TItiO>~HvfbhwO#380Y|U!Mjio%;g=&nMzD}8S(V)zyZ2d3$@?+6VJ8j6^w8?uRx}C>m*l6pbY1_;N)g3DRcD1)*(>9y>ka z?Ht13kJa+vb}Wi^(ZwLRgBWR1y?me8q6Ci@MHk>4hbNL$;P6C}pom@`>g5N*oj_}Q zCz9)6Xk*C{laTvBEe~(Ud*~V9dQUN+cck|Wlt)D_k~Rp`NEgW&LZ2&$eQv0y&kdAE zdifk_6DdoN4XHu#cG@t_OTUbu_*SU|!^Pc4>rB3q;jqzy7fBq_&D z5lJd=Q$&)6#Qr_f)4z-5@m~H-ibhu0X9RuDb;rTbmY+)+4sAWUq#Zq5#fNISw4GK# z^D?(p7y;oUTg6!Ueq@XC4)m#Hz(`V#8!(bo5Vxfvaa$S@x25r(ZE37r?$wqEgGe#^ zjGz;{{yoTT8(C^tyd}It+yYBITi{=+<=xw9fxEmda92RDU<*teWY$R6&2h6vk_zH> zIV5hEBjR>BE^e1)al6bKq+XbJz$5}mL;(*cZY1r zt%8Ygz$)WntCYo7DT(pj9m1C*Wi1nVkt&Ok+8xrFOOL6zB|Ybeg@w1fn*p@35V>=3 zy~K7nbMH7-DXg7=?Hnk=093*uWa06ixMybW@S{!Rpxslf|-9| zJC4xjMWpX`5z+ZdNThga{veGa^+M!McJ*B&WQ?E!F3CC&8zqflEj&b#>~#QZ1tltj z$Fs0l?mQQ))4bmNTYA<&c_;wA%Pq~H{a$V^$yyeQBK3lJJ5ERIDN%flycptw*i{F6 zx+ZC2oe=1BDh#WpL7Z8qA|W|+l~Z^vdkyf)hrFhDPxrTK6(QlG5YT)NO)z1mT~ z(30C~M?tkb<`P=cS~iMQ&stE6Bo)P$92HyAT2b;cWLqDb&x-Na#rXH%5&m1ZiM$~M zxnKxE9uzm1qPV$?ia{O^fy|M_WDV00K9h*}0fH_@vR{Z~FLxahg1ilPEr^jE6eC#_ zBRMKYay*12hYP!oJ>cpW!!>kAaBb~fgJMjIVoXNGn2d)o;fiB!-$U^sq2j%qVOWUC zHk_d-R(w>fxV3^5>QAu2*+V;P1*VX&{~3S*rT9y4OJ2!T_bIPc1jFH(>nUv$%OO3^(^T*Qe*11CLk5L7Q}gA~50 z(Rt7e4vF5>kb0k1;5jn-n0X_!N0v;sY9D{pam3fEs2{a6eN;=KINmLQDOQqHD#bZ}oJ2ha{&7RuCrOVpP0Z!K!SC^H#{navwn94l7zorYXexfm)P{e=Mxl}-dh ze;jw#hoQp;djZy=`*JCUa-2YV+w@JzqHjvkbi*PW(Nx`{>e1AsMNLLiRf~!P$x(92 z7FCX>+DZjHfgN<9IR_PnU_iR#&@hhba>eip4TQ}baZ&~1viS^0@ znxqeD+AFwIg_{y8q*sY}Yo~MVuOF1j(8(oBno(rokr`RiaX6|=K$!BY$d6uX(#to@ zb?9Mk~ape@&?%C_7THHMR_o*;ifC-lcz2hFJTnY=i zM^2IzU~aTENh5#IABOWLao=YGZvByE%Ah~O>F+23CFDqbcxq5JOB>l$X#lI<`d*Lj zp$r;wbnU`=XZ9i&<^)zAytk;=%gV$lTFdvy7>JPz;#`~~o8>-tCMLKs&YLRf;zF__ znb%9(wF;RV53y5t`n%?~PW=nat#ET|AEmVhx3botgNSAK-qr%QuAc|3HAvuY+gd(W z(bnP&J)XL4YXx~nSc}wltwk@_xnpa6mEP?XXinU_PysoIRXM`l{-N8Idhz0&Bo)z< zXvuhC6Mvf&+OX9zac*9cA8jVeMkXUM0*qZ_c{);CY@T1TT2I&9c)EBRbvGEv9hHjY z`N_qmZqH!2&=e26sUfv*I zzX-kg2J-%skML<%q~_zaT&qEMyZj=2+0!ZY$mnq4!zA7 zPenfX334ZPchZT1bN68o&Dqn1(oao@gSkDZxZ9+5+SLSF5oY#N9uqkH@qrxx zp&rwT2aIOwdO^EO6*|L?f_7n>8bTwQZj(3Ym~nf1S<=THWjijT`}|YW3N+5c5JZ21 zM(!}+W7=`L?Y&Ll{C;X0mPBQ(AIxSqrPt`Uu{20F*q0;HzM>UDG1cd&hamiGOPI>! zwOL84$JjOcfiV3uA*dO@yiBe`lQmfM-PxzEmsHeXsBhp$sgm{ET_a)gNk9E|7rfAi zDflg~K8s($9!3Pe{G>=+H@`?zF#N7}$@&$jw&bae*JV|OnbsXtWqPYR9IT4Z0`Xc% zgKxjmq0^_XW1`1_bJl0btgd-8_$}$1SYGf?F1YL3k87Cm2%e)yJ*-pFb&Bz)>z5C^#qyx}?hQUux$_D&j1$d?b| z#jL0V(=dr<`7|BF%nS2qna`^xaNlPp%?a~;Nq^K#qLA&|6Z(Gr85GrH__Z^B+8NA% zg8TJjR)%dewDXG@+BIruQdk3_x0~`62zZ|HZ_G(Blgt*U-5cl~J;&=SLBBZ~I~J6EvN0wH`le_4K1J1^OezeSU?I?K%;3uq#4iJH9z`>z0D$&&!7;^BOIe#B_RF4bz^)zLxpHev=rQKi*$reCvUk zAMU5U2gV0NB<50y@9>!KOq#zkNqOI!&}$>w%Y47*ZSxjeB_XVr`j{W&4cXMOk%0j{ z4N0UIA%%i@0>$uZ8(Ppp4z|S0VL{wrBok8k1`H%4SuBjS-)1aMB9flqhpj+U)@hLT z?yT`;gT4Dj{c~m7AT$}J;(YcVIfFecGfq)N(9tZFW2U^UpGB%dsWI%bm|vT~HX(Kk zH}m@b63$vfDa2^Nha09{F^fiPBzX5th-#B}(@y3!XbK4pg>rETe`Qjym$7_jBnfB! z*9RQBB11@Q&6+pzFfn$QVD+SHe^uMfFe~~H2$*t*{KJtKyIOg;)ov2{A?-oSA;=ov z&vVIfr)8X^eb?=Kkz5HSC8y*B@!y;zNFf#8kwk#uUGe7k$TRmz$c+FD1m1k(;2+7@ z<2AoGjl>Ta3@an2y+8Nfv>}tPpx>fBEZPgiv`l?Wf-lkx4BkgKzF~!+K?ExWw?D?# ztta|N{^n3=9Q+tBHnwWn+mEFm#vY?u>E-I8cVv6T|- zTBjSH>Di@>`S#6jT=@JP_mP5F0KN zAmx?FRwZ33Y++UM2({IBxYd~3G!5>tV0lnZf{64CSq)A5A2K#z_YdUf=pPd1_nH0~ zRE<7`+zMu-4M#K(OqQi3lUWhxu0O_;;WXrcPA;VFq5MBU0lT!X0HxcE^a-BXeD44c zNlV0v_gz?|)xQ3*5ec&-E%{fH{fB)_^hr)kbjQq@=lbC9{^^g7e0}Agtv~yR(|bPq zsV{ut+ec4*XYY;4AGJ;{PQa=0ae{m&^R&fKl-maH^`RMO|XY}`8{mk$E>SKR(=1UvjJ@dr%=f3ufPoKQ@t3Mb* z^V&qN!_sfO_oMf}@wxAO`!B|R{TH5n@K65TTi;y#?Z?0Rdgb3?8wdHp|pX|QGL>Ig$&xZoHG<`M19KZ+_><>|5p^{boAz-oBsy%@geJ zq<*$tzRqoo9oFsWh==13M|jo9j)Gg5eKW5mZFD!#S>?42!;ap&-9z9W0{0O38;`&X z|MhqO)!!d||JP+`%6re!8-FX`xeeXsB(NWTWzyFe4;eHwxKC`|XrY^F*`J zxzMavTI*wjcH;R~v))bKLGn@QYWrm6!d$0%xiaq5ZHv6qsT=4TWChAzYtAi}3Qj%v z6mI*>!dz{xRdzC0)>iI!GPhS-on3jiqB9)R-S`Xvx9rD~cA)V9H(2|VtxJ_Xq-1OE z$`P3U%+&F{RNAY~gT%YjJlSm3=V}jG)Z@*@1uNlUZa!C|eQ{(4s*g}1d~o-8dwzOq zKULdlKG*0>5Sa`|cNk)d?v>Tk({#VkIXx5O@dFfXR8|jC^g`zlr&d-UrD(15af;R| ztB=tgR{AMYuL=)%oF_CFpP)M+eUk1-e~Rua%`1mVsPome3O-g`Gd+>kEExhAK;ngq zmm14Q2wTM47i+br-EgaRlDp*m{=gMDgdWd+QTa z9dganr=_1Ki79m4<~`qR)lN`O1Wr0R*O2%E&xz0}C$-V+)L!JN2%V-t4UEoE;aaox zNuB`8OZ2to7tWuj4=ulN=F&c; zeewA-xmKlJX>zZbm_`dsv)@8i&wz_+8%d%hKYVTiiH>8WbN|M}{RgOq-(HRVsP z2EHu4RG7b1tu6Y#2)>->X~&xv>`zeFboU`}b^+hd#)f>2fH+RVM3J`CS&s2L|k5(g0zZ6+tL&*bH`1G>C^pif>qSdiBb_RDLB2ITcaxg2NN zlEF^_KQ+EEakg1ojKvow=^h7vi}%_Tcu%Ln54;a#zz4h!d_iF?Rc^In^1?z~u6!yXS6)rZZD7$}OUspo{+x|m-Qv_y)kft?C&pTF zd37}*ueOqu&`K%oD=TTXrp4O$5_u<+q#NbT@`SYk#i_A1*bQROhl_h(Oky2~Evt={ z=_$yovU>4jZV4b^q07k@2I~_^xjvDSS0{kMqz0@ra(yx@uTJKGc^;VSa(${#UY#lc zr+#2RAlIh{<<;pSV0IsDP?YO4!}99P2=E()Eym>ffgSSdfpK8D6E-pA`oWUCdaw*! z?}u%6$@N3K<<&#OJ1wDwHsVskN&yM$kZ=_eu1)12Sss$@F zcka+DDyzjNb5Ay3n{Q59m z&3)w4r@_i3*^^BSPR`ewS03TS{rox+bF;`Rvm~0EZBk5{qMA)Go}G3>Gm4kf0blH( zFNPymj#%LEs4w<$U+gil2tVb^__#0jgar&ld{WHdDPNJpu>Txm^QL{Dwe(`uBS}|S z7$MPorP*3+qnByURy*yS~XX?dTdtO#nnr%gHv{kvj5+izC z#igE5FJrcmw7Jbe#BQ1MmUs&nNY;yVsp7J*6qi_?v6?Jb>WVyH2gik&vNAcbj4q(g zkSZfpL26BrGSrRF)tY?#!{XN65xzbs*(of(U}>#c`4MS#lsE>a3-uK>ZQW`79gA0+ z)yDZ|T&-Q6YbD|sazo{$N;A5Yir_?=u9yPiI+aN*G%qzel~y*@?lkdm2PNkQr}7#N zBkU}C5SBfhBu6%m0PeZ8b*HpE4-<>&DLrMSBiNU9gG4E0+$dtPG?Nk-LAC31D@jlI F{{wfsChPzJ From 6fdac1ddabd115280bd5c2c7861874bce0bcff43 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:05:46 +0900 Subject: [PATCH 08/29] found mistake --- src/frontend/circom/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 1a1ad994..6473ec11 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -56,6 +56,8 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); } + // To Do; this is a mistake + // l is the size of piblic input and output let l = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); let n_cols = l; From c5070e7feb15a79fedeb3a2e81e5f9a333b91d23 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Wed, 20 Sep 2023 21:43:45 +0900 Subject: [PATCH 09/29] fixed cargo.toml --- Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56e60742..b9a87201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -ark-ec = "0.4.2" +ark-ec = "^0.4.0" ark-ff = "^0.4.0" ark-poly = "^0.4.0" ark-std = "^0.4.0" @@ -13,11 +13,12 @@ ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } rayon = "1.7.0" byteorder = "=1.4.3" -ark-bn254 = { version = "=0.4.0" } +ark-bn254 = "0.4.0" hex = "=0.4.3" +ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } # tmp imports for espresso's sumcheck -ark-serialize = "0.4.2" +ark-serialize = "^0.4.0" espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", package="transcript"} From 99f07f52564124f47873c67fe79e229d99345458 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:08:05 +0900 Subject: [PATCH 10/29] Imported ark-circom as crate --- Cargo.toml | 28 +- src/frontend/circom/mod.rs | 13 +- src/frontend/circom/r1cs_reader.rs | 250 --------- src/frontend/circom/witness/circom.rs | 165 ------ src/frontend/circom/witness/memory.rs | 273 ---------- src/frontend/circom/witness/mod.rs | 24 - .../circom/witness/witness_calculator.rs | 499 ------------------ 7 files changed, 12 insertions(+), 1240 deletions(-) delete mode 100644 src/frontend/circom/r1cs_reader.rs delete mode 100644 src/frontend/circom/witness/circom.rs delete mode 100644 src/frontend/circom/witness/memory.rs delete mode 100644 src/frontend/circom/witness/mod.rs delete mode 100644 src/frontend/circom/witness/witness_calculator.rs diff --git a/Cargo.toml b/Cargo.toml index b9a87201..a2616532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,43 +11,27 @@ ark-std = "^0.4.0" ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge"] } ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } -rayon = "1.7.0" -byteorder = "=1.4.3" -ark-bn254 = "0.4.0" -hex = "=0.4.3" ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } +ark-bn254 = "0.4.0" +thiserror = "1.0" +rayon = "1.7.0" +num-bigint = "0.4" +color-eyre = "=0.6.2" # tmp imports for espresso's sumcheck ark-serialize = "^0.4.0" espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", package="transcript"} -# wasm operations for Circom's witness calculation -wasmer = { version = "=2.3.0", default-features = false } -fnv = { version = "=1.0.7", default-features = false } -num = { version = "=0.4.0" } -num-traits = { version = "=0.2.15", default-features = false } -num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] } - -# error handling -color-eyre = "=0.6.2" -criterion = "=0.3.6" -cfg-if = "=1.0.0" -anyhow = "1.0" -thiserror = "1.0" - [dev-dependencies] ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0"} ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["crh"] } -serde_json = "=1.0.94" [features] -default = ["parallel", "nova", "hypernova", "wasmer/default", "circom-2"] +default = ["parallel", "nova", "hypernova"] hypernova=[] nova=[] -wasm = ["wasmer/js-default"] -circom-2 = [] parallel = [ "ark-std/parallel", diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 6473ec11..775957b7 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -3,16 +3,14 @@ use std::{fs::File, io::BufReader, path::PathBuf, error::Error}; use num_bigint::BigInt; use color_eyre::Result; -use ark_bn254::{Bn254, Fr}; use ark_ff::PrimeField; use ark_ec::pairing::Pairing; +use ark_circom::{circom::r1cs_reader, WitnessCalculator}; +use ark_bn254::{Bn254, Fr}; use crate::ccs::r1cs::R1CS; use crate::utils::vec::SparseMatrix; -mod r1cs_reader; -mod witness; - pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, ::ScalarField)>; @@ -56,8 +54,9 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); } - // To Do; this is a mistake - // l is the size of piblic input and output + // TODO: Refine this logic. + // Ideally, `l` should represent only the size of the public input and output. + // However, currently, all elements in the R1CS or Z are treated as public. let l = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); let n_cols = l; @@ -87,7 +86,7 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> // Calculate the witness given the WASM filepath and inputs. pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { - let mut calculator = witness::WitnessCalculator::new(wasm_filepath.clone())?; + let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; calculator.calculate_witness(inputs, true) } diff --git a/src/frontend/circom/r1cs_reader.rs b/src/frontend/circom/r1cs_reader.rs deleted file mode 100644 index 975e4fdf..00000000 --- a/src/frontend/circom/r1cs_reader.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! R1CS circom file reader -//! Copied from -//! Spec: -use byteorder::{LittleEndian, ReadBytesExt}; -use std::io::{Error, ErrorKind}; - -use ark_ec::pairing::Pairing; -use ark_serialize::{CanonicalDeserialize, SerializationError, SerializationError::IoError}; -use ark_std::io::{Read, Seek, SeekFrom}; - -use std::collections::HashMap; - -type IoResult = Result; - -pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); -pub type ConstraintVec = Vec<(usize, ::ScalarField)>; - -#[derive(Clone, Debug)] -pub struct R1CS { - pub num_inputs: usize, - pub num_aux: usize, - pub num_variables: usize, - pub constraints: Vec>, - pub wire_mapping: Option>, -} - -impl From> for R1CS { - fn from(file: R1CSFile) -> Self { - let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize; - let num_variables = file.header.n_wires as usize; - let num_aux = num_variables - num_inputs; - R1CS { - num_aux, - num_inputs, - num_variables, - constraints: file.constraints, - wire_mapping: Some(file.wire_mapping.iter().map(|e| *e as usize).collect()), - } - } -} - -pub struct R1CSFile { - pub version: u32, - pub header: Header, - pub constraints: Vec>, - pub wire_mapping: Vec, -} - -impl R1CSFile { - /// reader must implement the Seek trait, for example with a Cursor - /// - /// ```rust,ignore - /// let reader = BufReader::new(Cursor::new(&data[..])); - /// ``` - pub fn new(mut reader: R) -> IoResult> { - let mut magic = [0u8; 4]; - reader.read_exact(&mut magic)?; - if magic != [0x72, 0x31, 0x63, 0x73] { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "Invalid magic number", - ))); - } - - let version = reader.read_u32::()?; - if version != 1 { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "Unsupported version", - ))); - } - - let num_sections = reader.read_u32::()?; - - // todo: handle sec_size correctly - // section type -> file offset - let mut sec_offsets = HashMap::::new(); - let mut sec_sizes = HashMap::::new(); - - // get file offset of each section - for _ in 0..num_sections { - let sec_type = reader.read_u32::()?; - let sec_size = reader.read_u64::()?; - let offset = reader.stream_position()?; - sec_offsets.insert(sec_type, offset); - sec_sizes.insert(sec_type, sec_size); - reader.seek(SeekFrom::Current(sec_size as i64))?; - } - - let header_type = 1; - let constraint_type = 2; - let wire2label_type = 3; - - let header_offset = sec_offsets.get(&header_type).ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "No section offset for header type found", - ) - }); - - reader.seek(SeekFrom::Start(*header_offset?))?; - - let header_size = sec_sizes.get(&header_type).ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "No section size for header type found", - ) - }); - - let header = Header::new(&mut reader, *header_size?)?; - - let constraint_offset = sec_offsets.get(&constraint_type).ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "No section offset for constraint type found", - ) - }); - - reader.seek(SeekFrom::Start(*constraint_offset?))?; - - let constraints = read_constraints::<&mut R, E>(&mut reader, &header)?; - - let wire2label_offset = sec_offsets.get(&wire2label_type).ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "No section offset for wire2label type found", - ) - }); - - reader.seek(SeekFrom::Start(*wire2label_offset?))?; - - let wire2label_size = sec_sizes.get(&wire2label_type).ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "No section size for wire2label type found", - ) - }); - - let wire_mapping = read_map(&mut reader, *wire2label_size?, &header)?; - - Ok(R1CSFile { - version, - header, - constraints, - wire_mapping, - }) - } -} - -pub struct Header { - pub field_size: u32, - pub prime_size: Vec, - pub n_wires: u32, - pub n_pub_out: u32, - pub n_pub_in: u32, - pub n_prv_in: u32, - pub n_labels: u64, - pub n_constraints: u32, -} - -impl Header { - fn new(mut reader: R, size: u64) -> IoResult
{ - let field_size = reader.read_u32::()?; - if field_size != 32 { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "This parser only supports 32-byte fields", - ))); - } - - if size != 32 + field_size as u64 { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "Invalid header section size", - ))); - } - - let mut prime_size = vec![0u8; field_size as usize]; - reader.read_exact(&mut prime_size)?; - - if prime_size - != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") - .unwrap() - { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "This parser only supports bn256", - ))); - } - - Ok(Header { - field_size, - prime_size, - n_wires: reader.read_u32::()?, - n_pub_out: reader.read_u32::()?, - n_pub_in: reader.read_u32::()?, - n_prv_in: reader.read_u32::()?, - n_labels: reader.read_u64::()?, - n_constraints: reader.read_u32::()?, - }) - } -} - -fn read_constraint_vec(mut reader: R) -> IoResult> { - let n_vec = reader.read_u32::()? as usize; - let mut vec = Vec::with_capacity(n_vec); - for _ in 0..n_vec { - vec.push(( - reader.read_u32::()? as usize, - E::ScalarField::deserialize_uncompressed(&mut reader)?, - )); - } - Ok(vec) -} - -fn read_constraints( - mut reader: R, - header: &Header, -) -> IoResult>> { - // todo check section size - let mut vec = Vec::with_capacity(header.n_constraints as usize); - for _ in 0..header.n_constraints { - vec.push(( - read_constraint_vec::<&mut R, E>(&mut reader)?, - read_constraint_vec::<&mut R, E>(&mut reader)?, - read_constraint_vec::<&mut R, E>(&mut reader)?, - )); - } - Ok(vec) -} - -fn read_map(mut reader: R, size: u64, header: &Header) -> IoResult> { - if size != header.n_wires as u64 * 8 { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "Invalid map section size", - ))); - } - let mut vec = Vec::with_capacity(header.n_wires as usize); - for _ in 0..header.n_wires { - vec.push(reader.read_u64::()?); - } - if vec[0] != 0 { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "Wire 0 should always be mapped to 0", - ))); - } - Ok(vec) -} \ No newline at end of file diff --git a/src/frontend/circom/witness/circom.rs b/src/frontend/circom/witness/circom.rs deleted file mode 100644 index f9f5aedd..00000000 --- a/src/frontend/circom/witness/circom.rs +++ /dev/null @@ -1,165 +0,0 @@ -use color_eyre::Result; -use wasmer::{Function, Instance, Value}; - -#[derive(Clone, Debug)] -pub struct Wasm(Instance); - -pub trait CircomBase { - fn init(&self, sanity_check: bool) -> Result<()>; - fn func(&self, name: &str) -> &Function; - fn get_ptr_witness_buffer(&self) -> Result; - fn get_ptr_witness(&self, w: u32) -> Result; - fn get_n_vars(&self) -> Result; - fn get_signal_offset32( - &self, - p_sig_offset: u32, - component: u32, - hash_msb: u32, - hash_lsb: u32, - ) -> Result<()>; - fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()>; - fn get_u32(&self, name: &str) -> Result; - // Only exists natively in Circom2, hardcoded for Circom - fn get_version(&self) -> Result; -} - -pub trait Circom { - fn get_fr_len(&self) -> Result; - fn get_ptr_raw_prime(&self) -> Result; -} - -pub trait Circom2 { - fn get_field_num_len32(&self) -> Result; - fn get_raw_prime(&self) -> Result<()>; - fn read_shared_rw_memory(&self, i: u32) -> Result; - fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()>; - fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()>; - fn get_witness(&self, i: u32) -> Result<()>; - fn get_witness_size(&self) -> Result; -} - -impl Circom for Wasm { - fn get_fr_len(&self) -> Result { - self.get_u32("getFrLen") - } - - fn get_ptr_raw_prime(&self) -> Result { - self.get_u32("getPRawPrime") - } -} - -#[cfg(feature = "circom-2")] -impl Circom2 for Wasm { - fn get_field_num_len32(&self) -> Result { - self.get_u32("getFieldNumLen32") - } - - fn get_raw_prime(&self) -> Result<()> { - let func = self.func("getRawPrime"); - func.call(&[])?; - Ok(()) - } - - fn read_shared_rw_memory(&self, i: u32) -> Result { - let func = self.func("readSharedRWMemory"); - let result = func.call(&[i.into()])?; - Ok(result[0].unwrap_i32() as u32) - } - - fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()> { - let func = self.func("writeSharedRWMemory"); - func.call(&[i.into(), v.into()])?; - Ok(()) - } - - fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()> { - let func = self.func("setInputSignal"); - func.call(&[hmsb.into(), hlsb.into(), pos.into()])?; - Ok(()) - } - - fn get_witness(&self, i: u32) -> Result<()> { - let func = self.func("getWitness"); - func.call(&[i.into()])?; - Ok(()) - } - - fn get_witness_size(&self) -> Result { - self.get_u32("getWitnessSize") - } -} - -impl CircomBase for Wasm { - fn init(&self, sanity_check: bool) -> Result<()> { - let func = self.func("init"); - func.call(&[Value::I32(sanity_check as i32)])?; - Ok(()) - } - - fn get_ptr_witness_buffer(&self) -> Result { - self.get_u32("getWitnessBuffer") - } - - fn get_ptr_witness(&self, w: u32) -> Result { - let func = self.func("getPWitness"); - let res = func.call(&[w.into()])?; - - Ok(res[0].unwrap_i32() as u32) - } - - fn get_n_vars(&self) -> Result { - self.get_u32("getNVars") - } - - fn get_signal_offset32( - &self, - p_sig_offset: u32, - component: u32, - hash_msb: u32, - hash_lsb: u32, - ) -> Result<()> { - let func = self.func("getSignalOffset32"); - func.call(&[ - p_sig_offset.into(), - component.into(), - hash_msb.into(), - hash_lsb.into(), - ])?; - - Ok(()) - } - - fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()> { - let func = self.func("setSignal"); - func.call(&[c_idx.into(), component.into(), signal.into(), p_val.into()])?; - - Ok(()) - } - - // Default to version 1 if it isn't explicitly defined - fn get_version(&self) -> Result { - match self.0.exports.get_function("getVersion") { - Ok(func) => Ok(func.call(&[])?[0].unwrap_i32() as u32), - Err(_) => Ok(1), - } - } - - fn get_u32(&self, name: &str) -> Result { - let func = self.func(name); - let result = func.call(&[])?; - Ok(result[0].unwrap_i32() as u32) - } - - fn func(&self, name: &str) -> &Function { - self.0 - .exports - .get_function(name) - .unwrap_or_else(|_| panic!("function {} not found", name)) - } -} - -impl Wasm { - pub fn new(instance: Instance) -> Self { - Self(instance) - } -} diff --git a/src/frontend/circom/witness/memory.rs b/src/frontend/circom/witness/memory.rs deleted file mode 100644 index 1298b411..00000000 --- a/src/frontend/circom/witness/memory.rs +++ /dev/null @@ -1,273 +0,0 @@ -//! Safe-ish interface for reading and writing specific types to the WASM runtime's memory -use ark_serialize::CanonicalDeserialize; -use num_traits::ToPrimitive; -use wasmer::{Memory, MemoryView}; - -// TODO: Decide whether we want Ark here or if it should use a generic BigInt package -use ark_bn254::FrConfig; -use ark_ff::MontConfig; -use ark_ff::{BigInteger, BigInteger256, Zero}; - -use num_bigint::{BigInt, BigUint}; - -use color_eyre::Result; -use std::str::FromStr; -use std::{convert::TryFrom, ops::Deref}; - -#[derive(Clone, Debug)] -pub struct SafeMemory { - pub memory: Memory, - pub prime: BigInt, - - short_max: BigInt, - short_min: BigInt, - r_inv: BigInt, - n32: usize, -} - -impl Deref for SafeMemory { - type Target = Memory; - - fn deref(&self) -> &Self::Target { - &self.memory - } -} - -impl SafeMemory { - /// Creates a new SafeMemory - pub fn new(memory: Memory, n32: usize, prime: BigInt) -> Self { - // TODO: Figure out a better way to calculate these - let short_max = BigInt::from(0x8000_0000u64); - let short_min = BigInt::from_biguint( - num_bigint::Sign::NoSign, - BigUint::try_from(FrConfig::MODULUS).unwrap(), - ) - &short_max; - let r_inv = BigInt::from_str( - "9915499612839321149637521777990102151350674507940716049588462388200839649614", - ) - .unwrap(); - - Self { - memory, - prime, - - short_max, - short_min, - r_inv, - n32, - } - } - - /// Gets an immutable view to the memory in 32 byte chunks - pub fn view(&self) -> MemoryView { - self.memory.view() - } - - /// Returns the next free position in the memory - pub fn free_pos(&self) -> u32 { - self.view()[0].get() - } - - /// Sets the next free position in the memory - pub fn set_free_pos(&mut self, ptr: u32) { - self.write_u32(0, ptr); - } - - /// Allocates a U32 in memory - pub fn alloc_u32(&mut self) -> u32 { - let p = self.free_pos(); - self.set_free_pos(p + 8); - p - } - - /// Writes a u32 to the specified memory offset - pub fn write_u32(&mut self, ptr: usize, num: u32) { - let buf = unsafe { self.memory.data_unchecked_mut() }; - buf[ptr..ptr + std::mem::size_of::()].copy_from_slice(&num.to_le_bytes()); - } - - /// Reads a u32 from the specified memory offset - pub fn read_u32(&self, ptr: usize) -> u32 { - let buf = unsafe { self.memory.data_unchecked() }; - - let mut bytes = [0; 4]; - bytes.copy_from_slice(&buf[ptr..ptr + std::mem::size_of::()]); - - u32::from_le_bytes(bytes) - } - - /// Allocates `self.n32 * 4 + 8` bytes in the memory - pub fn alloc_fr(&mut self) -> u32 { - let p = self.free_pos(); - self.set_free_pos(p + self.n32 as u32 * 4 + 8); - p - } - - /// Writes a Field Element to memory at the specified offset, truncating - /// to smaller u32 types if needed and adjusting the sign via 2s complement - pub fn write_fr(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - if fr < &self.short_max && fr > &self.short_min { - if fr >= &BigInt::zero() { - self.write_short_positive(ptr, fr)?; - } else { - self.write_short_negative(ptr, fr)?; - } - } else { - self.write_long_normal(ptr, fr)?; - } - - Ok(()) - } - - /// Reads a Field Element from the memory at the specified offset - pub fn read_fr(&self, ptr: usize) -> Result { - let view = self.memory.view::(); - - let res = if view[ptr + 4 + 3].get() & 0x80 != 0 { - let mut num = self.read_big(ptr + 8, self.n32)?; - if view[ptr + 4 + 3].get() & 0x40 != 0 { - num = (num * &self.r_inv) % &self.prime - } - num - } else if view[ptr + 3].get() & 0x40 != 0 { - let mut num = self.read_u32(ptr).into(); - // handle small negative - num -= BigInt::from(0x100000000i64); - num - } else { - self.read_u32(ptr).into() - }; - - Ok(res) - } - - fn write_short_positive(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - let num = fr.to_i32().expect("not a short positive"); - self.write_u32(ptr, num as u32); - self.write_u32(ptr + 4, 0); - Ok(()) - } - - fn write_short_negative(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - // 2s complement - let num = fr - &self.short_min; - let num = num - &self.short_max; - let num = num + BigInt::from(0x0001_0000_0000i64); - - let num = num - .to_u32() - .expect("could not cast as u32 (should never happen)"); - - self.write_u32(ptr, num); - self.write_u32(ptr + 4, 0); - Ok(()) - } - - fn write_long_normal(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - self.write_u32(ptr, 0); - self.write_u32(ptr + 4, i32::MIN as u32); // 0x80000000 - self.write_big(ptr + 8, fr)?; - Ok(()) - } - - fn write_big(&self, ptr: usize, num: &BigInt) -> Result<()> { - let buf = unsafe { self.memory.data_unchecked_mut() }; - - // TODO: How do we handle negative bignums? - let (_, num) = num.clone().into_parts(); - let num = BigInteger256::try_from(num).unwrap(); - - let bytes = num.to_bytes_le(); - let len = bytes.len(); - buf[ptr..ptr + len].copy_from_slice(&bytes); - - Ok(()) - } - - /// Reads `num_bytes * 32` from the specified memory offset in a Big Integer - pub fn read_big(&self, ptr: usize, num_bytes: usize) -> Result { - let buf = unsafe { self.memory.data_unchecked() }; - let buf = &buf[ptr..ptr + num_bytes * 32]; - - // TODO: Is there a better way to read big integers? - let big = BigInteger256::deserialize_uncompressed(buf).unwrap(); - let big = BigUint::try_from(big).unwrap(); - Ok(big.into()) - } -} - -// TODO: Figure out how to read / write numbers > u32 -// circom-witness-calculator: Wasm + Memory -> expose BigInts so that they can be consumed by any proof system -// ark-circom: -// 1. can read zkey -// 2. can generate witness from inputs -// 3. can generate proofs -// 4. can serialize proofs in the desired format -#[cfg(test)] -mod tests { - use super::*; - use num_traits::ToPrimitive; - use std::str::FromStr; - use wasmer::{MemoryType, Store}; - - fn new() -> SafeMemory { - SafeMemory::new( - Memory::new(&Store::default(), MemoryType::new(1, None, false)).unwrap(), - 2, - BigInt::from_str( - "21888242871839275222246405745257275088548364400416034343698204186575808495617", - ) - .unwrap(), - ) - } - - #[test] - fn i32_bounds() { - let mem = new(); - let i32_max = i32::MAX as i64 + 1; - assert_eq!(mem.short_min.to_i64().unwrap(), -i32_max); - assert_eq!(mem.short_max.to_i64().unwrap(), i32_max); - } - - #[test] - fn read_write_32() { - let mut mem = new(); - let num = u32::MAX; - - let inp = mem.read_u32(0); - assert_eq!(inp, 0); - - mem.write_u32(0, num); - let inp = mem.read_u32(0); - assert_eq!(inp, num); - } - - #[test] - fn read_write_fr_small_positive() { - read_write_fr(BigInt::from(1_000_000)); - } - - #[test] - fn read_write_fr_small_negative() { - read_write_fr(BigInt::from(-1_000_000)); - } - - #[test] - fn read_write_fr_big_positive() { - read_write_fr(BigInt::from(500000000000i64)); - } - - // TODO: How should this be handled? - #[test] - #[ignore] - fn read_write_fr_big_negative() { - read_write_fr(BigInt::from_str("-500000000000").unwrap()) - } - - fn read_write_fr(num: BigInt) { - let mut mem = new(); - mem.write_fr(0, &num).unwrap(); - let res = mem.read_fr(0).unwrap(); - assert_eq!(res, num); - } -} diff --git a/src/frontend/circom/witness/mod.rs b/src/frontend/circom/witness/mod.rs deleted file mode 100644 index 51708aa7..00000000 --- a/src/frontend/circom/witness/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -mod witness_calculator; -pub use witness_calculator::WitnessCalculator; - -mod memory; -pub(super) use memory::SafeMemory; - -mod circom; -pub(super) use circom::{CircomBase, Wasm}; - -#[cfg(feature = "circom-2")] -pub(super) use circom::Circom2; - -pub(super) use circom::Circom; - -use fnv::FnvHasher; -use std::hash::Hasher; - -pub(crate) fn fnv(inp: &str) -> (u32, u32) { - let mut hasher = FnvHasher::default(); - hasher.write(inp.as_bytes()); - let h = hasher.finish(); - - ((h >> 32) as u32, h as u32) -} diff --git a/src/frontend/circom/witness/witness_calculator.rs b/src/frontend/circom/witness/witness_calculator.rs deleted file mode 100644 index 49582b9f..00000000 --- a/src/frontend/circom/witness/witness_calculator.rs +++ /dev/null @@ -1,499 +0,0 @@ -use super::{fnv, CircomBase, SafeMemory, Wasm}; -use color_eyre::Result; -use num_bigint::BigInt; -use num_traits::Zero; -use std::cell::Cell; -use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, RuntimeError, Store}; - -#[cfg(feature = "circom-2")] -use num::ToPrimitive; - -#[cfg(feature = "circom-2")] -use super::Circom2; - -use super::Circom; - -#[derive(Clone, Debug)] -pub struct WitnessCalculator { - pub instance: Wasm, - pub memory: SafeMemory, - pub n64: u32, - pub circom_version: u32, -} - -// Error type to signal end of execution. -// From https://docs.wasmer.io/integrations/examples/exit-early -#[derive(thiserror::Error, Debug, Clone, Copy)] -#[error("{0}")] -struct ExitCode(u32); - -#[cfg(feature = "circom-2")] -fn from_array32(arr: Vec) -> BigInt { - let mut res = BigInt::zero(); - let radix = BigInt::from(0x100000000u64); - for &val in arr.iter() { - res = res * &radix + BigInt::from(val); - } - res -} - -#[cfg(feature = "circom-2")] -fn to_array32(s: &BigInt, size: usize) -> Vec { - let mut res = vec![0; size]; - let mut rem = s.clone(); - let radix = BigInt::from(0x100000000u64); - let mut c = size; - while !rem.is_zero() { - c -= 1; - res[c] = (&rem % &radix).to_u32().unwrap(); - rem /= &radix; - } - - res -} - -impl WitnessCalculator { - pub fn new(path: impl AsRef) -> Result { - Self::from_file(path) - } - - pub fn from_file(path: impl AsRef) -> Result { - let store = Store::default(); - let module = Module::from_file(&store, path)?; - Self::from_module(module) - } - - pub fn from_module(module: Module) -> Result { - let store = module.store(); - - // Set up the memory - let memory = Memory::new(store, MemoryType::new(2000, None, false)).unwrap(); - let import_object = imports! { - "env" => { - "memory" => memory.clone(), - }, - // Host function callbacks from the WASM - "runtime" => { - "error" => runtime::error(store), - "logSetSignal" => runtime::log_signal(store), - "logGetSignal" => runtime::log_signal(store), - "logFinishComponent" => runtime::log_component(store), - "logStartComponent" => runtime::log_component(store), - "log" => runtime::log_component(store), - "exceptionHandler" => runtime::exception_handler(store), - "showSharedRWMemory" => runtime::show_memory(store), - "printErrorMessage" => runtime::print_error_message(store), - "writeBufferMessage" => runtime::write_buffer_message(store), - } - }; - let instance = Wasm::new(Instance::new(&module, &import_object)?); - - let version = instance.get_version().unwrap_or(1); - - // Circom 2 feature flag with version 2 - #[cfg(feature = "circom-2")] - fn new_circom2(instance: Wasm, memory: Memory, version: u32) -> Result { - let n32 = instance.get_field_num_len32()?; - let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); - instance.get_raw_prime()?; - let mut arr = vec![0; n32 as usize]; - for i in 0..n32 { - let res = instance.read_shared_rw_memory(i)?; - arr[(n32 as usize) - (i as usize) - 1] = res; - } - let prime = from_array32(arr); - - let n64 = ((prime.bits() - 1) / 64 + 1) as u32; - safe_memory.prime = prime; - - Ok(WitnessCalculator { - instance, - memory: safe_memory, - n64, - circom_version: version, - }) - } - - fn new_circom1(instance: Wasm, memory: Memory, version: u32) -> Result { - // Fallback to Circom 1 behavior - let n32 = (instance.get_fr_len()? >> 2) - 2; - let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); - let ptr = instance.get_ptr_raw_prime()?; - let prime = safe_memory.read_big(ptr as usize, n32 as usize)?; - - let n64 = ((prime.bits() - 1) / 64 + 1) as u32; - safe_memory.prime = prime; - - Ok(WitnessCalculator { - instance, - memory: safe_memory, - n64, - circom_version: version, - }) - } - - // Three possibilities: - // a) Circom 2 feature flag enabled, WASM runtime version 2 - // b) Circom 2 feature flag enabled, WASM runtime version 1 - // c) Circom 1 default behavior - // - // Once Circom 2 support is more stable, feature flag can be removed - - cfg_if::cfg_if! { - if #[cfg(feature = "circom-2")] { - match version { - 2 => new_circom2(instance, memory, version), - 1 => new_circom1(instance, memory, version), - _ => panic!("Unknown Circom version") - } - } else { - new_circom1(instance, memory, version) - } - } - } - - pub fn calculate_witness)>>( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(sanity_check)?; - - cfg_if::cfg_if! { - if #[cfg(feature = "circom-2")] { - match self.circom_version { - 2 => self.calculate_witness_circom2(inputs, sanity_check), - 1 => self.calculate_witness_circom1(inputs, sanity_check), - _ => panic!("Unknown Circom version") - } - } else { - self.calculate_witness_circom1(inputs, sanity_check) - } - } - } - - // Circom 1 default behavior - fn calculate_witness_circom1)>>( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(sanity_check)?; - - let old_mem_free_pos = self.memory.free_pos(); - let p_sig_offset = self.memory.alloc_u32(); - let p_fr = self.memory.alloc_fr(); - - // allocate the inputs - for (name, values) in inputs.into_iter() { - let (msb, lsb) = fnv(&name); - - self.instance - .get_signal_offset32(p_sig_offset, 0, msb, lsb)?; - - let sig_offset = self.memory.read_u32(p_sig_offset as usize) as usize; - - for (i, value) in values.into_iter().enumerate() { - self.memory.write_fr(p_fr as usize, &value)?; - self.instance - .set_signal(0, 0, (sig_offset + i) as u32, p_fr)?; - } - } - - let mut w = Vec::new(); - - let n_vars = self.instance.get_n_vars()?; - for i in 0..n_vars { - let ptr = self.instance.get_ptr_witness(i)? as usize; - let el = self.memory.read_fr(ptr)?; - w.push(el); - } - - self.memory.set_free_pos(old_mem_free_pos); - - Ok(w) - } - - // Circom 2 feature flag with version 2 - #[cfg(feature = "circom-2")] - fn calculate_witness_circom2)>>( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(sanity_check)?; - - let n32 = self.instance.get_field_num_len32()?; - - // allocate the inputs - for (name, values) in inputs.into_iter() { - let (msb, lsb) = fnv(&name); - - for (i, value) in values.into_iter().enumerate() { - let f_arr = to_array32(&value, n32 as usize); - for j in 0..n32 { - self.instance - .write_shared_rw_memory(j, f_arr[(n32 as usize) - 1 - (j as usize)])?; - } - self.instance.set_input_signal(msb, lsb, i as u32)?; - } - } - - let mut w = Vec::new(); - - let witness_size = self.instance.get_witness_size()?; - for i in 0..witness_size { - self.instance.get_witness(i)?; - let mut arr = vec![0; n32 as usize]; - for j in 0..n32 { - arr[(n32 as usize) - 1 - (j as usize)] = self.instance.read_shared_rw_memory(j)?; - } - w.push(from_array32(arr)); - } - - Ok(w) - } - - pub fn calculate_witness_element< - E: ark_ec::pairing::Pairing, - I: IntoIterator)>, - >( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - use ark_ff::PrimeField; - let witness = self.calculate_witness(inputs, sanity_check)?; - let modulus = ::MODULUS; - - // convert it to field elements - use num_traits::Signed; - let witness = witness - .into_iter() - .map(|w| { - let w = if w.sign() == num_bigint::Sign::Minus { - // Need to negate the witness element if negative - modulus.into() - w.abs().to_biguint().unwrap() - } else { - w.to_biguint().unwrap() - }; - E::ScalarField::from(w) - }) - .collect::>(); - - Ok(witness) - } - - pub fn get_witness_buffer(&self) -> Result> { - let ptr = self.instance.get_ptr_witness_buffer()? as usize; - - let view = self.memory.memory.view::(); - - let len = self.instance.get_n_vars()? * self.n64 * 8; - let arr = view[ptr..ptr + len as usize] - .iter() - .map(Cell::get) - .collect::>(); - - Ok(arr) - } -} - -// callback hooks for debugging -mod runtime { - use super::*; - - pub fn error(store: &Store) -> Function { - #[allow(unused)] - #[allow(clippy::many_single_char_names)] - fn func(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) -> Result<(), RuntimeError> { - // NOTE: We can also get more information why it is failing, see p2str etc here: - // https://github.com/iden3/circom_runtime/blob/master/js/witness_calculator.js#L52-L64 - println!("runtime error, exiting early: {a} {b} {c} {d} {e} {f}",); - Err(RuntimeError::user(Box::new(ExitCode(1)))) - } - Function::new_native(store, func) - } - - // Circom 2.0 - pub fn exception_handler(store: &Store) -> Function { - #[allow(unused)] - fn func(a: i32) {} - Function::new_native(store, func) - } - - // Circom 2.0 - pub fn show_memory(store: &Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_native(store, func) - } - - // Circom 2.0 - pub fn print_error_message(store: &Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_native(store, func) - } - - // Circom 2.0 - pub fn write_buffer_message(store: &Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_native(store, func) - } - - pub fn log_signal(store: &Store) -> Function { - #[allow(unused)] - fn func(a: i32, b: i32) {} - Function::new_native(store, func) - } - - pub fn log_component(store: &Store) -> Function { - #[allow(unused)] - fn func(a: i32) {} - Function::new_native(store, func) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::{collections::HashMap, path::PathBuf}; - - struct TestCase<'a> { - circuit_path: &'a str, - inputs_path: &'a str, - n_vars: u32, - n64: u32, - witness: &'a [&'a str], - } - - pub fn root_path(p: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push(p); - path.to_string_lossy().to_string() - } - - #[test] - fn multiplier_1() { - run_test(TestCase { - circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), - inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(), - n_vars: 4, - n64: 4, - witness: &["1", "33", "3", "11"], - }); - } - - #[test] - fn multiplier_2() { - run_test(TestCase { - circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), - inputs_path: root_path("test-vectors/mycircuit-input2.json").as_str(), - n_vars: 4, - n64: 4, - witness: &[ - "1", - "21888242871839275222246405745257275088548364400416034343698204186575672693159", - "21888242871839275222246405745257275088548364400416034343698204186575796149939", - "11", - ], - }); - } - - #[test] - fn multiplier_3() { - run_test(TestCase { - circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(), - inputs_path: root_path("test-vectors/mycircuit-input3.json").as_str(), - n_vars: 4, - n64: 4, - witness: &[ - "1", - "21888242871839275222246405745257275088548364400416034343698204186575808493616", - "10944121435919637611123202872628637544274182200208017171849102093287904246808", - "2", - ], - }); - } - - #[test] - fn safe_multipler() { - let witness = - std::fs::read_to_string(root_path("test-vectors/safe-circuit-witness.json")).unwrap(); - let witness: Vec = serde_json::from_str(&witness).unwrap(); - let witness = &witness.iter().map(|x| x.as_ref()).collect::>(); - run_test(TestCase { - circuit_path: root_path("test-vectors/circuit2.wasm").as_str(), - inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(), - n_vars: 132, // 128 + 4 - n64: 4, - witness, - }); - } - - #[test] - fn smt_verifier() { - let witness = - std::fs::read_to_string(root_path("test-vectors/smtverifier10-witness.json")).unwrap(); - let witness: Vec = serde_json::from_str(&witness).unwrap(); - let witness = &witness.iter().map(|x| x.as_ref()).collect::>(); - - run_test(TestCase { - circuit_path: root_path("test-vectors/smtverifier10.wasm").as_str(), - inputs_path: root_path("test-vectors/smtverifier10-input.json").as_str(), - n_vars: 4794, - n64: 4, - witness, - }); - } - - use serde_json::Value; - use std::str::FromStr; - - fn value_to_bigint(v: Value) -> BigInt { - match v { - Value::String(inner) => BigInt::from_str(&inner).unwrap(), - Value::Number(inner) => BigInt::from(inner.as_u64().expect("not a u32")), - _ => panic!("unsupported type"), - } - } - - fn run_test(case: TestCase) { - let mut wtns = WitnessCalculator::new(case.circuit_path).unwrap(); - assert_eq!( - wtns.memory.prime.to_str_radix(16), - "30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase() - ); - assert_eq!({ wtns.instance.get_n_vars().unwrap() }, case.n_vars); - assert_eq!({ wtns.n64 }, case.n64); - - let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap(); - let inputs: std::collections::HashMap = - serde_json::from_str(&inputs_str).unwrap(); - - let inputs = inputs - .iter() - .map(|(key, value)| { - let res = match value { - Value::String(inner) => { - vec![BigInt::from_str(inner).unwrap()] - } - Value::Number(inner) => { - vec![BigInt::from(inner.as_u64().expect("not a u32"))] - } - Value::Array(inner) => inner.iter().cloned().map(value_to_bigint).collect(), - _ => panic!(), - }; - - (key.clone(), res) - }) - .collect::>(); - - let res = wtns.calculate_witness(inputs, false).unwrap(); - for (r, w) in res.iter().zip(case.witness) { - assert_eq!(r, &BigInt::from_str(w).unwrap()); - } - } -} From 95576a228e8b3dd034cf871f282197a2f5e1b720 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:34:58 +0900 Subject: [PATCH 11/29] improved l in R1CS as the number of public I/O --- src/frontend/circom/mod.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 775957b7..470716df 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -31,17 +31,18 @@ pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> } // Extract R1CS constraints from the provided R1CS file path. -pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result>, Box> { +pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result<(Vec>, usize), Box> { let file = File::open(r1cs_filepath)?; let reader = BufReader::new(file); let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; + let num_public_inputs = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - Ok(r1cs.constraints) + Ok((r1cs.constraints, num_public_inputs)) } // Convert R1CS constraints for Bn254 to the format suited for folding-schemes. -pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> R1CS { +pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, public_inputs: usize) -> R1CS { let mut a_matrix: Vec> = Vec::new(); let mut b_matrix: Vec> = Vec::new(); let mut c_matrix: Vec> = Vec::new(); @@ -53,12 +54,9 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>) -> b_matrix.push(bi.into_iter().map(|(index, scalar)| (scalar, index)).collect()); c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); } - - // TODO: Refine this logic. - // Ideally, `l` should represent only the size of the public input and output. - // However, currently, all elements in the R1CS or Z are treated as public. - let l = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); - let n_cols = l; + + let l = public_inputs; + let n_cols = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); let A = SparseMatrix:: { n_rows, @@ -100,8 +98,9 @@ fn num_bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result>, witness: &Vec, + public_inputs: usize, ) -> Result<(R1CS, Vec), Box> { - let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints); + let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, public_inputs); let z: Vec = witness .iter() @@ -154,7 +153,7 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let constraints = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); + let (constraints, public_inputs) = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); assert!(!constraints.is_empty()); let converted_constraints: Vec> = constraints @@ -173,7 +172,7 @@ mod tests { let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); assert!(!witness.is_empty()); - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness) + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) .expect("Error"); assert!(!z.is_empty()); From 104d07b56e7f70d596a9f989009ea67ac4934fa5 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 25 Sep 2023 23:21:44 +0900 Subject: [PATCH 12/29] separate test functions into success/failure and unify variable to pub_io_len --- src/frontend/circom/mod.rs | 99 +++++++++++-------- .../circom/test_folder/test_circuit.circom | 13 +++ 2 files changed, 71 insertions(+), 41 deletions(-) create mode 100644 src/frontend/circom/test_folder/test_circuit.circom diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 470716df..fa7633a9 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -36,13 +36,13 @@ pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result<(Vec::new(reader)?; - let num_public_inputs = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; + let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - Ok((r1cs.constraints, num_public_inputs)) + Ok((r1cs.constraints, pub_io_len)) } // Convert R1CS constraints for Bn254 to the format suited for folding-schemes. -pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, public_inputs: usize) -> R1CS { +pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub_io_len: usize) -> R1CS { let mut a_matrix: Vec> = Vec::new(); let mut b_matrix: Vec> = Vec::new(); let mut c_matrix: Vec> = Vec::new(); @@ -55,7 +55,7 @@ pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); } - let l = public_inputs; + let l = pub_io_len; let n_cols = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); let A = SparseMatrix:: { @@ -98,9 +98,9 @@ fn num_bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result>, witness: &Vec, - public_inputs: usize, + pub_io_len: usize, ) -> Result<(R1CS, Vec), Box> { - let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, public_inputs); + let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, pub_io_len); let z: Vec = witness .iter() @@ -119,64 +119,81 @@ pub fn circom_to_folding_schemes_r1cs_and_z( mod tests { use ark_bn254::Bn254; use num_bigint::BigInt; - + use std::env; + use super::Constraints; use super::calculate_witness; use super::circom_to_folding_schemes_r1cs_and_z; use super::convert_constraints_bigint_to_scalar; use super::extract_constraints_from_r1cs; - + + /* + test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. + In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly. + In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. + */ + #[test] - fn test_circom_to_folding_conversion() { - let current_dir = std::env::current_dir().unwrap(); - + fn test_circom_conversion_success() { + let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); let r1cs_filepath = base_path.join("test_circuit.r1cs"); let wasm_filepath = base_path.join("test_circuit.wasm"); - - /* - This is the test_circuit.cirom file. - When step_in equals 3, it will pass the test function. - - template Example () { - signal input step_in; - signal output step_out; - signal temp; - - temp <== step_in * step_in; - step_out <== temp * step_in + step_in + 5; - step_out === 35; - } - component main = Example(); - */ - + assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - + let (constraints, public_inputs) = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); assert!(!constraints.is_empty()); - + let converted_constraints: Vec> = constraints .iter() .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) .collect(); assert_eq!(constraints.len(), converted_constraints.len()); - - let inputs = vec![ - // success - ("step_in".to_string(), vec![BigInt::from(3)]), - // fail - // ("step_in".to_string(), vec![BigInt::from(6)]), - ]; - + + let inputs = vec![("step_in".to_string(), vec![BigInt::from(3)])]; + let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); assert!(!witness.is_empty()); - + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) .expect("Error"); assert!(!z.is_empty()); + + r1cs.check_relation(&z).expect("Error"); + } + #[test] + #[should_panic(expected = "Error")] + fn test_circom_conversion_failure() { + let current_dir = env::current_dir().unwrap(); + let base_path = current_dir.join("src/frontend/circom/test_folder"); + let r1cs_filepath = base_path.join("test_circuit.r1cs"); + let wasm_filepath = base_path.join("test_circuit.wasm"); + + assert!(r1cs_filepath.exists()); + assert!(wasm_filepath.exists()); + + let (constraints, public_inputs) = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); + assert!(!constraints.is_empty()); + + let converted_constraints: Vec> = constraints + .iter() + .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) + .collect(); + assert_eq!(constraints.len(), converted_constraints.len()); + + let inputs = vec![("step_in".to_string(), vec![BigInt::from(6)])]; + + let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); + assert!(!witness.is_empty()); + + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) + .expect("Error"); + assert!(!z.is_empty()); + r1cs.check_relation(&z).expect("Error"); - } - + } } + diff --git a/src/frontend/circom/test_folder/test_circuit.circom b/src/frontend/circom/test_folder/test_circuit.circom new file mode 100644 index 00000000..aa17932d --- /dev/null +++ b/src/frontend/circom/test_folder/test_circuit.circom @@ -0,0 +1,13 @@ +pragma circom 2.0.3; + +template Example () { + signal input step_in; + signal output step_out; + signal temp; + + temp <== step_in * step_in; + step_out <== temp * step_in + step_in + 5; + step_out === 35; +} + +component main = Example(); From 1d70036fd5c3b598863a2523b132565736f7cd83 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 1 Oct 2023 15:38:44 +0900 Subject: [PATCH 13/29] removed bn254 & abstracted to PrimeField, but still some work --- src/frontend/circom/mod.rs | 163 ++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 4 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index fa7633a9..9f1803a2 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -6,14 +6,168 @@ use color_eyre::Result; use ark_ff::PrimeField; use ark_ec::pairing::Pairing; use ark_circom::{circom::r1cs_reader, WitnessCalculator}; -use ark_bn254::{Bn254, Fr}; use crate::ccs::r1cs::R1CS; use crate::utils::vec::SparseMatrix; -pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); -pub type ConstraintVec = Vec<(usize, ::ScalarField)>; +pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); +pub type ConstraintVec = Vec<(usize, F)>; +pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints + where E: Pairing, + F: PrimeField, +{ + let convert_vec = |vec: ConstraintVec| -> ConstraintVec { + vec.into_iter() + .filter_map(|(index, element)| { + let bigint_e: <::ScalarField as PrimeField>::BigInt = element.into_bigint(); + let generic_biguint: num_bigint::BigUint = bigint_e.into(); // BigUintに変換 + let f_element: F = F::from(generic_biguint); + Some((index, f_element)) + }) + .collect() + }; + + ( + convert_vec(constraints.0), + convert_vec(constraints.1), + convert_vec(constraints.2), + ) +} + +pub fn extract_constraints_from_r1cs( + r1cs_filepath: &PathBuf +) -> Result<(Vec>, usize), Box> +where + E: Pairing, + F: PrimeField, +{ + let file = File::open(r1cs_filepath)?; + let reader = BufReader::new(file); + + let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; + let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; + let r1cs = r1cs_reader::R1CS::::from(r1cs_file); + let converted_constraints: Vec> = r1cs.constraints.into_iter() + .map(|c| convert_constraints_bigint_to_scalar::(c)) + .collect(); + + Ok((converted_constraints, pub_io_len)) +} + +pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub_io_len: usize) -> R1CS +where + F: PrimeField, +{ + let mut a_matrix: Vec> = Vec::new(); + let mut b_matrix: Vec> = Vec::new(); + let mut c_matrix: Vec> = Vec::new(); + + let n_rows = constraints.len(); + + for (ai, bi, ci) in constraints { + a_matrix.push(ai.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + b_matrix.push(bi.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + } + + let l = pub_io_len; + let n_cols = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); + + let A = SparseMatrix { + n_rows, + n_cols, + coeffs: a_matrix, + }; + let B = SparseMatrix { + n_rows, + n_cols, + coeffs: b_matrix, + }; + let C = SparseMatrix { + n_rows, + n_cols, + coeffs: c_matrix, + }; + + R1CS { l, A, B, C } +} + +// Calculate the witness given the WASM filepath and inputs. +pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { + let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; + calculator.calculate_witness(inputs, true) +} + +// Helper function to convert `num_bigint::BigInt` to `ark_ff::BigInt`. +fn num_bigint_to_ark_bigint(value: &BigInt) -> Result> { + let big_uint = value.to_biguint().ok_or_else(|| "BigInt is negative".to_string())?; + F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into()) +} + +pub fn circom_to_folding_schemes_r1cs_and_z( + constraints: Vec>, + witness: &Vec, + pub_io_len: usize, +) -> Result<(R1CS, Vec), Box> +where + F: PrimeField, +{ + let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, pub_io_len); + + let z: Result, _> = witness + .iter() + .map(|big_int| { + let ark_big_int = num_bigint_to_ark_bigint::(big_int)?; + F::from_bigint(ark_big_int).ok_or_else(|| "Failed to convert bigint to field element".to_string().into()) + }) + .collect(); + + z.map(|z| (folding_schemes_r1cs, z)) +} + +#[cfg(test)] +mod tests { + use super::*; + // use ark_pallas::Fr; + use num_bigint::BigInt; + use std::env; + use ark_bn254::{Bn254, Fr}; + + #[test] + fn test_circom_conversion_success() { + let current_dir = env::current_dir().unwrap(); + let base_path = current_dir.join("src/frontend/circom/test_folder"); + let r1cs_filepath = base_path.join("test_circuit.r1cs"); + let wasm_filepath = base_path.join("test_circuit.wasm"); + + assert!(r1cs_filepath.exists()); + assert!(wasm_filepath.exists()); + + let (constraints, public_inputs) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); + assert!(!constraints.is_empty()); + + let converted_constraints: Vec> = constraints + .iter() + .map(|constraint| convert_constraints_bigint_to_scalar::(constraint.clone())) + .collect(); + assert_eq!(constraints.len(), converted_constraints.len()); + + let inputs = vec![("step_in".to_string(), vec![BigInt::from(3)])]; + let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); + assert!(!witness.is_empty()); + + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) + .expect("Error"); + assert!(!z.is_empty()); + r1cs.check_relation(&z).expect("Error"); + } + + // ... other tests +} + + +/* // Convert the BigInt constraints to Bn254's ScalarField constraints. pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints { let convert_vec = |vec: ConstraintVec| -> ConstraintVec { @@ -193,7 +347,8 @@ mod tests { .expect("Error"); assert!(!z.is_empty()); + // check_relation function is implemented in folding_schems/src/ccs/r1cs.rs r1cs.check_relation(&z).expect("Error"); } } - +*/ From 877ee39a68289ec7c040861bd726e00304c1d1fc Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Sun, 1 Oct 2023 20:07:23 +0900 Subject: [PATCH 14/29] add comments and clean up code --- src/frontend/circom/mod.rs | 215 +++++++------------------------------ 1 file changed, 36 insertions(+), 179 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 9f1803a2..9a491cfa 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -10,9 +10,11 @@ use ark_circom::{circom::r1cs_reader, WitnessCalculator}; use crate::ccs::r1cs::R1CS; use crate::utils::vec::SparseMatrix; +// Define the sparse matrices on PrimeFiled pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; +// Convert the generic type of constraints from Pairing's ScalarField to PrimeField. pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints where E: Pairing, F: PrimeField, @@ -20,21 +22,20 @@ pub fn convert_constraints_bigint_to_scalar(constraints: Constraints| -> ConstraintVec { vec.into_iter() .filter_map(|(index, element)| { + // Convert element into BigInt, then to BigUint, then to the destination PrimeField let bigint_e: <::ScalarField as PrimeField>::BigInt = element.into_bigint(); - let generic_biguint: num_bigint::BigUint = bigint_e.into(); // BigUintに変換 + let generic_biguint: num_bigint::BigUint = bigint_e.into(); let f_element: F = F::from(generic_biguint); Some((index, f_element)) }) .collect() }; - ( - convert_vec(constraints.0), - convert_vec(constraints.1), - convert_vec(constraints.2), - ) + (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) } +// Extract constraints on Pairing's ScalarField from an .r1cs file +// and convert them into constraints on PrimeField pub fn extract_constraints_from_r1cs( r1cs_filepath: &PathBuf ) -> Result<(Vec>, usize), Box> @@ -42,9 +43,11 @@ where E: Pairing, F: PrimeField, { + // Open the .r1cs file and create a reader let file = File::open(r1cs_filepath)?; let reader = BufReader::new(file); + // Read the R1CS file, extract the constraints, and then convert them into new constraints on PrimeField let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); @@ -55,6 +58,7 @@ where Ok((converted_constraints, pub_io_len)) } +// Convert a set of constraints from ark-circom into R1CS format of folding_schemes pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub_io_len: usize) -> R1CS where F: PrimeField, @@ -90,7 +94,12 @@ where coeffs: c_matrix, }; - R1CS { l, A, B, C } + R1CS:: { + l, + A, + B, + C, + } } // Calculate the witness given the WASM filepath and inputs. @@ -99,12 +108,14 @@ pub fn calculate_witness)>>(wasm_fil calculator.calculate_witness(inputs, true) } -// Helper function to convert `num_bigint::BigInt` to `ark_ff::BigInt`. +// Convert a num_bigint's BigInt to PrimeField's BigInt fn num_bigint_to_ark_bigint(value: &BigInt) -> Result> { let big_uint = value.to_biguint().ok_or_else(|| "BigInt is negative".to_string())?; F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into()) } +// Convert R1CS constraints and witness from ark-circom format +// into folding-schemes R1CS and z format. pub fn circom_to_folding_schemes_r1cs_and_z( constraints: Vec>, witness: &Vec, @@ -129,11 +140,16 @@ where #[cfg(test)] mod tests { use super::*; - // use ark_pallas::Fr; use num_bigint::BigInt; use std::env; use ark_bn254::{Bn254, Fr}; + /* + test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. + In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly. + In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. + */ + #[test] fn test_circom_conversion_success() { let current_dir = env::current_dir().unwrap(); @@ -144,7 +160,7 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let (constraints, public_inputs) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); + let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); assert!(!constraints.is_empty()); let converted_constraints: Vec> = constraints @@ -157,169 +173,13 @@ mod tests { let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); assert!(!witness.is_empty()); - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) .expect("Error"); assert!(!z.is_empty()); r1cs.check_relation(&z).expect("Error"); } - // ... other tests -} - - -/* -// Convert the BigInt constraints to Bn254's ScalarField constraints. -pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints { - let convert_vec = |vec: ConstraintVec| -> ConstraintVec { - vec.into_iter() - .filter_map(|(index, bigint)| { - match Fr::from_bigint(bigint.into()) { - Some(scalar) => Some((index, scalar)), - None => None - } - }) - .collect() - }; - - (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) -} - -// Extract R1CS constraints from the provided R1CS file path. -pub fn extract_constraints_from_r1cs(r1cs_filepath: &PathBuf) -> Result<(Vec>, usize), Box> { - let file = File::open(r1cs_filepath)?; - let reader = BufReader::new(file); - - let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; - let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; - let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - Ok((r1cs.constraints, pub_io_len)) -} - -// Convert R1CS constraints for Bn254 to the format suited for folding-schemes. -pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub_io_len: usize) -> R1CS { - let mut a_matrix: Vec> = Vec::new(); - let mut b_matrix: Vec> = Vec::new(); - let mut c_matrix: Vec> = Vec::new(); - - let n_rows = constraints.len(); - - for (ai, bi, ci) in constraints { - a_matrix.push(ai.into_iter().map(|(index, scalar)| (scalar, index)).collect()); - b_matrix.push(bi.into_iter().map(|(index, scalar)| (scalar, index)).collect()); - c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); - } - - let l = pub_io_len; - let n_cols = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); - - let A = SparseMatrix:: { - n_rows, - n_cols, - coeffs: a_matrix, - }; - let B = SparseMatrix:: { - n_rows, - n_cols, - coeffs: b_matrix, - }; - let C = SparseMatrix:: { - n_rows, - n_cols, - coeffs: c_matrix, - }; - - R1CS:: { - l, - A, - B, - C, - } -} - -// Calculate the witness given the WASM filepath and inputs. -pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { - let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; - calculator.calculate_witness(inputs, true) -} - -// Helper function to convert `num_bigint::BigInt` to `ark_ff::BigInt`. -fn num_bigint_to_ark_bigint(value: &num_bigint::BigInt) -> Result, Box> { - let big_uint = value.to_biguint().ok_or_else(|| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt is negative")))?; - Ok(ark_ff::BigInt::<4>::try_from(big_uint).map_err(|_| Box::new(std::io::Error::new(std::io::ErrorKind::Other, "BigInt conversion failed")))?) -} - -// Convert R1CS constraints and witness from Circom format to folding-schemes R1CS and z format. -pub fn circom_to_folding_schemes_r1cs_and_z( - constraints: Vec>, - witness: &Vec, - pub_io_len: usize, -) -> Result<(R1CS, Vec), Box> { - let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, pub_io_len); - - let z: Vec = witness - .iter() - .filter_map(|big_int| { - match num_bigint_to_ark_bigint(big_int) { - Ok(ark_big_int) => Fr::from_bigint(ark_big_int.into()), - Err(_) => None - } - }) - .collect(); - - Ok((folding_schemes_r1cs, z)) -} - -#[cfg(test)] -mod tests { - use ark_bn254::Bn254; - use num_bigint::BigInt; - use std::env; - - use super::Constraints; - use super::calculate_witness; - use super::circom_to_folding_schemes_r1cs_and_z; - use super::convert_constraints_bigint_to_scalar; - use super::extract_constraints_from_r1cs; - - /* - test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. - In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly. - In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. - */ - #[test] - fn test_circom_conversion_success() { - let current_dir = env::current_dir().unwrap(); - let base_path = current_dir.join("src/frontend/circom/test_folder"); - let r1cs_filepath = base_path.join("test_circuit.r1cs"); - let wasm_filepath = base_path.join("test_circuit.wasm"); - - assert!(r1cs_filepath.exists()); - assert!(wasm_filepath.exists()); - - let (constraints, public_inputs) = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); - assert!(!constraints.is_empty()); - - let converted_constraints: Vec> = constraints - .iter() - .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) - .collect(); - assert_eq!(constraints.len(), converted_constraints.len()); - - let inputs = vec![("step_in".to_string(), vec![BigInt::from(3)])]; - - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); - assert!(!witness.is_empty()); - - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) - .expect("Error"); - assert!(!z.is_empty()); - - r1cs.check_relation(&z).expect("Error"); - } - - #[test] - #[should_panic(expected = "Error")] fn test_circom_conversion_failure() { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); @@ -329,26 +189,23 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let (constraints, public_inputs) = extract_constraints_from_r1cs(&r1cs_filepath).expect("Error"); + let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); assert!(!constraints.is_empty()); - - let converted_constraints: Vec> = constraints + + let converted_constraints: Vec> = constraints .iter() - .map(|constraint| convert_constraints_bigint_to_scalar(constraint.clone())) + .map(|constraint| convert_constraints_bigint_to_scalar::(constraint.clone())) .collect(); assert_eq!(constraints.len(), converted_constraints.len()); - + let inputs = vec![("step_in".to_string(), vec![BigInt::from(6)])]; - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); assert!(!witness.is_empty()); - - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, public_inputs) + + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) .expect("Error"); assert!(!z.is_empty()); - - // check_relation function is implemented in folding_schems/src/ccs/r1cs.rs r1cs.check_relation(&z).expect("Error"); } -} -*/ + +} \ No newline at end of file From 1c41806f27aa772cd845530683d0e17a32e4bcba Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 5 Oct 2023 20:34:11 +0900 Subject: [PATCH 15/29] move ark-bn254 in dev-dependencies --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a2616532..e776055b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ ark-crypto-primitives = { version = "^0.4.0", default-features = false, features ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } -ark-bn254 = "0.4.0" thiserror = "1.0" rayon = "1.7.0" num-bigint = "0.4" @@ -27,6 +26,7 @@ espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", pack ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0"} ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["crh"] } +ark-bn254 = "0.4.0" [features] default = ["parallel", "nova", "hypernova"] From 10e098685ccc0554dfc9a2ef98539a51d86d0fcd Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 5 Oct 2023 21:16:36 +0900 Subject: [PATCH 16/29] abstracted test function --- src/frontend/circom/mod.rs | 150 +++++++++++++++++++++---------------- src/frontend/mod.rs | 2 +- 2 files changed, 85 insertions(+), 67 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 9a491cfa..4cf3208f 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -1,11 +1,11 @@ -use std::{fs::File, io::BufReader, path::PathBuf, error::Error}; +use std::{error::Error, fs::File, io::BufReader, path::PathBuf}; -use num_bigint::BigInt; use color_eyre::Result; +use num_bigint::BigInt; -use ark_ff::PrimeField; -use ark_ec::pairing::Pairing; use ark_circom::{circom::r1cs_reader, WitnessCalculator}; +use ark_ec::pairing::Pairing; +use ark_ff::PrimeField; use crate::ccs::r1cs::R1CS; use crate::utils::vec::SparseMatrix; @@ -14,30 +14,38 @@ use crate::utils::vec::SparseMatrix; pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; -// Convert the generic type of constraints from Pairing's ScalarField to PrimeField. -pub fn convert_constraints_bigint_to_scalar(constraints: Constraints) -> Constraints - where E: Pairing, - F: PrimeField, +// Convert the generic type of constraints from Pairing's ScalarField to PrimeField. +pub fn convert_constraints_bigint_to_scalar( + constraints: Constraints, +) -> Constraints +where + E: Pairing, + F: PrimeField, { let convert_vec = |vec: ConstraintVec| -> ConstraintVec { vec.into_iter() .filter_map(|(index, element)| { // Convert element into BigInt, then to BigUint, then to the destination PrimeField - let bigint_e: <::ScalarField as PrimeField>::BigInt = element.into_bigint(); + let bigint_e: <::ScalarField as PrimeField>::BigInt = + element.into_bigint(); let generic_biguint: num_bigint::BigUint = bigint_e.into(); let f_element: F = F::from(generic_biguint); Some((index, f_element)) }) .collect() - }; - - (convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2)) + }; + + ( + convert_vec(constraints.0), + convert_vec(constraints.1), + convert_vec(constraints.2), + ) } // Extract constraints on Pairing's ScalarField from an .r1cs file // and convert them into constraints on PrimeField pub fn extract_constraints_from_r1cs( - r1cs_filepath: &PathBuf + r1cs_filepath: &PathBuf, ) -> Result<(Vec>, usize), Box> where E: Pairing, @@ -51,7 +59,9 @@ where let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - let converted_constraints: Vec> = r1cs.constraints.into_iter() + let converted_constraints: Vec> = r1cs + .constraints + .into_iter() .map(|c| convert_constraints_bigint_to_scalar::(c)) .collect(); @@ -59,7 +69,10 @@ where } // Convert a set of constraints from ark-circom into R1CS format of folding_schemes -pub fn convert_to_folding_schemes_r1cs(constraints: Vec>, pub_io_len: usize) -> R1CS +pub fn convert_to_folding_schemes_r1cs( + constraints: Vec>, + pub_io_len: usize, +) -> R1CS where F: PrimeField, { @@ -70,9 +83,21 @@ where let n_rows = constraints.len(); for (ai, bi, ci) in constraints { - a_matrix.push(ai.into_iter().map(|(index, scalar)| (scalar, index)).collect()); - b_matrix.push(bi.into_iter().map(|(index, scalar)| (scalar, index)).collect()); - c_matrix.push(ci.into_iter().map(|(index, scalar)| (scalar, index)).collect()); + a_matrix.push( + ai.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); + b_matrix.push( + bi.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); + c_matrix.push( + ci.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); } let l = pub_io_len; @@ -94,27 +119,27 @@ where coeffs: c_matrix, }; - R1CS:: { - l, - A, - B, - C, - } + R1CS:: { l, A, B, C } } // Calculate the witness given the WASM filepath and inputs. -pub fn calculate_witness)>>(wasm_filepath: &PathBuf, inputs: I) -> Result> { +pub fn calculate_witness)>>( + wasm_filepath: &PathBuf, + inputs: I, +) -> Result> { let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; calculator.calculate_witness(inputs, true) } // Convert a num_bigint's BigInt to PrimeField's BigInt fn num_bigint_to_ark_bigint(value: &BigInt) -> Result> { - let big_uint = value.to_biguint().ok_or_else(|| "BigInt is negative".to_string())?; + let big_uint = value + .to_biguint() + .ok_or_else(|| "BigInt is negative".to_string())?; F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into()) } -// Convert R1CS constraints and witness from ark-circom format +// Convert R1CS constraints and witness from ark-circom format // into folding-schemes R1CS and z format. pub fn circom_to_folding_schemes_r1cs_and_z( constraints: Vec>, @@ -130,7 +155,11 @@ where .iter() .map(|big_int| { let ark_big_int = num_bigint_to_ark_bigint::(big_int)?; - F::from_bigint(ark_big_int).ok_or_else(|| "Failed to convert bigint to field element".to_string().into()) + F::from_bigint(ark_big_int).ok_or_else(|| { + "Failed to convert bigint to field element" + .to_string() + .into() + }) }) .collect(); @@ -140,9 +169,9 @@ where #[cfg(test)] mod tests { use super::*; + use ark_bn254::{Bn254, Fr}; use num_bigint::BigInt; use std::env; - use ark_bn254::{Bn254, Fr}; /* test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. @@ -150,17 +179,17 @@ mod tests { In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. */ - #[test] - fn test_circom_conversion_success() { + fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec)>) { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); let r1cs_filepath = base_path.join("test_circuit.r1cs"); let wasm_filepath = base_path.join("test_circuit.wasm"); - + assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - - let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); + + let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath) + .expect("Error extracting constraints"); assert!(!constraints.is_empty()); let converted_constraints: Vec> = constraints @@ -169,43 +198,32 @@ mod tests { .collect(); assert_eq!(constraints.len(), converted_constraints.len()); - let inputs = vec![("step_in".to_string(), vec![BigInt::from(3)])]; - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); + let witness = calculate_witness(&wasm_filepath, inputs).expect("Error calculating witness"); assert!(!witness.is_empty()); - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) - .expect("Error"); + let (r1cs, z) = + circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) + .expect("Error converting to folding schemes"); assert!(!z.is_empty()); - r1cs.check_relation(&z).expect("Error"); - } - #[test] - fn test_circom_conversion_failure() { - let current_dir = env::current_dir().unwrap(); - let base_path = current_dir.join("src/frontend/circom/test_folder"); - let r1cs_filepath = base_path.join("test_circuit.r1cs"); - let wasm_filepath = base_path.join("test_circuit.wasm"); - - assert!(r1cs_filepath.exists()); - assert!(wasm_filepath.exists()); - - let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath).expect("Error"); - assert!(!constraints.is_empty()); + let check_result = std::panic::catch_unwind(|| { + r1cs.check_relation(&z).unwrap(); + }); - let converted_constraints: Vec> = constraints - .iter() - .map(|constraint| convert_constraints_bigint_to_scalar::(constraint.clone())) - .collect(); - assert_eq!(constraints.len(), converted_constraints.len()); + match (expect_success, check_result) { + (true, Ok(_)) => {} + (false, Err(_)) => {} + (true, Err(_)) => panic!("Expected success but got a failure."), + (false, Ok(_)) => panic!("Expected a failure but got success."), + } + } - let inputs = vec![("step_in".to_string(), vec![BigInt::from(6)])]; - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error"); - assert!(!witness.is_empty()); + #[test] + fn test_circom_conversion() { + // expect it to pass for correct inputs + test_circom_conversion_logic(true, vec![("step_in".to_string(), vec![BigInt::from(3)])]); - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) - .expect("Error"); - assert!(!z.is_empty()); - r1cs.check_relation(&z).expect("Error"); + // expect it to fail for incorrect inputs + test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(4)])]); } - -} \ No newline at end of file +} diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index a6ab5532..80a1046b 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -1,2 +1,2 @@ pub mod arkworks; -pub mod circom; \ No newline at end of file +pub mod circom; From a3fe2e4d63e00dc59ec4d8743ba7b6d37b27fd8d Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:24:10 +0900 Subject: [PATCH 17/29] fixed github action's error --- src/frontend/circom/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 4cf3208f..17c8054a 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -1,4 +1,4 @@ -use std::{error::Error, fs::File, io::BufReader, path::PathBuf}; +use std::{error::Error, fs::File, io::BufReader, path::Path}; use color_eyre::Result; use num_bigint::BigInt; @@ -14,6 +14,8 @@ use crate::utils::vec::SparseMatrix; pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; +type ExtractedConstraintsResult = Result<(Vec>, usize), Box>; + // Convert the generic type of constraints from Pairing's ScalarField to PrimeField. pub fn convert_constraints_bigint_to_scalar( constraints: Constraints, @@ -24,13 +26,13 @@ where { let convert_vec = |vec: ConstraintVec| -> ConstraintVec { vec.into_iter() - .filter_map(|(index, element)| { + .map(|(index, element)| { // Convert element into BigInt, then to BigUint, then to the destination PrimeField let bigint_e: <::ScalarField as PrimeField>::BigInt = element.into_bigint(); let generic_biguint: num_bigint::BigUint = bigint_e.into(); let f_element: F = F::from(generic_biguint); - Some((index, f_element)) + (index, f_element) }) .collect() }; @@ -45,8 +47,8 @@ where // Extract constraints on Pairing's ScalarField from an .r1cs file // and convert them into constraints on PrimeField pub fn extract_constraints_from_r1cs( - r1cs_filepath: &PathBuf, -) -> Result<(Vec>, usize), Box> + r1cs_filepath: &Path, +) -> ExtractedConstraintsResult where E: Pairing, F: PrimeField, @@ -124,7 +126,7 @@ where // Calculate the witness given the WASM filepath and inputs. pub fn calculate_witness)>>( - wasm_filepath: &PathBuf, + wasm_filepath: &Path, inputs: I, ) -> Result> { let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; @@ -143,7 +145,7 @@ fn num_bigint_to_ark_bigint(value: &BigInt) -> Result( constraints: Vec>, - witness: &Vec, + witness: &[BigInt], pub_io_len: usize, ) -> Result<(R1CS, Vec), Box> where From 25b900058a81977f0a02e46d7734a68568fefd34 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Fri, 6 Oct 2023 00:21:17 +0900 Subject: [PATCH 18/29] cargo fmt --- src/frontend/circom/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 17c8054a..dfb94287 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -46,9 +46,7 @@ where // Extract constraints on Pairing's ScalarField from an .r1cs file // and convert them into constraints on PrimeField -pub fn extract_constraints_from_r1cs( - r1cs_filepath: &Path, -) -> ExtractedConstraintsResult +pub fn extract_constraints_from_r1cs(r1cs_filepath: &Path) -> ExtractedConstraintsResult where E: Pairing, F: PrimeField, From f7e61940a5be74af10b30b38fda7580ab93c3a83 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:38:54 +0900 Subject: [PATCH 19/29] remove convert_constraints_bigint_to_scalar function --- src/frontend/circom/mod.rs | 66 ++++++-------------------------------- 1 file changed, 10 insertions(+), 56 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index dfb94287..e661dfc7 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -16,56 +16,22 @@ pub type ConstraintVec = Vec<(usize, F)>; type ExtractedConstraintsResult = Result<(Vec>, usize), Box>; -// Convert the generic type of constraints from Pairing's ScalarField to PrimeField. -pub fn convert_constraints_bigint_to_scalar( - constraints: Constraints, -) -> Constraints +// Extract the r1cs file +pub fn extract_constraints_from_r1cs(r1cs_filepath: &Path) -> ExtractedConstraintsResult where E: Pairing, - F: PrimeField, -{ - let convert_vec = |vec: ConstraintVec| -> ConstraintVec { - vec.into_iter() - .map(|(index, element)| { - // Convert element into BigInt, then to BigUint, then to the destination PrimeField - let bigint_e: <::ScalarField as PrimeField>::BigInt = - element.into_bigint(); - let generic_biguint: num_bigint::BigUint = bigint_e.into(); - let f_element: F = F::from(generic_biguint); - (index, f_element) - }) - .collect() - }; - - ( - convert_vec(constraints.0), - convert_vec(constraints.1), - convert_vec(constraints.2), - ) -} - -// Extract constraints on Pairing's ScalarField from an .r1cs file -// and convert them into constraints on PrimeField -pub fn extract_constraints_from_r1cs(r1cs_filepath: &Path) -> ExtractedConstraintsResult -where - E: Pairing, - F: PrimeField, { // Open the .r1cs file and create a reader let file = File::open(r1cs_filepath)?; let reader = BufReader::new(file); - // Read the R1CS file, extract the constraints, and then convert them into new constraints on PrimeField + // Read the R1CS file and extract the constraints directly let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - let converted_constraints: Vec> = r1cs - .constraints - .into_iter() - .map(|c| convert_constraints_bigint_to_scalar::(c)) - .collect(); + let constraints: Vec> = r1cs.constraints; - Ok((converted_constraints, pub_io_len)) + Ok((constraints, pub_io_len)) } // Convert a set of constraints from ark-circom into R1CS format of folding_schemes @@ -169,16 +135,10 @@ where #[cfg(test)] mod tests { use super::*; - use ark_bn254::{Bn254, Fr}; + use ark_bn254::Bn254; use num_bigint::BigInt; use std::env; - /* - test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. - In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly. - In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. - */ - fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec)>) { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); @@ -188,21 +148,15 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath) + let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath) .expect("Error extracting constraints"); assert!(!constraints.is_empty()); - let converted_constraints: Vec> = constraints - .iter() - .map(|constraint| convert_constraints_bigint_to_scalar::(constraint.clone())) - .collect(); - assert_eq!(constraints.len(), converted_constraints.len()); - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error calculating witness"); assert!(!witness.is_empty()); let (r1cs, z) = - circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len) + circom_to_folding_schemes_r1cs_and_z(constraints.clone(), &witness, pub_io_len) .expect("Error converting to folding schemes"); assert!(!z.is_empty()); @@ -224,6 +178,6 @@ mod tests { test_circom_conversion_logic(true, vec![("step_in".to_string(), vec![BigInt::from(3)])]); // expect it to fail for incorrect inputs - test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(4)])]); + test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(7)])]); } -} +} \ No newline at end of file From c84c6a0330c16d5b6c0f4e2ce18b8202e4dfcd51 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:35:44 +0900 Subject: [PATCH 20/29] fixed n_cols --- src/frontend/circom/mod.rs | 39 +++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index e661dfc7..836a5f49 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -14,10 +14,12 @@ use crate::utils::vec::SparseMatrix; pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; -type ExtractedConstraintsResult = Result<(Vec>, usize), Box>; +type ExtractedConstraintsResult = Result<(Vec>, usize, usize), Box>; // Extract the r1cs file -pub fn extract_constraints_from_r1cs(r1cs_filepath: &Path) -> ExtractedConstraintsResult +pub fn extract_constraints_from_r1cs( + r1cs_filepath: &Path, +) -> ExtractedConstraintsResult where E: Pairing, { @@ -29,15 +31,17 @@ where let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; let r1cs = r1cs_reader::R1CS::::from(r1cs_file); + let num_variables = r1cs.num_variables; let constraints: Vec> = r1cs.constraints; - Ok((constraints, pub_io_len)) + Ok((constraints, pub_io_len, num_variables)) } // Convert a set of constraints from ark-circom into R1CS format of folding_schemes pub fn convert_to_folding_schemes_r1cs( constraints: Vec>, pub_io_len: usize, + num_variables: usize, ) -> R1CS where F: PrimeField, @@ -67,7 +71,7 @@ where } let l = pub_io_len; - let n_cols = a_matrix.first().map(|vec| vec.len()).unwrap_or(0); + let n_cols = num_variables; let A = SparseMatrix { n_rows, @@ -111,11 +115,13 @@ pub fn circom_to_folding_schemes_r1cs_and_z( constraints: Vec>, witness: &[BigInt], pub_io_len: usize, + num_variables: usize, ) -> Result<(R1CS, Vec), Box> where F: PrimeField, { - let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, pub_io_len); + let folding_schemes_r1cs = + convert_to_folding_schemes_r1cs(constraints, pub_io_len, num_variables); let z: Result, _> = witness .iter() @@ -139,6 +145,12 @@ mod tests { use num_bigint::BigInt; use std::env; + /* + test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. + In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly. + In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. + */ + fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec)>) { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); @@ -148,16 +160,21 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let (constraints, pub_io_len) = extract_constraints_from_r1cs::(&r1cs_filepath) - .expect("Error extracting constraints"); + let (constraints, pub_io_len, num_variables) = + extract_constraints_from_r1cs::(&r1cs_filepath) + .expect("Error extracting constraints"); assert!(!constraints.is_empty()); let witness = calculate_witness(&wasm_filepath, inputs).expect("Error calculating witness"); assert!(!witness.is_empty()); - let (r1cs, z) = - circom_to_folding_schemes_r1cs_and_z(constraints.clone(), &witness, pub_io_len) - .expect("Error converting to folding schemes"); + let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z( + constraints, + &witness, + pub_io_len, + num_variables, + ) + .expect("Error converting to folding schemes"); assert!(!z.is_empty()); let check_result = std::panic::catch_unwind(|| { @@ -180,4 +197,4 @@ mod tests { // expect it to fail for incorrect inputs test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(7)])]); } -} \ No newline at end of file +} From 68bcc3d60ea15eefae2bbfcee626424a2c9bbc42 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:36:17 +0900 Subject: [PATCH 21/29] fixed n_cols --- src/frontend/circom/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 836a5f49..284d29cc 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -168,13 +168,9 @@ mod tests { let witness = calculate_witness(&wasm_filepath, inputs).expect("Error calculating witness"); assert!(!witness.is_empty()); - let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z( - constraints, - &witness, - pub_io_len, - num_variables, - ) - .expect("Error converting to folding schemes"); + let (r1cs, z) = + circom_to_folding_schemes_r1cs_and_z(constraints, &witness, pub_io_len, num_variables) + .expect("Error converting to folding schemes"); assert!(!z.is_empty()); let check_result = std::panic::catch_unwind(|| { From c9ed92f0b56e6d0be9ac964326a386bc3829aefe Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Wed, 11 Oct 2023 18:42:14 +0900 Subject: [PATCH 22/29] Add functionality to compile Circom files in tests --- .gitignore | 5 ++++- src/frontend/circom/mod.rs | 20 ++++++++++++++++-- src/frontend/circom/test_folder/compile.sh | 2 ++ .../circom/test_folder/test_circuit.wasm | Bin 34475 -> 0 bytes 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/frontend/circom/test_folder/compile.sh delete mode 100644 src/frontend/circom/test_folder/test_circuit.wasm diff --git a/.gitignore b/.gitignore index 869df07d..e8d59829 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /target -Cargo.lock \ No newline at end of file +Cargo.lock +# Circom generated files +/src/frontend/circom/test_folder/test_circuit.r1cs +/src/frontend/circom/test_folder/test_circuit_js/ \ No newline at end of file diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 284d29cc..a97164c2 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -143,7 +143,19 @@ mod tests { use super::*; use ark_bn254::Bn254; use num_bigint::BigInt; - use std::env; + use std::{env, process::Command}; + + // Compile the .circom file to generate the .r1cs and .wasm files. + fn compile_circom_to_r1cs_and_wasm(base_path: &std::path::Path) { + let script_path = base_path.join("compile.sh"); + + let status = Command::new("bash") + .arg(script_path) + .status() + .expect("Failed to execute circom compilation script."); + + assert!(status.success(), "Circom compilation script failed."); + } /* test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. @@ -154,8 +166,11 @@ mod tests { fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec)>) { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); + + compile_circom_to_r1cs_and_wasm(&base_path); + let r1cs_filepath = base_path.join("test_circuit.r1cs"); - let wasm_filepath = base_path.join("test_circuit.wasm"); + let wasm_filepath = base_path.join("test_circuit_js/test_circuit.wasm"); assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); @@ -173,6 +188,7 @@ mod tests { .expect("Error converting to folding schemes"); assert!(!z.is_empty()); + // Check the relationship for R1CS let check_result = std::panic::catch_unwind(|| { r1cs.check_relation(&z).unwrap(); }); diff --git a/src/frontend/circom/test_folder/compile.sh b/src/frontend/circom/test_folder/compile.sh new file mode 100644 index 00000000..49c4412a --- /dev/null +++ b/src/frontend/circom/test_folder/compile.sh @@ -0,0 +1,2 @@ +#!/bin/bash +circom ./src/frontend/circom/test_folder/test_circuit.circom --r1cs --wasm --prime bn128 --output ./src/frontend/circom/test_folder/ \ No newline at end of file diff --git a/src/frontend/circom/test_folder/test_circuit.wasm b/src/frontend/circom/test_folder/test_circuit.wasm deleted file mode 100644 index 0d01077c8316b002e1b2a7387fd67ab3a47b9734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34475 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 Date: Wed, 11 Oct 2023 19:35:24 +0900 Subject: [PATCH 23/29] Remove test_circuit.r1cs --- src/frontend/circom/test_folder/test_circuit.r1cs | Bin 540 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/frontend/circom/test_folder/test_circuit.r1cs diff --git a/src/frontend/circom/test_folder/test_circuit.r1cs b/src/frontend/circom/test_folder/test_circuit.r1cs deleted file mode 100644 index 8c475f0f7491f84950ab9ac9fbcba986306a2b86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 540 zcmXRiOfF_*U|?VdVkRK&0dar;M1lA~@L}@Tht3lVc2;`4FxH5TXl&f(8n8oif#Jg< zzZ3(QI;b{?4lE={9%Kg)5Hycibs+Z Date: Thu, 12 Oct 2023 00:11:42 +0900 Subject: [PATCH 24/29] Introduce CircomFrontend trait and simplify with CircomWrapper struct --- src/frontend/circom/mod.rs | 301 +++++++++++++++++++++---------------- 1 file changed, 169 insertions(+), 132 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index a97164c2..633ce4c0 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -1,4 +1,4 @@ -use std::{error::Error, fs::File, io::BufReader, path::Path}; +use std::{error::Error, fs::File, io::BufReader, marker::PhantomData, path::PathBuf}; use color_eyre::Result; use num_bigint::BigInt; @@ -10,150 +10,193 @@ use ark_ff::PrimeField; use crate::ccs::r1cs::R1CS; use crate::utils::vec::SparseMatrix; -// Define the sparse matrices on PrimeFiled +// Define the sparse matrices on PrimeFiled. pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; +pub type ExtractedConstraintsResult = + Result<(Vec>, usize, usize), Box>; -type ExtractedConstraintsResult = Result<(Vec>, usize, usize), Box>; - -// Extract the r1cs file -pub fn extract_constraints_from_r1cs( - r1cs_filepath: &Path, -) -> ExtractedConstraintsResult -where - E: Pairing, -{ - // Open the .r1cs file and create a reader - let file = File::open(r1cs_filepath)?; - let reader = BufReader::new(file); - - // Read the R1CS file and extract the constraints directly - let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; - let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; - let r1cs = r1cs_reader::R1CS::::from(r1cs_file); - let num_variables = r1cs.num_variables; - let constraints: Vec> = r1cs.constraints; - - Ok((constraints, pub_io_len, num_variables)) +// Trait that provides an interface for extracting R1CS and Z vector from Circom. +pub trait CircomFrontend { + fn get(&self, inputs: &[(String, Vec)]) -> Result<(R1CS, Vec), Box>; } -// Convert a set of constraints from ark-circom into R1CS format of folding_schemes -pub fn convert_to_folding_schemes_r1cs( - constraints: Vec>, - pub_io_len: usize, - num_variables: usize, -) -> R1CS -where - F: PrimeField, -{ - let mut a_matrix: Vec> = Vec::new(); - let mut b_matrix: Vec> = Vec::new(); - let mut c_matrix: Vec> = Vec::new(); - - let n_rows = constraints.len(); - - for (ai, bi, ci) in constraints { - a_matrix.push( - ai.into_iter() - .map(|(index, scalar)| (scalar, index)) - .collect(), - ); - b_matrix.push( - bi.into_iter() - .map(|(index, scalar)| (scalar, index)) - .collect(), - ); - c_matrix.push( - ci.into_iter() - .map(|(index, scalar)| (scalar, index)) - .collect(), - ); +impl CircomFrontend for CircomWrapper { + fn get( + &self, + inputs: &[(String, Vec)], + ) -> Result<(R1CS, Vec), Box> { + self.extract_and_convert(inputs) } - - let l = pub_io_len; - let n_cols = num_variables; - - let A = SparseMatrix { - n_rows, - n_cols, - coeffs: a_matrix, - }; - let B = SparseMatrix { - n_rows, - n_cols, - coeffs: b_matrix, - }; - let C = SparseMatrix { - n_rows, - n_cols, - coeffs: c_matrix, - }; - - R1CS:: { l, A, B, C } } -// Calculate the witness given the WASM filepath and inputs. -pub fn calculate_witness)>>( - wasm_filepath: &Path, - inputs: I, -) -> Result> { - let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?; - calculator.calculate_witness(inputs, true) +// A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses +// based on file paths to Circom's .r1cs and .wasm. +pub struct CircomWrapper { + r1cs_filepath: PathBuf, + wasm_filepath: PathBuf, + _marker: PhantomData, } -// Convert a num_bigint's BigInt to PrimeField's BigInt -fn num_bigint_to_ark_bigint(value: &BigInt) -> Result> { - let big_uint = value - .to_biguint() - .ok_or_else(|| "BigInt is negative".to_string())?; - F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into()) -} +impl CircomWrapper { + // Creates a new instance of the CircomWrapper with the file paths. + pub fn new(r1cs_filepath: PathBuf, wasm_filepath: PathBuf) -> Self { + CircomWrapper { + r1cs_filepath, + wasm_filepath, + _marker: PhantomData, + } + } + + // Aggregates multiple functions to obtain R1CS and Z as defined in folding-schemes from Circom. + pub fn extract_and_convert( + &self, + inputs: &[(String, Vec)], + ) -> Result<(R1CS, Vec), Box> { + let (constraints, pub_io_len, num_variables) = self.extract_constraints_from_r1cs()?; + let witness = self.calculate_witness(inputs)?; + self.circom_to_folding_schemes_r1cs_and_z(constraints, &witness, pub_io_len, num_variables) + } + + // Extracts constraints from the r1cs file. + pub fn extract_constraints_from_r1cs(&self) -> ExtractedConstraintsResult + where + E: Pairing, + { + // Opens the .r1cs file and create a reader. + let file = File::open(&self.r1cs_filepath)?; + let reader = BufReader::new(file); + + // Reads the R1CS file and extract the constraints directly. + let r1cs_file = r1cs_reader::R1CSFile::::new(reader)?; + let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize; + let r1cs = r1cs_reader::R1CS::::from(r1cs_file); + let num_variables = r1cs.num_variables; + let constraints: Vec> = r1cs.constraints; + + Ok((constraints, pub_io_len, num_variables)) + } + + // Converts a set of constraints from ark-circom into R1CS format of folding-schemes. + pub fn convert_to_folding_schemes_r1cs( + &self, + constraints: Vec>, + pub_io_len: usize, + num_variables: usize, + ) -> R1CS + where + F: PrimeField, + { + let mut a_matrix: Vec> = Vec::new(); + let mut b_matrix: Vec> = Vec::new(); + let mut c_matrix: Vec> = Vec::new(); + + let n_rows = constraints.len(); + + for (ai, bi, ci) in constraints { + a_matrix.push( + ai.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); + b_matrix.push( + bi.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); + c_matrix.push( + ci.into_iter() + .map(|(index, scalar)| (scalar, index)) + .collect(), + ); + } -// Convert R1CS constraints and witness from ark-circom format -// into folding-schemes R1CS and z format. -pub fn circom_to_folding_schemes_r1cs_and_z( - constraints: Vec>, - witness: &[BigInt], - pub_io_len: usize, - num_variables: usize, -) -> Result<(R1CS, Vec), Box> -where - F: PrimeField, -{ - let folding_schemes_r1cs = - convert_to_folding_schemes_r1cs(constraints, pub_io_len, num_variables); - - let z: Result, _> = witness - .iter() - .map(|big_int| { - let ark_big_int = num_bigint_to_ark_bigint::(big_int)?; - F::from_bigint(ark_big_int).ok_or_else(|| { - "Failed to convert bigint to field element" - .to_string() - .into() + let l = pub_io_len; + let n_cols = num_variables; + + let A = SparseMatrix { + n_rows, + n_cols, + coeffs: a_matrix, + }; + let B = SparseMatrix { + n_rows, + n_cols, + coeffs: b_matrix, + }; + let C = SparseMatrix { + n_rows, + n_cols, + coeffs: c_matrix, + }; + + R1CS:: { l, A, B, C } + } + + // Calculates the witness given the Wasm filepath and inputs. + pub fn calculate_witness(&self, inputs: &[(String, Vec)]) -> Result> { + let mut calculator = WitnessCalculator::new(&self.wasm_filepath)?; + calculator.calculate_witness(inputs.to_vec().into_iter(), true) + } + + // Converts a num_bigint input to `PrimeField`'s BigInt. + pub fn num_bigint_to_ark_bigint( + &self, + value: &BigInt, + ) -> Result> { + let big_uint = value + .to_biguint() + .ok_or_else(|| "BigInt is negative".to_string())?; + F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into()) + } + + // Converts R1CS constraints and witness from ark-circom format + // into folding-schemes R1CS and z format. + pub fn circom_to_folding_schemes_r1cs_and_z( + &self, + constraints: Vec>, + witness: &[BigInt], + pub_io_len: usize, + num_variables: usize, + ) -> Result<(R1CS, Vec), Box> + where + F: PrimeField, + { + let folding_schemes_r1cs = + self.convert_to_folding_schemes_r1cs(constraints, pub_io_len, num_variables); + + let z: Result, _> = witness + .iter() + .map(|big_int| { + let ark_big_int = self.num_bigint_to_ark_bigint::(big_int)?; + F::from_bigint(ark_big_int).ok_or_else(|| { + "Failed to convert bigint to field element" + .to_string() + .into() + }) }) - }) - .collect(); + .collect(); - z.map(|z| (folding_schemes_r1cs, z)) + z.map(|z| (folding_schemes_r1cs, z)) + } } #[cfg(test)] mod tests { - use super::*; + use crate::frontend::circom::{CircomFrontend, CircomWrapper}; use ark_bn254::Bn254; use num_bigint::BigInt; use std::{env, process::Command}; - // Compile the .circom file to generate the .r1cs and .wasm files. + // Compiles the .circom file to generate the .r1cs and .wasm files. fn compile_circom_to_r1cs_and_wasm(base_path: &std::path::Path) { let script_path = base_path.join("compile.sh"); - + let status = Command::new("bash") .arg(script_path) .status() .expect("Failed to execute circom compilation script."); - + assert!(status.success(), "Circom compilation script failed."); } @@ -175,20 +218,14 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let (constraints, pub_io_len, num_variables) = - extract_constraints_from_r1cs::(&r1cs_filepath) - .expect("Error extracting constraints"); - assert!(!constraints.is_empty()); - - let witness = calculate_witness(&wasm_filepath, inputs).expect("Error calculating witness"); - assert!(!witness.is_empty()); + let circom_frontend = + CircomWrapper::::new(r1cs_filepath.clone(), wasm_filepath.clone()); - let (r1cs, z) = - circom_to_folding_schemes_r1cs_and_z(constraints, &witness, pub_io_len, num_variables) - .expect("Error converting to folding schemes"); - assert!(!z.is_empty()); + let (r1cs, z) = circom_frontend + .get(&inputs) + .expect("Error processing input"); - // Check the relationship for R1CS + // Checks the relationship of R1CS. let check_result = std::panic::catch_unwind(|| { r1cs.check_relation(&z).unwrap(); }); @@ -203,10 +240,10 @@ mod tests { #[test] fn test_circom_conversion() { - // expect it to pass for correct inputs + // expect it to pass for correct inputs. test_circom_conversion_logic(true, vec![("step_in".to_string(), vec![BigInt::from(3)])]); - // expect it to fail for incorrect inputs + // expect it to fail for incorrect inputs. test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(7)])]); } } From f25b987bfc63d9ec62f7c100d1dbd5ab8c790dc1 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:42:12 +0900 Subject: [PATCH 25/29] deleted the CircomFrontend --- Cargo.toml | 3 +-- src/frontend/circom/mod.rs | 25 +++++-------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e776055b..69f276b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ ark-ec = "^0.4.0" ark-ff = "^0.4.0" ark-poly = "^0.4.0" ark-std = "^0.4.0" -ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge"] } +ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge", "crh"] } ark-relations = { version = "^0.4.0", default-features = false } ark-r1cs-std = { version = "^0.4.0", default-features = false } ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } @@ -25,7 +25,6 @@ espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", pack [dev-dependencies] ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0"} -ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["crh"] } ark-bn254 = "0.4.0" [features] diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 633ce4c0..14cc0300 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -16,21 +16,7 @@ pub type ConstraintVec = Vec<(usize, F)>; pub type ExtractedConstraintsResult = Result<(Vec>, usize, usize), Box>; -// Trait that provides an interface for extracting R1CS and Z vector from Circom. -pub trait CircomFrontend { - fn get(&self, inputs: &[(String, Vec)]) -> Result<(R1CS, Vec), Box>; -} - -impl CircomFrontend for CircomWrapper { - fn get( - &self, - inputs: &[(String, Vec)], - ) -> Result<(R1CS, Vec), Box> { - self.extract_and_convert(inputs) - } -} - -// A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses +// A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses // based on file paths to Circom's .r1cs and .wasm. pub struct CircomWrapper { r1cs_filepath: PathBuf, @@ -183,7 +169,7 @@ impl CircomWrapper { #[cfg(test)] mod tests { - use crate::frontend::circom::{CircomFrontend, CircomWrapper}; + use crate::frontend::circom::CircomWrapper; use ark_bn254::Bn254; use num_bigint::BigInt; use std::{env, process::Command}; @@ -218,11 +204,10 @@ mod tests { assert!(r1cs_filepath.exists()); assert!(wasm_filepath.exists()); - let circom_frontend = - CircomWrapper::::new(r1cs_filepath.clone(), wasm_filepath.clone()); + let circom_wrapper = CircomWrapper::::new(r1cs_filepath, wasm_filepath); - let (r1cs, z) = circom_frontend - .get(&inputs) + let (r1cs, z) = circom_wrapper + .extract_and_convert(&inputs) .expect("Error processing input"); // Checks the relationship of R1CS. From 233c0dbe9340f7a2ba0560f0dae744b0782bdf05 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Thu, 12 Oct 2023 21:02:11 +0900 Subject: [PATCH 26/29] improved --- src/frontend/circom/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 14cc0300..739ab41c 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -35,7 +35,7 @@ impl CircomWrapper { } // Aggregates multiple functions to obtain R1CS and Z as defined in folding-schemes from Circom. - pub fn extract_and_convert( + pub fn extract_r1cs_and_z( &self, inputs: &[(String, Vec)], ) -> Result<(R1CS, Vec), Box> { @@ -207,7 +207,7 @@ mod tests { let circom_wrapper = CircomWrapper::::new(r1cs_filepath, wasm_filepath); let (r1cs, z) = circom_wrapper - .extract_and_convert(&inputs) + .extract_r1cs_and_z(&inputs) .expect("Error processing input"); // Checks the relationship of R1CS. From 938b72366077f19641bf88ab99662238a8d2c40c Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:20:12 +0900 Subject: [PATCH 27/29] fixed clippy lint checks of github actions --- src/frontend/circom/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index 739ab41c..bdd4f644 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -13,8 +13,8 @@ use crate::utils::vec::SparseMatrix; // Define the sparse matrices on PrimeFiled. pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); pub type ConstraintVec = Vec<(usize, F)>; -pub type ExtractedConstraintsResult = - Result<(Vec>, usize, usize), Box>; +type ExtractedConstraints = (Vec>, usize, usize); +pub type ExtractedConstraintsResult = Result, Box>; // A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses // based on file paths to Circom's .r1cs and .wasm. @@ -122,7 +122,7 @@ impl CircomWrapper { // Calculates the witness given the Wasm filepath and inputs. pub fn calculate_witness(&self, inputs: &[(String, Vec)]) -> Result> { let mut calculator = WitnessCalculator::new(&self.wasm_filepath)?; - calculator.calculate_witness(inputs.to_vec().into_iter(), true) + calculator.calculate_witness(inputs.iter().cloned(), true) } // Converts a num_bigint input to `PrimeField`'s BigInt. From e49c5106d79b30edeeae787002b5f8f4d7cbf4d0 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:13:50 +0900 Subject: [PATCH 28/29] probably fixed github actions error by changing the github yaml --- .github/workflows/ci.yml | 8 ++++++++ src/frontend/circom/mod.rs | 21 ++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfbfde42..e73085b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,14 @@ jobs: # use the more efficient nextest - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 + - name: Install Circom + run: | + git clone https://github.com/iden3/circom.git + cd circom + cargo build --release + cargo install --path circom + - name: Execute compile.sh + run: bash ./src/frontend/circom/test_folder/compile.sh - name: Build # This build will be reused by nextest, # and also checks (--all-targets) that benches don't bit-rot diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index bdd4f644..d0654438 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -172,19 +172,7 @@ mod tests { use crate::frontend::circom::CircomWrapper; use ark_bn254::Bn254; use num_bigint::BigInt; - use std::{env, process::Command}; - - // Compiles the .circom file to generate the .r1cs and .wasm files. - fn compile_circom_to_r1cs_and_wasm(base_path: &std::path::Path) { - let script_path = base_path.join("compile.sh"); - - let status = Command::new("bash") - .arg(script_path) - .status() - .expect("Failed to execute circom compilation script."); - - assert!(status.success(), "Circom compilation script failed."); - } + use std::env; /* test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom. @@ -192,12 +180,15 @@ mod tests { In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS. */ + /* + To generate .r1cs and .wasm files, run the below command in the terminal. + bash ./src/frontend/circom/test_folder/compile.sh + */ + fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec)>) { let current_dir = env::current_dir().unwrap(); let base_path = current_dir.join("src/frontend/circom/test_folder"); - compile_circom_to_r1cs_and_wasm(&base_path); - let r1cs_filepath = base_path.join("test_circuit.r1cs"); let wasm_filepath = base_path.join("test_circuit_js/test_circuit.wasm"); From b5e61f404ea059b8f952f5def66b5e4171e06f48 Mon Sep 17 00:00:00 2001 From: Y5 <76672645+yugonsan@users.noreply.github.com> Date: Wed, 18 Oct 2023 00:08:38 +0900 Subject: [PATCH 29/29] fixed github yaml, fmt, and clippy --- .github/workflows/ci.yml | 12 ++++++------ src/frontend/circom/mod.rs | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e73085b6..d8e8e3ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,13 +49,13 @@ jobs: # use the more efficient nextest - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 - - name: Install Circom + - name: Download Circom run: | - git clone https://github.com/iden3/circom.git - cd circom - cargo build --release - cargo install --path circom - - name: Execute compile.sh + mkdir -p $HOME/bin + curl -sSfL https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64 -o $HOME/bin/circom + chmod +x $HOME/bin/circom + echo "$HOME/bin" >> $GITHUB_PATH + - name: Execute compile.sh to generate .r1cs and .wasm from .circom run: bash ./src/frontend/circom/test_folder/compile.sh - name: Build # This build will be reused by nextest, diff --git a/src/frontend/circom/mod.rs b/src/frontend/circom/mod.rs index d0654438..1a8ad41e 100644 --- a/src/frontend/circom/mod.rs +++ b/src/frontend/circom/mod.rs @@ -15,6 +15,7 @@ pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec) pub type ConstraintVec = Vec<(usize, F)>; type ExtractedConstraints = (Vec>, usize, usize); pub type ExtractedConstraintsResult = Result, Box>; +pub type R1CSandZ = (R1CS, Vec); // A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses // based on file paths to Circom's .r1cs and .wasm. @@ -38,7 +39,7 @@ impl CircomWrapper { pub fn extract_r1cs_and_z( &self, inputs: &[(String, Vec)], - ) -> Result<(R1CS, Vec), Box> { + ) -> Result, Box> { let (constraints, pub_io_len, num_variables) = self.extract_constraints_from_r1cs()?; let witness = self.calculate_witness(inputs)?; self.circom_to_folding_schemes_r1cs_and_z(constraints, &witness, pub_io_len, num_variables) @@ -181,7 +182,7 @@ mod tests { */ /* - To generate .r1cs and .wasm files, run the below command in the terminal. + To generate .r1cs and .wasm files, run the below command in the terminal. bash ./src/frontend/circom/test_folder/compile.sh */