start implementing GM17 proof system

This commit is contained in:
Alex Vlasov 2019-02-01 18:36:50 +03:00
parent e775b47d99
commit ff6c46240b
7 changed files with 1691 additions and 75 deletions

700
src/gm17/generator.rs Normal file

@ -0,0 +1,700 @@
use super::super::verbose_flag;
use rand::Rng;
use std::sync::Arc;
use pairing::{
Engine,
Wnaf,
CurveProjective,
CurveAffine
};
use ff::{
PrimeField,
Field
};
use super::{
Parameters,
VerifyingKey
};
use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{
EvaluationDomain,
Scalar
};
use ::multicore::{
Worker
};
// /// Generates a random common reference string for
// /// a circuit.
// pub fn generate_random_parameters<E, C, R>(
// circuit: C,
// rng: &mut R
// ) -> Result<Parameters<E>, SynthesisError>
// where E: Engine, C: Circuit<E>, R: Rng
// {
// let g1 = rng.gen();
// let g2 = rng.gen();
// let alpha = rng.gen();
// let beta = rng.gen();
// let gamma = rng.gen();
// let delta = rng.gen();
// let tau = rng.gen();
// generate_parameters::<E, C>(
// circuit,
// g1,
// g2,
// alpha,
// beta,
// gamma,
// delta,
// tau
// )
// }
/// This is our assembly structure that we'll use to synthesize the
/// circuit into a SAP. Square arithmetic problem is different from QAP in a form:
/// it's A*A - C = 0 instead of A*B - C = 0
struct KeypairAssembly<E: Engine> {
num_inputs: usize,
num_aux: usize,
num_constraints: usize,
num_r1cs_aux: usize,
num_r1cs_constraints: usize,
at_inputs: Vec<Vec<(E::Fr, usize)>>,
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
at_aux: Vec<Vec<(E::Fr, usize)>>,
ct_aux: Vec<Vec<(E::Fr, usize)>>
}
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
type Root = Self;
fn alloc<F, A, AR>(
&mut self,
_: A,
_: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
// There is no assignment, so we don't even invoke the
// function for obtaining one.
let index = self.num_aux;
self.num_aux += 1;
self.num_r1cs_aux += 1;
self.at_aux.push(vec![]);
self.ct_aux.push(vec![]);
Ok(Variable(Index::Aux(index)))
}
fn alloc_input<F, A, AR>(
&mut self,
_: A,
_: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
// There is no assignment, so we don't even invoke the
// function for obtaining one.
let index = self.num_inputs;
self.num_inputs += 1;
self.at_inputs.push(vec![]);
self.ct_inputs.push(vec![]);
Ok(Variable(Index::Input(index)))
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
_: A,
a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{
use std::ops::{Add, Sub};
// this is where reduction happens. First we need to re-arrange initial constraints
// from the form <a,x>*<b,x> = <c,x> to an artificial
// <a - b,x> * <a - b,x> = y
// <a + b,x> * <a + b,x> = 4*<c,x> + y
fn quadruple<E: Engine>(
coeff: E::Fr
) -> E::Fr {
let mut tmp = coeff;
tmp.double();
tmp.double();
tmp
}
fn eval<E: Engine>(
l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize
)
{
for (index, coeff) in l.0 {
match index {
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint))
}
}
}
// <a - b,x> * <a - b,x> = x_i
let i = self.num_constraints;
let y = self.alloc(
|| format!("SAP reduction y_{}", i),
|| Ok(E::Fr::one())
).expect("must allocate SAP reduction variable");
self.num_r1cs_aux -= 1;
let lc_a = a(LinearCombination::zero());
let lc_b = b(LinearCombination::zero());
let lc_c = c(LinearCombination::zero());
let lc_a_minus_b = lc_a.clone().sub(&lc_b);
let mut lc_y: LinearCombination<E> = LinearCombination::zero();
lc_y = lc_y.add(y);
eval(lc_a_minus_b, &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
eval(lc_y, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
self.num_constraints += 1;
// <a + b,x> * <a + b,x> = 4*<c,x> + y
let lc_a_plus_b = lc_a.add(&lc_b);
let mut lc_c_quadrupled: LinearCombination<E> = LinearCombination::zero();
for s in &lc_c.0 {
let tmp = quadruple::<E>(s.1);
lc_c_quadrupled = lc_c_quadrupled + (tmp, s.0);
}
lc_c_quadrupled = lc_c_quadrupled.add(y);
eval(lc_a_plus_b, &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
eval(lc_c_quadrupled, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
self.num_constraints += 1;
self.num_r1cs_constraints += 1;
}
fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self)
{
// Do nothing; we don't care about namespaces in this context.
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
/// Create parameters for a circuit, given some toxic waste.
pub fn generate_parameters<E, C>(
circuit: C,
g1: E::G1,
g2: E::G2,
alpha: E::Fr,
beta: E::Fr,
gamma: E::Fr,
// delta: E::Fr,
tau: E::Fr
) -> Result<(), SynthesisError>
// Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let verbose = verbose_flag();
let mut assembly = KeypairAssembly {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
num_r1cs_aux: 0,
num_r1cs_constraints: 0,
at_inputs: vec![],
ct_inputs: vec![],
at_aux: vec![],
ct_aux: vec![]
};
// Allocate the "one" input variable
let input_0 = assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
// Synthesize the circuit.
circuit.synthesize(&mut assembly)?;
let num_inputs_without_identity = assembly.num_inputs - 1;
// inputs must be constrained manually in SAP style,
// so input 0 (identity) is constrained as 1*1=1
{
use std::ops::{Add, Sub};
fn eval_lc<E: Engine>(
l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize
)
{
for (index, coeff) in l.0 {
match index {
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint))
}
}
}
let mut lc_input_0_a: LinearCombination<E> = LinearCombination::zero();
lc_input_0_a = lc_input_0_a.add(input_0.clone());
eval_lc(lc_input_0_a, &mut assembly.at_inputs, &mut assembly.at_aux, assembly.num_constraints);
assembly.num_constraints += 1;
}
let num_constraints_before_inputs_constraining = assembly.num_constraints;
let num_aux_before_inputs_constraining = assembly.num_aux;
// Other inputs are constrained as x_i * 1 = x_i where
// 1 is actually input number 0 (identity)
for i in 1..assembly.num_inputs {
assembly.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc + Variable(Index::Input(0)),
|lc| lc + Variable(Index::Input(i)),
);
}
// check that each input generates 2 constraints
assert_eq!(num_inputs_without_identity * 2 +
num_constraints_before_inputs_constraining,
assembly.num_constraints,
"each input must produce two extra constraints");
// and that it creates one extra variable
assert_eq!(num_inputs_without_identity +
num_aux_before_inputs_constraining,
assembly.num_aux,
"each input must generate an extra variable");
assert_eq!(assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux,
assembly.num_inputs + assembly.num_aux,
"each constraint in principle adds one variable");
if verbose {eprintln!("Constraint system size is {}", assembly.num_constraints)};
// Create bases for blind evaluation of polynomials at tau
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
let mut domain = EvaluationDomain::from_coeffs(powers_of_tau)?;
// Compute G1 window table
let mut g1_wnaf = Wnaf::new();
let g1_wnaf = g1_wnaf.base(g1, {
2*(assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux)
+ assembly.num_r1cs_constraints + assembly.num_r1cs_aux
+ 2*(assembly.num_inputs + assembly.num_r1cs_constraints)
});
// Compute gamma*G2 window table
let mut g2_wnaf = Wnaf::new();
// let gamma_g2 = g2.into_affine().mul(gamma.into_repr());
let g2_wnaf = g2_wnaf.base(g2, {
// B query
assembly.num_inputs + assembly.num_aux
// alternatively expressed as
// assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux
});
let worker = Worker::new();
// let z_at_tau = {
// // Compute powers of tau
// if verbose {eprintln!("computing powers of tau...")};
// let start = std::time::Instant::now();
// {
// let domain = domain.as_mut();
// worker.scope(domain.len(), |scope, chunk| {
// for (i, subdomain) in domain.chunks_mut(chunk).enumerate()
// {
// scope.spawn(move || {
// let mut current_power = tau.pow(&[(i*chunk) as u64]);
// for p in subdomain {
// p.0 = current_power;
// current_power.mul_assign(&tau);
// }
// });
// }
// });
// }
// if verbose {eprintln!("powers of tau stage 1 done in {} s", start.elapsed().as_millis() as f64 / 1000.0);};
// // z_at_tau = t(x)
// let z_at_tau = domain.z(&tau);
// z_at_tau
// };
let domain_length = domain.as_ref().len();
if verbose {eprintln!("Domain length is {} ", domain_length)};
// G1^{gamma^2 * Z(t) * t^i} for 0 <= i < 2^m - 1 for 2^m domains
let mut gamma2_z_t_g1 = vec![E::G1::zero(); domain.as_ref().len() - 1];
let mut z_at_tau = E::Fr::zero();
{
// Compute powers of tau
if verbose {eprintln!("computing powers of tau...")};
let start = std::time::Instant::now();
{
let domain = domain.as_mut();
worker.scope(domain.len(), |scope, chunk| {
for (i, subdomain) in domain.chunks_mut(chunk).enumerate()
{
scope.spawn(move || {
let mut current_power = tau.pow(&[(i*chunk) as u64]);
for p in subdomain {
p.0 = current_power;
current_power.mul_assign(&tau);
}
});
}
});
}
if verbose {eprintln!("powers of tau stage 1 done in {} s", start.elapsed().as_millis() as f64 / 1000.0);};
// z_at_tau = t(x)
z_at_tau = domain.z(&tau);
let mut gamma2_z_t = z_at_tau;
gamma2_z_t.mul_assign(&gamma);
gamma2_z_t.mul_assign(&gamma);
if verbose {eprintln!("computing the `G1^(gamma^2 * Z(t) * t^i)` query with multiple threads...")};
let start = std::time::Instant::now();
// Compute the H query with multiple threads
worker.scope(gamma2_z_t_g1.len(), |scope, chunk| {
for (gamma2_z_t_g1, p) in gamma2_z_t_g1.chunks_mut(chunk).zip(domain.as_ref().chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (gamma2_z_t_g1, p) in gamma2_z_t_g1.iter_mut().zip(p.iter())
{
// Compute final exponent
let mut exp = p.0;
exp.mul_assign(&gamma2_z_t);
// Exponentiate
*gamma2_z_t_g1 = g1_wnaf.scalar(exp.into_repr());
}
// Batch normalize
E::G1::batch_normalization(gamma2_z_t_g1);
});
}
});
if verbose {eprintln!("computing the `G1^(gamma^2 * Z(t) * t^i)` query done in {} s", start.elapsed().as_millis() as f64 / 1000.0);};
}
// G1^{gamma * A_i(t)} for 0 <= i <= num_variables
let mut a_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
// G2^{gamma * A_i(t)} for 0 <= i <= num_variables
let mut a_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux];
// G1^{gamma^2 * C_i(t) + (alpha + beta) * gamma * A_i(t)}
// for num_inputs + 1 < i <= num_variables
let mut c_1_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
// G1^{2 * gamma^2 * Z(t) * A_i(t)} for 0 <= i <= num_variables
let mut c_2_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
// G1^{gamma * Z(t)}
let mut gamma_zt = gamma;
gamma_zt.mul_assign(&z_at_tau);
let gamma_z = g1.into_affine().mul(gamma.into_repr());
// G2^{gamma * Z(t)}
let gamma_z_g2 = g2.into_affine().mul(gamma.into_repr());
let mut ab_gamma = alpha;
ab_gamma.add_assign(&beta);
ab_gamma.mul_assign(&gamma);
// G1^{(alpha + beta) * gamma * Z(t)}
let ab_gamma_z_g1 = g1.into_affine().mul(ab_gamma.into_repr());
let mut gamma2_z2 = gamma;
gamma2_z2.mul_assign(&z_at_tau);
gamma2_z2.square();
// G1^{gamma^2 * Z(t)^2}
let gamma2_z2_g1 = g1.into_affine().mul(gamma2_z2.into_repr());
// G^{gamma^2 * Z(t) * t^i} for 0 <= i < 2^m - 1 for 2^m domains
let mut gamma2_z_t = vec![E::G1::zero(); domain.as_ref().len() - 1];
if verbose {eprintln!("using inverse FFT to convert to intepolation coefficients...")};
let start = std::time::Instant::now();
// Use inverse FFT to convert to intepolation coefficients
domain.ifft(&worker);
let powers_of_tau = domain.into_coeffs();
// domain is now a set of scalars
if verbose {eprintln!("powers of tau evaluation in radix2 domain in {} s", start.elapsed().as_millis() as f64 / 1000.0)};
if verbose {eprintln!("evaluating polynomials...")};
let start = std::time::Instant::now();
// overall strategy:
// a_g1, a_g2, c_1_g1, c_2_g1 should be combined together by computing
// ab = (alpha + beta)
// g_2 = gamma^2
// t0 = gamma*A_i(t)
// t1 = g_2*C_t(t)
// a_g1 = t0*G1
// a_g2 = t0*G2
// c_1_g1 = (t1 + ab*t0)*G1
// c_2_g1 = (2*gamma*z_at_tau*t0)*G1
fn eval_stage_1<E: Engine>(
// wNAF window tables
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
g2_wnaf: &Wnaf<usize, &[E::G2], &mut Vec<i64>>,
// powers of tau coefficients
powers_of_tau: &[Scalar<E>],
// SAP polynomials
at: &[Vec<(E::Fr, usize)>],
ct: &[Vec<(E::Fr, usize)>],
// Resulting evaluated SAP polynomials
a_g1: &mut [E::G1],
a_g2: &mut [E::G2],
c_1_g1: &mut [E::G1],
c_2_g1: &mut [E::G1],
// Trapdoors
alpha: &E::Fr,
beta: &E::Fr,
gamma: &E::Fr,
z_at_tau: &E::Fr,
// Worker
worker: &Worker
)
{
// Sanity check
assert_eq!(a_g1.len(), at.len());
assert_eq!(a_g1.len(), ct.len());
assert_eq!(a_g1.len(), a_g2.len());
assert_eq!(a_g1.len(), c_1_g1.len());
assert_eq!(a_g1.len(), c_2_g1.len());
// compute once
let mut ab = *alpha;
ab.add_assign(&beta);
let mut gamma2 = *gamma;
gamma2.square();
// Evaluate polynomials in multiple threads
worker.scope(a_g1.len(), |scope, chunk| {
for (((((a_g1, a_g2), c_1_g1), c_2_g1), at), ct) in a_g1.chunks_mut(chunk)
.zip(a_g2.chunks_mut(chunk))
.zip(c_1_g1.chunks_mut(chunk))
.zip(c_2_g1.chunks_mut(chunk))
.zip(at.chunks(chunk))
.zip(ct.chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || {
for (((((a_g1, a_g2), c_1_g1), c_2_g1), at), ct) in a_g1.iter_mut()
.zip(a_g2.iter_mut())
.zip(c_1_g1.iter_mut())
.zip(c_2_g1.iter_mut())
.zip(at.iter())
.zip(ct.iter())
{
fn eval_at_tau<E: Engine>(
powers_of_tau: &[Scalar<E>],
p: &[(E::Fr, usize)]
) -> E::Fr
{
let mut acc = E::Fr::zero();
for &(ref coeff, index) in p {
let mut n = powers_of_tau[index].0;
n.mul_assign(coeff);
acc.add_assign(&n);
}
acc
}
// Evaluate SAP polynomials at tau
// t0 = gamma*A_i(t)
let mut t0 = eval_at_tau(powers_of_tau, at);
t0.mul_assign(&gamma);
// t1 = gamma^2*C_t(t)
let mut t1 = eval_at_tau(powers_of_tau, ct);
t1.mul_assign(&gamma2);
// a_g1 = t0*G1
// a_g2 = t0*G2
// c_1_g1 = (t1 + ab*t0)*G1
// c_2_g1 = (2*gamma*z_at_tau*t0)*G1
// Compute a_g1 and a_g2
if !t0.is_zero() {
*a_g1 = g1_wnaf.scalar(t0.into_repr());
*a_g2 = g2_wnaf.scalar(t0.into_repr());
}
let mut c_1_g1_factor = t0;
c_1_g1_factor.mul_assign(&ab);
c_1_g1_factor.add_assign(&t1);
// (2*gamma*z_at_tau*t0) inplace
t0.mul_assign(&z_at_tau);
t0.mul_assign(&gamma);
t0.double();
*c_1_g1 = g1_wnaf.scalar(c_1_g1_factor.into_repr());
*c_2_g1 = g1_wnaf.scalar(t0.into_repr());
}
// Batch normalize
E::G1::batch_normalization(a_g1);
E::G2::batch_normalization(a_g2);
E::G1::batch_normalization(c_1_g1);
E::G1::batch_normalization(c_2_g1);
});
};
});
}
// Evaluate for inputs.
eval_stage_1(
&g1_wnaf,
&g2_wnaf,
&powers_of_tau,
&assembly.at_inputs,
&assembly.ct_inputs,
&mut a_g1[0..assembly.num_inputs],
&mut a_g2[0..assembly.num_inputs],
&mut c_1_g1[0..assembly.num_inputs],
&mut c_2_g1[0..assembly.num_inputs],
&alpha,
&beta,
&gamma,
&z_at_tau,
&worker
);
// Evaluate for inputs.
eval_stage_1(
&g1_wnaf,
&g2_wnaf,
&powers_of_tau,
&assembly.at_aux,
&assembly.ct_aux,
&mut a_g1[assembly.num_inputs..],
&mut a_g2[assembly.num_inputs..],
&mut c_1_g1[assembly.num_inputs..],
&mut c_2_g1[assembly.num_inputs..],
&alpha,
&beta,
&gamma,
&z_at_tau,
&worker
);
// for _ in 0..assembly.num_inputs {
// c_1_g1.remove(0);
// }
if verbose {eprintln!("evaluating polynomials done in {} s", start.elapsed().as_millis() as f64 / 1000.0);};
// // Don't allow any elements be unconstrained, so that
// // the L query is always fully dense.
// for e in l.iter() {
// if e.is_zero() {
// return Err(SynthesisError::UnconstrainedVariable);
// }
// }
// let g1 = g1.into_affine();
// let g2 = g2.into_affine();
// let vk = VerifyingKey::<E> {
// alpha_g1: g1.mul(alpha).into_affine(),
// beta_g1: g1.mul(beta).into_affine(),
// beta_g2: g2.mul(beta).into_affine(),
// gamma_g2: g2.mul(gamma).into_affine(),
// delta_g1: g1.mul(delta).into_affine(),
// delta_g2: g2.mul(delta).into_affine(),
// ic: ic.into_iter().map(|e| e.into_affine()).collect()
// };
println!("Has generated {} points", a_g1.len());
Ok(())
// Ok(Parameters {
// vk: vk,
// h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
// l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
// // Filter points at infinity away from A/B queries
// a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
// b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
// b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect())
// })
}

