diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index fe1fbdc65..21b48e064 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -234,7 +234,7 @@ where let worker = Worker::new(); - let mut h = vec![E::G1::identity(); powers_of_tau.as_ref().len() - 1]; + let mut h = vec![E::G1Affine::identity(); powers_of_tau.as_ref().len() - 1]; { // Compute powers of tau { @@ -267,17 +267,20 @@ where scope.spawn(move |_scope| { // Set values of the H query to g1^{(tau^i * t(tau)) / delta} - for (h, p) in h.iter_mut().zip(p.iter()) { - // Compute final exponent - let mut exp = p.0; - exp.mul_assign(&coeff); + let h_proj: Vec<_> = p[..h.len()] + .iter() + .map(|p| { + // Compute final exponent + let mut exp = p.0; + exp.mul_assign(&coeff); - // Exponentiate - *h = g1_wnaf.scalar(&exp); - } + // Exponentiate + g1_wnaf.scalar(&exp) + }) + .collect(); // Batch normalize - E::G1::batch_normalization(h); + E::G1::batch_normalize(&h_proj, h); }); } }); @@ -287,11 +290,11 @@ where powers_of_tau.ifft(&worker); let powers_of_tau = powers_of_tau.into_coeffs(); - let mut a = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g1 = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g2 = vec![E::G2::identity(); assembly.num_inputs + assembly.num_aux]; - let mut ic = vec![E::G1::identity(); assembly.num_inputs]; - let mut l = vec![E::G1::identity(); assembly.num_aux]; + let mut a = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![E::G2Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut ic = vec![E::G1Affine::identity(); assembly.num_inputs]; + let mut l = vec![E::G1Affine::identity(); assembly.num_aux]; fn eval( // wNAF window tables @@ -307,10 +310,10 @@ where ct: &[Vec<(E::Fr, usize)>], // Resulting evaluated QAP polynomials - a: &mut [E::G1], - b_g1: &mut [E::G1], - b_g2: &mut [E::G2], - ext: &mut [E::G1], + a: &mut [E::G1Affine], + b_g1: &mut [E::G1Affine], + b_g2: &mut [E::G2Affine], + ext: &mut [E::G1Affine], // Inverse coefficient for ext elements inv: &E::Fr, @@ -345,11 +348,16 @@ where let mut g2_wnaf = g2_wnaf.shared(); scope.spawn(move |_scope| { - for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a + let mut a_proj = vec![E::G1::identity(); a.len()]; + let mut b_g1_proj = vec![E::G1::identity(); b_g1.len()]; + let mut b_g2_proj = vec![E::G2::identity(); b_g2.len()]; + let mut ext_proj = vec![E::G1::identity(); ext.len()]; + + for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a_proj .iter_mut() - .zip(b_g1.iter_mut()) - .zip(b_g2.iter_mut()) - .zip(ext.iter_mut()) + .zip(b_g1_proj.iter_mut()) + .zip(b_g2_proj.iter_mut()) + .zip(ext_proj.iter_mut()) .zip(at.iter()) .zip(bt.iter()) .zip(ct.iter()) @@ -398,10 +406,10 @@ where } // Batch normalize - E::G1::batch_normalization(a); - E::G1::batch_normalization(b_g1); - E::G2::batch_normalization(b_g2); - E::G1::batch_normalization(ext); + E::G1::batch_normalize(&a_proj, a); + E::G1::batch_normalize(&b_g1_proj, b_g1); + E::G2::batch_normalize(&b_g2_proj, b_g2); + E::G1::batch_normalize(&ext_proj, ext); }); } }); @@ -461,31 +469,28 @@ where 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(), + ic, }; Ok(Parameters { 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()), + h: Arc::new(h), + l: Arc::new(l), // Filter points at infinity away from A/B queries a: Arc::new( a.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), b_g1: Arc::new( b_g1.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), b_g2: Arc::new( b_g2.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), }) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 4e824c60d..222e8ba8c 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -396,10 +396,12 @@ impl CurveProjective for Fr { type Affine = Fr; type Base = Fr; - fn batch_normalization(_: &mut [Self]) {} + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + assert_eq!(p.len(), q.len()); - fn is_normalized(&self) -> bool { - true + for (p, q) in p.iter().zip(q.iter_mut()) { + *q = p.into_affine(); + } } fn into_affine(&self) -> Fr { diff --git a/group/src/lib.rs b/group/src/lib.rs index 2d2e14508..eee06824e 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -101,13 +101,9 @@ pub trait CurveProjective: type Base: Field; type Affine: CurveAffine; - /// Normalizes a slice of projective elements so that - /// conversion to affine is cheap. - fn batch_normalization(v: &mut [Self]); - - /// Checks if the point is already "normalized" so that - /// cheap affine conversion is possible. - fn is_normalized(&self) -> bool; + /// Converts a batch of projective elements into affine elements. This function will + /// panic if `p.len() != q.len()`. + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]); /// Converts this element into its affine representation. fn into_affine(&self) -> Self::Affine; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 9cb62898a..ca5b50813 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -378,10 +378,6 @@ fn random_transformation_tests() { for _ in 0..10 { let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::>(); - for i in &v { - assert!(!i.is_normalized()); - } - use rand::distributions::{Distribution, Uniform}; let between = Uniform::new(0, 1000); // Sprinkle in some normalized points @@ -393,17 +389,12 @@ fn random_transformation_tests() { v[s] = v[s].into_affine().into_projective(); } - let expected_v = v - .iter() - .map(|v| v.into_affine().into_projective()) - .collect::>(); - G::batch_normalization(&mut v); + let expected_v = v.iter().map(|v| v.into_affine()).collect::>(); - for i in &v { - assert!(i.is_normalized()); - } + let mut normalized = vec![G::Affine::identity(); v.len()]; + G::batch_normalize(&v, &mut normalized); - assert_eq!(v, expected_v); + assert_eq!(normalized, expected_v); } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 8728c4cf6..0df9a3eca 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -661,60 +661,47 @@ macro_rules! curve_impl { type Base = $basefield; type Affine = $affine; - fn is_normalized(&self) -> bool { - self.is_identity().into() || self.z == $basefield::one() - } + fn batch_normalize(p: &[Self], q: &mut [$affine]) { + assert_eq!(p.len(), q.len()); - fn batch_normalization(v: &mut [Self]) { - // Montgomery’s Trick and Fast Implementation of Masked AES - // Genelle, Prouff and Quisquater - // Section 3.2 + let mut acc = $basefield::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of $affine to store the product + // of previous z-coordinates seen. + q.x = acc; - // First pass: compute [a, ab, abc, ...] - let mut prod = Vec::with_capacity(v.len()); - let mut tmp = $basefield::one(); - for g in v - .iter_mut() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - { - tmp.mul_assign(&g.z); - prod.push(tmp); + // We will end up skipping all identities in p + if bool::from(!p.is_identity()) { + acc *= p.z; + } } - // Invert `tmp`. - tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero. + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); - // Second pass: iterate backwards to compute inverses - for (g, s) in v - .iter_mut() - // Backwards - .rev() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - // Backwards, skip last element, fill in one for last term. - .zip( - prod.into_iter() - .rev() - .skip(1) - .chain(Some($basefield::one())), - ) - { - // tmp := tmp * g.z; g.z := tmp * s = 1/z - let mut newtmp = tmp; - newtmp.mul_assign(&g.z); - g.z = tmp; - g.z.mul_assign(&s); - tmp = newtmp; - } + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); - // Perform affine transformations - for g in v.iter_mut().filter(|g| !g.is_normalized()) { - let mut z = g.z.square(); // 1/z^2 - g.x.mul_assign(&z); // x/z^2 - z.mul_assign(&g.z); // 1/z^3 - g.y.mul_assign(&z); // y/z^3 - g.z = $basefield::one(); // z = 1 + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + if bool::from(!skip) { + acc *= p.z; + } + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + if skip.into() { + *q = $affine::identity(); + } else { + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = false; + } } }