use super::super::verbose_flag;
use crate::rand::Rng;
use std::sync::Arc;
use crate::pairing::{CurveAffine, CurveProjective, Engine, Wnaf};
use crate::pairing::ff::{Field, PrimeField};
use super::{Parameters, VerifyingKey};
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
use crate::domain::{EvaluationDomain, Scalar};
use crate::worker::Worker;
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>,
{
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>,
{
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};
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)),
}
}
}
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;
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,
{
}
fn pop_namespace(&mut self) {
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
pub fn generate_parameters<E, C>(
circuit: C,
g1: E::G1,
g2: E::G2,
alpha: E::Fr,
beta: E::Fr,
gamma: E::Fr,
tau: E::Fr,
) -> Result<(), 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![],
};
let input_0 = assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
circuit.synthesize(&mut assembly)?;
let num_inputs_without_identity = assembly.num_inputs - 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;
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)));
}
assert_eq!(
num_inputs_without_identity * 2 + num_constraints_before_inputs_constraining,
assembly.num_constraints,
"each input must produce two extra constraints"
);
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)
};
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
let mut domain = EvaluationDomain::from_coeffs(powers_of_tau)?;
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)
});
let mut g2_wnaf = Wnaf::new();
let g2_wnaf = g2_wnaf.base(g2, {
assembly.num_inputs + assembly.num_aux
});
let worker = Worker::new();
let domain_length = domain.as_ref().len();
if verbose {
eprintln!("Domain length is {} ", domain_length)
};
let mut gamma2_z_t_g1 = vec![E::G1::zero(); domain.as_ref().len() - 1];
let mut z_at_tau = E::Fr::zero();
{
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 = 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();
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 |_| {
for (gamma2_z_t_g1, p) in gamma2_z_t_g1.iter_mut().zip(p.iter()) {
let mut exp = p.0;
exp.mul_assign(&gamma2_z_t);
*gamma2_z_t_g1 = g1_wnaf.scalar(exp.into_repr());
}
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);
};
}
let mut a_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
let mut a_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux];
let mut c_1_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
let mut c_2_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
let mut gamma_zt = gamma;
gamma_zt.mul_assign(&z_at_tau);
let gamma_z = g1.into_affine().mul(gamma.into_repr());
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);
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();
let gamma2_z2_g1 = g1.into_affine().mul(gamma2_z2.into_repr());
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();
domain.ifft(&worker);
let powers_of_tau = domain.into_coeffs();
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();
fn eval_stage_1<E: Engine>(
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
g2_wnaf: &Wnaf<usize, &[E::G2], &mut Vec<i64>>,
powers_of_tau: &[Scalar<E>],
at: &[Vec<(E::Fr, usize)>],
ct: &[Vec<(E::Fr, usize)>],
a_g1: &mut [E::G1],
a_g2: &mut [E::G2],
c_1_g1: &mut [E::G1],
c_2_g1: &mut [E::G1],
alpha: &E::Fr,
beta: &E::Fr,
gamma: &E::Fr,
z_at_tau: &E::Fr,
worker: &Worker,
) {
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());
let mut ab = *alpha;
ab.add_assign(&beta);
let mut gamma2 = *gamma;
gamma2.square();
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
}
let mut t0 = eval_at_tau(powers_of_tau, at);
t0.mul_assign(&gamma);
let mut t1 = eval_at_tau(powers_of_tau, ct);
t1.mul_assign(&gamma2);
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);
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());
}
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);
});
}
});
}
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,
);
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,
);
if verbose {
eprintln!("evaluating polynomials done in {} s", start.elapsed().as_millis() as f64 / 1000.0);
};
println!("Has generated {} points", a_g1.len());
Ok(())
}