563
src/gm17/mod.rs Normal file

@ -0,0 +1,563 @@
use pairing::{
Engine,
CurveAffine,
EncodedPoint
};
use ::{
SynthesisError
};
use multiexp::SourceBuilder;
use std::io::{self, Read, Write};
use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)]
mod tests;
mod generator;
// mod prover;
// mod verifier;
pub use self::generator::*;
// pub use self::prover::*;
// pub use self::verifier::*;
#[derive(Debug, Clone)]
pub struct Proof<E: Engine> {
pub a: E::G1Affine,
pub b: E::G2Affine,
pub c: E::G1Affine
}
impl<E: Engine> PartialEq for Proof<E> {
fn eq(&self, other: &Self) -> bool {
self.a == other.a &&
self.b == other.b &&
self.c == other.c
}
}
impl<E: Engine> Proof<E> {
pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.a.into_compressed().as_ref())?;
writer.write_all(self.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?;
Ok(())
}
pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let a = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
Ok(Proof {
a: a,
b: b,
c: c
})
}
}
#[derive(Clone)]
pub struct VerifyingKey<E: Engine> {
pub h_g2: E::G2Affine,
// alpha in g1 for verifying and for creating A/C elements of
// proof. Never the point at infinity.
pub alpha_g1: E::G1Affine,
// beta in g2 for verifying. Never the point at infinity.
pub beta_g2: E::G2Affine,
// gamma in g1 for verifying. Never the point at infinity.
pub gamma_g1: E::G1Affine,
// gamma in g2 for verifying. Never the point at infinity.
pub gamma_g2: E::G2Affine,
// Elements of the form G^{gamma * A_i(t) + (alpha + beta) * A_i(t)}
// for all public inputs. Because all public inputs have a dummy constraint,
// this is the same size as the number of inputs, and never contains points
// at infinity.
pub ic: Vec<E::G1Affine>
}
impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool {
self.h_g2 == other.h_g2 &&
self.alpha_g1 == other.alpha_g1 &&
self.beta_g2 == other.beta_g2 &&
self.gamma_g1 == other.gamma_g1 &&
self.gamma_g2 == other.gamma_g2 &&
self.ic == other.ic
}
}
impl<E: Engine> VerifyingKey<E> {
pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.h_g2.into_uncompressed().as_ref())?;
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
writer.write_all(self.gamma_g1.into_uncompressed().as_ref())?;
writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?;
writer.write_u32::<BigEndian>(self.ic.len() as u32)?;
for ic in &self.ic {
writer.write_all(ic.into_uncompressed().as_ref())?;
}
Ok(())
}
pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g2_repr.as_mut())?;
let h_h2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let gamma_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize;
let mut ic = vec![];
for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
ic.push(g1);
}
Ok(VerifyingKey {
h_g2: h_h2,
alpha_g1: alpha_g1,
beta_g2: beta_g2,
gamma_g1: gamma_g1,
gamma_g2: gamma_g2,
ic: ic
})
}
}
#[derive(Clone)]
pub struct Parameters<E: Engine> {
pub vk: VerifyingKey<E>,
pub a_g1: Arc<Vec<E::G1Affine>>,
pub a_g2: Arc<Vec<E::G2Affine>>,
pub c_1_g1: Arc<Vec<E::G1Affine>>,
pub c_2_g1: Arc<Vec<E::G1Affine>>,
pub gamma_z: E::G1Affine,
pub gamma_z_g2: E::G2Affine,
pub ab_gamma_z_g1: E::G1Affine,
pub gamma2_z2_g1: E::G1Affine,
pub gamma2_z_t: Arc<Vec<E::G1Affine>>,
}
impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Self) -> bool {
self.vk == other.vk &&
self.a_g1 == other.a_g1 &&
self.a_g2 == other.a_g2 &&
self.c_1_g1 == other.c_1_g1 &&
self.c_2_g1 == other.c_2_g1 &&
self.gamma_z == other.gamma_z &&
self.gamma_z_g2 == other.gamma_z_g2 &&
self.ab_gamma_z_g1 == other.ab_gamma_z_g1 &&
self.gamma2_z2_g1 == other.gamma2_z2_g1 &&
self.gamma2_z_t == other.gamma2_z_t
}
}
// impl<E: Engine> Parameters<E> {
// pub fn write<W: Write>(
// &self,
// mut writer: W
// ) -> io::Result<()>
// {
// self.vk.write(&mut writer)?;
// writer.write_u32::<BigEndian>(self.h.len() as u32)?;
// for g in &self.h[..] {
// writer.write_all(g.into_uncompressed().as_ref())?;
// }
// writer.write_u32::<BigEndian>(self.l.len() as u32)?;
// for g in &self.l[..] {
// writer.write_all(g.into_uncompressed().as_ref())?;
// }
// writer.write_u32::<BigEndian>(self.a.len() as u32)?;
// for g in &self.a[..] {
// writer.write_all(g.into_uncompressed().as_ref())?;
// }
// writer.write_u32::<BigEndian>(self.b_g1.len() as u32)?;
// for g in &self.b_g1[..] {
// writer.write_all(g.into_uncompressed().as_ref())?;
// }
// writer.write_u32::<BigEndian>(self.b_g2.len() as u32)?;
// for g in &self.b_g2[..] {
// writer.write_all(g.into_uncompressed().as_ref())?;
// }
// Ok(())
// }
// pub fn read<R: Read>(
// mut reader: R,
// checked: bool
// ) -> io::Result<Self>
// {
// let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
// let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
// reader.read_exact(repr.as_mut())?;
// if checked {
// repr
// .into_affine()
// } else {
// repr
// .into_affine_unchecked()
// }
// .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
// .and_then(|e| if e.is_zero() {
// Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
// } else {
// Ok(e)
// })
// };
// let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
// let mut repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
// reader.read_exact(repr.as_mut())?;
// if checked {
// repr
// .into_affine()
// } else {
// repr
// .into_affine_unchecked()
// }
// .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
// .and_then(|e| if e.is_zero() {
// Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
// } else {
// Ok(e)
// })
// };
// let vk = VerifyingKey::<E>::read(&mut reader)?;
// let mut h = vec![];
// let mut l = vec![];
// let mut a = vec![];
// let mut b_g1 = vec![];
// let mut b_g2 = vec![];
// {
// let len = reader.read_u32::<BigEndian>()? as usize;
// for _ in 0..len {
// h.push(read_g1(&mut reader)?);
// }
// }
// {
// let len = reader.read_u32::<BigEndian>()? as usize;
// for _ in 0..len {
// l.push(read_g1(&mut reader)?);
// }
// }
// {
// let len = reader.read_u32::<BigEndian>()? as usize;
// for _ in 0..len {
// a.push(read_g1(&mut reader)?);
// }
// }
// {
// let len = reader.read_u32::<BigEndian>()? as usize;
// for _ in 0..len {
// b_g1.push(read_g1(&mut reader)?);
// }
// }
// {
// let len = reader.read_u32::<BigEndian>()? as usize;
// for _ in 0..len {
// b_g2.push(read_g2(&mut reader)?);
// }
// }
// Ok(Parameters {
// vk: vk,
// h: Arc::new(h),
// l: Arc::new(l),
// a: Arc::new(a),
// b_g1: Arc::new(b_g1),
// b_g2: Arc::new(b_g2)
// })
// }
// }
// pub struct PreparedVerifyingKey<E: Engine> {
// /// Pairing result of alpha*beta
// alpha_g1_beta_g2: E::Fqk,
// /// -gamma in G2
// neg_gamma_g2: <E::G2Affine as CurveAffine>::Prepared,
// /// -delta in G2
// neg_delta_g2: <E::G2Affine as CurveAffine>::Prepared,
// /// Copy of IC from `VerifiyingKey`.
// ic: Vec<E::G1Affine>
// }
// pub trait ParameterSource<E: Engine> {
// type G1Builder: SourceBuilder<E::G1Affine>;
// type G2Builder: SourceBuilder<E::G2Affine>;
// fn get_vk(
// &mut self,
// num_ic: usize
// ) -> Result<VerifyingKey<E>, SynthesisError>;
// fn get_h(
// &mut self,
// num_h: usize
// ) -> Result<Self::G1Builder, SynthesisError>;
// fn get_l(
// &mut self,
// num_l: usize
// ) -> Result<Self::G1Builder, SynthesisError>;
// fn get_a(
// &mut self,
// num_inputs: usize,
// num_aux: usize
// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
// fn get_b_g1(
// &mut self,
// num_inputs: usize,
// num_aux: usize
// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
// fn get_b_g2(
// &mut self,
// num_inputs: usize,
// num_aux: usize
// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
// }
// impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
// type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
// type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
// fn get_vk(
// &mut self,
// _: usize
// ) -> Result<VerifyingKey<E>, SynthesisError>
// {
// Ok(self.vk.clone())
// }
// fn get_h(
// &mut self,
// _: usize
// ) -> Result<Self::G1Builder, SynthesisError>
// {
// Ok((self.h.clone(), 0))
// }
// fn get_l(
// &mut self,
// _: usize
// ) -> Result<Self::G1Builder, SynthesisError>
// {
// Ok((self.l.clone(), 0))
// }
// fn get_a(
// &mut self,
// num_inputs: usize,
// _: usize
// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
// {
// Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
// }
// fn get_b_g1(
// &mut self,
// num_inputs: usize,
// _: usize
// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
// {
// Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
// }
// fn get_b_g2(
// &mut self,
// num_inputs: usize,
// _: usize
// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
// {
// Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
// }
// }
// #[cfg(test)]
// mod test_with_bls12_381 {
// use super::*;
// use {Circuit, SynthesisError, ConstraintSystem};
// use rand::{Rand, thread_rng};
// use ff::{Field};
// use pairing::bls12_381::{Bls12, Fr};
// #[test]
// fn serialization() {
// struct MySillyCircuit<E: Engine> {
// a: Option<E::Fr>,
// b: Option<E::Fr>
// }
// impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
// fn synthesize<CS: ConstraintSystem<E>>(
// self,
// cs: &mut CS
// ) -> Result<(), SynthesisError>
// {
// let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
// let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
// let c = cs.alloc_input(|| "c", || {
// let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
// let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
// a.mul_assign(&b);
// Ok(a)
// })?;
// cs.enforce(
// || "a*b=c",
// |lc| lc + a,
// |lc| lc + b,
// |lc| lc + c
// );
// Ok(())
// }
// }
// let rng = &mut thread_rng();
// let params = generate_random_parameters::<Bls12, _, _>(
// MySillyCircuit { a: None, b: None },
// rng
// ).unwrap();
// {
// let mut v = vec![];
// params.write(&mut v).unwrap();
// assert_eq!(v.len(), 2136);
// let de_params = Parameters::read(&v[..], true).unwrap();
// assert!(params == de_params);
// let de_params = Parameters::read(&v[..], false).unwrap();
// assert!(params == de_params);
// }
// let pvk = prepare_verifying_key::<Bls12>(&params.vk);
// for _ in 0..100 {
// let a = Fr::rand(rng);
// let b = Fr::rand(rng);
// let mut c = a;
// c.mul_assign(&b);
// let proof = create_random_proof(
// MySillyCircuit {
// a: Some(a),
// b: Some(b)
// },
// &params,
// rng
// ).unwrap();
// let mut v = vec![];
// proof.write(&mut v).unwrap();
// assert_eq!(v.len(), 192);
// let de_proof = Proof::read(&v[..]).unwrap();
// assert!(proof == de_proof);
// assert!(verify_proof(&pvk, &proof, &[c]).unwrap());
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
// }
// }
// }

