group: Direct-to-affine CurveProjective::batch_normalize

Replaces the mutating CurveProjective::batch_normalization API, and
removes the need for CurveProjective::is_normalized.

The new temporary implementation in pairing::bls12_381::ec is adapted
from bls12_381::g1.
This commit is contained in:
Jack Grigg 2020-05-15 17:08:43 +12:00
parent 4969ad4d93
commit b94d567076
5 changed files with 84 additions and 103 deletions

View File

@ -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<E: Engine>(
// 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(),
),
})

View File

@ -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 {

View File

@ -101,13 +101,9 @@ pub trait CurveProjective:
type Base: Field;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// 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;

View File

@ -378,10 +378,6 @@ fn random_transformation_tests<G: CurveProjective>() {
for _ in 0..10 {
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>();
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<G: CurveProjective>() {
v[s] = v[s].into_affine().into_projective();
}
let expected_v = v
.iter()
.map(|v| v.into_affine().into_projective())
.collect::<Vec<_>>();
G::batch_normalization(&mut v);
let expected_v = v.iter().map(|v| v.into_affine()).collect::<Vec<_>>();
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);
}
}

View File

@ -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]) {
// Montgomerys 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;
}
}
}