Hash the constraint systems to check integrity.

This commit is contained in:
Sean Bowe 2018-02-24 08:01:16 -07:00
parent 23d17b9042
commit 4441a0da41
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
4 changed files with 234 additions and 135 deletions

View File

@ -18,6 +18,8 @@ blake2 = "0.7"
digest = "0.7"
bellman = "0.0.8"
byteorder = "1"
[features]
default = ["u128-support"]
u128-support = ["pairing/u128-support"]

View File

@ -397,94 +397,6 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
}
}
#[test]
fn test_input_circuit_with_bls12_381() {
use pairing::bls12_381::*;
use rand::{SeedableRng, Rng, XorShiftRng};
use ::circuit::test::*;
use jubjub::{JubjubBls12, fs};
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let tree_depth = 29;
let value: u64 = 1;
let value_randomness: fs::Fs = rng.gen();
let ak: edwards::Point<Bls12, Unknown> = edwards::Point::rand(rng, params);
let g_d: edwards::Point<Bls12, Unknown> = edwards::Point::rand(rng, params);
let commitment_randomness: fs::Fs = rng.gen();
let rsk: fs::Fs = rng.gen();
let auth_path = vec![Some((rng.gen(), rng.gen())); tree_depth];
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let instance = Spend {
params: params,
value: Some(value),
value_randomness: Some(value_randomness),
rsk: Some(rsk),
ak: Some(ak),
g_d: Some(g_d),
commitment_randomness: Some(commitment_randomness),
auth_path: auth_path
};
instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 97379);
}
// use bellman::groth16::*;
// let groth_params = generate_random_parameters::<Bls12, _, _>(Spend {
// params: params,
// value: None,
// value_randomness: None,
// rsk: None,
// ak: None,
// g_d: None,
// commitment_randomness: None,
// auth_path: vec![None; 29]
// }, rng).unwrap();
// let pvk = prepare_verifying_key(&groth_params.vk);
// use std::time::{Duration, Instant};
// // Let's benchmark stuff!
// const SAMPLES: u32 = 50;
// let mut total_proving = Duration::new(0, 0);
// for _ in 0..SAMPLES {
// let start = Instant::now();
// {
// let c = Spend {
// params: params,
// value: Some(1),
// value_randomness: Some(value_randomness.clone()),
// rsk: Some(rsk.clone()),
// ak: Some(ak.clone()),
// g_d: Some(g_d.clone()),
// commitment_randomness: Some(commitment_randomness.clone()),
// auth_path: auth_path.clone()
// };
// create_random_proof(c, &groth_params, rng).unwrap();
// }
// total_proving += 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);
// panic!("Average proving time: {:?} seconds", proving_avg);
}
/// This is an output circuit instance.
pub struct Output<'a, E: JubjubEngine> {
pub params: &'a E::Params,
@ -747,7 +659,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
}
#[test]
fn test_output_circuit_with_bls12_381() {
fn test_input_circuit_with_bls12_381() {
use pairing::bls12_381::*;
use rand::{SeedableRng, Rng, XorShiftRng};
use ::circuit::test::*;
@ -756,6 +668,48 @@ fn test_output_circuit_with_bls12_381() {
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let tree_depth = 29;
let value: u64 = 1;
let value_randomness: fs::Fs = rng.gen();
let ak: edwards::Point<Bls12, Unknown> = edwards::Point::rand(rng, params);
let g_d: edwards::Point<Bls12, Unknown> = edwards::Point::rand(rng, params);
let commitment_randomness: fs::Fs = rng.gen();
let rsk: fs::Fs = rng.gen();
let auth_path = vec![Some((rng.gen(), rng.gen())); tree_depth];
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let instance = Spend {
params: params,
value: Some(value),
value_randomness: Some(value_randomness),
rsk: Some(rsk),
ak: Some(ak),
g_d: Some(g_d),
commitment_randomness: Some(commitment_randomness),
auth_path: auth_path
};
instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 97379);
assert_eq!(cs.hash(), "4d8e71c91a621e41599ea488ee89f035c892a260a595d3c85a20a82daa2d1654");
}
}
#[test]
fn test_output_circuit_with_bls12_381() {
use pairing::bls12_381::*;
use rand::{SeedableRng, Rng, XorShiftRng};
use ::circuit::test::*;
use jubjub::{JubjubBls12, fs};
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let value: u64 = 1;
let value_randomness: fs::Fs = rng.gen();
let g_d: edwards::Point<Bls12, Unknown> = edwards::Point::rand(rng, params);
@ -779,51 +733,7 @@ fn test_output_circuit_with_bls12_381() {
instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 7827);
assert_eq!(cs.hash(), "225a2df7e21b9af8b436ffb9dadd645e4df843a5151c7481b0553422d5eaa793");
}
// use bellman::groth16::*;
// let groth_params = generate_random_parameters::<Bls12, _, _>(Output {
// params: params,
// value: None,
// value_randomness: None,
// g_d: None,
// p_d: None,
// commitment_randomness: None,
// esk: None
// }, rng).unwrap();
// let pvk = prepare_verifying_key(&groth_params.vk);
// use std::time::{Duration, Instant};
// // Let's benchmark stuff!
// const SAMPLES: u32 = 50;
// let mut total_proving = Duration::new(0, 0);
// for _ in 0..SAMPLES {
// let start = Instant::now();
// {
// let c = Output {
// params: params,
// value: Some(1),
// value_randomness: Some(value_randomness),
// g_d: Some(g_d.clone()),
// p_d: Some(p_d.clone()),
// commitment_randomness: Some(commitment_randomness),
// esk: Some(esk.clone())
// };
// create_random_proof(c, &groth_params, rng).unwrap();
// }
// total_proving += 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);
// panic!("Average proving time: {:?} seconds", proving_avg);
}

