From 52cb5d10bdf6bf91772014a3a51304b17c4ccb09 Mon Sep 17 00:00:00 2001 From: ibmp33 <2285673866@qq.com> Date: Fri, 28 Jun 2024 14:09:00 +0800 Subject: [PATCH] chore: add gpu support for groth16 --- algebraic/Cargo.toml | 25 +- algebraic/src/circom_circuit.rs | 148 ++++++- algebraic/src/lib.rs | 15 + algebraic/src/r1cs_file.rs | 208 ++++++++- algebraic/src/reader.rs | 228 +++++++++- algebraic/src/utils.rs | 19 + algebraic/src/witness/witness_calculator.rs | 3 + groth16/Cargo.toml | 50 +-- groth16/src/api.rs | 196 ++++++++- groth16/src/circuit.rs | 168 ------- groth16/src/groth16.rs | 198 ++++++++- groth16/src/json_utils.rs | 225 +++++++++- groth16/src/lib.rs | 31 +- groth16/src/reader.rs | 230 ---------- groth16/src/witness/circom.rs | 133 ------ groth16/src/witness/memory.rs | 42 -- groth16/src/witness/mod.rs | 55 --- groth16/src/witness/witness_calculator.rs | 464 -------------------- plonky/Cargo.toml | 2 +- recursion/Cargo.toml | 4 +- 20 files changed, 1249 insertions(+), 1195 deletions(-) delete mode 100644 groth16/src/circuit.rs delete mode 100644 groth16/src/reader.rs delete mode 100644 groth16/src/witness/circom.rs delete mode 100644 groth16/src/witness/memory.rs delete mode 100644 groth16/src/witness/mod.rs delete mode 100644 groth16/src/witness/witness_calculator.rs diff --git a/algebraic/Cargo.toml b/algebraic/Cargo.toml index 771b5896..3dc90db5 100644 --- a/algebraic/Cargo.toml +++ b/algebraic/Cargo.toml @@ -3,7 +3,7 @@ name = "algebraic" version = "0.0.1" edition = "2021" license = "Apache-2.0" -description = "Eigen Algbraic based on R1CS" +description = "Eigen Algebraic based on R1CS" documentation = "eigen.cash" homepage = "eigen.cash" @@ -18,17 +18,24 @@ itertools = "0.8.1" log = "0.4.11" num-bigint = "0.3.3" num-traits = "0.2.8" -serde = { version = "1.0", features = [ "derive" ] } -serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["arbitrary_precision"] } hex = "*" wasmer = { version = "4.3.2", default-features = false } -thiserror="1.0" +thiserror = "1.0" anyhow = "1.0.79" fnv = { version = "1.0.3", default-features = false } num = { version = "0.4.0" } byteorder = "1" -franklin-crypto = { git = "https://github.com/matter-labs/franklin-crypto", branch = "beta", features = [ "plonk" ], version = "0.0.5"} -#franklin-crypto = { path = "../../franklin-crypto", features = [ "plonk" ], version = "0.0.5"} + +# Add cuda and opencl dependencies directly +ff = { version = "0.13.0", features = ["derive"], optional = true } +pairing = { version = "0.23.0", optional = true } +bellperson = { version = "0.25", default-features = true, optional = true } + +[target.'cfg(not(any(feature = "cuda", feature = "opencl")))'.dependencies] +franklin-crypto = { git = "https://github.com/matter-labs/franklin-crypto", branch = "beta", features = ["plonk"], version = "0.0.5" } +# franklin-crypto = { path = "../../franklin-crypto", features = [ "plonk" ], version = "0.0.5"} [dev-dependencies] env_logger = "0.10" @@ -37,9 +44,9 @@ env_logger = "0.10" rand = "0.4" [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2.51", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.51", features = ["serde-serialize"] } wasm-bindgen-futures = "0.4.1" -rand = { version="0.6.5", features = ["wasm-bindgen"] } +rand = { version = "0.6.5", features = ["wasm-bindgen"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3" @@ -47,3 +54,5 @@ wasm-bindgen-test = "0.3" [features] default = ["franklin-crypto/multicore", "wasmer/singlepass"] wasm = ["wasmer/js-default"] +cuda = ["ff", "pairing", "bellperson/cuda", "wasmer/singlepass"] +opencl = ["ff", "pairing", "bellperson/opencl", "wasmer/singlepass"] \ No newline at end of file diff --git a/algebraic/src/circom_circuit.rs b/algebraic/src/circom_circuit.rs index 13e99c6c..d7be939d 100644 --- a/algebraic/src/circom_circuit.rs +++ b/algebraic/src/circom_circuit.rs @@ -2,14 +2,23 @@ #![allow(clippy::needless_range_loop)] extern crate rand; -use itertools::Itertools; -use std::collections::BTreeMap; -use std::str; - +#[cfg(not(any(feature = "cuda", feature = "opencl")))] use crate::bellman_ce::{ pairing::Engine, Circuit, ConstraintSystem, Index, LinearCombination, PrimeField, ScalarEngine, SynthesisError, Variable, }; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use crate::bellperson::{ + Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable, +}; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use ff::PrimeField; +use itertools::Itertools; +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub use num_bigint::BigUint; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::str; use crate::utils::repr_to_big; @@ -24,19 +33,30 @@ pub struct CircuitJson { pub num_variables: usize, } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub type Constraint = ( Vec<(usize, ::Fr)>, Vec<(usize, ::Fr)>, Vec<(usize, ::Fr)>, ); +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub type Constraint = (Vec<(usize, E)>, Vec<(usize, E)>, Vec<(usize, E)>); // R1CSfile's CustomGates +#[cfg(not(any(feature = "cuda", feature = "opencl")))] #[derive(Debug, Default, Clone)] pub struct CustomGates { pub template_name: String, pub parameters: Vec, } +#[cfg(any(feature = "cuda", feature = "opencl"))] +#[derive(Debug, Default, Clone)] +pub struct CustomGates { + pub template_name: String, + pub parameters: Vec, +} + // R1CSfile's CustomGatesUses #[derive(Debug, Default, Clone)] pub struct CustomGatesUses { @@ -45,6 +65,19 @@ pub struct CustomGatesUses { } /// R1CS spec: https://www.sikoba.com/docs/SKOR_GD_R1CS_Format.pdf +#[cfg(any(feature = "cuda", feature = "opencl"))] +#[derive(Clone, Debug)] +pub struct R1CS { + pub num_inputs: usize, + pub num_aux: usize, + pub num_variables: usize, + pub num_outputs: usize, + pub constraints: Vec>, + pub custom_gates: Vec>, + pub custom_gates_uses: Vec, +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] #[derive(Clone, Debug)] pub struct R1CS { pub num_inputs: usize, @@ -56,6 +89,7 @@ pub struct R1CS { pub custom_gates_uses: Vec, } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] #[derive(Clone, Debug)] pub struct CircomCircuit { pub r1cs: R1CS, @@ -64,7 +98,17 @@ pub struct CircomCircuit { pub aux_offset: usize, // debug symbols } +#[cfg(any(feature = "cuda", feature = "opencl"))] +#[derive(Clone, Debug)] +pub struct CircomCircuit { + pub r1cs: R1CS, + pub witness: Option>, + pub wire_mapping: Option>, + pub aux_offset: usize, + // debug symbols +} +#[cfg(not(any(feature = "cuda", feature = "opencl")))] impl CircomCircuit { pub fn get_public_inputs(&self) -> Option> { match &self.witness { @@ -91,9 +135,37 @@ impl CircomCircuit { } } +#[cfg(any(feature = "cuda", feature = "opencl"))] +impl CircomCircuit { + pub fn get_public_inputs(&self) -> Option> { + match &self.witness { + None => None, + Some(w) => match &self.wire_mapping { + None => Some(w[1..self.r1cs.num_inputs].to_vec()), + Some(m) => Some( + m[1..self.r1cs.num_inputs] + .iter() + .map(|i| w[*i]) + .collect_vec(), + ), + }, + } + } + + pub fn get_public_inputs_json(&self) -> String { + let inputs = self.get_public_inputs(); + let inputs = match inputs { + None => return String::from("[]"), + Some(inp) => inp.iter().map(|x| repr_to_big(x)).collect_vec(), + }; + serde_json::to_string_pretty(&inputs).unwrap() + } +} + /// Our demo circuit implements this `Circuit` trait which /// is used during paramgen and proving in order to /// synthesize the constraint system. +#[cfg(not(any(feature = "cuda", feature = "opencl")))] impl Circuit for CircomCircuit { //noinspection RsBorrowChecker fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { @@ -160,3 +232,71 @@ impl Circuit for CircomCircuit { Ok(()) } } + +#[cfg(any(feature = "cuda", feature = "opencl"))] +impl Circuit for CircomCircuit { + //noinspection RsBorrowChecker + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { + let witness = &self.witness; + let wire_mapping = &self.wire_mapping; + for i in 1..self.r1cs.num_inputs { + cs.alloc_input( + || format!("variable {}", i), + || { + Ok(match witness { + None => E::from_str_vartime(&format!("alloc input {} error", i)).unwrap(), + Some(w) => match wire_mapping { + None => w[i], + Some(m) => w[m[i]], + }, + }) + }, + )?; + } + for i in 0..self.r1cs.num_aux { + cs.alloc( + || format!("aux {}", i + self.aux_offset), + || { + Ok(match witness { + None => { + E::from_str_vartime(&format!("alloc aux {} error", i + self.aux_offset)) + .unwrap() + } + Some(w) => match wire_mapping { + None => w[i + self.r1cs.num_inputs], + Some(m) => w[m[i + self.r1cs.num_inputs]], + }, + }) + }, + )?; + } + + let make_index = |index| { + if index < self.r1cs.num_inputs { + Index::Input(index) + } else { + Index::Aux(index - self.r1cs.num_inputs + self.aux_offset) + } + }; + let make_lc = |lc_data: Vec<(usize, E)>| { + lc_data.iter().fold( + LinearCombination::::zero(), + |lc: LinearCombination, (index, coeff)| { + lc + (*coeff, Variable::new_unchecked(make_index(*index))) + }, + ) + }; + for (i, constraint) in self.r1cs.constraints.iter().enumerate() { + // 0 * LC = 0 must be ignored + if !((constraint.0.is_empty() || constraint.1.is_empty()) && constraint.2.is_empty()) { + cs.enforce( + || format!("{}", i), + |_| make_lc(constraint.0.clone()), + |_| make_lc(constraint.1.clone()), + |_| make_lc(constraint.2.clone()), + ); + } + } + Ok(()) + } +} diff --git a/algebraic/src/lib.rs b/algebraic/src/lib.rs index c75362a7..37e9ccd3 100644 --- a/algebraic/src/lib.rs +++ b/algebraic/src/lib.rs @@ -4,11 +4,18 @@ extern crate serde; #[macro_use] extern crate hex_literal; +#[cfg(any(feature = "cuda", feature = "opencl"))] +extern crate bellperson; extern crate byteorder; +#[cfg(any(feature = "cuda", feature = "opencl"))] +extern crate ff; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] extern crate franklin_crypto; extern crate itertools; extern crate num_bigint; extern crate num_traits; +#[cfg(any(feature = "cuda", feature = "opencl"))] +extern crate pairing; extern crate rand; pub mod circom_circuit; @@ -17,10 +24,18 @@ pub mod reader; pub mod utils; pub mod witness; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub use crate::ff::*; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub use bellman_ce::pairing::ff; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub use franklin_crypto::bellman as bellman_ce; +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub use bellperson::groth16::*; +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub use ff::*; + #[cfg(target_arch = "wasm32")] extern crate wasm_bindgen; diff --git a/algebraic/src/r1cs_file.rs b/algebraic/src/r1cs_file.rs index 0c5ead1b..7f33be3b 100644 --- a/algebraic/src/r1cs_file.rs +++ b/algebraic/src/r1cs_file.rs @@ -1,9 +1,12 @@ // some codes borrowed from https://github.com/poma/zkutil/blob/master/src/r1cs_reader.rs // Implement of https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md #![allow(unused_variables, dead_code, non_snake_case)] +#[cfg(not(any(feature = "cuda", feature = "opencl")))] use crate::bellman_ce::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use crate::circom_circuit::{Constraint, CustomGates, CustomGatesUses}; use byteorder::{LittleEndian, ReadBytesExt}; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use ff::PrimeField; use std::{ collections::BTreeMap, io::{Error, ErrorKind, Read, Result, Seek, SeekFrom}, @@ -24,6 +27,7 @@ pub struct Header { } // R1CSFile parse result +#[cfg(not(any(feature = "cuda", feature = "opencl")))] #[derive(Debug, Default)] pub struct R1CSFile { pub version: u32, @@ -33,7 +37,7 @@ pub struct R1CSFile { pub custom_gates: Vec>, pub custom_gates_uses: Vec, } - +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn read_field(mut reader: R) -> Result { let mut repr = E::Fr::zero().into_repr(); repr.read_le(&mut reader)?; @@ -41,6 +45,33 @@ fn read_field(mut reader: R) -> Result { Ok(fr) } +// R1CSFile parse result +#[cfg(any(feature = "cuda", feature = "opencl"))] +#[derive(Debug, Default)] +pub struct R1CSFile { + pub version: u32, + pub header: Header, + pub constraints: Vec>, + pub wire_mapping: Vec, + pub custom_gates: Vec>, + pub custom_gates_uses: Vec, +} +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn read_field(mut reader: R) -> Result { + let mut repr = E::default().to_repr(); + let repr_slice = repr.as_mut(); + reader.read_exact(repr_slice)?; + let maybe_field = E::from_repr(repr); + if maybe_field.is_some().unwrap_u8() == 1 { + Ok(maybe_field.unwrap()) + } else { + Err(Error::new( + ErrorKind::InvalidData, + "Invalid field representation", + )) + } +} + const HEADER_TYPE: u32 = 1; const CONSTRAINT_TYPE: u32 = 2; const WIRE2LABEL_TYPE: u32 = 3; @@ -70,7 +101,7 @@ fn read_header(mut reader: R, size: u64) -> Result
{ use_custom_gates: false, }) } - +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn read_constraint_vec( mut reader: R, header: &Header, @@ -88,6 +119,25 @@ fn read_constraint_vec( Ok(vec) } +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn read_constraint_vec( + mut reader: R, + header: &Header, +) -> Result> { + 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, + read_field::<&mut R, E>(&mut reader)?, + )); + // sort by key + vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + } + Ok(vec) +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn read_constraints( mut reader: R, size: u64, @@ -105,6 +155,24 @@ fn read_constraints( Ok(vec) } +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn read_constraints( + mut reader: R, + size: u64, + header: &Header, +) -> Result>> { + // 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, header)?, + read_constraint_vec::<&mut R, E>(&mut reader, header)?, + read_constraint_vec::<&mut R, E>(&mut reader, header)?, + )); + } + Ok(vec) +} + fn read_map(mut reader: R, size: u64, header: &Header) -> Result> { if size != header.n_wires as u64 * 8 { return Err(Error::new( @@ -140,6 +208,7 @@ fn read_to_string(mut reader: R) -> String { String::from_utf8_lossy(&buf).to_string() } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn read_custom_gates_list( mut reader: R, size: u64, @@ -163,6 +232,30 @@ fn read_custom_gates_list( Ok(custom_gates) } +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn read_custom_gates_list( + mut reader: R, + size: u64, + header: &Header, +) -> Result>> { + let num = reader.read_u32::()?; + let mut custom_gates: Vec> = vec![]; + for i in 0..num { + let mut custom_gate = CustomGates:: { + template_name: read_to_string(&mut reader), + parameters: vec![], + }; + let num_parameters = reader.read_u32::()?; + for _i in 0..num_parameters { + custom_gate + .parameters + .push(read_field::<&mut R, E>(&mut reader)?); + } + custom_gates.push(custom_gate); + } + Ok(custom_gates) +} + fn read_custom_gates_uses_list( mut reader: R, size: u64, @@ -198,6 +291,7 @@ fn read_custom_gates_uses_list( Ok(custom_gates_uses) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn from_reader(mut reader: R) -> Result> { let mut magic = [0u8; 4]; reader.read_exact(&mut magic)?; @@ -306,6 +400,116 @@ pub fn from_reader(mut reader: R) -> Result(mut reader: R) -> Result> { + let mut magic = [0u8; 4]; + reader.read_exact(&mut magic)?; + if magic != [0x72, 0x31, 0x63, 0x73] { + // magic = "r1cs" + return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number")); + } + + let version = reader.read_u32::()?; + if version != 1 { + return Err(Error::new(ErrorKind::InvalidData, "Unsupported version")); + } + + let num_sections = reader.read_u32::()?; + + // section type -> file offset + let mut section_offsets = BTreeMap::::new(); + let mut section_sizes = BTreeMap::::new(); + + // get file offset of each section, we donot support custom gate yet, so ignore the + // last two sections. + for i in 0..(num_sections) { + let section_type = reader.read_u32::()?; + let section_size = reader.read_u64::()?; + let offset = reader.stream_position()?; + section_offsets.insert(section_type, offset); + section_sizes.insert(section_type, section_size); + reader.seek(SeekFrom::Current(section_size as i64))?; + } + + reader.seek(SeekFrom::Start(*section_offsets.get(&HEADER_TYPE).unwrap()))?; + let mut header = read_header(&mut reader, *section_sizes.get(&HEADER_TYPE).unwrap())?; + if section_offsets.contains_key(&CUSTOM_GATES_USE) + && section_offsets.contains_key(&CUSTOM_GATES_LIST) + { + header.use_custom_gates = true; + } + if !(header.field_size == 32 || header.field_size == 8) { + return Err(Error::new( + ErrorKind::InvalidData, + "This parser only supports 32-bytes or 8-bytes fields", + )); + } + if header.field_size != (E::NUM_BITS + 7) / 8 { + return Err(Error::new(ErrorKind::InvalidData, "Different prime")); + } + if !(header.prime_size + == hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") + || header.prime_size + == hex!("01000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73") + || header.prime_size == hex!("01000000ffffffff")) + { + return Err(Error::new( + ErrorKind::InvalidData, + "This parser only supports bn256 or GL", + )); + } + reader.seek(SeekFrom::Start( + *section_offsets.get(&CONSTRAINT_TYPE).unwrap(), + ))?; + let constraints = read_constraints::<&mut R, E>( + &mut reader, + *section_sizes.get(&CONSTRAINT_TYPE).unwrap(), + &header, + )?; + + reader.seek(SeekFrom::Start( + *section_offsets.get(&WIRE2LABEL_TYPE).unwrap(), + ))?; + let wire_mapping = read_map( + &mut reader, + *section_sizes.get(&WIRE2LABEL_TYPE).unwrap(), + &header, + )?; + let mut custom_gates: Vec> = vec![]; + if section_offsets.contains_key(&CUSTOM_GATES_LIST) { + reader.seek(SeekFrom::Start( + *section_offsets.get(&CUSTOM_GATES_LIST).unwrap(), + ))?; + custom_gates = read_custom_gates_list( + &mut reader, + *section_sizes.get(&CUSTOM_GATES_LIST).unwrap(), + &header, + )?; + } + + let mut custom_gates_uses: Vec = vec![]; + if section_offsets.contains_key(&CUSTOM_GATES_USE) { + reader.seek(SeekFrom::Start( + *section_offsets.get(&CUSTOM_GATES_USE).unwrap(), + ))?; + custom_gates_uses = read_custom_gates_uses_list( + &mut reader, + *section_sizes.get(&CUSTOM_GATES_USE).unwrap(), + &header, + )?; + } + + Ok(R1CSFile { + version, + header, + constraints, + wire_mapping, + custom_gates, + custom_gates_uses, + }) +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] #[cfg(test)] mod tests { use super::*; diff --git a/algebraic/src/reader.rs b/algebraic/src/reader.rs index 8a3c8d26..784537ac 100644 --- a/algebraic/src/reader.rs +++ b/algebraic/src/reader.rs @@ -1,17 +1,19 @@ +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use crate::bellman_ce::{ + kate_commitment::{Crs, CrsForLagrangeForm, CrsForMonomialForm}, + pairing::Engine, + Field, PrimeField, PrimeFieldRepr, ScalarEngine, +}; use anyhow::{bail, Result}; use byteorder::{LittleEndian, ReadBytesExt}; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use ff::PrimeField; use itertools::Itertools; use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; use std::io::{BufReader, Read, Seek}; use std::str; -use crate::bellman_ce::{ - kate_commitment::{Crs, CrsForLagrangeForm, CrsForMonomialForm}, - pairing::Engine, - Field, PrimeField, PrimeFieldRepr, ScalarEngine, -}; - use crate::circom_circuit::{CircuitJson, R1CS}; /// get universal setup file by filename @@ -21,6 +23,7 @@ fn get_universal_setup_file_buff_reader(setup_file_name: &str) -> Result(filename: &str) -> Crs { let mut buf_reader = get_universal_setup_file_buff_reader(filename) .unwrap_or_else(|_| panic!("read key_monomial_form file err, {}", filename)); @@ -28,6 +31,7 @@ pub fn load_key_monomial_form(filename: &str) -> Crs( option_filename: Option, ) -> Option> { @@ -44,6 +48,7 @@ pub fn maybe_load_key_lagrange_form( } /// load witness file by filename with autodetect encoding (bin or json). +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_witness_from_file(filename: &str) -> Vec { if filename.ends_with("json") { load_witness_from_json_file::(filename) @@ -53,6 +58,7 @@ pub fn load_witness_from_file(filename: &str) -> Vec { } /// load witness from json file by filename +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_witness_from_json_file(filename: &str) -> Vec { let reader = OpenOptions::new() .read(true) @@ -62,6 +68,7 @@ pub fn load_witness_from_json_file(filename: &str) -> Vec(reader: R) -> Vec { let witness: Vec = serde_json::from_reader(reader).expect("Unable to read."); witness @@ -71,6 +78,7 @@ fn load_witness_from_json(reader: R) -> Vec { } /// load witness from bin file by filename +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_witness_from_bin_file(filename: &str) -> Vec { let reader = OpenOptions::new() .read(true) @@ -81,11 +89,13 @@ pub fn load_witness_from_bin_file(filename: &str) -> Vec } /// load witness from u8 array +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_witness_from_array(buffer: Vec) -> Result> { load_witness_from_bin_reader::(buffer.as_slice()) } /// load witness from u8 array by a reader +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_witness_from_bin_reader(mut reader: R) -> Result> { let mut wtns_header = [0u8; 4]; reader.read_exact(&mut wtns_header)?; @@ -140,6 +150,7 @@ pub fn load_witness_from_bin_reader(mut reader: R) -> } /// load r1cs file by filename with autodetect encoding (bin or json) +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn load_r1cs(filename: &str) -> R1CS { if filename.ends_with("json") { load_r1cs_from_json_file(filename) @@ -150,6 +161,7 @@ pub fn load_r1cs(filename: &str) -> R1CS { } /// load r1cs from json file by filename +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn load_r1cs_from_json_file(filename: &str) -> R1CS { let reader = OpenOptions::new() .read(true) @@ -159,6 +171,7 @@ fn load_r1cs_from_json_file(filename: &str) -> R1CS { } /// load r1cs from json by a reader +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn load_r1cs_from_json(reader: R) -> R1CS { let circuit_json: CircuitJson = serde_json::from_reader(reader).expect("Unable to read."); @@ -195,6 +208,7 @@ fn load_r1cs_from_json(reader: R) -> R1CS { } /// load r1cs from bin file by filename +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn load_r1cs_from_bin_file(filename: &str) -> (R1CS, Vec) { let reader = OpenOptions::new() .read(true) @@ -204,6 +218,7 @@ fn load_r1cs_from_bin_file(filename: &str) -> (R1CS, Vec(reader: R) -> (R1CS, Vec) { let file = crate::r1cs_file::from_reader::(reader).expect("Unable to read."); let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize; @@ -222,3 +237,204 @@ pub fn load_r1cs_from_bin(reader: R) -> (R1CS(filename: &str) -> Vec { + if filename.ends_with("json") { + load_witness_from_json_file::(filename) + } else { + load_witness_from_bin_file::(filename) + } +} + +/// load witness from json file by filename +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_witness_from_json_file(filename: &str) -> Vec { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); + load_witness_from_json::>(BufReader::new(reader)) +} + +/// load witness from json by a reader +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn load_witness_from_json(reader: R) -> Vec { + let witness: Vec = serde_json::from_reader(reader).expect("Unable to read."); + witness + .into_iter() + .map(|x| E::from_str_vartime(&x).unwrap()) + .collect::>() +} + +/// load witness from bin file by filename +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_witness_from_bin_file(filename: &str) -> Vec { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); + load_witness_from_bin_reader::>(BufReader::new(reader)) + .expect("read witness failed") +} + +/// load witness from u8 array +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_witness_from_array(buffer: Vec) -> Result> { + load_witness_from_bin_reader::(buffer.as_slice()) +} + +/// load witness from u8 array by a reader +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_witness_from_bin_reader(mut reader: R) -> Result> { + let mut wtns_header = [0u8; 4]; + reader.read_exact(&mut wtns_header)?; + if wtns_header != [119, 116, 110, 115] { + // python -c 'print([ord(c) for c in "wtns"])' => [119, 116, 110, 115] + bail!("Invalid file header"); + } + let version = reader.read_u32::()?; + log::trace!("wtns version {}", version); + if version > 2 { + bail!("unsupported file version"); + } + let num_sections = reader.read_u32::()?; + if num_sections != 2 { + bail!("invalid num sections".to_string()); + } + // read the first section + let sec_type = reader.read_u32::()?; + if sec_type != 1 { + bail!("invalid section type".to_string()); + } + let sec_size = reader.read_u64::()?; + if sec_size != 4 + 32 + 4 { + bail!("invalid section len".to_string()); + } + let field_size = reader.read_u32::()?; + if field_size != 32 { + bail!("invalid field byte size".to_string()); + } + let mut prime = vec![0u8; field_size as usize]; + reader.read_exact(&mut prime)?; + if prime != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") { + bail!("invalid curve prime".to_string()); + } + let witness_len = reader.read_u32::()?; + log::trace!("witness len {}", witness_len); + let sec_type = reader.read_u32::()?; + if sec_type != 2 { + bail!("invalid section type".to_string()); + } + let sec_size = reader.read_u64::()?; + if sec_size != (witness_len * field_size) as u64 { + bail!(format!("Invalid witness section size {}", sec_size)); + } + let mut result = Vec::with_capacity(witness_len as usize); + for _ in 0..witness_len { + let mut repr = E::default().to_repr(); + let repr_slice = repr.as_mut(); + if reader.read_exact(repr_slice).is_err() { + continue; + } + let maybe_field_elem = E::from_repr(repr); + if maybe_field_elem.is_some().unwrap_u8() == 1 { + result.push(maybe_field_elem.unwrap()); + } else { + continue; + } + // repr.read_le(&mut reader)?; + // result.push(E::Fr::from_repr(repr)?); + } + Ok(result) +} + +/// load r1cs file by filename with autodetect encoding (bin or json) +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_r1cs(filename: &str) -> R1CS { + if filename.ends_with("json") { + load_r1cs_from_json_file(filename) + } else { + let (r1cs, _wire_mapping) = load_r1cs_from_bin_file(filename); + r1cs + } +} + +/// load r1cs from json file by filename +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn load_r1cs_from_json_file(filename: &str) -> R1CS { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); + load_r1cs_from_json(BufReader::new(reader)) +} + +/// load r1cs from json by a reader +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn load_r1cs_from_json(reader: R) -> R1CS { + let circuit_json: CircuitJson = serde_json::from_reader(reader).expect("Unable to read."); + + let num_inputs = circuit_json.num_inputs + circuit_json.num_outputs + 1; + let num_aux = circuit_json.num_variables - num_inputs; + + let convert_constraint = |lc: &BTreeMap| { + lc.iter() + .map(|(index, coeff)| (index.parse().unwrap(), E::from_str_vartime(coeff).unwrap())) + .collect_vec() + }; + + let constraints = circuit_json + .constraints + .iter() + .map(|c| { + ( + convert_constraint(&c[0]), + convert_constraint(&c[1]), + convert_constraint(&c[2]), + ) + }) + .collect_vec(); + + R1CS { + num_inputs, + num_aux, + num_variables: circuit_json.num_variables, + num_outputs: circuit_json.num_outputs, + constraints, + custom_gates: vec![], + custom_gates_uses: vec![], + } +} + +/// load r1cs from bin file by filename +#[cfg(any(feature = "cuda", feature = "opencl"))] +fn load_r1cs_from_bin_file(filename: &str) -> (R1CS, Vec) { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); + load_r1cs_from_bin(BufReader::new(reader)) +} + +/// load r1cs from bin by a reader +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn load_r1cs_from_bin(reader: R) -> (R1CS, Vec) { + let file = crate::r1cs_file::from_reader::(reader).expect("Unable to read."); + 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, + num_outputs: file.header.n_pub_out as usize, + constraints: file.constraints, + custom_gates: file.custom_gates, + custom_gates_uses: file.custom_gates_uses, + }, + file.wire_mapping.iter().map(|e| *e as usize).collect_vec(), + ) +} diff --git a/algebraic/src/utils.rs b/algebraic/src/utils.rs index 9f0e65d3..19740745 100644 --- a/algebraic/src/utils.rs +++ b/algebraic/src/utils.rs @@ -1,19 +1,35 @@ +#[cfg(not(any(feature = "cuda", feature = "opencl")))] use crate::bellman_ce::pairing::{ff::PrimeField, Engine}; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] use franklin_crypto::plonk::circuit::bigint::field::RnsParameters; pub use num_bigint::BigUint; use num_traits::Num; use std::fmt::Display; //export some more funcs +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub use franklin_crypto::plonk::circuit::bigint::bigint::{biguint_to_fe, fe_to_biguint}; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] /// convert a hex integer representation ("0x...") to decimal representation pub fn repr_to_big(r: T) -> String { BigUint::from_str_radix(&format!("{}", r)[2..], 16) .unwrap() .to_str_radix(10) } +#[cfg(any(feature = "cuda", feature = "opencl"))] +pub fn repr_to_big(r: T) -> String { + let hex_str = format!("{:?}", r); + let trim_quotes = hex_str + .trim_start_matches("Scalar(0x") + .trim_end_matches(')'); + let clean_hex = trim_quotes.trim_matches('"').trim_start_matches("0x"); + BigUint::from_str_radix(clean_hex, 16) + .map(|bigint: BigUint| bigint.to_str_radix(10)) + .unwrap() +} +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn from_single_size_limb_witnesses( witnesses: &[BigUint], params: &RnsParameters, @@ -48,6 +64,7 @@ fn from_single_size_limb_witnesses( biguint_to_fe(this_value) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] fn from_double_size_limb_witnesses( witnesses: &[BigUint], top_limb_may_overflow: bool, @@ -122,6 +139,7 @@ fn from_double_size_limb_witnesses( // refer to plonk/circuit/bigint/field, merge the limbs into prime field without allocting // inside a cs +#[cfg(not(any(feature = "cuda", feature = "opencl")))] pub fn witness_to_field( limbs: &[BigUint], params: &RnsParameters, @@ -134,6 +152,7 @@ pub fn witness_to_field( } #[cfg(test)] +#[cfg(not(any(feature = "cuda", feature = "opencl")))] mod tests { use super::*; use crate::bellman_ce::pairing::bn256::Bn256; diff --git a/algebraic/src/witness/witness_calculator.rs b/algebraic/src/witness/witness_calculator.rs index fc1146af..f317c9d4 100644 --- a/algebraic/src/witness/witness_calculator.rs +++ b/algebraic/src/witness/witness_calculator.rs @@ -1,7 +1,10 @@ // copied and modified by https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/witness_calculator.rs +#[cfg(not(any(feature = "cuda", feature = "opencl")))] use crate::bellman_ce::ScalarEngine; use crate::witness::{circom::Wasm, fnv, memory::SafeMemory}; use anyhow::{bail, Result}; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use ff::PrimeField as ScalarEngine; use num::ToPrimitive; use num_bigint::BigInt; use num_bigint::Sign; diff --git a/groth16/Cargo.toml b/groth16/Cargo.toml index 1113feaf..6712b2bd 100644 --- a/groth16/Cargo.toml +++ b/groth16/Cargo.toml @@ -3,52 +3,42 @@ name = "groth16" version = "0.0.1" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] byteorder = "1" - hex-literal = "0.2.1" itertools = "0.8.1" log = "0.4.11" num-bigint = "0.3.3" num-traits = "0.2.8" -serde = { version = "1.0", features = [ "derive" ] } -serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["arbitrary_precision"] } hex = "*" -thiserror="1.0" +thiserror = "1.0" fnv = { version = "1.0.3", default-features = false } num = { version = "0.4.0" } - anyhow = "1.0.79" regex = "0.2" - -profiler_macro = {git = "https://github.com/ChengYueJia/profiler-rs", rev = "6d6a77f8"} +profiler_macro = { git = "https://github.com/ChengYueJia/profiler-rs", rev = "6d6a77f8" } ark-std = { version = "0.4.0", optional = true } +algebraic = { path = "../algebraic", default-features = false } -#[target.'cfg(any(feature = "cuda", feature = "opencl"))'.dependencies] -ff = { version = "0.13.0", features = ["derive"] } -pairing = "0.23.0" -group = "0.13.0" -blstrs = { version = "0.7.0", features = ["__private_bench"] } -bellperson = { version = "0.25", default-features = false } -rand = { version = "0.8", features = ["getrandom"] } -rand_core = "0.6" -wasmer = { version = "4.3.2", default-features = false } +ff = { version = "0.13.0", features = ["derive"], optional = true } +pairing = { version = "0.23.0", optional = true } +group = { version = "0.13.0", optional = true } +blstrs = { version = "0.7.0", features = ["__private_bench"], optional = true } +bellperson = { version = "0.25", default-features = false, optional = true } +rand_new = { package = "rand", version = "0.8", features = ["getrandom"], optional = true } +rand_core = { version = "0.6", optional = true } + +[target.'cfg(not(any(feature = "cuda", feature = "opencl")))'.dependencies] +franklin-crypto = { git = "https://github.com/matter-labs/franklin-crypto", branch = "beta", features = ["plonk"], version = "0.0.5" } +rand_old = { package = "rand", version = "0.4", optional = true } [dev-dependencies] env_logger = "0.10" [features] -#default = ["franklin-crypto/multicore", "algebraic/default"] -default = ["bellperson/default", "wasmer/singlepass"] -profiler = ["ark-std/print-trace"] -cuda = ["bellperson/cuda", "wasmer/singlepass"] -opencl = ["bellperson/opencl", "wasmer/singlepass"] - -#[target.'cfg(not(any(feature = "cuda", feature = "opencl")))'.dependencies] -#franklin-crypto = { git = "https://github.com/matter-labs/franklin-crypto", branch = "beta", features = [ "plonk" ], version = "0.0.5" } -# franklin-crypto = { path = "../../franklin-crypto", features = [ "plonk" ], version = "0.0.5" } -#algebraic = { path = "../algebraic", default-features=false } -#rand = "0.4" - +default = ["rand_old", "franklin-crypto/multicore", "algebraic/default"] +profiler = ["rand_old", "ark-std/print-trace"] +cuda = ["ff", "pairing", "group", "blstrs", "bellperson/cuda", "algebraic/cuda", "rand_new", "rand_core"] +opencl = ["ff", "pairing", "group", "blstrs", "bellperson/opencl", "algebraic/opencl", "rand_new", "rand_core"] \ No newline at end of file diff --git a/groth16/src/api.rs b/groth16/src/api.rs index d8e367dd..3e8f38c9 100644 --- a/groth16/src/api.rs +++ b/groth16/src/api.rs @@ -1,18 +1,64 @@ -use crate::circuit::CircomCircuit; -use crate::groth16::Groth16; -use crate::json_utils::*; -use crate::reader::load_r1cs; -use crate::template::CONTRACT_TEMPLATE; -use crate::witness::{load_input_for_witness, WitnessCalculator}; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use crate::bellman_ce::{ + groth16::{Parameters, Proof, VerifyingKey}, + pairing::{ + bls12_381::{Bls12, Fr as Fr_bls12381}, + bn256::{Bn256, Fr}, + }, +}; +use crate::{groth16::Groth16, json_utils::*, template::CONTRACT_TEMPLATE}; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use algebraic::bellman_ce::Engine; +use algebraic::{ + circom_circuit::CircomCircuit, + reader::load_r1cs, + witness::{load_input_for_witness, WitnessCalculator}, + Field, PrimeField, +}; use anyhow::{anyhow, bail, Result}; +#[cfg(any(feature = "cuda", feature = "opencl"))] use bellperson::{gpu, groth16::*}; +#[cfg(any(feature = "cuda", feature = "opencl"))] use blstrs::{Bls12, Scalar}; -use ff::{Field, PrimeField}; +#[cfg(any(feature = "cuda", feature = "opencl"))] use group::WnafGroup; use num_traits::Zero; +#[cfg(any(feature = "cuda", feature = "opencl"))] use pairing::{Engine, MultiMillerLoop}; +#[cfg(any(feature = "cuda", feature = "opencl"))] +use rand_new as rand; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use rand_old as rand; use regex::Regex; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub fn groth16_setup( + curve_type: &str, + circuit_file: &str, + pk_file: &str, + vk_file: &str, + to_hex: bool, +) -> Result<()> { + let mut rng = rand::thread_rng(); + match curve_type { + "BN128" => { + let circuit = create_circuit_from_file::(circuit_file, None); + let (pk, vk) = Groth16::circuit_specific_setup(circuit, &mut rng)?; + write_pk_vk_to_files(curve_type, pk, vk, pk_file, vk_file, to_hex)? + } + "BLS12381" => { + let circuit = create_circuit_from_file::(circuit_file, None); + let (pk, vk) = Groth16::circuit_specific_setup(circuit, &mut rng)?; + write_pk_vk_to_files(curve_type, pk, vk, pk_file, vk_file, to_hex)? + } + _ => { + bail!(format!("Unknown curve type: {}", curve_type)) + } + }; + Ok(()) +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn groth16_setup( curve_type: &str, circuit_file: &str, @@ -35,6 +81,70 @@ pub fn groth16_setup( Ok(()) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +#[allow(clippy::too_many_arguments)] +pub fn groth16_prove( + curve_type: &str, + circuit_file: &str, + wtns_file: &str, + pk_file: &str, + input_file: &str, + public_input_file: &str, + proof_file: &str, + to_hex: bool, +) -> Result<()> { + let mut rng = rand::thread_rng(); + + let mut wtns = WitnessCalculator::from_file(wtns_file)?; + let inputs = load_input_for_witness(input_file); + let w = wtns.calculate_witness(inputs, false)?; + match curve_type { + "BN128" => { + let pk: Parameters = read_pk_from_file(pk_file, false)?; + let w = w + .iter() + .map(|wi| { + if wi.is_zero() { + Fr::zero() + } else { + Fr::from_str(&wi.to_string()).unwrap() + } + }) + .collect::>(); + let circuit = create_circuit_from_file::(circuit_file, Some(w)); + let proof = Groth16::prove(&pk, circuit.clone(), &mut rng)?; + let proof_json = serialize_proof(&proof, curve_type, to_hex)?; + std::fs::write(proof_file, proof_json)?; + let input_json = circuit.get_public_inputs_json(); + std::fs::write(public_input_file, input_json)?; + } + "BLS12381" => { + let pk: Parameters = read_pk_from_file(pk_file, false)?; + let w = w + .iter() + .map(|wi| { + if wi.is_zero() { + Fr_bls12381::zero() + } else { + Fr_bls12381::from_str(&wi.to_string()).unwrap() + } + }) + .collect::>(); + let circuit = create_circuit_from_file::(circuit_file, Some(w)); + let proof = Groth16::prove(&pk, circuit.clone(), &mut rng)?; + let proof_json = serialize_proof(&proof, curve_type, to_hex)?; + std::fs::write(proof_file, proof_json)?; + let input_json = circuit.get_public_inputs_json(); + std::fs::write(public_input_file, input_json)?; + } + _ => { + bail!(format!("Unknown curve type: {}", curve_type)) + } + }; + Ok(()) +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] #[allow(clippy::too_many_arguments)] pub fn groth16_prove( curve_type: &str, @@ -80,6 +190,49 @@ pub fn groth16_prove( Ok(()) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub fn groth16_verify( + curve_type: &str, + vk_file: &str, + public_input_file: &str, + proof_file: &str, +) -> Result<()> { + match curve_type { + "BN128" => { + let vk = read_vk_from_file(vk_file)?; + let inputs = read_public_input_from_file::(public_input_file)?; + let proof = read_proof_from_file(proof_file)?; + + let verification_result = + Groth16::<_, CircomCircuit>::verify_with_processed_vk(&vk, &inputs, &proof); + + if verification_result.is_err() || !verification_result.unwrap() { + bail!("verify failed"); + } + } + + "BLS12381" => { + let vk = read_vk_from_file(vk_file)?; + let inputs = read_public_input_from_file::(public_input_file)?; + let proof = read_proof_from_file(proof_file)?; + + let verification_result = + Groth16::<_, CircomCircuit>::verify_with_processed_vk(&vk, &inputs, &proof); + + if verification_result.is_err() || !verification_result.unwrap() { + bail!("verify failed"); + } + } + + _ => { + bail!(format!("Unknown curve type: {}", curve_type)) + } + } + + Ok(()) +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn groth16_verify( curve_type: &str, vk_file: &str, @@ -219,6 +372,20 @@ pub fn generate_verifier(vk_file_path: &str, sol_file_path: &str) -> Result<()> Ok(()) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +fn create_circuit_from_file( + circuit_file: &str, + witness: Option>, +) -> CircomCircuit { + CircomCircuit { + r1cs: load_r1cs(circuit_file), + witness, + wire_mapping: None, + aux_offset: 0, + } +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] fn create_circuit_from_file( circuit_file: &str, witness: Option>, @@ -231,6 +398,15 @@ fn create_circuit_from_file( } } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +fn read_pk_from_file(file_path: &str, checked: bool) -> Result> { + let file = + std::fs::File::open(file_path).map_err(|e| anyhow!("Open {}, {:?}", file_path, e))?; + let mut reader = std::io::BufReader::new(file); + Ok(Parameters::::read(&mut reader, checked)?) +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] fn read_pk_from_file(file_path: &str, checked: bool) -> Result> where E: MultiMillerLoop, @@ -248,7 +424,13 @@ fn read_vk_from_file(file_path: &str) -> Result> { let json_data = std::fs::read_to_string(file_path)?; Ok(to_verification_key::

