From 765e6c7004eea83c280201b79f84b547bb22397b Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Wed, 29 Apr 2020 22:56:00 +0200 Subject: [PATCH 1/4] Circom 0.5 binary formats support --- .gitignore | 2 + phase2/.gitignore | 6 +- phase2/src/bin/new.rs | 13 ++- phase2/src/bin/prove.rs | 26 ++++- phase2/src/circom_circuit.rs | 214 ++++++++++++++++++++++++++++++++++- 5 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..faa18382 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode + diff --git a/phase2/.gitignore b/phase2/.gitignore index 9fdddcfa..14dafa65 100644 --- a/phase2/.gitignore +++ b/phase2/.gitignore @@ -6,4 +6,8 @@ phase1radix2m* /*.json /*.bin /*.params -/verifier.sol \ No newline at end of file +/*.r1cs +/*.wasm +/*.sym +/verifier.sol +/build/ diff --git a/phase2/src/bin/new.rs b/phase2/src/bin/new.rs index 8d98c999..88452da7 100644 --- a/phase2/src/bin/new.rs +++ b/phase2/src/bin/new.rs @@ -5,14 +5,19 @@ extern crate exitcode; use std::fs::File; use phase2::parameters::MPCParameters; use phase2::circom_circuit::circuit_from_json_file; +use phase2::circom_circuit::circuit_from_r1cs_file; fn main() { let args: Vec = std::env::args().collect(); if args.len() != 4 { - println!("Usage: \n "); + println!("Usage: \n> "); std::process::exit(exitcode::USAGE); } let circuit_filename = &args[1]; + let circuit_filename_ext = match std::path::Path::new(circuit_filename).extension() { + Some(os) => os.to_str().unwrap(), + None => "" + }; let params_filename = &args[2]; let radix_directory = &args[3]; @@ -21,7 +26,11 @@ fn main() { // Import the circuit and create the initial parameters using phase 1 println!("Creating initial parameters for {}...", circuit_filename); let params = { - let c = circuit_from_json_file(&circuit_filename); + let c = if circuit_filename_ext.eq_ignore_ascii_case("JSON") { + circuit_from_json_file(&circuit_filename) + } else { + circuit_from_r1cs_file(&circuit_filename) + }; MPCParameters::new(c, should_filter_points_at_infinity, radix_directory).unwrap() }; diff --git a/phase2/src/bin/prove.rs b/phase2/src/bin/prove.rs index f627bdf1..23805dcf 100644 --- a/phase2/src/bin/prove.rs +++ b/phase2/src/bin/prove.rs @@ -15,13 +15,15 @@ use phase2::circom_circuit::{ create_rng, proof_to_json_file, circuit_from_json_file, - witness_from_json_file + circuit_from_r1cs_file, + witness_from_json_file, + witness_from_wtns_file }; fn main() { let args: Vec = std::env::args().collect(); if args.len() != 6 { - println!("Usage: \n "); + println!("Usage: \n> > "); std::process::exit(exitcode::USAGE); } let circuit_filename = &args[1]; @@ -29,11 +31,27 @@ fn main() { let params_filename = &args[3]; let proof_filename = &args[4]; let public_filename = &args[5]; + let circuit_filename_ext = match std::path::Path::new(circuit_filename).extension() { + Some(os) => os.to_str().unwrap(), + None => "" + }; + let witness_filename_ext = match std::path::Path::new(witness_filename).extension() { + Some(os) => os.to_str().unwrap(), + None => "" + }; let rng = create_rng(); let params = load_params_file(params_filename); - let mut circuit = circuit_from_json_file(circuit_filename); - circuit.witness = Some(witness_from_json_file::(witness_filename)); + let mut circuit = if circuit_filename_ext.eq_ignore_ascii_case("JSON") { + circuit_from_json_file(&circuit_filename) + } else { + circuit_from_r1cs_file(&circuit_filename) + }; + circuit.witness = if witness_filename_ext.eq_ignore_ascii_case("JSON") { + Some(witness_from_json_file::(&witness_filename)) + } else { + Some(witness_from_wtns_file::(&witness_filename)) + }; println!("Proving..."); let proof = prove(circuit.clone(), ¶ms, rng).unwrap(); diff --git a/phase2/src/circom_circuit.rs b/phase2/src/circom_circuit.rs index b760dfd3..b8f849fd 100644 --- a/phase2/src/circom_circuit.rs +++ b/phase2/src/circom_circuit.rs @@ -4,13 +4,17 @@ extern crate rand; use std::str; use std::fs; use std::fs::{OpenOptions, File}; -use std::io::{Read, Write}; +use std::io::{Read, Write, Seek, Error, ErrorKind}; use std::collections::BTreeMap; use std::iter::repeat; use std::sync::Arc; use itertools::Itertools; use rand::{Rng, OsRng}; use parameters::MPCParameters; +use byteorder::{LittleEndian, ReadBytesExt}; +use bellman_ce::pairing::ff::Field; +use bellman_ce::pairing::ff::PrimeFieldRepr; +use bellman_ce::pairing::ff::PrimeFieldDecodingError; use bellman_ce::{ Circuit, @@ -329,6 +333,62 @@ pub fn witness_from_json(reader: R) -> Vec{ return witness.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::>(); } +pub fn witness_from_wtns_file(filename: &str) -> Vec { + let mut reader = OpenOptions::new() + .read(true) + .open(filename) + .expect("unable to open."); + return witness_from_wtns::(&mut reader).unwrap(); +} + +pub fn witness_from_wtns(mut reader: &mut R) -> std::io::Result> { + + let mut magic = [0;4]; + + reader.read(&mut magic)?; + if magic != [b'w',b't',b'n',b's'] { + return Err(Error::new(ErrorKind::InvalidData, "Invalid file type")); + } + + let version = reader.read_u32::()?; + if version > 1 { + return Err(Error::new(ErrorKind::InvalidData, "Version not supported")); + } + + let n8 = reader.read_u32::()?; + if n8 != 32 { + return Err(Error::new(ErrorKind::InvalidData, "Field size is not 256 bits.")); + } + + let mut q: [u64;4] = [0;4]; + for i in 0..4 { + q[i] = reader.read_u64::()?; + } + if q != [ + 0x43e1f593f0000001, + 0x2833e84879b97091, + 0xb85045b68181585d, + 0x30644e72e131a029 + ] + { + return Err(Error::new(ErrorKind::InvalidData, "Circuit not in the bn256 curve field.")); + } + + let n = reader.read_u32::()?; + + let mut res : Vec = Vec::new(); + let mut v = E::Fr::zero().into_repr(); + for _i in 0..n { + v.read_le(&mut reader)?; + match E::Fr::from_repr(v) { + Err(e) => return Err(Error::new(ErrorKind::InvalidData, e)), + Ok(v) => res.push(v), + } + } + + Ok(res) +} + pub fn circuit_from_json_file(filename: &str) -> CircomCircuit:: { let reader = OpenOptions::new() .read(true) @@ -363,3 +423,155 @@ pub fn circuit_from_json(reader: R) -> CircomCircuit:: { pub fn create_rng() -> Box { return Box::new(OsRng::new().unwrap()) } + + + + +fn circuit_from_r1cs_read_header(circuit : &mut CircomCircuit, reader: &mut R) -> std::io::Result<()> { + + let n8 = reader.read_u32::()?; + if n8 != 32 { + return Err(Error::new(ErrorKind::InvalidData, "Field size is not 256 bits.")); + } + + let mut q: [u64;4] = [0;4]; + for i in 0..4 { + q[i] = reader.read_u64::()?; + } + + if q != [ + 0x43e1f593f0000001, + 0x2833e84879b97091, + 0xb85045b68181585d, + 0x30644e72e131a029 + ] + { + return Err(Error::new(ErrorKind::InvalidData, "Circuit not in the bn256 curve field.")); + } + + + let n_vars = (reader.read_u32::()?) as usize; + let n_outputs = (reader.read_u32::()?) as usize; + let n_pub_inputs = (reader.read_u32::()?) as usize; + let _n_prv_inputs = (reader.read_u32::()?) as usize; + let _n_labels = (reader.read_u64::()?) as usize; + let n_constraints = (reader.read_u32::()?) as usize; + + circuit.num_inputs = n_pub_inputs + n_outputs + 1; + circuit.num_aux = n_vars-circuit.num_inputs; + circuit.num_constraints = n_constraints; + + Ok(()) +} + +fn circuit_from_r1cs_read_lc(mut reader: &mut R) -> std::io::Result< Vec<(usize, E::Fr)> > { + let mut lc : Vec<(usize, E::Fr)> = Vec::new(); + let n_coefs = reader.read_u32::()?; + for _i in 0..n_coefs { + let coef_id = (reader.read_u32::()?) as usize; + let mut coef_val = E::Fr::zero().into_repr(); + coef_val.read_le(&mut reader)?; + match E::Fr::from_repr(coef_val) { + Err(e) => return Err(Error::new(ErrorKind::InvalidData, e)), + Ok(v) => lc.push((coef_id, v)), + } + } + + Ok(lc) +} + +fn circuit_from_r1cs_read_constraint(circuit : &mut CircomCircuit, mut reader: &mut R) -> std::io::Result<()> { + let lc_a = circuit_from_r1cs_read_lc::(&mut reader)?; + let lc_b = circuit_from_r1cs_read_lc::(&mut reader)?; + let lc_c = circuit_from_r1cs_read_lc::(&mut reader)?; + + circuit.constraints.push((lc_a, lc_b, lc_c)); + + Ok(()) +} + +fn circuit_from_r1cs_read_constraints(mut circuit : &mut CircomCircuit, mut reader: &mut R) -> std::io::Result<()> { + for _i in 0..circuit.num_constraints { + circuit_from_r1cs_read_constraint::(&mut circuit, &mut reader)?; + } + Ok(()) +} + + + +pub fn circuit_from_r1cs(mut reader: R) -> std::io::Result> { + let mut magic = [0;4]; + let mut circuit= CircomCircuit { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + witness: None, + constraints: Vec::new() + }; + let mut pos :i64 = 0; + + reader.read(&mut magic)?; + pos +=4; + + if magic != [b'r',b'1',b'c',b's'] { + return Err(Error::new(ErrorKind::InvalidData, "Invalid file type")); + } + + + let version = reader.read_u32::()?; + pos += 4; + if version > 1 { + return Err(Error::new(ErrorKind::InvalidData, "Version not supported")); + } + + let mut header_pos: Option = None; + let mut constraints_pos: Option = None; + + let n_sections = reader.read_u32::()?; + pos += 4; + for _i in 0..n_sections { + let section_type = reader.read_u32::()?; + pos += 4; + let section_len = (reader.read_u64::()?) as i64; + pos += 8; + match section_type { + 1 => match header_pos { + None => header_pos = Some(pos), + Some(_) => return Err(Error::new(ErrorKind::InvalidData, "2 Headers sections in the file")) + }, + 2 => match constraints_pos { + None => constraints_pos = Some(pos), + Some(_) => return Err(Error::new(ErrorKind::InvalidData, "2 Constraints sections in the file")) + }, + _ => () + } + reader.seek(std::io::SeekFrom::Current(section_len))?; + pos = pos + section_len; + } + + match header_pos { + Some(p) => { + reader.seek(std::io::SeekFrom::Start(p as u64))?; + circuit_from_r1cs_read_header(&mut circuit, &mut reader)?; + }, + None => return Err(Error::new(ErrorKind::InvalidData, "No header section")) + } + + match constraints_pos { + Some(p) => { + reader.seek(std::io::SeekFrom::Start(p as u64))?; + circuit_from_r1cs_read_constraints(&mut circuit, &mut reader)?; + }, + None => return Err(Error::new(ErrorKind::InvalidData, "No constraints section")) + } + + Ok(circuit) +} + +pub fn circuit_from_r1cs_file(filename: &str) -> CircomCircuit:: { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .expect("unable to open."); + return circuit_from_r1cs(reader).unwrap(); +} \ No newline at end of file From 568d9dfa9e7cbab7ff784eaf992134172e155aba Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Thu, 18 Jun 2020 09:52:02 +0200 Subject: [PATCH 2/4] Update also phase2 verify_contribution --- phase2/src/bin/verify_contribution.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/phase2/src/bin/verify_contribution.rs b/phase2/src/bin/verify_contribution.rs index 4a4675b4..974d2572 100644 --- a/phase2/src/bin/verify_contribution.rs +++ b/phase2/src/bin/verify_contribution.rs @@ -5,6 +5,7 @@ use std::fs::OpenOptions; use phase2::parameters::*; use phase2::circom_circuit::circuit_from_json_file; +use phase2::circom_circuit::circuit_from_r1cs_file; fn main() { let args: Vec = std::env::args().collect(); @@ -13,6 +14,11 @@ fn main() { std::process::exit(exitcode::USAGE); } let circuit_filename = &args[1]; + let circuit_filename_ext = match std::path::Path::new(circuit_filename).extension() { + Some(os) => os.to_str().unwrap(), + None => "" + }; + let old_params_filename = &args[2]; let new_params_filename = &args[3]; let radix_directory = &args[4]; @@ -35,7 +41,12 @@ fn main() { let contribution = verify_contribution(&old_params, &new_params).expect("should verify"); let should_filter_points_at_infinity = false; - let verification_result = new_params.verify(circuit_from_json_file(&circuit_filename), should_filter_points_at_infinity, radix_directory).unwrap(); + let c = if circuit_filename_ext.eq_ignore_ascii_case("JSON") { + circuit_from_json_file(&circuit_filename) + } else { + circuit_from_r1cs_file(&circuit_filename) + }; + let verification_result = new_params.verify(c, should_filter_points_at_infinity, radix_directory).unwrap(); assert!(contains_contribution(&verification_result, &contribution)); println!("Contribution {} verified.", new_params_filename); } From e30840a40fb7b1fc6986f0f3b37bcb35890e4709 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Sat, 20 Jun 2020 13:04:43 +0200 Subject: [PATCH 3/4] Add link to r1csFile and fix the command line explanation in phase2/bin/verify_contribution --- phase2/src/bin/verify_contribution.rs | 2 +- phase2/src/circom_circuit.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phase2/src/bin/verify_contribution.rs b/phase2/src/bin/verify_contribution.rs index 974d2572..54301233 100644 --- a/phase2/src/bin/verify_contribution.rs +++ b/phase2/src/bin/verify_contribution.rs @@ -10,7 +10,7 @@ use phase2::circom_circuit::circuit_from_r1cs_file; fn main() { let args: Vec = std::env::args().collect(); if args.len() != 5 { - println!("Usage: \n "); + println!("Usage: \n> "); std::process::exit(exitcode::USAGE); } let circuit_filename = &args[1]; diff --git a/phase2/src/circom_circuit.rs b/phase2/src/circom_circuit.rs index b8f849fd..d28aeb8d 100644 --- a/phase2/src/circom_circuit.rs +++ b/phase2/src/circom_circuit.rs @@ -426,7 +426,7 @@ pub fn create_rng() -> Box { - +// For the format specification see: https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md fn circuit_from_r1cs_read_header(circuit : &mut CircomCircuit, reader: &mut R) -> std::io::Result<()> { let n8 = reader.read_u32::()?; From 221a21956b628ad6942ebf9bc5b0bd4b144b5878 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Wed, 27 Jan 2021 17:50:13 +0100 Subject: [PATCH 4/4] Adapt to wtns version 2 --- phase2/src/circom_circuit.rs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/phase2/src/circom_circuit.rs b/phase2/src/circom_circuit.rs index d28aeb8d..4783f906 100644 --- a/phase2/src/circom_circuit.rs +++ b/phase2/src/circom_circuit.rs @@ -342,7 +342,7 @@ pub fn witness_from_wtns_file(filename: &str) -> Vec { } pub fn witness_from_wtns(mut reader: &mut R) -> std::io::Result> { - + let mut magic = [0;4]; reader.read(&mut magic)?; @@ -351,10 +351,15 @@ pub fn witness_from_wtns(mut reader: &mut R) -> std::io::Res } let version = reader.read_u32::()?; - if version > 1 { + if version > 2 { return Err(Error::new(ErrorKind::InvalidData, "Version not supported")); } + let _ = reader.read_u32::()?; + + let _ = reader.read_u32::()?; + let _ = reader.read_u64::()?; + let n8 = reader.read_u32::()?; if n8 != 32 { return Err(Error::new(ErrorKind::InvalidData, "Field size is not 256 bits.")); @@ -364,18 +369,21 @@ pub fn witness_from_wtns(mut reader: &mut R) -> std::io::Res for i in 0..4 { q[i] = reader.read_u64::()?; } - if q != [ + if q != [ 0x43e1f593f0000001, 0x2833e84879b97091, 0xb85045b68181585d, - 0x30644e72e131a029 - ] + 0x30644e72e131a029 + ] { return Err(Error::new(ErrorKind::InvalidData, "Circuit not in the bn256 curve field.")); } let n = reader.read_u32::()?; - + + let _ = reader.read_u32::()?; + let _ = reader.read_u64::()?; + let mut res : Vec = Vec::new(); let mut v = E::Fr::zero().into_repr(); for _i in 0..n { @@ -389,6 +397,7 @@ pub fn witness_from_wtns(mut reader: &mut R) -> std::io::Res Ok(res) } + pub fn circuit_from_json_file(filename: &str) -> CircomCircuit:: { let reader = OpenOptions::new() .read(true) @@ -439,12 +448,12 @@ fn circuit_from_r1cs_read_header(circuit : &mut CircomCircuit q[i] = reader.read_u64::()?; } - if q != [ + if q != [ 0x43e1f593f0000001, 0x2833e84879b97091, 0xb85045b68181585d, - 0x30644e72e131a029 - ] + 0x30644e72e131a029 + ] { return Err(Error::new(ErrorKind::InvalidData, "Circuit not in the bn256 curve field.")); } @@ -476,7 +485,7 @@ fn circuit_from_r1cs_read_lc(mut reader: &mut R) -> std::io:: Ok(v) => lc.push((coef_id, v)), } } - + Ok(lc) } @@ -554,7 +563,7 @@ pub fn circuit_from_r1cs(mut reader: R) -> std::io::Re reader.seek(std::io::SeekFrom::Start(p as u64))?; circuit_from_r1cs_read_header(&mut circuit, &mut reader)?; }, - None => return Err(Error::new(ErrorKind::InvalidData, "No header section")) + None => return Err(Error::new(ErrorKind::InvalidData, "No header section")) } match constraints_pos { @@ -562,7 +571,7 @@ pub fn circuit_from_r1cs(mut reader: R) -> std::io::Re reader.seek(std::io::SeekFrom::Start(p as u64))?; circuit_from_r1cs_read_constraints(&mut circuit, &mut reader)?; }, - None => return Err(Error::new(ErrorKind::InvalidData, "No constraints section")) + None => return Err(Error::new(ErrorKind::InvalidData, "No constraints section")) } Ok(circuit) @@ -574,4 +583,4 @@ pub fn circuit_from_r1cs_file(filename: &str) -> CircomCircuit:: { .open(filename) .expect("unable to open."); return circuit_from_r1cs(reader).unwrap(); -} \ No newline at end of file +}