diff --git a/phase2/src/bin/export_keys.rs b/phase2/src/bin/export_keys.rs index 2241a6c..73a61c3 100644 --- a/phase2/src/bin/export_keys.rs +++ b/phase2/src/bin/export_keys.rs @@ -1,64 +1,11 @@ -extern crate bellman_ce; -extern crate rand; extern crate phase2; extern crate exitcode; -extern crate serde; -extern crate serde_json; -extern crate num_bigint; -extern crate num_traits; -extern crate itertools; -use std::fs; -use std::fs::OpenOptions; -use std::iter::repeat; -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use phase2::parameters::MPCParameters; -use phase2::utils::{ - p1_to_vec, - p2_to_vec, - pairing_to_vec, +use phase2::circom_circuit::{ + proving_key_json_file, + verification_key_json_file, + load_params_file }; -use bellman_ce::pairing::{ - Engine, - bn256::{ - Bn256, - } -}; - -#[derive(Serialize, Deserialize)] -struct ProvingKeyJson { - #[serde(rename = "A")] - pub a: Vec>, - #[serde(rename = "B1")] - pub b1: Vec>, - #[serde(rename = "B2")] - pub b2: Vec>>, - #[serde(rename = "C")] - pub c: Vec>>, - pub vk_alfa_1: Vec, - pub vk_beta_1: Vec, - pub vk_delta_1: Vec, - pub vk_beta_2: Vec>, - pub vk_delta_2: Vec>, - #[serde(rename = "hExps")] - pub h: Vec>, - // Todo: add json fields: nPublic, nVars, polsA, polsB, polsC, protocol: groth -} - -#[derive(Serialize, Deserialize)] -struct VerifyingKeyJson { - #[serde(rename = "IC")] - pub ic: Vec>, - pub vk_alfa_1: Vec, - pub vk_beta_2: Vec>, - pub vk_gamma_2: Vec>, - pub vk_delta_2: Vec>, - pub vk_alfabeta_12: Vec>>, - pub protocol: String, - #[serde(rename = "nPublic")] - pub inputs_count: usize, -} fn main() { let args: Vec = std::env::args().collect(); @@ -69,46 +16,9 @@ fn main() { let params_filename = &args[1]; let vk_filename = &args[2]; let pk_filename = &args[3]; - - let disallow_points_at_infinity = false; - println!("Exporting {}...", params_filename); - - let reader = OpenOptions::new() - .read(true) - .open(params_filename) - .expect("unable to open."); - let params = MPCParameters::read(reader, disallow_points_at_infinity, true).expect("unable to read params"); - let params = params.get_params(); - - let proving_key = ProvingKeyJson { - a: params.a.iter().map(|e| p1_to_vec(e)).collect_vec(), - b1: params.b_g1.iter().map(|e| p1_to_vec(e)).collect_vec(), - b2: params.b_g2.iter().map(|e| p2_to_vec(e)).collect_vec(), - c: repeat(None).take(params.vk.ic.len()).chain(params.l.iter().map(|e| Some(p1_to_vec(e)))).collect_vec(), - vk_alfa_1: p1_to_vec(¶ms.vk.alpha_g1), - vk_beta_1: p1_to_vec(¶ms.vk.beta_g1), - vk_delta_1: p1_to_vec(¶ms.vk.delta_g1), - vk_beta_2: p2_to_vec(¶ms.vk.beta_g2), - vk_delta_2: p2_to_vec(¶ms.vk.delta_g2), - h: params.h.iter().map(|e| p1_to_vec(e)).collect_vec(), - }; - - let verification_key = VerifyingKeyJson { - ic: params.vk.ic.iter().map(|e| p1_to_vec(e)).collect_vec(), - vk_alfa_1: p1_to_vec(¶ms.vk.alpha_g1), - vk_beta_2: p2_to_vec(¶ms.vk.beta_g2), - vk_gamma_2: p2_to_vec(¶ms.vk.gamma_g2), - vk_delta_2: p2_to_vec(¶ms.vk.delta_g2), - vk_alfabeta_12: pairing_to_vec(&Bn256::pairing(params.vk.alpha_g1, params.vk.beta_g2)), - inputs_count: params.vk.ic.len() - 1, - protocol: String::from("groth"), - }; - - let pk_json = serde_json::to_string(&proving_key).unwrap(); - let vk_json = serde_json::to_string(&verification_key).unwrap(); - fs::write(pk_filename, pk_json.as_bytes()).unwrap(); - fs::write(vk_filename, vk_json.as_bytes()).unwrap(); - + let params = load_params_file(params_filename); + proving_key_json_file(¶ms, pk_filename).unwrap(); + verification_key_json_file(¶ms, vk_filename).unwrap(); println!("Created {} and {}.", pk_filename, vk_filename); } diff --git a/phase2/src/bin/generate_verifier.rs b/phase2/src/bin/generate_verifier.rs index bc2bb54..eabb843 100644 --- a/phase2/src/bin/generate_verifier.rs +++ b/phase2/src/bin/generate_verifier.rs @@ -1,26 +1,9 @@ -#![allow(unused_imports)] - extern crate phase2; -extern crate bellman_ce; -extern crate num_bigint; -extern crate num_traits; extern crate exitcode; -extern crate serde; -use std::fmt; -use std::fs; -use std::fs::OpenOptions; -use num_bigint::BigUint; -use num_traits::Num; -use phase2::utils::repr_to_big; -use phase2::parameters::MPCParameters; -use bellman_ce::pairing::{ - Engine, - CurveAffine, - ff::PrimeField, - bn256::{ - Bn256, - } +use phase2::circom_circuit::{ + load_params_file, + create_verifier_sol_file }; fn main() { @@ -31,48 +14,7 @@ fn main() { } let params_filename = &args[1]; let verifier_filename = &args[2]; - - let should_filter_points_at_infinity = false; - let bytes = include_bytes!("../verifier_groth.sol"); - let template = String::from_utf8_lossy(bytes); - - let reader = OpenOptions::new() - .read(true) - .open(params_filename) - .expect("unable to open."); - - let params = MPCParameters::read(reader, should_filter_points_at_infinity, true).expect("unable to read params"); - let vk = ¶ms.get_params().vk; - - let p1_to_str = |p: &::G1Affine| { - let x = repr_to_big(p.get_x().into_repr()); - let y = repr_to_big(p.get_y().into_repr()); - return format!("{}, {}", x, y) - }; - let p2_to_str = |p: &::G2Affine| { - let x = p.get_x(); - let y = p.get_y(); - let x_c0 = repr_to_big(x.c0.into_repr()); - let x_c1 = repr_to_big(x.c1.into_repr()); - let y_c0 = repr_to_big(y.c0.into_repr()); - let y_c1 = repr_to_big(y.c1.into_repr()); - format!("[{}, {}], [{}, {}]", x_c0, x_c1, y_c0, y_c1) - }; - - let template = template.replace("<%vk_alfa1%>", &*p1_to_str(&vk.alpha_g1)); - let template = template.replace("<%vk_beta2%>", &*p2_to_str(&vk.beta_g2)); - let template = template.replace("<%vk_gamma2%>", &*p2_to_str(&vk.gamma_g2)); - let template = template.replace("<%vk_delta2%>", &*p2_to_str(&vk.delta_g2)); - - let template = template.replace("<%vk_ic_length%>", &*vk.ic.len().to_string()); - let template = template.replace("<%vk_input_length%>", &*(vk.ic.len() - 1).to_string()); - - let mut vi = String::from(""); - for i in 0..vk.ic.len() { - vi = format!("{}{}vk.IC[{}] = Pairing.G1Point({});\n", vi, if vi.len() == 0 { "" } else { " " }, i, &*p1_to_str(&vk.ic[i])); - } - let template = template.replace("<%vk_ic_pts%>", &*vi); - - fs::write(verifier_filename, template.as_bytes()).unwrap(); + let params = load_params_file(params_filename); + create_verifier_sol_file(¶ms, verifier_filename).unwrap(); println!("Created {}", verifier_filename); } \ No newline at end of file diff --git a/phase2/src/bin/prove.rs b/phase2/src/bin/prove.rs index 2c32299..48cd492 100644 --- a/phase2/src/bin/prove.rs +++ b/phase2/src/bin/prove.rs @@ -7,27 +7,15 @@ extern crate num_traits; extern crate itertools; use std::fs; -use std::fs::OpenOptions; -use serde::{Deserialize, Serialize}; -use itertools::Itertools; -use phase2::parameters::MPCParameters; -use phase2::circom_circuit::CircomCircuit; -use phase2::utils::{ - repr_to_big, - p1_to_vec, - p2_to_vec, +use bellman_ce::pairing::bn256::Bn256; +use phase2::circom_circuit::{ + CircomCircuit, + load_params_file, + prove, + verify, + create_rng, + proof_to_json_file }; -use bellman_ce::groth16::{prepare_verifying_key, create_random_proof, verify_proof}; -use bellman_ce::pairing::ff::PrimeField; - -#[derive(Serialize, Deserialize)] -struct ProofJson { - pub protocol: String, - pub pi_a: Vec, - pub pi_b: Vec>, - pub pi_c: Vec, -} - fn main() { let args: Vec = std::env::args().collect(); @@ -41,48 +29,21 @@ fn main() { let proof_filename = &args[4]; let public_filename = &args[5]; - let should_filter_points_at_infinity = false; - let rng = &mut rand::XorShiftRng::new_unseeded(); // TODO: change this unsafe unseeded random (!) - - let mut c = CircomCircuit::from_json_file(circuit_filename); - c.load_witness_json_file(witness_filename); - let input = c.inputs.to_vec(); - - let reader = OpenOptions::new() - .read(true) - .open(params_filename) - .expect("unable to open."); - - let mut params = MPCParameters::read(reader, should_filter_points_at_infinity, true).expect("unable to read params"); - - params.filter_params(); - let params = params.get_params(); + let rng = create_rng(); + let params = load_params_file(params_filename); + let mut circuit = CircomCircuit::from_json_file(circuit_filename); + circuit.witness = Some(CircomCircuit::::witness_from_json_file(witness_filename)); println!("Proving..."); - let proof = create_random_proof(c, &*params, rng).unwrap(); + let proof = prove(circuit.clone(), ¶ms, rng).unwrap(); - println!("Checking proof"); - let pvk = prepare_verifying_key(¶ms.vk); - let result = verify_proof( - &pvk, - &proof, - &input[1..] - ).unwrap(); - assert!(result, "Proof is correct"); + println!("Verifying proof"); + let correct = verify(&circuit, ¶ms, &proof).unwrap(); + assert!(correct, "Proof is correct"); - let proof = ProofJson { - protocol: "groth".to_string(), - pi_a: p1_to_vec(&proof.a), - pi_b: p2_to_vec(&proof.b), - pi_c: p1_to_vec(&proof.c), - }; - - let proof_json = serde_json::to_string(&proof).unwrap(); - fs::write(proof_filename, proof_json.as_bytes()).unwrap(); - - let public_inputs = input[1..].iter().map(|x| repr_to_big(x.into_repr())).collect_vec(); - let public_json = serde_json::to_string(&public_inputs).unwrap(); - fs::write(public_filename, public_json.as_bytes()).unwrap(); + println!("Saving {} and {}", proof_filename, public_filename); + proof_to_json_file(&proof, proof_filename).unwrap(); + fs::write(public_filename, circuit.get_public_inputs_json().as_bytes()).unwrap(); println!("Done!") } diff --git a/phase2/src/circom_circuit.rs b/phase2/src/circom_circuit.rs index 00e4c4d..1c37435 100644 --- a/phase2/src/circom_circuit.rs +++ b/phase2/src/circom_circuit.rs @@ -1,23 +1,15 @@ -#![allow(unused_imports)] - extern crate bellman_ce; +extern crate rand; use std::str; use std::fs; use std::fs::OpenOptions; +use std::io::{Read, Write}; use std::collections::BTreeMap; +use std::iter::repeat; use itertools::Itertools; -use std::io::{ - Read, - Write, -}; - -use bellman_ce::pairing::{ - Engine, - ff::{ - PrimeField, - }, -}; +use rand::{Rng, OsRng}; +use parameters::MPCParameters; use bellman_ce::{ Circuit, @@ -26,8 +18,30 @@ use bellman_ce::{ Index, ConstraintSystem, LinearCombination, + groth16::{ + Parameters, + Proof, + prepare_verifying_key, + create_random_proof, + verify_proof, + }, + pairing::{ + Engine, + ff::{ + PrimeField, + }, + bn256::{ + Bn256, + } + } }; +use crate::utils::{ + repr_to_big, + p1_to_vec, + p2_to_vec, + pairing_to_vec, +}; #[derive(Serialize, Deserialize)] struct CircuitJson { @@ -40,13 +54,54 @@ struct CircuitJson { pub num_variables: usize, } +#[derive(Serialize, Deserialize)] +struct ProofJson { + pub protocol: String, + pub pi_a: Vec, + pub pi_b: Vec>, + pub pi_c: Vec, +} + +#[derive(Serialize, Deserialize)] +struct ProvingKeyJson { + #[serde(rename = "A")] + pub a: Vec>, + #[serde(rename = "B1")] + pub b1: Vec>, + #[serde(rename = "B2")] + pub b2: Vec>>, + #[serde(rename = "C")] + pub c: Vec>>, + pub vk_alfa_1: Vec, + pub vk_beta_1: Vec, + pub vk_delta_1: Vec, + pub vk_beta_2: Vec>, + pub vk_delta_2: Vec>, + #[serde(rename = "hExps")] + pub h: Vec>, + // Todo: add json fields: nPublic, nVars, polsA, polsB, polsC, protocol: groth +} + +#[derive(Serialize, Deserialize)] +struct VerifyingKeyJson { + #[serde(rename = "IC")] + pub ic: Vec>, + pub vk_alfa_1: Vec, + pub vk_beta_2: Vec>, + pub vk_gamma_2: Vec>, + pub vk_delta_2: Vec>, + pub vk_alfabeta_12: Vec>>, + pub protocol: String, + #[serde(rename = "nPublic")] + pub inputs_count: usize, +} + #[derive(Clone)] pub struct CircomCircuit { pub num_inputs: usize, pub num_aux: usize, pub num_constraints: usize, - pub inputs: Vec, - pub aux: Vec, + pub witness: Option>, pub constraints: Vec<( Vec<(usize, E::Fr)>, Vec<(usize, E::Fr)>, @@ -55,21 +110,6 @@ pub struct CircomCircuit { } impl<'a, E: Engine> CircomCircuit { - pub fn load_witness_json_file(&mut self, filename: &str) { - let reader = OpenOptions::new() - .read(true) - .open(filename) - .expect("unable to open."); - self.load_witness_json(reader); - } - - pub fn load_witness_json(&mut self, reader: R) { - let witness: Vec = serde_json::from_reader(reader).unwrap(); - let witness = witness.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::>(); - self.inputs = witness[..self.num_inputs].to_vec(); - self.aux = witness[self.num_inputs..].to_vec(); - } - pub fn from_json_file(filename: &str) -> CircomCircuit:: { let reader = OpenOptions::new() .read(true) @@ -96,11 +136,39 @@ impl<'a, E: Engine> CircomCircuit { num_inputs: num_inputs, num_aux: num_aux, num_constraints: circuit_json.num_variables, - inputs: vec![], - aux: vec![], + witness: None, constraints: constraints, }; } + + pub fn witness_from_json_file(filename: &str) -> Vec { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .expect("unable to open."); + return CircomCircuit::::witness_from_json(reader); + } + + pub fn witness_from_json(reader: R) -> Vec{ + let witness: Vec = serde_json::from_reader(reader).unwrap(); + return witness.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::>(); + } + + pub fn get_public_inputs(&self) -> Option> { + return match self.witness.clone() { + None => None, + Some(w) => Some(w[1..self.num_inputs].to_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.into_repr())).collect_vec() + }; + return serde_json::to_string(&inputs).unwrap(); + } } /// Our demo circuit implements this `Circuit` trait which @@ -112,17 +180,24 @@ impl<'a, E: Engine> Circuit for CircomCircuit { cs: &mut CS ) -> Result<(), SynthesisError> { + let witness = &self.witness.clone(); for i in 1..self.num_inputs { cs.alloc_input(|| format!("variable {}", i), || { - Ok(if self.inputs.len() > 0 { self.inputs[i] } else { E::Fr::from_str("1").unwrap() }) + Ok(match witness { + None => E::Fr::from_str("1").unwrap(), + Some(w) => w[i], + }) })?; } for i in 0..self.num_aux { cs.alloc(|| format!("aux {}", i), || { - Ok(if self.aux.len() > 0 { self.aux[i] } else { E::Fr::from_str("1").unwrap() }) + Ok(match witness { + None => E::Fr::from_str("1").unwrap(), + Some(w) => w[i + self.num_inputs], + }) })?; } @@ -147,3 +222,133 @@ impl<'a, E: Engine> Circuit for CircomCircuit { Ok(()) } } + +pub fn prove(circuit: CircomCircuit, params: &Parameters, mut rng: R) -> Result, SynthesisError> { + return create_random_proof(circuit, params, &mut rng); +} + +pub fn verify(circuit: &CircomCircuit, params: &Parameters, proof: &Proof) -> Result { + let inputs = match circuit.get_public_inputs() { + None => return Err(SynthesisError::AssignmentMissing), + Some(inp) => inp, + }; + return verify_proof( + &prepare_verifying_key(¶ms.vk), + proof, + &inputs + ); +} + +pub fn create_verifier_sol(params: &Parameters) -> String { + // TODO: use a simple template engine + let bytes = include_bytes!("verifier_groth.sol"); + let template = String::from_utf8_lossy(bytes); + + let p1_to_str = |p: &::G1Affine| { + let x = repr_to_big(p.get_x().into_repr()); + let y = repr_to_big(p.get_y().into_repr()); + return format!("{}, {}", x, y) + }; + let p2_to_str = |p: &::G2Affine| { + let x = p.get_x(); + let y = p.get_y(); + let x_c0 = repr_to_big(x.c0.into_repr()); + let x_c1 = repr_to_big(x.c1.into_repr()); + let y_c0 = repr_to_big(y.c0.into_repr()); + let y_c1 = repr_to_big(y.c1.into_repr()); + format!("[{}, {}], [{}, {}]", x_c0, x_c1, y_c0, y_c1) + }; + + let template = template.replace("<%vk_alfa1%>", &*p1_to_str(¶ms.vk.alpha_g1)); + let template = template.replace("<%vk_beta2%>", &*p2_to_str(¶ms.vk.beta_g2)); + let template = template.replace("<%vk_gamma2%>", &*p2_to_str(¶ms.vk.gamma_g2)); + let template = template.replace("<%vk_delta2%>", &*p2_to_str(¶ms.vk.delta_g2)); + + let template = template.replace("<%vk_ic_length%>", &*params.vk.ic.len().to_string()); + let template = template.replace("<%vk_input_length%>", &*(params.vk.ic.len() - 1).to_string()); + + let mut vi = String::from(""); + for i in 0..params.vk.ic.len() { + vi = format!("{}{}vk.IC[{}] = Pairing.G1Point({});\n", vi, if vi.len() == 0 { "" } else { " " }, i, &*p1_to_str(¶ms.vk.ic[i])); + } + let template = template.replace("<%vk_ic_pts%>", &*vi); + + return template; +} + +pub fn create_verifier_sol_file(params: &Parameters, filename: &str) -> std::io::Result<()> { + return fs::write(filename, create_verifier_sol(params).as_bytes()); +} + +pub fn proof_to_json(proof: &Proof) -> Result { + return serde_json::to_string(&ProofJson { + protocol: "groth".to_string(), + pi_a: p1_to_vec(&proof.a), + pi_b: p2_to_vec(&proof.b), + pi_c: p1_to_vec(&proof.c), + }); +} + +pub fn proof_to_json_file(proof: &Proof, filename: &str) -> std::io::Result<()> { + let str = proof_to_json(proof).unwrap(); // TODO: proper error handling + return fs::write(filename, str.as_bytes()); +} + +pub fn load_params_file(filename: &str) -> Parameters { + let reader = OpenOptions::new() + .read(true) + .open(filename) + .expect("unable to open."); + return load_params(reader); +} + +pub fn load_params(reader: R) -> Parameters { + let should_filter_points_at_infinity = false; + let mut params = MPCParameters::read(reader, should_filter_points_at_infinity, true).expect("unable to read params"); + params.filter_params(); + return params.get_params().clone(); +} + +pub fn proving_key_json(params: &Parameters) -> Result { + let proving_key = ProvingKeyJson { + a: params.a.iter().map(|e| p1_to_vec(e)).collect_vec(), + b1: params.b_g1.iter().map(|e| p1_to_vec(e)).collect_vec(), + b2: params.b_g2.iter().map(|e| p2_to_vec(e)).collect_vec(), + c: repeat(None).take(params.vk.ic.len()).chain(params.l.iter().map(|e| Some(p1_to_vec(e)))).collect_vec(), + vk_alfa_1: p1_to_vec(¶ms.vk.alpha_g1), + vk_beta_1: p1_to_vec(¶ms.vk.beta_g1), + vk_delta_1: p1_to_vec(¶ms.vk.delta_g1), + vk_beta_2: p2_to_vec(¶ms.vk.beta_g2), + vk_delta_2: p2_to_vec(¶ms.vk.delta_g2), + h: params.h.iter().map(|e| p1_to_vec(e)).collect_vec(), + }; + return serde_json::to_string(&proving_key); +} + +pub fn proving_key_json_file(params: &Parameters, filename: &str) -> std::io::Result<()> { + let str = proving_key_json(params).unwrap(); // TODO: proper error handling + return fs::write(filename, str.as_bytes()); +} + +pub fn verification_key_json(params: &Parameters) -> Result { + let verification_key = VerifyingKeyJson { + ic: params.vk.ic.iter().map(|e| p1_to_vec(e)).collect_vec(), + vk_alfa_1: p1_to_vec(¶ms.vk.alpha_g1), + vk_beta_2: p2_to_vec(¶ms.vk.beta_g2), + vk_gamma_2: p2_to_vec(¶ms.vk.gamma_g2), + vk_delta_2: p2_to_vec(¶ms.vk.delta_g2), + vk_alfabeta_12: pairing_to_vec(&Bn256::pairing(params.vk.alpha_g1, params.vk.beta_g2)), + inputs_count: params.vk.ic.len() - 1, + protocol: String::from("groth"), + }; + return serde_json::to_string(&verification_key); +} + +pub fn verification_key_json_file(params: &Parameters, filename: &str) -> std::io::Result<()> { + let str = verification_key_json(params).unwrap(); // TODO: proper error handling + return fs::write(filename, str.as_bytes()); +} + +pub fn create_rng() -> Box { + return Box::new(OsRng::new().unwrap()) +} \ No newline at end of file diff --git a/phase2/src/utils.rs b/phase2/src/utils.rs index 818f565..56200c8 100644 --- a/phase2/src/utils.rs +++ b/phase2/src/utils.rs @@ -16,9 +16,7 @@ use bellman_ce::pairing::{ CurveAffine, CurveProjective, Wnaf, - Engine, bn256::{ - Bn256, G2, G1Affine, G2Affine,