diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index dfc27c2..8128e61 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -19,6 +19,9 @@ use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +pub const NUM_BLINDINGS: usize = 4; +// pub const NUM_BLINDINGS: usize = 0; + #[derive(Clone, Debug, Eq)] pub struct SxyAdvice { pub s: E::G1Affine, @@ -34,7 +37,6 @@ impl PartialEq for SxyAdvice { } } - #[derive(Clone, Debug, Eq)] pub struct Proof { pub r: E::G1Affine, diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index ff78c2d..6e730ee 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; -use super::parameters::{Parameters}; +use super::parameters::{Parameters, NUM_BLINDINGS}; use crate::SynthesisError; @@ -139,6 +139,9 @@ pub fn create_proof, S: SynthesisDriver>( create_proof_on_srs::(circuit, ¶meters.srs) } +extern crate rand; +use self::rand::{Rand, Rng, thread_rng}; + pub fn create_proof_on_srs, S: SynthesisDriver>( circuit: &C, srs: &SRS @@ -204,13 +207,108 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let mut transcript = Transcript::new(&[]); - let r = multiexp( - srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(), - wires.c.iter().rev() + let rng = &mut thread_rng(); + + // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4} + let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); + + // let blindings: Vec = 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, + >( + max: usize, + largest_negative_power: usize, + largest_positive_power: usize, + srs: &'a SRS, + 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 + >( + largest_negative_power: usize, + largest_positive_power: usize, + polynomial_coefficients: I, + mut point: EE::Fr, + srs: &'a SRS, + ) -> 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::( + 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(Some(E::Fr::zero()).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); @@ -226,17 +324,48 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( rx1.push(E::Fr::zero()); 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 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); - } + // y^{-2n} + let tmp = y_inv.pow(&[2*n as u64]); + mut_distribute_consequitive_powers( + &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 mut tmp = SxEval::new(y, n); S::synthesize(&mut tmp, circuit)?; @@ -244,12 +373,14 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( tmp.poly() }; + // TODO: Parallelize // r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already 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} - for (r, s) in rxy_prime[0..(2 * n)] + for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)] .iter_mut() .rev() .zip(s_poly_negative) @@ -257,54 +388,79 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( r.add_assign(&s); } // 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); } } + // 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 // contained degree in respect to X are from -4*n to 3*n including X^0 let mut txy = multiply_polynomials::(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 - let t = multiexp( - srs.g_positive_x_alpha[0..(3 * n)] - .iter() - .chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()), - txy[(4 * n + 1)..] - .iter() - .chain_ext(txy[0..4 * n].iter().rev()), - ).into_affine(); + + let t = polynomial_commitment( + srs.d, + (4 * n) + 2*NUM_BLINDINGS, + 3 * n, + srs, + // skip what would be zero power + 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); + let z: E::Fr = transcript.get_challenge_scalar(); let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; - // TODO: use the faster way to evaluate the polynomials - let mut rz = E::Fr::zero(); - { - let mut tmp = z.pow(&[n as u64]); + let rz = { + let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]); - for coeff in rx1.iter().rev() { - let mut coeff = *coeff; - coeff.mul_assign(&tmp); - rz.add_assign(&coeff); - tmp.mul_assign(&z_inv); - } - } + evaluate_at_consequitive_powers(&rx1, tmp, z) + }; - let mut rzy = E::Fr::zero(); - { - let mut tmp = z.pow(&[n as u64]); + // let mut rz = 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); - } - } + // for coeff in rx1.iter().rev() { + // let mut coeff = *coeff; + // coeff.mul_assign(&tmp); + // 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(&rzy); @@ -313,62 +469,88 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let zy_opening = { // 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; point.mul_assign(&z); - let poly = kate_divison( - rx1.iter(), - point, - ); - let negative_poly = poly[0..2*n].iter().rev(); - let positive_poly = poly[2*n..].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() + polynomial_commitment_opening( + 2 * n + NUM_BLINDINGS, + n, + &rx1, + point, + srs + ) + + // 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 = { - rx1[2 * n].add_assign(&rzy); // restore + assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1); - 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; r.mul_assign(&r1); t.add_assign(&r); } - let mut val = E::Fr::zero(); - { - assert_eq!(txy.len(), 3*n + 1 + 4*n); - let mut tmp = z.pow(&[(3*n) as u64]); + let val = { + let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]); - for coeff in txy.iter().rev() { - let mut coeff = *coeff; - coeff.mul_assign(&tmp); - val.add_assign(&coeff); - tmp.mul_assign(&z_inv); - } - } + evaluate_at_consequitive_powers(&txy, tmp, z) + }; - 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( - txy.iter(), - z, - ); + // for coeff in txy.iter().rev() { + // let mut coeff = *coeff; + // coeff.mul_assign(&tmp); + // val.add_assign(&coeff); + // tmp.mul_assign(&z_inv); + // } + // } - let negative_poly = poly[0..4*n].iter().rev(); - let positive_poly = poly[4*n..].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() + txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val); + + polynomial_commitment_opening( + 4*n + 2*NUM_BLINDINGS, + 3*n, + &txy, + z, + 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 { @@ -427,3 +609,67 @@ fn my_fun_circuit_test() { let elapsed = start.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 Circuit for MyCircuit { + fn synthesize>(&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::::new( + 20, + Fr::from_str("22222").unwrap(), + Fr::from_str("33333333").unwrap(), + ); + let proof = self::create_proof_on_srs::(&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::::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()); +} diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 768500d..2bded1b 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -19,7 +19,7 @@ use crate::sonic::srs::SRS; pub struct MultiVerifier, S: SynthesisDriver, R: Rng> { circuit: C, - batch: Batch, + pub(crate) batch: Batch, k_map: Vec, n: usize, q: usize, diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 958c0bd..d011879 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -183,6 +183,35 @@ pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> ( 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(¤t_power); + + current_power.mul_assign(&base); + } + }); + } + }); +} + pub fn multiexp< 'a, G: CurveAffine, @@ -202,6 +231,8 @@ where let s: Vec<::Repr> = s.into_iter().map(|e| e.into_repr()).collect::>(); let g: Vec = g.into_iter().map(|e| *e).collect::>(); + assert_eq!(s.len(), g.len(), "scalars and exponents must have the same length"); + let pool = Worker::new(); 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); 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::>(); + 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); } \ No newline at end of file