Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Circom 0.5 binary formats support #17

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode

6 changes: 5 additions & 1 deletion phase2/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ phase1radix2m*
/*.json
/*.bin
/*.params
/verifier.sol
/*.r1cs
/*.wasm
/*.sym
/verifier.sol
/build/
13 changes: 11 additions & 2 deletions phase2/src/bin/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = std::env::args().collect();
if args.len() != 4 {
println!("Usage: \n<in_circuit.json> <out_params.params> <path/to/phase1radix>");
println!("Usage: \n<in_circuit.<r1cs|json>> <out_params.params> <path/to/phase1radix>");
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];

Expand All @@ -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()
};

Expand Down
26 changes: 22 additions & 4 deletions phase2/src/bin/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,43 @@ 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<String> = std::env::args().collect();
if args.len() != 6 {
println!("Usage: \n<circuit.json> <witness.json> <params> <proof.json> <public.json>");
println!("Usage: \n<circuit.<r1cs|json>> <witness.<wtns|json>> <params> <proof.json> <public.json>");
std::process::exit(exitcode::USAGE);
}
let circuit_filename = &args[1];
let witness_filename = &args[2];
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::<Bn256>(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::<Bn256>(&witness_filename))
} else {
Some(witness_from_wtns_file::<Bn256>(&witness_filename))
};

println!("Proving...");
let proof = prove(circuit.clone(), &params, rng).unwrap();
Expand Down
15 changes: 13 additions & 2 deletions phase2/src/bin/verify_contribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ 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<String> = std::env::args().collect();
if args.len() != 5 {
println!("Usage: \n<in_circuit.json> <in_old_params.params> <in_new_params.params> <path/to/phase1radix>");
println!("Usage: \n<in_circuit.<r1cs|json>> <in_old_params.params> <in_new_params.params> <path/to/phase1radix>");
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];
Expand All @@ -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);
}
223 changes: 222 additions & 1 deletion phase2/src/circom_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -329,6 +333,71 @@ pub fn witness_from_json<E: Engine, R: Read>(reader: R) -> Vec<E::Fr>{
return witness.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::<Vec<E::Fr>>();
}

pub fn witness_from_wtns_file<E: Engine>(filename: &str) -> Vec<E::Fr> {
let mut reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
return witness_from_wtns::<E, File>(&mut reader).unwrap();
}

pub fn witness_from_wtns<E: Engine, R: Read>(mut reader: &mut R) -> std::io::Result<Vec<E::Fr>> {

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::<LittleEndian>()?;
if version > 2 {
return Err(Error::new(ErrorKind::InvalidData, "Version not supported"));
}

let _ = reader.read_u32::<LittleEndian>()?;

let _ = reader.read_u32::<LittleEndian>()?;
let _ = reader.read_u64::<LittleEndian>()?;

let n8 = reader.read_u32::<LittleEndian>()?;
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::<LittleEndian>()?;
}
if q != [
0x43e1f593f0000001,
0x2833e84879b97091,
0xb85045b68181585d,
0x30644e72e131a029
]
{
return Err(Error::new(ErrorKind::InvalidData, "Circuit not in the bn256 curve field."));
}

let n = reader.read_u32::<LittleEndian>()?;

let _ = reader.read_u32::<LittleEndian>()?;
let _ = reader.read_u64::<LittleEndian>()?;

let mut res : Vec<E::Fr> = 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<E: Engine>(filename: &str) -> CircomCircuit::<E> {
let reader = OpenOptions::new()
.read(true)
Expand Down Expand Up @@ -363,3 +432,155 @@ pub fn circuit_from_json<E: Engine, R: Read>(reader: R) -> CircomCircuit::<E> {
pub fn create_rng() -> Box<dyn Rng> {
return Box::new(OsRng::new().unwrap())
}



// For the format specification see: https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md
fn circuit_from_r1cs_read_header<E: Engine, R:Read>(circuit : &mut CircomCircuit<E>, reader: &mut R) -> std::io::Result<()> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a link to the format?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


let n8 = reader.read_u32::<LittleEndian>()?;
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::<LittleEndian>()?;
}

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::<LittleEndian>()?) as usize;
let n_outputs = (reader.read_u32::<LittleEndian>()?) as usize;
let n_pub_inputs = (reader.read_u32::<LittleEndian>()?) as usize;
let _n_prv_inputs = (reader.read_u32::<LittleEndian>()?) as usize;
let _n_labels = (reader.read_u64::<LittleEndian>()?) as usize;
let n_constraints = (reader.read_u32::<LittleEndian>()?) 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<E: Engine, R:Read>(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::<LittleEndian>()?;
for _i in 0..n_coefs {
let coef_id = (reader.read_u32::<LittleEndian>()?) 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<E: Engine, R:Read>(circuit : &mut CircomCircuit<E>, mut reader: &mut R) -> std::io::Result<()> {
let lc_a = circuit_from_r1cs_read_lc::<E,R>(&mut reader)?;
let lc_b = circuit_from_r1cs_read_lc::<E,R>(&mut reader)?;
let lc_c = circuit_from_r1cs_read_lc::<E,R>(&mut reader)?;

circuit.constraints.push((lc_a, lc_b, lc_c));

Ok(())
}

fn circuit_from_r1cs_read_constraints<E: Engine, R:Read>(mut circuit : &mut CircomCircuit<E>, mut reader: &mut R) -> std::io::Result<()> {
for _i in 0..circuit.num_constraints {
circuit_from_r1cs_read_constraint::<E,R>(&mut circuit, &mut reader)?;
}
Ok(())
}



pub fn circuit_from_r1cs<E: Engine, R:Read + Seek>(mut reader: R) -> std::io::Result<CircomCircuit::<E>> {
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::<LittleEndian>()?;
pos += 4;
if version > 1 {
return Err(Error::new(ErrorKind::InvalidData, "Version not supported"));
}

let mut header_pos: Option<i64> = None;
let mut constraints_pos: Option<i64> = None;

let n_sections = reader.read_u32::<LittleEndian>()?;
pos += 4;
for _i in 0..n_sections {
let section_type = reader.read_u32::<LittleEndian>()?;
pos += 4;
let section_len = (reader.read_u64::<LittleEndian>()?) 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<E: Engine>(filename: &str) -> CircomCircuit::<E> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
return circuit_from_r1cs(reader).unwrap();
}