329
src/gm17/tests/mod.rs Normal file

@ -0,0 +1,329 @@
use pairing::{
Engine
};
use ff:: {
Field,
PrimeField,
};
use super::super::tests::dummy_engine::*;
use super::super::tests::XORDemo;
use std::marker::PhantomData;
use ::{
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{
generate_parameters,
// prepare_verifying_key,
// create_proof,
// verify_proof
};
#[test]
fn test_gm17_xordemo() {
let g1 = Fr::one();
let g2 = Fr::one();
let alpha = Fr::from_str("48577").unwrap();
let beta = Fr::from_str("22580").unwrap();
let gamma = Fr::from_str("53332").unwrap();
// let delta = Fr::from_str("5481").unwrap();
let tau = Fr::from_str("3673").unwrap();
let params = {
let c = XORDemo::<DummyEngine> {
a: None,
b: None,
_marker: PhantomData
};
generate_parameters(
c,
g1,
g2,
alpha,
beta,
gamma,
tau
).unwrap()
};
// // This will synthesize the constraint system:
// //
// // public inputs: a_0 = 1, a_1 = c
// // aux inputs: a_2 = a, a_3 = b
// // constraints:
// // (a_0 - a_2) * (a_2) = 0
// // (a_0 - a_3) * (a_3) = 0
// // (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1)
// // (a_0) * 0 = 0
// // (a_1) * 0 = 0
// // The evaluation domain is 8. The H query should
// // have 7 elements (it's a quotient polynomial)
// assert_eq!(7, params.h.len());
// let mut root_of_unity = Fr::root_of_unity();
// // We expect this to be a 2^10 root of unity
// assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10]));
// // Let's turn it into a 2^3 root of unity.
// root_of_unity = root_of_unity.pow(&[1 << 7]);
// assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3]));
// assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
// // Let's compute all the points in our evaluation domain.
// let mut points = Vec::with_capacity(8);
// for i in 0..8 {
// points.push(root_of_unity.pow(&[i]));
// }
// // Let's compute t(tau) = (tau - p_0)(tau - p_1)...
// // = tau^8 - 1
// let mut t_at_tau = tau.pow(&[8]);
// t_at_tau.sub_assign(&Fr::one());
// {
// let mut tmp = Fr::one();
// for p in &points {
// let mut term = tau;
// term.sub_assign(p);
// tmp.mul_assign(&term);
// }
// assert_eq!(tmp, t_at_tau);
// }
// // We expect our H query to be 7 elements of the form...
// // {tau^i t(tau) / delta}
// let delta_inverse = delta.inverse().unwrap();
// let gamma_inverse = gamma.inverse().unwrap();
// {
// let mut coeff = delta_inverse;
// coeff.mul_assign(&t_at_tau);
// let mut cur = Fr::one();
// for h in params.h.iter() {
// let mut tmp = cur;
// tmp.mul_assign(&coeff);
// assert_eq!(*h, tmp);
// cur.mul_assign(&tau);
// }
// }
// // The density of the IC query is 2 (2 inputs)
// assert_eq!(2, params.vk.ic.len());
// // The density of the L query is 2 (2 aux variables)
// assert_eq!(2, params.l.len());
// // The density of the A query is 4 (each variable is in at least one A term)
// assert_eq!(4, params.a.len());
// // The density of the B query is 2 (two variables are in at least one B term)
// assert_eq!(2, params.b_g1.len());
// assert_eq!(2, params.b_g2.len());
// /*
// Lagrange interpolation polynomials in our evaluation domain:
// ,-------------------------------. ,-------------------------------. ,-------------------------------.
// | A TERM | | B TERM | | C TERM |
// `-------------------------------. `-------------------------------' `-------------------------------'
// | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 |
// | 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 |
// | 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 |
// | 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 |
// | 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
// | 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
// `-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------'
// Example for u_0:
// sage: r = 64513
// sage: Fr = GF(r)
// sage: omega = (Fr(5)^63)^(2^7)
// sage: tau = Fr(3673)
// sage: R.<x> = PolynomialRing(Fr, 'x')
// sage: def eval(tau, c0, c1, c2, c3, c4):
// ....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)])
// ....: return p.substitute(tau)
// sage: eval(tau, 1, 1, 0, 1, 0)
// 59158
// */
// let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
// Fr::from_str(&format!("{}", e)).unwrap()
// }).collect::<Vec<Fr>>();
// let v_i = [0, 0, 60619, 30791].iter().map(|e| {
// Fr::from_str(&format!("{}", e)).unwrap()
// }).collect::<Vec<Fr>>();
// let w_i = [0, 23320, 41193, 41193].iter().map(|e| {
// Fr::from_str(&format!("{}", e)).unwrap()
// }).collect::<Vec<Fr>>();
// for (u, a) in u_i.iter()
// .zip(&params.a[..])
// {
// assert_eq!(u, a);
// }
// for (v, b) in v_i.iter()
// .filter(|&&e| e != Fr::zero())
// .zip(&params.b_g1[..])
// {
// assert_eq!(v, b);
// }
// for (v, b) in v_i.iter()
// .filter(|&&e| e != Fr::zero())
// .zip(&params.b_g2[..])
// {
// assert_eq!(v, b);
// }
// for i in 0..4 {
// let mut tmp1 = beta;
// tmp1.mul_assign(&u_i[i]);
// let mut tmp2 = alpha;
// tmp2.mul_assign(&v_i[i]);
// tmp1.add_assign(&tmp2);
// tmp1.add_assign(&w_i[i]);
// if i < 2 {
// // Check the correctness of the IC query elements
// tmp1.mul_assign(&gamma_inverse);
// assert_eq!(tmp1, params.vk.ic[i]);
// } else {
// // Check the correctness of the L query elements
// tmp1.mul_assign(&delta_inverse);
// assert_eq!(tmp1, params.l[i - 2]);
// }
// }
// // Check consistency of the other elements
// assert_eq!(alpha, params.vk.alpha_g1);
// assert_eq!(beta, params.vk.beta_g1);
// assert_eq!(beta, params.vk.beta_g2);
// assert_eq!(gamma, params.vk.gamma_g2);
// assert_eq!(delta, params.vk.delta_g1);
// assert_eq!(delta, params.vk.delta_g2);
// let pvk = prepare_verifying_key(&params.vk);
// let r = Fr::from_str("27134").unwrap();
// let s = Fr::from_str("17146").unwrap();
// let proof = {
// let c = XORDemo {
// a: Some(true),
// b: Some(false),
// _marker: PhantomData
// };
// create_proof(
// c,
// &params,
// r,
// s
// ).unwrap()
// };
// // A(x) =
// // a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) +
// // a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) +
// // a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) +
// // a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) +
// {
// // proof A = alpha + A(tau) + delta * r
// let mut expected_a = delta;
// expected_a.mul_assign(&r);
// expected_a.add_assign(&alpha);
// expected_a.add_assign(&u_i[0]); // a_0 = 1
// expected_a.add_assign(&u_i[1]); // a_1 = 1
// expected_a.add_assign(&u_i[2]); // a_2 = 1
// // a_3 = 0
// assert_eq!(proof.a, expected_a);
// }
// // B(x) =
// // a_0 * (0) +
// // a_1 * (0) +
// // a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) +
// // a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385)
// {
// // proof B = beta + B(tau) + delta * s
// let mut expected_b = delta;
// expected_b.mul_assign(&s);
// expected_b.add_assign(&beta);
// expected_b.add_assign(&v_i[0]); // a_0 = 1
// expected_b.add_assign(&v_i[1]); // a_1 = 1
// expected_b.add_assign(&v_i[2]); // a_2 = 1
// // a_3 = 0
// assert_eq!(proof.b, expected_b);
// }
// // C(x) =
// // a_0 * (0) +
// // a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) +
// // a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) +
// // a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449)
// //
// // If A * B = C at each point in the domain, then the following polynomial...
// // P(x) = A(x) * B(x) - C(x)
// // = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481
// //
// // ... should be divisible by t(x), producing the quotient polynomial:
// // h(x) = P(x) / t(x)
// // = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032
// {
// let mut expected_c = Fr::zero();
// // A * s
// let mut tmp = proof.a;
// tmp.mul_assign(&s);
// expected_c.add_assign(&tmp);
// // B * r
// let mut tmp = proof.b;
// tmp.mul_assign(&r);
// expected_c.add_assign(&tmp);
// // delta * r * s
// let mut tmp = delta;
// tmp.mul_assign(&r);
// tmp.mul_assign(&s);
// expected_c.sub_assign(&tmp);
// // L query answer
// // a_2 = 1, a_3 = 0
// expected_c.add_assign(&params.l[0]);
// // H query answer
// for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
// let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
// let mut tmp = params.h[i];
// tmp.mul_assign(&coeff);
// expected_c.add_assign(&tmp);
// }
// assert_eq!(expected_c, proof.c);
// }
// assert!(verify_proof(
// &pvk,
// &proof,
// &[Fr::one()]
// ).unwrap());
}

@ -7,8 +7,8 @@ use ff:: {
PrimeField, PrimeField,
}; };
mod dummy_engine; use super::super::tests::dummy_engine::*;
use self::dummy_engine::*; use super::super::tests::XORDemo;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -25,79 +25,6 @@ use super::{
verify_proof verify_proof
}; };
struct XORDemo<E: Engine> {
a: Option<bool>,
b: Option<bool>,
_marker: PhantomData<E>
}
impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let a_var = cs.alloc(|| "a", || {
if self.a.is_some() {
if self.a.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "a_boolean_constraint",
|lc| lc + CS::one() - a_var,
|lc| lc + a_var,
|lc| lc
);
let b_var = cs.alloc(|| "b", || {
if self.b.is_some() {
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "b_boolean_constraint",
|lc| lc + CS::one() - b_var,
|lc| lc + b_var,
|lc| lc
);
let c_var = cs.alloc_input(|| "c", || {
if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "c_xor_constraint",
|lc| lc + a_var + a_var,
|lc| lc + b_var,
|lc| lc + a_var + b_var - c_var
);
Ok(())
}
}
#[test] #[test]
fn test_xordemo() { fn test_xordemo() {
let g1 = Fr::one(); let g1 = Fr::one();

@ -14,6 +14,10 @@ pub mod multicore;
mod multiexp; mod multiexp;
pub mod domain; pub mod domain;
pub mod groth16; pub mod groth16;
pub mod gm17;
#[cfg(test)]
mod tests;
use pairing::{Engine}; use pairing::{Engine};
use ff::Field; use ff::Field;

93
src/tests/mod.rs Normal file

@ -0,0 +1,93 @@
use pairing::{
Engine
};
use ff:: {
Field,
PrimeField,
};
pub mod dummy_engine;
use self::dummy_engine::*;
use std::marker::PhantomData;
use ::{
Circuit,
ConstraintSystem,
SynthesisError
};
pub(crate) struct XORDemo<E: Engine> {
pub(crate) a: Option<bool>,
pub(crate) b: Option<bool>,
pub(crate) _marker: PhantomData<E>
}
impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let a_var = cs.alloc(|| "a", || {
if self.a.is_some() {
if self.a.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "a_boolean_constraint",
|lc| lc + CS::one() - a_var,
|lc| lc + a_var,
|lc| lc
);
let b_var = cs.alloc(|| "b", || {
if self.b.is_some() {
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "b_boolean_constraint",
|lc| lc + CS::one() - b_var,
|lc| lc + b_var,
|lc| lc
);
let c_var = cs.alloc_input(|| "c", || {
if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "c_xor_constraint",
|lc| lc + a_var + a_var,
|lc| lc + b_var,
|lc| lc + a_var + b_var - c_var
);
Ok(())
}
}