View File

@ -1,6 +1,8 @@
use pairing::{
Engine,
Field
Field,
PrimeField,
PrimeFieldRepr
};
use bellman::{
@ -12,6 +14,13 @@ use bellman::{
};
use std::collections::HashMap;
use std::fmt::Write;
use blake2::{Blake2s};
use digest::{FixedOutput, Input};
use byteorder::{BigEndian, ByteOrder};
use std::cmp::Ordering;
use std::collections::BTreeMap;
#[derive(Debug)]
enum NamedObject {
@ -34,6 +43,90 @@ pub struct TestConstraintSystem<E: Engine> {
aux: Vec<(E::Fr, String)>
}
#[derive(Clone, Copy)]
struct OrderedVariable(Variable);
impl Eq for OrderedVariable {}
impl PartialEq for OrderedVariable {
fn eq(&self, other: &OrderedVariable) -> bool {
match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a == b,
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
_ => false
}
}
}
impl PartialOrd for OrderedVariable {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedVariable {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
(Index::Aux(_), Index::Input(_)) => Ordering::Greater
}
}
}
fn proc_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
) -> BTreeMap<OrderedVariable, E::Fr>
{
let mut map = BTreeMap::new();
for &(var, coeff) in terms {
map.entry(OrderedVariable(var))
.or_insert(E::Fr::zero())
.add_assign(&coeff);
}
// Remove terms that have a zero coefficient to normalize
let mut to_remove = vec![];
for (var, coeff) in map.iter() {
if coeff.is_zero() {
to_remove.push(var.clone())
}
}
for var in to_remove {
map.remove(&var);
}
map
}
fn hash_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
h: &mut Blake2s
)
{
let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32];
BigEndian::write_u64(&mut buf[0..8], map.len() as u64);
h.process(&buf[0..8]);
for (var, coeff) in map {
match var.0.get_unchecked() {
Index::Input(i) => {
buf[0] = b'I';
BigEndian::write_u64(&mut buf[1..9], i as u64);
},
Index::Aux(i) => {
buf[0] = b'A';
BigEndian::write_u64(&mut buf[1..9], i as u64);
}
}
coeff.into_repr().write_be(&mut buf[9..]).unwrap();
h.process(&buf);
}
}
fn eval_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)],
@ -69,6 +162,98 @@ impl<E: Engine> TestConstraintSystem<E> {
}
}
pub fn pretty_print(&self) -> String {
let mut s = String::new();
let negone = {
let mut tmp = E::Fr::one();
tmp.negate();
tmp
};
let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| {
E::Fr::from_str("2").unwrap().pow(&[i as u64])
}).collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| {
write!(s, "(").unwrap();
let mut is_first = true;
for (var, coeff) in proc_lc::<E>(lc.as_ref()) {
if coeff == negone {
write!(s, " - ").unwrap();
} else if !is_first {
write!(s, " + ").unwrap();
}
is_first = false;
if coeff != E::Fr::one() && coeff != negone {
for (i, x) in powers_of_two.iter().enumerate() {
if x == &coeff {
write!(s, "2^{} . ", i).unwrap();
break;
}
}
write!(s, "{} . ", coeff).unwrap();
}
match var.0.get_unchecked() {
Index::Input(i) => {
write!(s, "`{}`", &self.inputs[i].1).unwrap();
},
Index::Aux(i) => {
write!(s, "`{}`", &self.aux[i].1).unwrap();
}
}
}
if is_first {
// Nothing was visited, print 0.
write!(s, "0").unwrap();
}
write!(s, ")").unwrap();
};
for &(ref a, ref b, ref c, ref name) in &self.constraints {
write!(&mut s, "\n").unwrap();
write!(&mut s, "{}: ", name).unwrap();
pp(&mut s, a);
write!(&mut s, " * ").unwrap();
pp(&mut s, b);
write!(&mut s, " = ").unwrap();
pp(&mut s, c);
}
write!(&mut s, "\n").unwrap();
s
}
pub fn hash(&self) -> String {
let mut h = Blake2s::new_keyed(&[], 32);
{
let mut buf = [0u8; 24];
BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64);
BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64);
BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64);
h.process(&buf);
}
for constraint in &self.constraints {
hash_lc::<E>(constraint.0.as_ref(), &mut h);
hash_lc::<E>(constraint.1.as_ref(), &mut h);
hash_lc::<E>(constraint.2.as_ref(), &mut h);
}
let mut s = String::new();
for b in h.fixed_result().as_ref() {
s += &format!("{:02x}", b);
}
s
}
pub fn which_is_unsatisfied(&self) -> Option<&str> {
for &(ref a, ref b, ref c, ref path) in &self.constraints {
let mut a = eval_lc::<E>(a.as_ref(), &self.inputs, &self.aux);

View File

@ -4,6 +4,8 @@ extern crate blake2;
extern crate digest;
extern crate rand;
extern crate byteorder;
pub mod jubjub;
pub mod circuit;
pub mod group_hash;