(&json_data)) } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +fn read_public_input_from_file(file_path: &str) -> Result> { + let json_data = std::fs::read_to_string(file_path)?; + Ok(to_public_input::(&json_data)) +} +#[cfg(any(feature = "cuda", feature = "opencl"))] fn read_public_input_from_file(file_path: &str) -> Result> { let json_data = std::fs::read_to_string(file_path)?; Ok(to_public_input(&json_data)) diff --git a/groth16/src/circuit.rs b/groth16/src/circuit.rs deleted file mode 100644 index c8d3f548..00000000 --- a/groth16/src/circuit.rs +++ /dev/null @@ -1,168 +0,0 @@ -// copy from https://github.com/poma/zkutil/blob/master/src/circom_circuit.rs partially -#![allow(clippy::needless_range_loop)] -extern crate rand; - -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; -use std::str; - -use crate::bellperson::{ - Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable, -}; -use ff::PrimeField; -pub use num_bigint::BigUint; -use num_traits::Num; - -pub fn repr_to_big(r: T) -> String { - let hex_str = format!("{:?}", r); - let trim_quotes = hex_str.trim_matches('"'); - let clean_hex = trim_quotes.trim_start_matches("0x"); - BigUint::from_str_radix(clean_hex, 16) - .map(|bigint| bigint.to_str_radix(10)) - .unwrap() -} - -#[derive(Serialize, Deserialize)] -pub struct CircuitJson { - pub constraints: Vec>>, - #[serde(rename = "nPubInputs")] - pub num_inputs: usize, - #[serde(rename = "nOutputs")] - pub num_outputs: usize, - #[serde(rename = "nVars")] - pub num_variables: usize, -} - -pub type Constraint = (Vec<(usize, E)>, Vec<(usize, E)>, Vec<(usize, E)>); - -// R1CSfile's CustomGates -#[derive(Debug, Default, Clone)] -pub struct CustomGates { - pub template_name: String, - pub parameters: Vec, -} - -// R1CSfile's CustomGatesUses -#[derive(Debug, Default, Clone)] -pub struct CustomGatesUses { - pub id: u64, - pub signals: Vec, -} - -/// R1CS spec: https://www.sikoba.com/docs/SKOR_GD_R1CS_Format.pdf -#[derive(Clone, Debug)] -pub struct R1CS { - pub num_inputs: usize, - pub num_aux: usize, - pub num_variables: usize, - pub num_outputs: usize, - pub constraints: Vec>, - pub custom_gates: Vec>, - pub custom_gates_uses: Vec, -} - -#[derive(Clone, Debug)] -pub struct CircomCircuit { - pub r1cs: R1CS, - pub witness: Option>, - pub wire_mapping: Option>, - pub aux_offset: usize, - // debug symbols -} - -impl CircomCircuit { - pub fn get_public_inputs(&self) -> Option> { - match &self.witness { - None => None, - Some(w) => match &self.wire_mapping { - None => Some(w[1..self.r1cs.num_inputs].to_vec()), - Some(m) => Some( - m[1..self.r1cs.num_inputs] - .iter() - .map(|i| w[*i]) - .collect_vec(), - ), - }, - } - } - - pub fn get_public_inputs_json(&self) -> String { - let inputs = self.get_public_inputs(); - let inputs = match inputs { - None => return String::from("[]"), - Some(inp) => inp.iter().map(|x| repr_to_big(x)).collect_vec(), - }; - serde_json::to_string_pretty(&inputs).unwrap() - } -} - -/// Our demo circuit implements this `Circuit` trait which -/// is used during paramgen and proving in order to -/// synthesize the constraint system. -impl Circuit for CircomCircuit { - //noinspection RsBorrowChecker - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - let witness = &self.witness; - let wire_mapping = &self.wire_mapping; - for i in 1..self.r1cs.num_inputs { - cs.alloc_input( - || format!("variable {}", i), - || { - Ok(match witness { - None => E::from_str_vartime(&format!("alloc input {} error", i)).unwrap(), - Some(w) => match wire_mapping { - None => w[i], - Some(m) => w[m[i]], - }, - }) - }, - )?; - } - for i in 0..self.r1cs.num_aux { - cs.alloc( - || format!("aux {}", i + self.aux_offset), - || { - Ok(match witness { - None => { - E::from_str_vartime(&format!("alloc aux {} error", i + self.aux_offset)) - .unwrap() - } - Some(w) => match wire_mapping { - None => w[i + self.r1cs.num_inputs], - Some(m) => w[m[i + self.r1cs.num_inputs]], - }, - }) - }, - )?; - } - - let make_index = |index| { - if index < self.r1cs.num_inputs { - Index::Input(index) - } else { - Index::Aux(index - self.r1cs.num_inputs + self.aux_offset) - } - }; - let make_lc = |lc_data: Vec<(usize, E)>| { - lc_data.iter().fold( - LinearCombination::::zero(), - |lc: LinearCombination, (index, coeff)| { - lc + (*coeff, Variable::new_unchecked(make_index(*index))) - }, - ) - }; - for (i, constraint) in self.r1cs.constraints.iter().enumerate() { - // 0 * LC = 0 must be ignored - if !((constraint.0.is_empty() || constraint.1.is_empty()) && constraint.2.is_empty()) { - cs.enforce( - || format!("{}", i), - |_| make_lc(constraint.0.clone()), - |_| make_lc(constraint.1.clone()), - |_| make_lc(constraint.2.clone()), - ); - } - } - Ok(()) - } -} diff --git a/groth16/src/groth16.rs b/groth16/src/groth16.rs index 843d6695..45199266 100644 --- a/groth16/src/groth16.rs +++ b/groth16/src/groth16.rs @@ -1,15 +1,28 @@ +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use crate::bellman_ce::{groth16::*, Circuit}; use anyhow::Result; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use franklin_crypto::bellman::pairing::Engine; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use rand_old::{self as rand, Rng}; + +#[cfg(any(feature = "cuda", feature = "opencl"))] use bellperson::{gpu, groth16::*, Circuit}; +#[cfg(any(feature = "cuda", feature = "opencl"))] use group::WnafGroup; +#[cfg(any(feature = "cuda", feature = "opencl"))] use pairing::{Engine, MultiMillerLoop}; +#[cfg(any(feature = "cuda", feature = "opencl"))] use rand_core::RngCore; use std::marker::PhantomData; +#[cfg(any(feature = "cuda", feature = "opencl"))] pub struct Groth16> { _engine: PhantomData, _circuit: PhantomData, } +#[cfg(any(feature = "cuda", feature = "opencl"))] impl Groth16 where E: MultiMillerLoop, @@ -54,16 +67,193 @@ where } } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub struct Groth16> { + _engine: PhantomData, + _circuit: PhantomData, +} +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +impl> Groth16 { + pub fn circuit_specific_setup( + circuit: C, + rng: &mut R, + ) -> Result<(Parameters, VerifyingKey)> { + let pk: Parameters = generate_random_parameters::(circuit, rng)?; + let vk = pk.vk.clone(); + + Ok((pk, vk)) + } + + pub fn prove( + circuit_pk: &Parameters, + input_and_witness: C, + rng: &mut R, + ) -> Result> { + let result = create_random_proof::(input_and_witness, circuit_pk, rng)?; + + Ok(result) + } + + pub fn verify_with_processed_vk( + circuit_vk: &VerifyingKey, + public_input: &[E::Fr], + proof: &Proof, + ) -> Result { + let circuit_pvk = prepare_verifying_key(circuit_vk); + let result = verify_proof(&circuit_pvk, proof, public_input)?; + + Ok(result) + } +} + +#[cfg(test)] +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +mod tests { + use franklin_crypto::bellman::{Field, PrimeField}; + use num_traits::Zero; + + use super::*; + use crate::bellman_ce::bls12_381::Bls12; + use crate::bellman_ce::bls12_381::Fr as Fr_bls12381; + use crate::bellman_ce::bn256::{Bn256, Fr}; + use algebraic::circom_circuit::CircomCircuit; + use algebraic::reader; + use algebraic::witness::{load_input_for_witness, WitnessCalculator}; + const INPUT_FILE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../test/multiplier.input.json"); + const CIRCUIT_FILE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../test/multiplier.r1cs"); + const WASM_FILE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../test/multiplier.wasm"); + const CIRCUIT_FILE_BLS12: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/test-vectors/mycircuit_bls12381.r1cs" + ); + const WASM_FILE_BLS12: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/test-vectors/mycircuit_bls12381.wasm" + ); + + #[test] + fn groth16_proof() -> Result<()> { + //1. SRS + let t = std::time::Instant::now(); + let circuit: CircomCircuit = CircomCircuit { + r1cs: reader::load_r1cs(CIRCUIT_FILE), + witness: None, + wire_mapping: None, + aux_offset: 0, + }; + let mut rng = rand::thread_rng(); + let params = Groth16::circuit_specific_setup(circuit, &mut rng)?; + let elapsed = t.elapsed().as_secs_f64(); + println!("1-groth16-bn128 setup run time: {} secs", elapsed); + + //2. Prove + let t1 = std::time::Instant::now(); + let mut wtns = WitnessCalculator::from_file(WASM_FILE)?; + let inputs = load_input_for_witness(INPUT_FILE); + let w = wtns.calculate_witness(inputs, false).unwrap(); + let w = w + .iter() + .map(|wi| { + if wi.is_zero() { + Fr::zero() + } else { + // println!("wi: {}", wi); + Fr::from_str(&wi.to_string()).unwrap() + } + }) + .collect::>(); + let circuit1: CircomCircuit = CircomCircuit { + r1cs: reader::load_r1cs(CIRCUIT_FILE), + witness: Some(w), + wire_mapping: None, + aux_offset: 0, + }; + let inputs = circuit1.get_public_inputs().unwrap(); + let proof = Groth16::prove(¶ms.0, circuit1, &mut rng)?; + let elapsed1 = t1.elapsed().as_secs_f64(); + println!("2-groth16-bn128 prove run time: {} secs", elapsed1); + + //3. Verify + let t2 = std::time::Instant::now(); + let verified = Groth16::<_, CircomCircuit>::verify_with_processed_vk( + ¶ms.1, &inputs, &proof, + )?; + let elapsed2 = t2.elapsed().as_secs_f64(); + println!("3-groth16-bn128 verify run time: {} secs", elapsed2); + + assert!(verified); + + Ok(()) + } + + #[test] + fn groth16_proof_bls12381() -> Result<()> { + //1. SRS + let t = std::time::Instant::now(); + let circuit: CircomCircuit = CircomCircuit { + r1cs: reader::load_r1cs(CIRCUIT_FILE_BLS12), + witness: None, + wire_mapping: None, + aux_offset: 0, + }; + let mut rng = rand::thread_rng(); + let params = Groth16::circuit_specific_setup(circuit, &mut rng)?; + let elapsed = t.elapsed().as_secs_f64(); + println!("1-groth16-bls12381 setup run time: {} secs", elapsed); + + //2. Prove + let t1 = std::time::Instant::now(); + // let mut wtns = WitnessCalculator::new(WASM_FILE_BLS12).unwrap(); + let mut wtns = WitnessCalculator::from_file(WASM_FILE_BLS12)?; + let inputs = load_input_for_witness(INPUT_FILE); + let w = wtns.calculate_witness(inputs, false).unwrap(); + let w = w + .iter() + .map(|wi| { + if wi.is_zero() { + Fr_bls12381::zero() + } else { + // println!("wi: {}", wi); + Fr_bls12381::from_str(&wi.to_string()).unwrap() + } + }) + .collect::>(); + let circuit1: CircomCircuit = CircomCircuit { + r1cs: reader::load_r1cs(CIRCUIT_FILE_BLS12), + witness: Some(w), + wire_mapping: None, + aux_offset: 0, + }; + let inputs = circuit1.get_public_inputs().unwrap(); + let proof = Groth16::prove(¶ms.0, circuit1, &mut rng)?; + let elapsed1 = t1.elapsed().as_secs_f64(); + println!("2-groth16-bls12381 prove run time: {} secs", elapsed1); + + //3. Verify + let t2 = std::time::Instant::now(); + let verified = Groth16::<_, CircomCircuit>::verify_with_processed_vk( + ¶ms.1, &inputs, &proof, + )?; + let elapsed2 = t2.elapsed().as_secs_f64(); + println!("3-groth16-bls12381 verify run time: {} secs", elapsed2); + + assert!(verified); + + Ok(()) + } +} + #[cfg(test)] +#[cfg(any(feature = "cuda", feature = "opencl"))] mod tests { use super::*; - use crate::circuit::CircomCircuit; - use crate::reader; - use crate::witness::{load_input_for_witness, WitnessCalculator}; + use algebraic::circom_circuit::CircomCircuit; + use algebraic::reader; + use algebraic::witness::{load_input_for_witness, WitnessCalculator}; use blstrs::{Bls12, Scalar}; use ff::{Field, PrimeField}; use num_traits::Zero; - use rand::rngs::OsRng; + use rand_new::rngs::OsRng; const INPUT_FILE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../test/multiplier.input.json"); const CIRCUIT_FILE_BLS12: &str = concat!( env!("CARGO_MANIFEST_DIR"), diff --git a/groth16/src/json_utils.rs b/groth16/src/json_utils.rs index 54ac302d..992c0502 100644 --- a/groth16/src/json_utils.rs +++ b/groth16/src/json_utils.rs @@ -1,9 +1,25 @@ -use crate::circuit::repr_to_big; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use crate::bellman_ce::pairing::{bls12_381::Bls12, bn256::Bn256}; +use algebraic::utils::repr_to_big; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use algebraic::PrimeField; use anyhow::Result; +#[cfg(any(feature = "cuda", feature = "opencl"))] use bellperson::groth16::*; +#[cfg(any(feature = "cuda", feature = "opencl"))] use blstrs::{Bls12, Fp, Fp2, G1Affine, G2Affine, Scalar}; +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +use franklin_crypto::bellman::{ + bls12_381::{ + Fq2 as Fq2_bls12381, G1Affine as G1Affine_bls12381, G2Affine as G2Affine_bls12381, + }, + bn256::{Fq2, G1Affine, G2Affine}, + groth16::{Proof, VerifyingKey}, + CurveAffine, +}; use num_bigint::BigUint; use num_traits::Num; +#[cfg(any(feature = "cuda", feature = "opencl"))] use pairing::MultiMillerLoop; use serde::{Deserialize, Serialize}; use serde_json::to_string; @@ -72,6 +88,29 @@ pub struct ProofFile { pub curve: String, } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub trait Parser: franklin_crypto::bellman::pairing::Engine { + fn parse_g1(e: &Self::G1Affine, to_hex: bool) -> (String, String); + fn parse_g2(e: &Self::G2Affine, to_hex: bool) -> (String, String, String, String); + fn parse_g1_json(e: &Self::G1Affine, to_hex: bool) -> G1 { + let parsed = Self::parse_g1(e, to_hex); + G1 { + x: parsed.0, + y: parsed.1, + } + } + fn parse_g2_json(e: &Self::G2Affine, to_hex: bool) -> G2 { + let parsed = Self::parse_g2(e, to_hex); + G2 { + x: (parsed.0, parsed.1).into(), + y: (parsed.2, parsed.3).into(), + } + } + fn to_g1(x: &str, y: &str) -> Self::G1Affine; + fn to_g2(x0: &str, x1: &str, y0: &str, y1: &str) -> Self::G2Affine; +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] pub trait Parser: MultiMillerLoop { fn parse_g1(e: &Self::G1Affine, to_hex: bool) -> (String, String); fn parse_g2(e: &Self::G2Affine, to_hex: bool) -> (String, String, String, String); @@ -93,6 +132,110 @@ pub trait Parser: MultiMillerLoop { fn to_g2(x0: &str, x1: &str, y0: &str, y1: &str) -> Self::G2Affine; } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub fn render_scalar_to_str(el: &F, to_hex: bool) -> String { + let repr = el.into_repr(); + if to_hex { + repr.to_string() + } else { + repr_to_big(repr) + } +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub fn render_str_to_scalar(value: &str) -> F { + let value = match value.starts_with("0x") { + true => BigUint::from_str_radix(&value[2..], 16) + .unwrap() + .to_str_radix(10), + _ => value.to_string(), + }; + F::from_str(&value).unwrap() +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub fn to_public_input(s: &str) -> Vec { + let input: Vec = serde_json::from_str(s).unwrap(); + input + .iter() + .map(|hex_str| render_str_to_scalar::(hex_str)) + .collect() +} +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +impl Parser for Bn256 { + fn parse_g1(e: &Self::G1Affine, to_hex: bool) -> (String, String) { + let (x, y) = e.into_xy_unchecked(); + ( + render_scalar_to_str(&x, to_hex), + render_scalar_to_str(&y, to_hex), + ) + } + + fn parse_g2(e: &Self::G2Affine, to_hex: bool) -> (String, String, String, String) { + let (x, y) = e.into_xy_unchecked(); + ( + render_scalar_to_str(&x.c0, to_hex), + render_scalar_to_str(&x.c1, to_hex), + render_scalar_to_str(&y.c0, to_hex), + render_scalar_to_str(&y.c1, to_hex), + ) + } + + fn to_g1(x: &str, y: &str) -> Self::G1Affine { + G1Affine::from_xy_unchecked(render_str_to_scalar(x), render_str_to_scalar(y)) + } + + fn to_g2(x0: &str, x1: &str, y0: &str, y1: &str) -> Self::G2Affine { + let x = Fq2 { + c0: render_str_to_scalar(x0), + c1: render_str_to_scalar(x1), + }; + let y = Fq2 { + c0: render_str_to_scalar(y0), + c1: render_str_to_scalar(y1), + }; + G2Affine::from_xy_unchecked(x, y) + } +} + +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +impl Parser for Bls12 { + fn parse_g1(e: &Self::G1Affine, to_hex: bool) -> (String, String) { + let (x, y) = e.into_xy_unchecked(); + ( + render_scalar_to_str(&x, to_hex), + render_scalar_to_str(&y, to_hex), + ) + } + + fn parse_g2(e: &Self::G2Affine, to_hex: bool) -> (String, String, String, String) { + let (x, y) = e.into_xy_unchecked(); + ( + render_scalar_to_str(&x.c0, to_hex), + render_scalar_to_str(&x.c1, to_hex), + render_scalar_to_str(&y.c0, to_hex), + render_scalar_to_str(&y.c1, to_hex), + ) + } + + fn to_g1(x: &str, y: &str) -> Self::G1Affine { + G1Affine_bls12381::from_xy_unchecked(render_str_to_scalar(x), render_str_to_scalar(y)) + } + + fn to_g2(x0: &str, x1: &str, y0: &str, y1: &str) -> Self::G2Affine { + let x = Fq2_bls12381 { + c0: render_str_to_scalar(x0), + c1: render_str_to_scalar(x1), + }; + let y = Fq2_bls12381 { + c0: render_str_to_scalar(y0), + c1: render_str_to_scalar(y1), + }; + G2Affine_bls12381::from_xy_unchecked(x, y) + } +} + +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn render_fp_to_str(fp: &Fp, to_hex: bool) -> String { let be_bytes = fp.to_bytes_be(); let mut hex_string = String::new(); @@ -107,6 +250,7 @@ pub fn render_fp_to_str(fp: &Fp, to_hex: bool) -> String { } } +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn render_str_to_fp(value: &str) -> Fp { let mut be_bytes = [0u8; 48]; let hex_str; @@ -127,6 +271,7 @@ pub fn render_str_to_fp(value: &str) -> Fp { Fp::from_bytes_be(&be_bytes).unwrap() } +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn render_str_to_scalar(value: &str) -> Scalar { let mut be_bytes = [0u8; 32]; let hex_str; @@ -147,6 +292,7 @@ pub fn render_str_to_scalar(value: &str) -> Scalar { Scalar::from_bytes_be(&be_bytes).unwrap() } +#[cfg(any(feature = "cuda", feature = "opencl"))] pub fn to_public_input(s: &str) -> Vec { let input: Vec = serde_json::from_str(s).unwrap(); input @@ -155,6 +301,7 @@ pub fn to_public_input(s: &str) -> Vec { .collect() } +#[cfg(any(feature = "cuda", feature = "opencl"))] impl Parser for Bls12 { fn parse_g1(e: &Self::G1Affine, to_hex: bool) -> (String, String) { let (x, y) = (e.x(), e.y()); @@ -251,9 +398,30 @@ pub fn to_proof(s: &str) -> Proof

