parallelize almost everything

This commit is contained in:
Alex Vlasov 2019-02-12 01:52:18 +03:00
parent 57a6ce94e8
commit 2d69758c18
4 changed files with 395 additions and 87 deletions

@ -19,6 +19,9 @@ use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
pub const NUM_BLINDINGS: usize = 4;
// pub const NUM_BLINDINGS: usize = 0;
#[derive(Clone, Debug, Eq)] #[derive(Clone, Debug, Eq)]
pub struct SxyAdvice<E: Engine> { pub struct SxyAdvice<E: Engine> {
pub s: E::G1Affine, pub s: E::G1Affine,
@ -34,7 +37,6 @@ impl<E: Engine> PartialEq for SxyAdvice<E> {
} }
} }
#[derive(Clone, Debug, Eq)] #[derive(Clone, Debug, Eq)]
pub struct Proof<E: Engine> { pub struct Proof<E: Engine> {
pub r: E::G1Affine, pub r: E::G1Affine,

@ -5,7 +5,7 @@ use std::marker::PhantomData;
use super::{Proof, SxyAdvice}; use super::{Proof, SxyAdvice};
use super::batch::Batch; use super::batch::Batch;
use super::poly::{SxEval, SyEval}; use super::poly::{SxEval, SyEval};
use super::parameters::{Parameters}; use super::parameters::{Parameters, NUM_BLINDINGS};
use crate::SynthesisError; use crate::SynthesisError;
@ -139,6 +139,9 @@ pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
create_proof_on_srs::<E, C, S>(circuit, &parameters.srs) create_proof_on_srs::<E, C, S>(circuit, &parameters.srs)
} }
extern crate rand;
use self::rand::{Rand, Rng, thread_rng};
pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>( pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C, circuit: &C,
srs: &SRS<E> srs: &SRS<E>
@ -204,13 +207,108 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
let mut transcript = Transcript::new(&[]); let mut transcript = Transcript::new(&[]);
let r = multiexp( let rng = &mut thread_rng();
srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(),
wires.c.iter().rev() // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4}
let blindings: Vec<E::Fr> = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect();
// let blindings: Vec<E::Fr> = vec![E::Fr::zero(); NUM_BLINDINGS];
// let max_n = 3*n + 1 + NUM_BLINDINGS;
// let max_n = 3*n + 1;
fn polynomial_commitment<
'a,
EE: Engine,
IS: IntoIterator<Item = &'a EE::Fr>,
>(
max: usize,
largest_negative_power: usize,
largest_positive_power: usize,
srs: &'a SRS<EE>,
s: IS,
) -> EE::G1Affine
where
IS::IntoIter: ExactSizeIterator,
{
// smallest power is d - max - largest_negative_power; It should either be 1 for use of positive powers only,
// of we should use part of the negative powers
let d = srs.d;
assert!(max >= largest_positive_power);
if d < max + largest_negative_power + 1 {
println!("Use negative powers for polynomial commitment");
let min_power = largest_negative_power + max - d;
let max_power = d + largest_positive_power - max;
println!("Min power = {}, max = {}", min_power, max_power);
// need to use negative powers to make a proper commitment
return multiexp(
srs.g_negative_x_alpha[0..min_power].iter().rev()
.chain_ext(srs.g_positive_x_alpha[..max_power].iter()),
s
).into_affine();
} else {
println!("Use positive powers only for polynomial commitment");
return multiexp(
srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(),
s
).into_affine();
}
}
fn polynomial_commitment_opening<
'a,
EE: Engine,
I: IntoIterator<Item = &'a EE::Fr>
>(
largest_negative_power: usize,
largest_positive_power: usize,
polynomial_coefficients: I,
mut point: EE::Fr,
srs: &'a SRS<EE>,
) -> EE::G1Affine
where I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
let poly = kate_divison(
polynomial_coefficients,
point,
);
let negative_poly = poly[0..largest_negative_power].iter().rev();
let positive_poly = poly[largest_negative_power..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
}
let r = polynomial_commitment::<E, _>(
n,
2*n + NUM_BLINDINGS,
n,
&srs,
blindings.iter().rev()
.chain_ext(wires.c.iter().rev())
// wires.c.iter().rev()
.chain_ext(wires.b.iter().rev()) .chain_ext(wires.b.iter().rev())
.chain_ext(Some(E::Fr::zero()).iter()) .chain_ext(Some(E::Fr::zero()).iter())
.chain_ext(wires.a.iter()), .chain_ext(wires.a.iter()),
).into_affine(); );
// let r = multiexp(
// // F <- g^{alpha*x^{d-max}*f(x)} = g^{alpha*x^{d-n}*\sum_{i = -2n - 4}^{n} k_i*x^{i}} =
// // = g^{alpha*\sum_{i = d - 3n - 4}^{n} k_i*x^{i}}
// // g^{alpha*(x^{d - 3n - 4}*k_{-2n-4} + ... + x^{d - n}*k_{0} + ... + x^{d + n + 1}*k_{n})
// srs.g_positive_x_alpha[(srs.d - max_n)..].iter(),
// blindings.iter().rev()
// .chain_ext(wires.c.iter().rev())
// // wires.c.iter().rev()
// .chain_ext(wires.b.iter().rev())
// .chain_ext(Some(E::Fr::zero()).iter())
// .chain_ext(wires.a.iter()),
// ).into_affine();
transcript.commit_point(&r); transcript.commit_point(&r);
@ -226,17 +324,48 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
rx1.push(E::Fr::zero()); rx1.push(E::Fr::zero());
rx1.extend(wires.a); rx1.extend(wires.a);
let mut rxy = rx1.clone();
// let mut rxy = rx1.clone();
// it's not yet blinded, so blind explicitly here
let mut rxy = rx1;
let mut rx1 = {
// c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4}
let mut tmp = blindings.clone();
// c_{n+4}*X^{-2n - 4}, c_{n+3}*X^{-2n - 3}, c_{n+2}*X^{-2n - 2}, c_{n+1}*X^{-2n - 1},
tmp.reverse();
tmp.extend(rxy.clone());
tmp
};
let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?;
let mut tmp = y.pow(&[n as u64]);
// evaluate r(X, y) // y^{-2n}
for rxy in rxy.iter_mut().rev() { let tmp = y_inv.pow(&[2*n as u64]);
rxy.mul_assign(&tmp); mut_distribute_consequitive_powers(
tmp.mul_assign(&y_inv); &mut rxy,
} tmp,
y,
);
// negative powers [-n, -1], positive [1, 2n] // let mut tmp = y.pow(&[n as u64]);
// // evaluate r(X, y)
// for rxy in rxy.iter_mut().rev() {
// rxy.mul_assign(&tmp);
// tmp.mul_assign(&y_inv);
// }
// Blindings are not affected by evaluation on Y cause blindings make constant term over Y
// We just add them here to have it now in a form of coefficients over X
let rxy = {
let mut tmp = blindings.clone();
tmp.reverse();
tmp.extend(rxy);
tmp
};
// negative powers [-1, -2n], positive [1, n]
let (s_poly_negative, s_poly_positive) = { let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n); let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit)?; S::synthesize(&mut tmp, circuit)?;
@ -244,12 +373,14 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
tmp.poly() tmp.poly()
}; };
// TODO: Parallelize
// r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already // r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already
let mut rxy_prime = rxy.clone(); let mut rxy_prime = rxy.clone();
{ {
rxy_prime.resize(4 * n + 1, E::Fr::zero()); // extend to have powers [n+1, 2n]
rxy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero());
// add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1} // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}
for (r, s) in rxy_prime[0..(2 * n)] for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)]
.iter_mut() .iter_mut()
.rev() .rev()
.zip(s_poly_negative) .zip(s_poly_negative)
@ -257,54 +388,79 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
r.add_assign(&s); r.add_assign(&s);
} }
// add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n} // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n}
for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) {
r.add_assign(&s); r.add_assign(&s);
} }
} }
// by this point all R related polynomials are blinded and evaluated for Y variable
// t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z // t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z
// contained degree in respect to X are from -4*n to 3*n including X^0 // contained degree in respect to X are from -4*n to 3*n including X^0
let mut txy = multiply_polynomials::<E>(rx1.clone(), rxy_prime); let mut txy = multiply_polynomials::<E>(rx1.clone(), rxy_prime);
txy[4 * n] = E::Fr::zero(); // -k(y) txy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y)
// commit to t(X, y) to later open at z // commit to t(X, y) to later open at z
let t = multiexp(
srs.g_positive_x_alpha[0..(3 * n)] let t = polynomial_commitment(
.iter() srs.d,
.chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()), (4 * n) + 2*NUM_BLINDINGS,
txy[(4 * n + 1)..] 3 * n,
.iter() srs,
.chain_ext(txy[0..4 * n].iter().rev()), // skip what would be zero power
).into_affine(); txy[0..(4 * n) + 2*NUM_BLINDINGS].iter()
.chain_ext(txy[(4 * n + 2*NUM_BLINDINGS + 1)..].iter()),
);
// let t = multiexp(
// srs.g_positive_x_alpha[0..(3 * n)]
// .iter()
// .chain_ext(srs.g_negative_x_alpha[0..(4 * n) + 2*NUM_BLINDINGS].iter()),
// txy[(4 * n + 1 + 2*NUM_BLINDINGS)..]
// .iter()
// .chain_ext(txy[0..(4 * n + 2*NUM_BLINDINGS)].iter().rev()),
// ).into_affine();
transcript.commit_point(&t); transcript.commit_point(&t);
let z: E::Fr = transcript.get_challenge_scalar(); let z: E::Fr = transcript.get_challenge_scalar();
let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?;
// TODO: use the faster way to evaluate the polynomials let rz = {
let mut rz = E::Fr::zero(); let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
{
let mut tmp = z.pow(&[n as u64]);
for coeff in rx1.iter().rev() { evaluate_at_consequitive_powers(&rx1, tmp, z)
let mut coeff = *coeff; };
coeff.mul_assign(&tmp);
rz.add_assign(&coeff);
tmp.mul_assign(&z_inv);
}
}
let mut rzy = E::Fr::zero(); // let mut rz = E::Fr::zero();
{ // {
let mut tmp = z.pow(&[n as u64]); // let mut tmp = z.pow(&[n as u64]);
for mut coeff in rxy.into_iter().rev() { // for coeff in rx1.iter().rev() {
coeff.mul_assign(&tmp); // let mut coeff = *coeff;
rzy.add_assign(&coeff); // coeff.mul_assign(&tmp);
tmp.mul_assign(&z_inv); // rz.add_assign(&coeff);
} // tmp.mul_assign(&z_inv);
} // }
// }
let rzy = {
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
evaluate_at_consequitive_powers(&rxy, tmp, z)
};
// let mut rzy = E::Fr::zero();
// {
// let mut tmp = z.pow(&[n as u64]);
// for mut coeff in rxy.into_iter().rev() {
// coeff.mul_assign(&tmp);
// rzy.add_assign(&coeff);
// tmp.mul_assign(&z_inv);
// }
// }
transcript.commit_scalar(&rz); transcript.commit_scalar(&rz);
transcript.commit_scalar(&rzy); transcript.commit_scalar(&rzy);
@ -313,62 +469,88 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
let zy_opening = { let zy_opening = {
// r(X, 1) - r(z, y) // r(X, 1) - r(z, y)
rx1[2 * n].sub_assign(&rzy); // subtract constant term from R(X, 1)
rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rzy);
let mut point = y; let mut point = y;
point.mul_assign(&z); point.mul_assign(&z);
let poly = kate_divison(
rx1.iter(),
point,
);
let negative_poly = poly[0..2*n].iter().rev(); polynomial_commitment_opening(
let positive_poly = poly[2*n..].iter(); 2 * n + NUM_BLINDINGS,
multiexp( n,
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( &rx1,
srs.g_positive_x[0..positive_poly.len()].iter() point,
), srs
negative_poly.chain_ext(positive_poly) )
).into_affine()
// let poly = kate_divison(
// rx1.iter(),
// point,
// );
// let negative_poly = poly[0..(2*n + NUM_BLINDINGS)].iter().rev();
// let positive_poly = poly[(2*n + NUM_BLINDINGS)..].iter();
// multiexp(
// srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
// srs.g_positive_x[0..positive_poly.len()].iter()
// ),
// negative_poly.chain_ext(positive_poly)
// ).into_affine()
}; };
let z_opening = { assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1);
rx1[2 * n].add_assign(&rzy); // restore
for (t, &r) in txy[2 * n..].iter_mut().zip(rx1.iter()) { let z_opening = {
rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore
// skip powers from until reach -2n - NUM_BLINDINGS
for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) {
let mut r = r; let mut r = r;
r.mul_assign(&r1); r.mul_assign(&r1);
t.add_assign(&r); t.add_assign(&r);
} }
let mut val = E::Fr::zero(); let val = {
{ let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]);
assert_eq!(txy.len(), 3*n + 1 + 4*n);
let mut tmp = z.pow(&[(3*n) as u64]);
for coeff in txy.iter().rev() { evaluate_at_consequitive_powers(&txy, tmp, z)
let mut coeff = *coeff; };
coeff.mul_assign(&tmp);
val.add_assign(&coeff);
tmp.mul_assign(&z_inv);
}
}
txy[4 * n].sub_assign(&val); // let mut val = E::Fr::zero();
// {
// assert_eq!(txy.len(), 3*n + 1 + 4*n + 2*NUM_BLINDINGS);
// let mut tmp = z.pow(&[(3*n) as u64]);
let poly = kate_divison( // for coeff in txy.iter().rev() {
txy.iter(), // let mut coeff = *coeff;
z, // coeff.mul_assign(&tmp);
); // val.add_assign(&coeff);
// tmp.mul_assign(&z_inv);
// }
// }
let negative_poly = poly[0..4*n].iter().rev(); txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val);
let positive_poly = poly[4*n..].iter();
multiexp( polynomial_commitment_opening(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 4*n + 2*NUM_BLINDINGS,
srs.g_positive_x[0..positive_poly.len()].iter() 3*n,
), &txy,
negative_poly.chain_ext(positive_poly) z,
).into_affine() srs)
// let poly = kate_divison(
// txy.iter(),
// z,
// );
// let negative_poly = poly[0..(4*n + 2*NUM_BLINDINGS)].iter().rev();
// let positive_poly = poly[(4*n + 2*NUM_BLINDINGS)..].iter();
// multiexp(
// srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
// srs.g_positive_x[0..positive_poly.len()].iter()
// ),
// negative_poly.chain_ext(positive_poly)
// ).into_affine()
}; };
Ok(Proof { Ok(Proof {
@ -427,3 +609,67 @@ fn my_fun_circuit_test() {
let elapsed = start.elapsed(); let elapsed = start.elapsed();
println!("time to verify: {:?}", elapsed); println!("time to verify: {:?}", elapsed);
} }
#[test]
fn polynomial_commitment_test() {
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
use rand::{thread_rng};
struct MyCircuit;
impl<E: Engine> Circuit<E> for MyCircuit {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let (a, b, _) = cs.multiply(|| {
Ok((
E::Fr::from_str("10").unwrap(),
E::Fr::from_str("20").unwrap(),
E::Fr::from_str("200").unwrap(),
))
})?;
cs.enforce_zero(LinearCombination::from(a) + a - b);
//let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
//cs.enforce_zero(LinearCombination::from(b) - multiplier);
Ok(())
}
}
let srs = SRS::<Bls12>::new(
20,
Fr::from_str("22222").unwrap(),
Fr::from_str("33333333").unwrap(),
);
let proof = self::create_proof_on_srs::<Bls12, _, Basic>(&MyCircuit, &srs).unwrap();
let z: Fr;
let y: Fr;
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y = transcript.get_challenge_scalar();
transcript.commit_point(&proof.t);
z = transcript.get_challenge_scalar();
}
use std::time::{Instant};
let rng = thread_rng();
let mut batch = MultiVerifier::<Bls12, _, Basic, _>::new(MyCircuit, &srs, rng).unwrap().batch;
// try to open commitment to r at r(z, 1);
let mut random = Fr::one();
random.double();
batch.add_opening(proof.z_opening, random, z);
batch.add_commitment_max_n(proof.r, random);
batch.add_opening_value(proof.rz, random);
assert!(batch.check_all());
}

@ -19,7 +19,7 @@ use crate::sonic::srs::SRS;
pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> { pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
circuit: C, circuit: C,
batch: Batch<E>, pub(crate) batch: Batch<E>,
k_map: Vec<usize>, k_map: Vec<usize>,
n: usize, n: usize,
q: usize, q: usize,

@ -183,6 +183,35 @@ pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> (
result result
} }
/// Multiply each coefficient by some power of the base in a form
/// `first_power * base^{i}`
pub fn mut_distribute_consequitive_powers<'a, F: Field> (
coeffs: &mut [F],
first_power: F,
base: F
)
{
use crate::multicore::Worker;
let worker = Worker::new();
worker.scope(coeffs.len(), |scope, chunk| {
for (i, coeffs_chunk) in coeffs.chunks_mut(chunk).enumerate()
{
scope.spawn(move |_| {
let mut current_power = base.pow(&[(i*chunk) as u64]);
current_power.mul_assign(&first_power);
for mut p in coeffs_chunk {
p.mul_assign(&current_power);
current_power.mul_assign(&base);
}
});
}
});
}
pub fn multiexp< pub fn multiexp<
'a, 'a,
G: CurveAffine, G: CurveAffine,
@ -202,6 +231,8 @@ where
let s: Vec<<G::Scalar as PrimeField>::Repr> = s.into_iter().map(|e| e.into_repr()).collect::<Vec<_>>(); let s: Vec<<G::Scalar as PrimeField>::Repr> = s.into_iter().map(|e| e.into_repr()).collect::<Vec<_>>();
let g: Vec<G> = g.into_iter().map(|e| *e).collect::<Vec<_>>(); let g: Vec<G> = g.into_iter().map(|e| *e).collect::<Vec<_>>();
assert_eq!(s.len(), g.len(), "scalars and exponents must have the same length");
let pool = Worker::new(); let pool = Worker::new();
let result = dense_multiexp( let result = dense_multiexp(
@ -588,5 +619,34 @@ fn test_mut_eval_at_powers() {
let acc_parallel = mut_evaluate_at_consequitive_powers(&mut b[..], first_power, x); let acc_parallel = mut_evaluate_at_consequitive_powers(&mut b[..], first_power, x);
assert_eq!(acc_parallel, acc); assert_eq!(acc_parallel, acc);
assert!(a == b);
}
#[test]
fn test_mut_distribute_powers() {
use rand::{self, Rand, Rng};
use pairing::bls12_381::Bls12;
use pairing::bls12_381::Fr;
const SAMPLES: usize = 100000;
let rng = &mut rand::thread_rng();
let mut a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let mut b = a.clone();
let x: Fr = rng.gen();
let n: u32 = rng.gen();
{
let mut tmp = x.pow(&[n as u64]);
for mut coeff in a.iter_mut() {
coeff.mul_assign(&tmp);
tmp.mul_assign(&x);
}
}
let first_power = x.pow(&[n as u64]);
mut_distribute_consequitive_powers(&mut b[..], first_power, x);
assert!(a == b); assert!(a == b);
} }