Points at infinity checking can be toggled + beacon cli arguments

This commit is contained in:
Brechtpd 2019-12-05 23:17:06 +01:00
parent 29b4e1ddee
commit 276e00c9e4
9 changed files with 32 additions and 430 deletions

@ -286,6 +286,7 @@ impl<E: Engine> Parameters<E> {
pub fn read<R: Read>( pub fn read<R: Read>(
mut reader: R, mut reader: R,
disallow_points_at_infinity: bool,
checked: bool checked: bool
) -> io::Result<Self> ) -> io::Result<Self>
{ {
@ -301,7 +302,7 @@ impl<E: Engine> Parameters<E> {
.into_affine_unchecked() .into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if /*e.is_zero()*/false { .and_then(|e| if disallow_points_at_infinity && e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else { } else {
Ok(e) Ok(e)
@ -320,7 +321,7 @@ impl<E: Engine> Parameters<E> {
.into_affine_unchecked() .into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if /*e.is_zero()*/false { .and_then(|e| if disallow_points_at_infinity && e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else { } else {
Ok(e) Ok(e)

@ -24,4 +24,4 @@ num-bigint = "0.2.3"
num-traits = "0.2.8" num-traits = "0.2.8"
itertools = "0.8.1" itertools = "0.8.1"
rust-crypto = "0.2" rust-crypto = "0.2"
hex-literal = "0.1" hex = "0.4.0"

@ -8,23 +8,26 @@ extern crate byteorder;
extern crate exitcode; extern crate exitcode;
extern crate itertools; extern crate itertools;
extern crate crypto; extern crate crypto;
extern crate hex;
use itertools::Itertools; use itertools::Itertools;
use std::fs::File; use std::fs::File;
use std::fs::OpenOptions; use std::fs::OpenOptions;
#[macro_use]
extern crate hex_literal;
fn main() { fn main() {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
if args.len() != 3 { if args.len() != 5 {
println!("Usage: \n<in_params.params> <out_params.params>"); println!("Usage: \n<in_params.params> <in_beacon_hash> <in_num_iterations_exp> <out_params.params>");
std::process::exit(exitcode::USAGE); std::process::exit(exitcode::USAGE);
} }
let in_params_filename = &args[1]; let in_params_filename = &args[1];
let out_params_filename = &args[2]; let beacon_hash = &args[2];
let num_iterations_exp = &args[3].parse::<usize>().unwrap();
let out_params_filename = &args[4];
let disallow_points_at_infinity = false;
// Create an RNG based on the outcome of the random beacon // Create an RNG based on the outcome of the random beacon
let mut rng = { let mut rng = {
@ -34,18 +37,17 @@ fn main() {
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use crypto::digest::Digest; use crypto::digest::Digest;
// Place block hash here (block number #564321) // The hash used for the beacon
let mut cur_hash: [u8; 32] = hex!("0000000000000000000a558a61ddc8ee4e488d647a747fe4dcc362fe2026c620"); let mut cur_hash = hex::decode(beacon_hash).unwrap();
// Performs 2^n hash iterations over it // Performs 2^n hash iterations over it
const N: usize = 10; let n: usize = *num_iterations_exp;
for i in 0..(1u64<<N) { for i in 0..(1u64<<n) {
// Print 1024 of the interstitial states // Print 1024 of the interstitial states
// so that verification can be // so that verification can be
// parallelized // parallelized
if i % (1u64<<(N-10)) == 0 { if i % (1u64<<(n-10)) == 0 {
print!("{}: ", i); print!("{}: ", i);
for b in cur_hash.iter() { for b in cur_hash.iter() {
print!("{:02x}", b); print!("{:02x}", b);
@ -80,7 +82,7 @@ fn main() {
.read(true) .read(true)
.open(in_params_filename) .open(in_params_filename)
.expect("unable to open."); .expect("unable to open.");
let mut params = phase2::MPCParameters::read(reader, true).expect("unable to read params"); let mut params = phase2::MPCParameters::read(reader, disallow_points_at_infinity, true).expect("unable to read params");
println!("Contributing to {}...", in_params_filename); println!("Contributing to {}...", in_params_filename);
let hash = params.contribute(&mut rng); let hash = params.contribute(&mut rng);

@ -1,409 +0,0 @@
extern crate bellman_ce;
extern crate rand;
extern crate phase2;
extern crate memmap;
extern crate num_bigint;
extern crate num_traits;
#[macro_use]
extern crate serde;
extern crate serde_json;
use serde::{Deserialize, Serialize};
use num_bigint::BigUint;
use num_traits::Num;
// For randomness (during paramgen and proof generation)
use rand::{thread_rng, ChaChaRng, Rng};
// For benchmarking
use std::time::{Duration, Instant};
use std::str;
use std::fs::File;
use std::fs::{OpenOptions, remove_file};
use std::io::Write;
use std::ops::DerefMut;
#[derive(Serialize, Deserialize)]
struct ProvingKeyJson {
#[serde(rename = "A")]
pub a: Vec<Vec<String>>,
#[serde(rename = "B1")]
pub b1: Vec<Vec<String>>,
#[serde(rename = "B2")]
pub b2: Vec<Vec<Vec<String>>>,
#[serde(rename = "C")]
pub c: Vec<Option<Vec<String>>>,
pub vk_alfa_1: Vec<String>,
pub vk_beta_1: Vec<String>,
pub vk_delta_1: Vec<String>,
pub vk_beta_2: Vec<Vec<String>>,
pub vk_delta_2: Vec<Vec<String>>,
#[serde(rename = "hExps")]
pub h: Vec<Vec<String>>,
}
#[derive(Serialize, Deserialize)]
struct VerifyingKeyJson {
#[serde(rename = "IC")]
pub ic: Vec<Vec<String>>,
pub vk_alfa_1: Vec<String>,
pub vk_beta_2: Vec<Vec<String>>,
pub vk_gamma_2: Vec<Vec<String>>,
pub vk_delta_2: Vec<Vec<String>>,
}
// Bring in some tools for using pairing-friendly curves
use bellman_ce::pairing::{
Engine,
CurveAffine,
ff::{Field, PrimeField},
};
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
use bellman_ce::pairing::bn256::{
Bn256,
};
// We'll use these interfaces to construct our circuit.
use bellman_ce::{
Circuit,
Variable,
Index,
LinearCombination,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system.
use bellman_ce::groth16::{
Proof,
prepare_verifying_key,
create_random_proof,
verify_proof,
};
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize)]
struct CircuitJson {
pub constraints: Vec<Vec<BTreeMap<String, String>>>,
#[serde(rename = "nPubInputs")]
pub num_inputs: usize,
#[serde(rename = "nOutputs")]
pub num_outputs: usize,
#[serde(rename = "nVars")]
pub num_variables: usize,
}
struct CircomCircuit<'a> {
pub file_name: &'a str,
pub witness: Vec<String>,
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for CircomCircuit<'a> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let mmap = unsafe { memmap::Mmap::map(&File::open(self.file_name)?) }?;
let content = str::from_utf8(&mmap).unwrap();
let circuit_json: CircuitJson = serde_json::from_str(&content).unwrap();
let num_public_inputs = circuit_json.num_inputs + circuit_json.num_outputs + 1;
println!("num public inputs: {}", num_public_inputs);
for i in 1..circuit_json.num_variables {
if i < num_public_inputs {
//println!("allocating public input {}", i);
cs.alloc_input(|| format!("variable {}", i), || {
println!("variable {}: {}", i, &self.witness[i]);
Ok(E::Fr::from_str(&self.witness[i]).unwrap())
});
} else {
//println!("allocating private input {}", i);
cs.alloc(|| format!("variable {}", i), || {
println!("variable {}: {}", i, &self.witness[i]);
Ok(E::Fr::from_str(&self.witness[i]).unwrap())
});
}
}
let mut constrained: BTreeMap<usize, bool> = BTreeMap::new();
let mut constraint_num = 0;
for (i, constraint) in circuit_json.constraints.iter().enumerate() {
let mut lcs = vec![];
for lc_description in constraint {
let mut lc = LinearCombination::<E>::zero();
//println!("lc_description: {:?}, i: {}, len: {}", lc_description, i, constraint.len());
for (var_index_str, coefficient_str) in lc_description {
//println!("var_index_str: {}, coefficient_str: {}", var_index_str, coefficient_str);
let var_index_num: usize = var_index_str.parse().unwrap();
let var_index = if var_index_num < num_public_inputs {
Index::Input(var_index_num)
} else {
Index::Aux(var_index_num - num_public_inputs)
};
constrained.insert(var_index_num, true);
if i == 2 {
lc = lc + (E::Fr::from_str(coefficient_str).unwrap(), Variable::new_unchecked(var_index));
} else {
lc = lc + (E::Fr::from_str(coefficient_str).unwrap(), Variable::new_unchecked(var_index));
}
}
lcs.push(lc);
}
cs.enforce(|| format!("constraint {}", constraint_num), |_| lcs[0].clone(), |_| lcs[1].clone(), |_| lcs[2].clone());
constraint_num += 1;
}
println!("contraints: {}", circuit_json.constraints.len());
let mut unconstrained: BTreeMap<usize, bool> = BTreeMap::new();
for i in 0..circuit_json.num_variables {
if !constrained.contains_key(&i) {
unconstrained.insert(i, true);
}
}
for (i, _) in unconstrained {
println!("variable {} is unconstrained", i);
}
Ok(())
}
}
fn main() {
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
//let rng = &mut thread_rng();
let mut rng = ChaChaRng::new_unseeded();
rng.set_counter(0u64, 1234567890u64);
let rng = &mut rng;
println!("Creating parameters...");
let should_filter_points_at_infinity = false;
let file_name = "circuit.json";
let mmap = unsafe { memmap::Mmap::map(&File::open("witness.json").unwrap()) }.unwrap();
let content = str::from_utf8(&mmap).unwrap();
let witness: Vec<String> = serde_json::from_str(&content).unwrap();
// Create parameters for our circuit
let mut params = {
let c = CircomCircuit {
file_name,
witness: witness.clone(),
};
phase2::MPCParameters::new(c, should_filter_points_at_infinity).unwrap()
};
let old_params = params.clone();
params.contribute(rng);
let first_contrib = phase2::verify_contribution(&old_params, &params).expect("should verify");
let old_params = params.clone();
params.contribute(rng);
let second_contrib = phase2::verify_contribution(&old_params, &params).expect("should verify");
let verification_result = params.verify(CircomCircuit {
file_name,
witness: witness.clone(),
}, should_filter_points_at_infinity).unwrap();
assert!(phase2::contains_contribution(&verification_result, &first_contrib));
assert!(phase2::contains_contribution(&verification_result, &second_contrib));
let params = params.get_params();
let mut f = File::create("circom.params").unwrap();
params.write(&mut f);
let mut proving_key = ProvingKeyJson {
a: vec![],
b1: vec![],
b2: vec![],
c: vec![],
vk_alfa_1: vec![],
vk_beta_1: vec![],
vk_delta_1: vec![],
vk_beta_2: vec![],
vk_delta_2: vec![],
h: vec![],
};
let repr_to_big = |r| {
BigUint::from_str_radix(&format!("{}", r)[2..], 16).unwrap().to_str_radix(10)
};
let p1_to_vec = |p : &<Bn256 as Engine>::G1Affine| {
let mut v = vec![];
let x = repr_to_big(p.get_x().into_repr());
v.push(x);
let y = repr_to_big(p.get_y().into_repr());
v.push(y);
if p.is_zero() {
v.push("0".to_string());
} else {
v.push("1".to_string());
}
v
};
let p2_to_vec = |p : &<Bn256 as Engine>::G2Affine| {
let mut v = vec![];
let x = p.get_x();
let mut x_v = vec![];
x_v.push(repr_to_big(x.c0.into_repr()));
x_v.push(repr_to_big(x.c1.into_repr()));
v.push(x_v);
let y = p.get_y();
let mut y_v = vec![];
y_v.push(repr_to_big(y.c0.into_repr()));
y_v.push(repr_to_big(y.c1.into_repr()));
v.push(y_v);
if p.is_zero() {
v.push(["0".to_string(), "0".to_string()].to_vec());
} else {
v.push(["1".to_string(), "0".to_string()].to_vec());
}
v
};
let a = params.a.clone();
for e in a.iter() {
proving_key.a.push(p1_to_vec(e));
}
let b1 = params.b_g1.clone();
for e in b1.iter() {
proving_key.b1.push(p1_to_vec(e));
}
let b2 = params.b_g2.clone();
for e in b2.iter() {
proving_key.b2.push(p2_to_vec(e));
}
let c = params.l.clone();
for _ in 0..params.vk.ic.len() {
proving_key.c.push(None);
}
for e in c.iter() {
proving_key.c.push(Some(p1_to_vec(e)));
}
let vk_alfa_1 = params.vk.alpha_g1.clone();
proving_key.vk_alfa_1 = p1_to_vec(&vk_alfa_1);
let vk_beta_1 = params.vk.beta_g1.clone();
proving_key.vk_beta_1 = p1_to_vec(&vk_beta_1);
let vk_delta_1 = params.vk.delta_g1.clone();
proving_key.vk_delta_1 = p1_to_vec(&vk_delta_1);
let vk_beta_2 = params.vk.beta_g2.clone();
proving_key.vk_beta_2 = p2_to_vec(&vk_beta_2);
let vk_delta_2 = params.vk.delta_g2.clone();
proving_key.vk_delta_2 = p2_to_vec(&vk_delta_2);
let h = params.h.clone();
for e in h.iter() {
proving_key.h.push(p1_to_vec(e));
}
let mut verification_key = VerifyingKeyJson {
ic: vec![],
vk_alfa_1: vec![],
vk_beta_2: vec![],
vk_gamma_2: vec![],
vk_delta_2: vec![],
};
let ic = params.vk.ic.clone();
for e in ic.iter() {
verification_key.ic.push(p1_to_vec(e));
}
verification_key.vk_alfa_1 = p1_to_vec(&vk_alfa_1);
verification_key.vk_beta_2 = p2_to_vec(&vk_beta_2);
//let vk_alfabeta_12 = vk_alfa_1.pairing_with(&vk_beta_2);
//println!("vk_alfabeta_12: {}", vk_alfabeta_12);
let vk_gamma_2 = params.vk.gamma_g2.clone();
verification_key.vk_gamma_2 = p2_to_vec(&vk_gamma_2);
verification_key.vk_delta_2 = p2_to_vec(&vk_delta_2);
let mut pk_file = OpenOptions::new().read(true).write(true).create_new(true).open("pk.json").unwrap();
let pk_json = serde_json::to_string(&proving_key).unwrap();
pk_file.set_len(pk_json.len() as u64);
let mut mmap = unsafe { memmap::Mmap::map(&pk_file) }.unwrap().make_mut().unwrap();
mmap.deref_mut().write_all(pk_json.as_bytes()).unwrap();
let mut vk_file = OpenOptions::new().read(true).write(true).create_new(true).open("vk.json").unwrap();
let vk_json = serde_json::to_string(&verification_key).unwrap();
vk_file.set_len(vk_json.len() as u64);
let mut mmap = unsafe { memmap::Mmap::map(&vk_file) }.unwrap().make_mut().unwrap();
mmap.deref_mut().write_all(vk_json.as_bytes()).unwrap();
/*
// Prepare the verification key (for proof verification)
let pvk = prepare_verifying_key(&params.vk);
println!("Creating proofs...");
// Let's benchmark stuff!
const SAMPLES: u32 = 1;
let mut total_proving = Duration::new(0, 0);
let mut total_verifying = Duration::new(0, 0);
// Just a place to put the proof data, so we can
// benchmark deserialization.
let mut proof_vec = vec![];
for _ in 0..SAMPLES {
proof_vec.truncate(0);
let start = Instant::now();
{
// Create an instance of our circuit (with the
// witness)
let c = CircomCircuit {
file_name,
witness: witness.clone(),
};
// Create a groth16 proof with our parameters.
let proof = create_random_proof(c, params, rng).unwrap();
println!("proof: {:?}", proof);
proof.write(&mut proof_vec).unwrap();
}
total_proving += start.elapsed();
let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof
assert!(verify_proof(
&pvk,
&proof,
&[]
).unwrap());
total_verifying += start.elapsed();
}
let proving_avg = total_proving / SAMPLES;
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (verifying_avg.as_secs() as f64);
println!("Average proving time: {:?} seconds", proving_avg);
println!("Average verifying time: {:?} seconds", verifying_avg);
*/
}

@ -23,6 +23,8 @@ fn main() {
let entropy = &args[2]; let entropy = &args[2];
let out_params_filename = &args[3]; let out_params_filename = &args[3];
let disallow_points_at_infinity = false;
// Create an RNG based on a mixture of system randomness and user provided randomness // Create an RNG based on a mixture of system randomness and user provided randomness
let mut rng = { let mut rng = {
use byteorder::{ReadBytesExt, BigEndian}; use byteorder::{ReadBytesExt, BigEndian};
@ -60,7 +62,7 @@ fn main() {
.read(true) .read(true)
.open(in_params_filename) .open(in_params_filename)
.expect("unable to open."); .expect("unable to open.");
let mut params = phase2::MPCParameters::read(reader, true).expect("unable to read params"); let mut params = phase2::MPCParameters::read(reader, disallow_points_at_infinity, true).expect("unable to read params");
println!("Contributing to {}...", in_params_filename); println!("Contributing to {}...", in_params_filename);
let hash = params.contribute(&mut rng); let hash = params.contribute(&mut rng);

@ -81,13 +81,15 @@ fn main() {
let vk_filename = &args[2]; let vk_filename = &args[2];
let pk_filename = &args[3]; let pk_filename = &args[3];
let disallow_points_at_infinity = false;
println!("Exporting {}...", params_filename); println!("Exporting {}...", params_filename);
let reader = OpenOptions::new() let reader = OpenOptions::new()
.read(true) .read(true)
.open(params_filename) .open(params_filename)
.expect("unable to open."); .expect("unable to open.");
let params = phase2::MPCParameters::read(reader, true).expect("unable to read params"); let params = phase2::MPCParameters::read(reader, disallow_points_at_infinity, true).expect("unable to read params");
let params = params.get_params(); let params = params.get_params();
let mut proving_key = ProvingKeyJson { let mut proving_key = ProvingKeyJson {

@ -13,9 +13,10 @@ fn main() {
let circuit_filename = &args[1]; let circuit_filename = &args[1];
let params_filename = &args[2]; let params_filename = &args[2];
let should_filter_points_at_infinity = false;
// Import the circuit and create the initial parameters using phase 1 // Import the circuit and create the initial parameters using phase 1
println!("Creating initial parameters for {}...", circuit_filename); println!("Creating initial parameters for {}...", circuit_filename);
let should_filter_points_at_infinity = false;
let params = { let params = {
let c = phase2::CircomCircuit { let c = phase2::CircomCircuit {
file_name: &circuit_filename, file_name: &circuit_filename,

@ -13,17 +13,19 @@ fn main() {
let old_params_filename = &args[2]; let old_params_filename = &args[2];
let new_params_filename = &args[3]; let new_params_filename = &args[3];
let disallow_points_at_infinity = false;
let old_reader = OpenOptions::new() let old_reader = OpenOptions::new()
.read(true) .read(true)
.open(old_params_filename) .open(old_params_filename)
.expect("unable to open old params"); .expect("unable to open old params");
let old_params = phase2::MPCParameters::read(old_reader, true).expect("unable to read old params"); let old_params = phase2::MPCParameters::read(old_reader, disallow_points_at_infinity, true).expect("unable to read old params");
let new_reader = OpenOptions::new() let new_reader = OpenOptions::new()
.read(true) .read(true)
.open(new_params_filename) .open(new_params_filename)
.expect("unable to open new params"); .expect("unable to open new params");
let new_params = phase2::MPCParameters::read(new_reader, true).expect("unable to read new params"); let new_params = phase2::MPCParameters::read(new_reader, disallow_points_at_infinity, true).expect("unable to read new params");
println!("Checking contribution {}...", new_params_filename); println!("Checking contribution {}...", new_params_filename);
let contribution = phase2::verify_contribution(&old_params, &new_params).expect("should verify"); let contribution = phase2::verify_contribution(&old_params, &new_params).expect("should verify");

@ -955,10 +955,11 @@ impl MPCParameters {
/// checks. /// checks.
pub fn read<R: Read>( pub fn read<R: Read>(
mut reader: R, mut reader: R,
disallow_points_at_infinity: bool,
checked: bool checked: bool
) -> io::Result<MPCParameters> ) -> io::Result<MPCParameters>
{ {
let params = Parameters::read(&mut reader, checked)?; let params = Parameters::read(&mut reader, disallow_points_at_infinity, checked)?;
let mut cs_hash = [0u8; 64]; let mut cs_hash = [0u8; 64];
reader.read_exact(&mut cs_hash)?; reader.read_exact(&mut cs_hash)?;