From f8d99df9574bde9b134ab44e22c8232b147c8940 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 26 Feb 2019 16:07:57 +0300 Subject: [PATCH] first part of the grand product argument is complete --- src/sonic/unhelped/grand_product_argument.rs | 406 ++++++++++++++++--- 1 file changed, 348 insertions(+), 58 deletions(-) diff --git a/src/sonic/unhelped/grand_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs index 9a69c0b..b132f7e 100644 --- a/src/sonic/unhelped/grand_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -14,13 +14,17 @@ pub struct GrandProductArgument { a_polynomials: Vec>, c_polynomials: Vec>, v_elements: Vec, + t_polynomial: Option>, n: usize } #[derive(Clone)] pub struct GrandProductProof { - l: E::G1Affine, - r: E::G1Affine + t_opening: E::G1Affine, + e_zinv: E::Fr, + e_opening: E::G1Affine, + f_y: E::Fr, + f_opening: E::G1Affine, } impl GrandProductArgument { @@ -63,9 +67,10 @@ impl GrandProductArgument { } assert_eq!(c_poly.len(), n); a_poly.extend(p0); - a_poly.push(c_poly[n - 2].inverse().unwrap()); - // a_{n+1} = c_{n-1}^-1 - let v = c_coeff.inverse().unwrap(); + // v = a_{n+1} = c_{n}^-1 + let v = c_poly[n-1].inverse().unwrap(); + a_poly.push(E::Fr::zero()); + // a_poly.push(v); // add c_{n+1} let mut c_coeff = E::Fr::one(); c_poly.push(c_coeff); @@ -76,6 +81,9 @@ impl GrandProductArgument { } assert_eq!(c_poly.len(), 2*n + 1); a_poly.extend(p1); + + assert_eq!(c_poly[n-1], c_poly[2*n]); + a_polynomials.push(a_poly); c_polynomials.push(c_poly); v_elements.push(v); @@ -85,12 +93,67 @@ impl GrandProductArgument { a_polynomials: a_polynomials, c_polynomials: c_polynomials, v_elements: v_elements, + t_polynomial: None, n: n } } + // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}] + pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS) -> E::G1Affine { + assert_eq!(a.len(), b.len()); + + let n = a.len(); + + multiexp( + srs.g_positive_x_alpha[0..(2*n+1)].iter(), + a.iter() + .chain_ext(Some(E::Fr::zero()).iter()) + .chain_ext(b.iter()) + ).into_affine() + } + + pub fn open_commitments_for_grand_product(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> Vec<(E::Fr, E::G1Affine)> { + let n = self.n; + + let mut yz = y; + yz.mul_assign(&z); + + let mut results = vec![]; + + for a_poly in self.a_polynomials.iter() { + let a = & a_poly[0..n]; + let b = & a_poly[(n+1)..]; + assert_eq!(a.len(), n); + assert_eq!(b.len(), n); + let mut val = evaluate_at_consequitive_powers(a, yz, yz); + { + let tmp = yz.pow([(n+2) as u64]); + let v = evaluate_at_consequitive_powers(b, tmp, yz); + val.add_assign(&v); + } + + let mut constant_term = val; + constant_term.negate(); + + let opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(constant_term).iter() + .chain_ext(a.iter()) + .chain_ext(Some(E::Fr::zero()).iter()) + .chain_ext(b.iter()), + yz, + &srs); + + results.push((val, opening)); + + } + + results + } + // Make a commitment for the begining of the protocol, returns commitment and `v` scalar - pub fn commit(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { + pub fn commit_to_individual_c_polynomials(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { let mut results = vec![]; @@ -109,7 +172,7 @@ impl GrandProductArgument { } // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form - pub fn evaluate_t_polynomial(&self, challenges: Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { + pub fn commit_to_t_polynomial(&mut self, challenges: & Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { assert_eq!(challenges.len(), self.a_polynomials.len()); let n = self.n; @@ -119,13 +182,14 @@ impl GrandProductArgument { for (((a, c), v), challenge) in self.a_polynomials.iter() .zip(self.c_polynomials.iter()) .zip(self.v_elements.iter()) - .zip(challenges.into_iter()) + .zip(challenges.iter()) { let mut a_xy = a.clone(); let mut c_xy = c.clone(); let v = *v; assert_eq!(a_xy.len(), 2*n + 1); + assert_eq!(c_xy.len(), 2*n + 1); // make a T polynomial @@ -151,27 +215,44 @@ impl GrandProductArgument { // 2n+2 term let mut tmp = y; tmp.negate(); + a_xy.push(tmp); assert_eq!(a_xy.len(), 2*n + 2); - let mut r = vec![E::Fr::zero(); 2*n+3]; + let mut r = vec![E::Fr::zero(); 2*n + 3]; r.extend(a_xy); r }; - // calculate product of the full term made of `a` poly with c(X^{-1}, 1) + X^-1 let r_prime: Vec = { let mut c_prime: Vec = c_xy.iter().rev().map(|el| *el).collect(); c_prime.push(E::Fr::one()); c_prime.push(E::Fr::zero()); + assert_eq!(c_prime.len(), 2*n + 3); + c_prime }; + // multiply polynomials with powers [-2n-2, -1] and [1, 2n+2], + // expect result to be [-2n+1, 2n+1] let mut t: Vec = multiply_polynomials::(r, r_prime); + assert_eq!(t.len(), 6*n + 7); + + // drain first powers due to the padding and last element due to requirement of being zero + for (i, el) in t[0..(2*n+3)].iter().enumerate() { + assert_eq!(*el, E::Fr::zero(), "{}", format!("Element {} is non-zero", i)); + } + + t.drain(0..(2*n+3)); + let last = t.pop(); + assert_eq!(last.unwrap(), E::Fr::zero(), "last element should be zero"); + + assert_eq!(t.len(), 4*n + 3); + let mut val = { let mut tmp = y; tmp.square(); @@ -180,28 +261,239 @@ impl GrandProductArgument { val.add_assign(&E::Fr::one()); - t[2*n+2].sub_assign(&val); + // subtract at constant term + assert_eq!(t[2*n+1], val); + + t[2*n+1].sub_assign(&val); + if t_polynomial.is_some() { if let Some(t_poly) = t_polynomial.as_mut() { - mul_add_polynomials(&mut t_poly[..], &t, challenge); + mul_add_polynomials(&mut t_poly[..], &t, *challenge); } } else { - mul_polynomial_by_scalar(&mut t, challenge); + mul_polynomial_by_scalar(&mut t, *challenge); t_polynomial = Some(t); } } let t_polynomial = t_polynomial.unwrap(); - polynomial_commitment( - srs.d, - 2*n + 2, - 2*n + 2, - srs, - t_polynomial.iter() - ) + let c = multiexp(srs.g_negative_x_alpha[0..(2*n+1)].iter().rev() + .chain_ext(srs.g_positive_x_alpha[0..(2*n+1)].iter()), + t_polynomial[0..(2*n+1)].iter() + .chain_ext(t_polynomial[(2*n+2)..].iter())).into_affine(); + + self.t_polynomial = Some(t_polynomial); + + c } + + // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form + pub fn make_argument(self, a_zy: & Vec, challenges: & Vec, y: E::Fr, z: E::Fr, srs: &SRS) -> GrandProductProof { + assert_eq!(a_zy.len(), self.a_polynomials.len()); + assert_eq!(challenges.len(), self.a_polynomials.len()); + + let n = self.n; + + let mut c_polynomials = self.c_polynomials; + let mut e_polynomial: Option> = None; + let mut f_polynomial: Option> = None; + + let mut yz = y; + yz.mul_assign(&z); + + let z_inv = z.inverse().unwrap(); + + for (((a, c), challenge), v) in a_zy.iter() + .zip(c_polynomials.into_iter()) + .zip(challenges.iter()) + .zip(self.v_elements.iter()) + { + // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 + let mut c_zy = yz.pow([(n + 1) as u64]); + c_zy.mul_assign(v); + c_zy.add_assign(a); + c_zy.mul_assign(&y); + + let mut z_n_plus_1 = z.pow([(n + 1) as u64]); + + let mut z_n_plus_2 = z_n_plus_1; + z_n_plus_2.mul_assign(&z); + + let mut z_2n_plus_2 = z_n_plus_1; + z_2n_plus_2.square(); + z_2n_plus_2.mul_assign(&y); + + z_n_plus_1.mul_assign(&y); + + c_zy.add_assign(&z_n_plus_1); + c_zy.add_assign(&z_n_plus_2); + c_zy.sub_assign(&z_2n_plus_2); + + c_zy.mul_assign(&z_inv); + + let mut rc = c_zy; + rc.mul_assign(challenge); + let mut ry = y; + ry.mul_assign(challenge); + + if e_polynomial.is_some() && f_polynomial.is_some() { + if let Some(e_poly) = e_polynomial.as_mut() { + if let Some(f_poly) = f_polynomial.as_mut() { + mul_add_polynomials(&mut e_poly[..], &c, rc); + mul_add_polynomials(&mut f_poly[..], &c, ry); + } + } + } else { + let mut e = c.clone(); + let mut f = c; + mul_polynomial_by_scalar(&mut e, rc); + mul_polynomial_by_scalar(&mut f, ry); + e_polynomial = Some(e); + f_polynomial = Some(f); + } + } + + let e_polynomial = e_polynomial.unwrap(); + let f_polynomial = f_polynomial.unwrap(); + + // evaluate e at z^-1 + + let mut e_val = evaluate_at_consequitive_powers(&e_polynomial, z_inv, z_inv); + e_val.negate(); + + // evaluate f at y + + let mut f_val = evaluate_at_consequitive_powers(&f_polynomial, y, y); + f_val.negate(); + + let e_opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(e_val).iter().chain_ext(e_polynomial.iter()), + z_inv, + srs); + + let f_opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(f_val).iter().chain_ext(f_polynomial.iter()), + y, + srs); + + e_val.negate(); + f_val.negate(); + + let mut t_poly = self.t_polynomial.unwrap(); + + let t_zy = { + let tmp = z_inv.pow([(2*n+2) as u64]); + evaluate_at_consequitive_powers(&t_poly, tmp, z) + }; + + t_poly[2*n + 2].sub_assign(&t_zy); + + let t_opening = polynomial_commitment_opening( + 2*n + 2, + 2*n + 2, + t_poly.iter(), + z, + srs); + + GrandProductProof { + t_opening: t_opening, + e_zinv: e_val, + e_opening: e_opening, + f_y: f_val, + f_opening: f_opening, + } + } + + + pub fn verify_ab_commitment(n: usize, + randomness: & Vec, + a_commitments: &Vec, + b_commitments: &Vec, + openings: &Vec<(E::Fr, E::G1Affine)>, + y: E::Fr, + z: E::Fr, + srs: &SRS + ) -> bool { + assert_eq!(randomness.len(), a_commitments.len()); + assert_eq!(openings.len(), a_commitments.len()); + assert_eq!(b_commitments.len(), a_commitments.len()); + let d = srs.d; + + // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + let g = srs.g_positive_x[0]; + + let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + let mut h_x_n_plus_one_precomp = srs.h_positive_x[n]; + h_x_n_plus_one_precomp.negate(); + let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare(); + + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); + + let a = multiexp( + a_commitments.iter(), + randomness.iter(), + ).into_affine(); + + let a = a.prepare(); + + let b = multiexp( + b_commitments.iter(), + randomness.iter(), + ).into_affine(); + + let b = b.prepare(); + + let mut yz_neg = y; + yz_neg.mul_assign(&z); + yz_neg.negate(); + + let mut ops = vec![]; + let mut value = E::Fr::zero(); + + for (el, r) in openings.iter().zip(randomness.iter()) { + let (v, o) = el; + ops.push(o.clone()); + let mut val = *v; + val.mul_assign(&r); + value.add_assign(&val); + } + + let value = g.mul(value.into_repr()).into_affine().prepare(); + + let openings = multiexp( + ops.iter(), + randomness.iter(), + ).into_affine(); + + let openings_zy = openings.mul(yz_neg.into_repr()).into_affine().prepare(); + let openings = openings.prepare(); + + + // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + E::final_exponentiation(&E::miller_loop(&[ + (&openings, &h_alpha_x_precomp), + (&openings_zy, &h_alpha_precomp), + (&a, &h_prep), + (&b, &h_x_n_plus_one_precomp), + (&value, &h_alpha_precomp) + ])).unwrap() == E::Fqk::one() + } + + + // pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { // let d = srs.d; @@ -240,57 +532,55 @@ impl GrandProductArgument { // } } -// #[test] -// fn test_argument() { -// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; -// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; -// use crate::sonic::srs::SRS; +#[test] +fn test_grand_product_argument() { + use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use crate::sonic::srs::SRS; -// let srs_x = Fr::from_str("23923").unwrap(); -// let srs_alpha = Fr::from_str("23728792").unwrap(); -// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); -// let n: usize = 1 << 16; -// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let n: usize = 1 << 1; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + // let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let coeffs = vec![Fr::from_str("1").unwrap(), Fr::from_str("2").unwrap()]; + let mut permutation = coeffs.clone(); + rng.shuffle(&mut permutation); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let a_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), coeffs.iter()).into_affine(); + let b_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), permutation.iter()).into_affine(); -// let commitments = argument.commit(&srs); + let mut argument = GrandProductArgument::new(vec![(coeffs, permutation)]); -// let proof = argument.make_argument(challenges.clone(), &srs); + let commitments_and_v_values = argument.commit_to_individual_c_polynomials(&srs); -// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + assert_eq!(commitments_and_v_values.len(), 1); -// assert!(valid); -// } + // let y : Fr = rng.gen(); -// #[test] -// fn test_argument_soundness() { -// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; -// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; -// use crate::sonic::srs::SRS; + let y = Fr::one(); -// let srs_x = Fr::from_str("23923").unwrap(); -// let srs_alpha = Fr::from_str("23728792").unwrap(); -// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); -// let n: usize = 1 << 16; -// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let t_commitment = argument.commit_to_t_polynomial(&challenges, y, &srs); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let commitments = argument.commit(&srs); + let z : Fr = rng.gen(); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let grand_product_openings = argument.open_commitments_for_grand_product(y, z, &srs); -// let proof = argument.make_argument(challenges.clone(), &srs); + let randomness = (0..1).map(|_| Fr::rand(rng)).collect::>(); -// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + let valid = GrandProductArgument::verify_ab_commitment(n, + &randomness, + &vec![a_commitment], + &vec![b_commitment], + &grand_product_openings, + y, + z, + &srs); -// assert!(!valid); -// } + assert!(valid); +}