{ } #[cfg(test)] +#[cfg(not(any(feature = "cuda", feature = "opencl")))] mod tests { use super::*; + #[test] + fn test_serialize_vk() { + let mut reader = std::io::BufReader::with_capacity( + 1 << 24, + std::fs::File::open("./test-vectors/verification_key.bin").unwrap(), + ); + let vk_from_bin = VerifyingKey::::read(&mut reader).unwrap(); + let result = serialize_vk(&vk_from_bin, "BN128", false).unwrap(); + std::fs::write("./test-vectors/verification_key.json", result) + .expect("Unable to write data to file"); + + let json_data = std::fs::read_to_string("./test-vectors/verification_key.json") + .expect("Unable to read the JSON file"); + let verifying_key_from_json = to_verification_key::(&json_data); + assert_eq!( + vk_from_bin.alpha_g1, verifying_key_from_json.alpha_g1, + "VerificationKey are not equal" + ); + } + #[test] fn test_serialize_vk_bls12381() { let mut reader = std::io::BufReader::with_capacity( @@ -273,19 +441,44 @@ mod tests { ); } - // #[test] - // fn test_serialize_proof() { - // let mut reader = std::io::BufReader::with_capacity( - // 1 << 24, - // std::fs::File::open("./test-vectors/proof.bin").unwrap(), - // ); - // let proof_from_bin = Proof::::read(&mut reader).unwrap(); - // let result = serialize_proof(&proof_from_bin, "BN128", false).unwrap(); - // std::fs::write("./test-vectors/proof.json", result).expect("Unable to write data to file"); - - // let json_data = std::fs::read_to_string("./test-vectors/proof.json") - // .expect("Unable to read the JSON file"); - // let proof_from_json = to_proof::(&json_data); - // assert_eq!(proof_from_bin.a, proof_from_json.a, "Proofs are not equal"); - // } + #[test] + fn test_serialize_proof() { + let mut reader = std::io::BufReader::with_capacity( + 1 << 24, + std::fs::File::open("./test-vectors/proof.bin").unwrap(), + ); + let proof_from_bin = Proof::::read(&mut reader).unwrap(); + let result = serialize_proof(&proof_from_bin, "BN128", false).unwrap(); + std::fs::write("./test-vectors/proof.json", result).expect("Unable to write data to file"); + + let json_data = std::fs::read_to_string("./test-vectors/proof.json") + .expect("Unable to read the JSON file"); + let proof_from_json = to_proof::(&json_data); + assert_eq!(proof_from_bin.a, proof_from_json.a, "Proofs are not equal"); + } +} + +#[cfg(test)] +#[cfg(any(feature = "cuda", feature = "opencl"))] +mod tests { + use super::*; + + #[test] + fn test_serialize_vk_bls12381() { + let mut reader = std::io::BufReader::with_capacity( + 1 << 24, + std::fs::File::open("./test-vectors/verification_key_bls12381.bin").unwrap(), + ); + let vk_from_bin = VerifyingKey::::read(&mut reader).unwrap(); + let result = serialize_vk(&vk_from_bin, "BLS12381", false).unwrap(); + std::fs::write("./test-vectors/verification_key_bls12381.json", result) + .expect("Unable to write data to file"); + let json_data = std::fs::read_to_string("./test-vectors/verification_key_bls12381.json") + .expect("Unable to read the JSON file"); + let verifying_key_from_json = to_verification_key::(&json_data); + assert_eq!( + vk_from_bin.alpha_g1, verifying_key_from_json.alpha_g1, + "VerificationKey are not equal" + ); + } } diff --git a/groth16/src/lib.rs b/groth16/src/lib.rs index f12fdd27..d8e5def1 100644 --- a/groth16/src/lib.rs +++ b/groth16/src/lib.rs @@ -1,29 +1,14 @@ -#[macro_use] -extern crate hex_literal; pub mod api; pub mod groth16; pub mod json_utils; mod template; -// #[cfg(any(feature = "cuda", feature = "opencl"))] -// mod gpu_specific { - pub mod witness; - pub mod circuit; - pub mod r1cs_file; - pub mod reader; - - pub use bellperson; -// } +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +mod non_gpu_specific { + pub use bellman_ce::pairing::ff; + pub use ff::*; + pub use franklin_crypto::bellman as bellman_ce; +} -// #[cfg(any(feature = "cuda", feature = "opencl"))] -// pub use gpu_specific::*; - -// #[cfg(not(any(feature = "cuda", feature = "opencl")))] -// mod non_gpu_specific { -// pub use bellman_ce::pairing::ff; -// pub use ff::*; -// pub use franklin_crypto::bellman as bellman_ce; -// } - -// #[cfg(not(any(feature = "cuda", feature = "opencl")))] -// pub use non_gpu_specific::*; \ No newline at end of file +#[cfg(not(any(feature = "cuda", feature = "opencl")))] +pub use non_gpu_specific::*; diff --git a/groth16/src/reader.rs b/groth16/src/reader.rs deleted file mode 100644 index e52c6b07..00000000 --- a/groth16/src/reader.rs +++ /dev/null @@ -1,230 +0,0 @@ -use anyhow::{bail, Result}; -use byteorder::{LittleEndian, ReadBytesExt}; -use itertools::Itertools; -use std::collections::BTreeMap; -use std::fs::{File, OpenOptions}; -use std::io::{BufReader, Read, Seek}; -use std::str; - -use ff::PrimeField; - -use crate::circuit::{CircuitJson, R1CS}; - -/// get universal setup file by filename -fn get_universal_setup_file_buff_reader(setup_file_name: &str) -> Result> { - let setup_file = File::open(setup_file_name)?; - Ok(BufReader::with_capacity(1 << 29, setup_file)) -} - -// /// load monomial form SRS by filename -// pub fn load_key_monomial_form(filename: &str) -> Crs { -// let mut buf_reader = get_universal_setup_file_buff_reader(filename) -// .unwrap_or_else(|_| panic!("read key_monomial_form file err, {}", filename)); -// Crs::::read(&mut buf_reader).expect("read key_monomial_form err") -// } - -// /// load optional lagrange form SRS by filename -// pub fn maybe_load_key_lagrange_form( -// option_filename: Option, -// ) -> Option> { -// match option_filename { -// None => None, -// Some(filename) => { -// let mut buf_reader = get_universal_setup_file_buff_reader(&filename) -// .unwrap_or_else(|_| panic!("read key_lagrange_form file err, {}", filename)); -// let key_lagrange_form = Crs::::read(&mut buf_reader) -// .expect("read key_lagrange_form err"); -// Some(key_lagrange_form) -// } -// } -// } - -/// load witness file by filename with autodetect encoding (bin or json). -pub fn load_witness_from_file(filename: &str) -> Vec { - if filename.ends_with("json") { - load_witness_from_json_file::(filename) - } else { - load_witness_from_bin_file::(filename) - } -} - -/// load witness from json file by filename -pub fn load_witness_from_json_file(filename: &str) -> Vec { - let reader = OpenOptions::new() - .read(true) - .open(filename) - .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); - load_witness_from_json::>(BufReader::new(reader)) -} - -/// load witness from json by a reader -fn load_witness_from_json(reader: R) -> Vec { - let witness: Vec = serde_json::from_reader(reader).expect("Unable to read."); - witness - .into_iter() - .map(|x| E::from_str_vartime(&x).unwrap()) - .collect::>() -} - -/// load witness from bin file by filename -pub fn load_witness_from_bin_file(filename: &str) -> Vec { - let reader = OpenOptions::new() - .read(true) - .open(filename) - .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); - load_witness_from_bin_reader::>(BufReader::new(reader)) - .expect("read witness failed") -} - -/// load witness from u8 array -pub fn load_witness_from_array(buffer: Vec) -> Result> { - load_witness_from_bin_reader::(buffer.as_slice()) -} - -/// load witness from u8 array by a reader -pub fn load_witness_from_bin_reader(mut reader: R) -> Result> { - let mut wtns_header = [0u8; 4]; - reader.read_exact(&mut wtns_header)?; - if wtns_header != [119, 116, 110, 115] { - // python -c 'print([ord(c) for c in "wtns"])' => [119, 116, 110, 115] - bail!("Invalid file header"); - } - let version = reader.read_u32::()?; - log::trace!("wtns version {}", version); - if version > 2 { - bail!("unsupported file version"); - } - let num_sections = reader.read_u32::()?; - if num_sections != 2 { - bail!("invalid num sections".to_string()); - } - // read the first section - let sec_type = reader.read_u32::()?; - if sec_type != 1 { - bail!("invalid section type".to_string()); - } - let sec_size = reader.read_u64::()?; - if sec_size != 4 + 32 + 4 { - bail!("invalid section len".to_string()); - } - let field_size = reader.read_u32::()?; - if field_size != 32 { - bail!("invalid field byte size".to_string()); - } - let mut prime = vec![0u8; field_size as usize]; - reader.read_exact(&mut prime)?; - if prime != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") { - bail!("invalid curve prime".to_string()); - } - let witness_len = reader.read_u32::()?; - log::trace!("witness len {}", witness_len); - let sec_type = reader.read_u32::()?; - if sec_type != 2 { - bail!("invalid section type".to_string()); - } - let sec_size = reader.read_u64::()?; - if sec_size != (witness_len * field_size) as u64 { - bail!(format!("Invalid witness section size {}", sec_size)); - } - let mut result = Vec::with_capacity(witness_len as usize); - for _ in 0..witness_len { - let mut repr = E::default().to_repr(); - let repr_slice = repr.as_mut(); - if reader.read_exact(repr_slice).is_err() { - continue; - } - let maybe_field_elem = E::from_repr(repr); - if maybe_field_elem.is_some().unwrap_u8() == 1 { - result.push(maybe_field_elem.unwrap()); - } else { - continue; - } - // repr.read_le(&mut reader)?; - // result.push(E::Fr::from_repr(repr)?); - } - Ok(result) -} - -/// load r1cs file by filename with autodetect encoding (bin or json) -pub fn load_r1cs(filename: &str) -> R1CS { - if filename.ends_with("json") { - load_r1cs_from_json_file(filename) - } else { - let (r1cs, _wire_mapping) = load_r1cs_from_bin_file(filename); - r1cs - } -} - -/// load r1cs from json file by filename -fn load_r1cs_from_json_file(filename: &str) -> R1CS { - let reader = OpenOptions::new() - .read(true) - .open(filename) - .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); - load_r1cs_from_json(BufReader::new(reader)) -} - -/// load r1cs from json by a reader -fn load_r1cs_from_json(reader: R) -> R1CS { - let circuit_json: CircuitJson = serde_json::from_reader(reader).expect("Unable to read."); - - let num_inputs = circuit_json.num_inputs + circuit_json.num_outputs + 1; - let num_aux = circuit_json.num_variables - num_inputs; - - let convert_constraint = |lc: &BTreeMap| { - lc.iter() - .map(|(index, coeff)| (index.parse().unwrap(), E::from_str_vartime(coeff).unwrap())) - .collect_vec() - }; - - let constraints = circuit_json - .constraints - .iter() - .map(|c| { - ( - convert_constraint(&c[0]), - convert_constraint(&c[1]), - convert_constraint(&c[2]), - ) - }) - .collect_vec(); - - R1CS { - num_inputs, - num_aux, - num_variables: circuit_json.num_variables, - num_outputs: circuit_json.num_outputs, - constraints, - custom_gates: vec![], - custom_gates_uses: vec![], - } -} - -/// load r1cs from bin file by filename -fn load_r1cs_from_bin_file(filename: &str) -> (R1CS, Vec) { - let reader = OpenOptions::new() - .read(true) - .open(filename) - .unwrap_or_else(|_| panic!("Unable to open {}.", filename)); - load_r1cs_from_bin(BufReader::new(reader)) -} - -/// load r1cs from bin by a reader -pub fn load_r1cs_from_bin(reader: R) -> (R1CS, Vec) { - let file = crate::r1cs_file::from_reader::(reader).expect("Unable to read."); - 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, - num_outputs: file.header.n_pub_out as usize, - constraints: file.constraints, - custom_gates: file.custom_gates, - custom_gates_uses: file.custom_gates_uses, - }, - file.wire_mapping.iter().map(|e| *e as usize).collect_vec(), - ) -} diff --git a/groth16/src/witness/circom.rs b/groth16/src/witness/circom.rs deleted file mode 100644 index 98707285..00000000 --- a/groth16/src/witness/circom.rs +++ /dev/null @@ -1,133 +0,0 @@ -// copied and modified by https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/circom.rs -use anyhow::Result; -use wasmer::{Function, Instance, Store, Value}; - -#[derive(Clone, Debug)] -pub struct Wasm(Instance); - -impl Wasm { - pub(crate) fn get_field_num_len32(&self, store: &mut Store) -> Result { - self.get_u32(store, "getFieldNumLen32") - } - - pub(crate) fn get_raw_prime(&self, store: &mut Store) -> Result<()> { - let func = self.func("getRawPrime"); - func.call(store, &[])?; - Ok(()) - } - - pub(crate) fn read_shared_rw_memory(&self, store: &mut Store, i: u32) -> Result { - let func = self.func("readSharedRWMemory"); - let result = func.call(store, &[i.into()])?; - Ok(result[0].unwrap_i32() as u32) - } - - pub(crate) fn write_shared_rw_memory(&self, store: &mut Store, i: u32, v: u32) -> Result<()> { - let func = self.func("writeSharedRWMemory"); - func.call(store, &[i.into(), v.into()])?; - Ok(()) - } - - pub(crate) fn set_input_signal( - &self, - store: &mut Store, - hmsb: u32, - hlsb: u32, - pos: u32, - ) -> Result<()> { - let func = self.func("setInputSignal"); - func.call(store, &[hmsb.into(), hlsb.into(), pos.into()])?; - Ok(()) - } - - pub(crate) fn get_witness(&self, store: &mut Store, i: u32) -> Result<()> { - let func = self.func("getWitness"); - func.call(store, &[i.into()])?; - Ok(()) - } - - pub(crate) fn get_witness_size(&self, store: &mut Store) -> Result { - self.get_u32(store, "getWitnessSize") - } - - pub(crate) fn init(&self, store: &mut Store, sanity_check: bool) -> Result<()> { - let func = self.func("init"); - func.call(store, &[Value::I32(sanity_check as i32)])?; - Ok(()) - } - - // pub(crate) fn get_ptr_witness_buffer(&self, store: &mut Store) -> Result { - // self.get_u32(store, "getWitnessBuffer") - // } - - // pub(crate) fn get_ptr_witness(&self, store: &mut Store, w: u32) -> Result { - // let func = self.func( "getPWitness"); - // let res = func.call(store, &[w.into()])?; - // - // Ok(res[0].unwrap_i32() as u32) - // } - - // pub(crate) fn get_signal_offset32( - // &self, - // store: &mut Store, - // p_sig_offset: u32, - // component: u32, - // hash_msb: u32, - // hash_lsb: u32, - // ) -> Result<()> { - // let func = self.func( "getSignalOffset32"); - // func.call( - // store, - // &[ - // p_sig_offset.into(), - // component.into(), - // hash_msb.into(), - // hash_lsb.into(), - // ], - // )?; - // - // Ok(()) - // } - // - // pub(crate) fn set_signal( - // &self, - // store: &mut Store, - // c_idx: u32, - // component: u32, - // signal: u32, - // p_val: u32, - // ) -> Result<()> { - // let func = self.func( "setSignal"); - // func.call( - // store, - // &[c_idx.into(), component.into(), signal.into(), p_val.into()], - // )?; - // - // Ok(()) - // } - - // Default to version 1 if it isn't explicitly defined - pub(crate) fn get_version(&self, store: &mut Store) -> Result { - match self.0.exports.get_function("getVersion") { - Ok(func) => Ok(func.call(store, &[])?[0].unwrap_i32() as u32), - Err(_) => Ok(1), - } - } - - pub(crate) fn get_u32(&self, store: &mut Store, name: &str) -> Result { - let func = self.func(name); - let result = func.call(store, &[])?; - Ok(result[0].unwrap_i32() as u32) - } - - pub(crate) fn func(&self, name: &str) -> &Function { - self.0 - .exports - .get_function(name) - .unwrap_or_else(|_| panic!("function {} not found", name)) - } - - pub fn new(instance: Instance) -> Self { - Self(instance) - } -} diff --git a/groth16/src/witness/memory.rs b/groth16/src/witness/memory.rs deleted file mode 100644 index f1ea578f..00000000 --- a/groth16/src/witness/memory.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Safe-ish interface for reading and writing specific types to the WASM runtime's memory, -//! modified from ark-circom -use num_bigint::BigInt; -use wasmer::Memory; - -#[derive(Clone, Debug)] -pub struct SafeMemory { - pub memory: Memory, - pub prime: BigInt, - // short_max: BigInt, - // short_min: BigInt, - // r_inv: BigInt, - // n32: usize, -} - -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::from_str( - // "21888242871839275222246405745257275088548364400416034343698204186575808495617", - // ) - // .unwrap(), - // ) - &short_max; - // let r_inv = BigInt::from_str( - // "9915499612839321149637521777990102151350674507940716049588462388200839649614", - // ) - // .unwrap(); - - Self { - memory, - prime, - // short_max, - // short_min, - // r_inv, - // n32, - } - } -} diff --git a/groth16/src/witness/mod.rs b/groth16/src/witness/mod.rs deleted file mode 100644 index 86838d46..00000000 --- a/groth16/src/witness/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -mod circom; -pub(crate) mod memory; -pub(super) mod witness_calculator; - -use fnv::FnvHasher; -use num_bigint::BigInt; -use num_traits::{One, Zero}; -use serde_json::Value; -use std::collections::HashMap; -use std::hash::Hasher; -use std::str::FromStr; -pub use witness_calculator::flat_array; -pub use witness_calculator::WitnessCalculator; - -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) -} - -pub fn load_input_for_witness(input_file: &str) -> HashMap> { - let inputs_str = std::fs::read_to_string(input_file) - .unwrap_or_else(|_| panic!("Unable to load {}", input_file)); - let inputs: std::collections::HashMap = - serde_json::from_str(&inputs_str).unwrap(); - - inputs - .iter() - .map(|(key, value)| { - let res = match value { - Value::String(inner) => { - vec![BigInt::from_str(inner).unwrap()] - } - Value::Bool(inner) => { - if *inner { - vec![BigInt::one()] - } else { - vec![BigInt::zero()] - } - } - Value::Number(inner) => { - vec![BigInt::from_str(&inner.to_string()).unwrap()] - //vec![BigInt::from(inner.as_u64().expect("not a u32"))] - } - //Value::Array(inner) => inner.iter().cloned().map(value_to_bigint).collect(), - Value::Array(inner) => flat_array(inner), - _ => panic!("{:?}", value), - }; - - (key.clone(), res) - }) - .collect::>() -} diff --git a/groth16/src/witness/witness_calculator.rs b/groth16/src/witness/witness_calculator.rs deleted file mode 100644 index d56cbf96..00000000 --- a/groth16/src/witness/witness_calculator.rs +++ /dev/null @@ -1,464 +0,0 @@ -// copied and modified by https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/witness_calculator.rs -use crate::witness::{circom::Wasm, fnv, memory::SafeMemory}; -use anyhow::{bail, Result}; -use ff::PrimeField; -use num::ToPrimitive; -use num_bigint::BigInt; -use num_bigint::Sign; -use num_traits::{One, Zero}; -use serde_json::Value; -use std::str::FromStr; -use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, Store}; - -#[cfg(not(feature = "wasm"))] -use std::fs::OpenOptions; -#[cfg(not(feature = "wasm"))] -use std::io::{BufWriter, Write}; - -use byteorder::{LittleEndian, WriteBytesExt}; - -pub struct WitnessCalculator { - pub instance: Wasm, - store: Store, - pub memory: SafeMemory, - pub n64: u32, - pub circom_version: u32, -} - -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 -} - -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 from_file(path: impl AsRef) -> Result { - let mut store = Store::default(); - let module = Module::from_file(&store, path)?; - let mut wtns = Self::from_module(&mut store, module)?; - wtns.store = store; - Ok(wtns) - } - - pub fn from_module(store: &mut Store, module: Module) -> Result { - // Set up the memory - let memory = Memory::new(store, MemoryType::new(2000, None, false))?; - 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(store, &module, &import_object)?); - - // Circom 2 feature flag with version 2 - fn new_circom( - store: &mut Store, - instance: Wasm, - memory: Memory, - ) -> Result { - let version = instance.get_version(store).unwrap_or(1); - - let n32 = instance.get_field_num_len32(store)?; - let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); - instance.get_raw_prime(store)?; - let mut arr = vec![0; n32 as usize]; - for i in 0..n32 { - let res = instance.read_shared_rw_memory(store, 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, - store: Store::default(), - memory: safe_memory, - n64, - circom_version: version, - }) - } - - new_circom(store, instance, memory) - } - - pub fn calculate_witness)>>( - &mut self, - // store: &mut Store, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(&mut self.store, sanity_check)?; - let wtns_u32 = self.calculate_witness_circom(inputs, sanity_check)?; - let n32 = self.instance.get_field_num_len32(&mut self.store)?; - - let mut wo = Vec::new(); - let witness_size = self.instance.get_witness_size(&mut self.store)?; - for i in 0..witness_size { - let mut arr = vec![0u32; n32 as usize]; - for j in 0..n32 { - arr[(n32 - 1 - j) as usize] = wtns_u32[(i * n32 + j) as usize]; - } - wo.push(from_array32(arr)); - } - Ok(wo) - } - - pub fn calculate_witness_bin)>>( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(&mut self.store, sanity_check)?; - self.calculate_witness_circom(inputs, sanity_check) - } - - // Circom 2 feature flag with version 2 - fn calculate_witness_circom)>>( - &mut self, - inputs: I, - sanity_check: bool, - ) -> Result> { - self.instance.init(&mut self.store, sanity_check)?; - - let n32 = self.instance.get_field_num_len32(&mut self.store)?; - - // 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( - &mut self.store, - j, - f_arr[(n32 as usize) - 1 - (j as usize)], - )?; - } - self.instance - .set_input_signal(&mut self.store, msb, lsb, i as u32)?; - } - } - - let mut w = Vec::new(); - - let witness_size = self.instance.get_witness_size(&mut self.store)?; - for i in 0..witness_size { - self.instance.get_witness(&mut self.store, i)?; - for j in 0..n32 { - w.push(self.instance.read_shared_rw_memory(&mut self.store, j)?); - } - } - - Ok(w) - } - - #[cfg(not(feature = "wasm"))] - pub fn save_witness_to_bin_file( - &mut self, - filename: &str, - w: &Vec, - ) -> Result<()> { - let writer = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(filename)?; - - let writer = BufWriter::new(writer); - self.save_witness_from_bin_writer::(writer, w) - } - - pub fn save_witness_from_bin_writer( - &mut self, - mut writer: W, - wtns: &Vec, - ) -> Result<()> { - let n32 = self.instance.get_field_num_len32(&mut self.store)?; - let wtns_header = [119, 116, 110, 115]; - writer.write_all(&wtns_header)?; - - let version = self.circom_version; - writer.write_u32::(version)?; - let num_section = 2u32; - writer.write_u32::(num_section)?; - - // id section 1 - let id_section = 1u32; - writer.write_u32::(id_section)?; - - let sec_size: u64 = (n32 * 4 + 8) as u64; - writer.write_u64::(sec_size)?; - - let field_size: u32 = n32 * 4; - writer.write_u32::(field_size)?; - - // write prime - let (sign, prime_buf) = self.memory.prime.to_bytes_le(); - if sign != Sign::Plus { - bail!(format!( - "Invalid prime: {}, must be positive", - self.memory.prime - )); - } - if prime_buf.len() as u32 != field_size { - bail!(format!( - "Invalid prime: {}, len must be of {}", - self.memory.prime, - prime_buf.len() - )); - } - writer.write_all(&prime_buf)?; - - // write witness size - let wtns_size = wtns.len() as u32 / n32; - writer.write_u32::(wtns_size)?; - // sec type - writer.write_u32::(2)?; - // sec size - writer.write_u64::((wtns_size * field_size) as u64)?; - - for w in wtns { - writer.write_u32::(*w)?; - } - Ok(()) - } -} - -#[allow(dead_code)] -pub 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() - .unwrap_or_else(|| panic!("{} not a u32", inner)), - ), - _ => panic!("unsupported type {:?}", v), - } -} - -pub fn flat_array(v: &[Value]) -> Vec { - let mut result = Vec::new(); - fn fill_array(out: &mut Vec, value: &Value) { - match value { - Value::Array(inner) => { - for v2 in inner.iter() { - fill_array(out, v2); - } - } - Value::Bool(inner) => { - if *inner { - out.push(BigInt::one()); - } else { - out.push(BigInt::zero()); - } - } - Value::String(inner) => { - out.push(BigInt::from_str(inner).unwrap()); - } - Value::Number(inner) => { - out.push(BigInt::from_str(&inner.to_string()).unwrap()); - } - _ => panic!(), - } - } - - for v2 in v.iter() { - fill_array(&mut result, v2); - } - result -} - -// callback hooks for debugging -mod runtime { - use super::*; - - pub fn error(store: &mut Store) -> Function { - #[allow(unused)] - #[allow(clippy::many_single_char_names)] - fn func( - a: i32, - b: i32, - c: i32, - d: i32, - e: i32, - f: i32, - ) -> std::result::Result<(), wasmer::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 - log::trace!("runtime error, exiting early: {a} {b} {c} {d} {e} {f}",); - Err(wasmer::RuntimeError::new("1")) - } - Function::new_typed(store, func) - } - - // Circom 2.0 - pub fn exception_handler(store: &mut Store) -> Function { - #[allow(unused)] - fn func(a: i32) {} - Function::new_typed(store, func) - } - - // Circom 2.0 - pub fn show_memory(store: &mut Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_typed(store, func) - } - - // Circom 2.0 - pub fn print_error_message(store: &mut Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_typed(store, func) - } - - // Circom 2.0 - pub fn write_buffer_message(store: &mut Store) -> Function { - #[allow(unused)] - fn func() {} - Function::new_typed(store, func) - } - - pub fn log_signal(store: &mut Store) -> Function { - #[allow(unused)] - fn func(a: i32, b: i32) {} - Function::new_typed(store, func) - } - - pub fn log_component(store: &mut Store) -> Function { - #[allow(unused)] - fn func(a: i32) {} - Function::new_typed(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, - 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(), - 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(), - 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(), - n64: 4, - witness: &[ - "1", - "21888242871839275222246405745257275088548364400416034343698204186575808493616", - "10944121435919637611123202872628637544274182200208017171849102093287904246808", - "2", - ], - }); - } - - // TODO: test complex samples - - fn run_test(case: TestCase) { - let mut wtns = WitnessCalculator::from_file(case.circuit_path).unwrap(); - assert_eq!( - wtns.memory.prime.to_str_radix(16), - "30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase() - ); - 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, true).unwrap(); - for (r, w) in res.iter().zip(case.witness) { - assert_eq!(r, &BigInt::from_str(w).unwrap()); - } - } -} diff --git a/plonky/Cargo.toml b/plonky/Cargo.toml index bcd98c1a..02963ca9 100644 --- a/plonky/Cargo.toml +++ b/plonky/Cargo.toml @@ -46,7 +46,7 @@ ark-std = { version = "0.4.0", optional = true } env_logger = "0.10" [features] -default = ["bellman_vk_codegen/multicore", "franklin-crypto/multicore", "ethabi", "recursive_aggregation_circuit/multicore"] +default = ["bellman_vk_codegen/multicore", "franklin-crypto/multicore", "ethabi", "recursive_aggregation_circuit/multicore", "algebraic/default"] profiler = ["ark-std/print-trace"] avx512 = ["fields/avx512"] wasm = [] diff --git a/recursion/Cargo.toml b/recursion/Cargo.toml index 428edcb2..9276fec4 100644 --- a/recursion/Cargo.toml +++ b/recursion/Cargo.toml @@ -52,6 +52,6 @@ rand = { version="0.6.5", features = ["wasm-bindgen"] } wasm-bindgen-test = "0.3" [features] -default = ["franklin-crypto/multicore", "wasmer/singlepass", "starky/default", "plonky/default"] +default = ["franklin-crypto/multicore", "wasmer/singlepass", "starky/default", "plonky/default", "algebraic/default"] wasm = ["wasmer/js-default"] -avx512 = ["fields/avx512", "plonky/avx512", "starky/avx512", "powdr/starky-avx512"] +avx512 = ["fields/avx512", "plonky/avx512", "starky/avx512", "powdr/starky-avx512"] \ No newline at end of file