Browse Source

Use 2-NAF for representing ATE_LOOP_COUNT in MNT Miller loop (#96)

* 2-NAF for MNT pairings

* Apply suggestions from code review

* Apply suggestions from code review

* fix

* no std

* patch

* ci

Co-authored-by: Weikeng Chen <w.k@berkeley.edu>
Co-authored-by: onewayfunc <onewayfunc@gmail.com>
master
mmagician 1 year ago
committed by GitHub
parent
commit
6512e48540
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 144 additions and 112 deletions
  1. +1
    -0
      .github/workflows/ci.yml
  2. +1
    -0
      Cargo.toml
  3. +1
    -1
      src/groups/curves/short_weierstrass/bls12/mod.rs
  4. +25
    -26
      src/groups/curves/short_weierstrass/mnt4/mod.rs
  5. +25
    -26
      src/groups/curves/short_weierstrass/mnt6/mod.rs
  6. +9
    -5
      src/groups/curves/short_weierstrass/mod.rs
  7. +8
    -5
      src/groups/curves/short_weierstrass/non_zero_affine.rs
  8. +8
    -5
      src/groups/curves/twisted_edwards/mod.rs
  9. +2
    -2
      src/groups/mod.rs
  10. +18
    -8
      src/pairing/mnt4/mod.rs
  11. +20
    -10
      src/pairing/mnt6/mod.rs
  12. +9
    -7
      src/pairing/mod.rs
  13. +17
    -17
      tests/arithmetic_tests.rs

+ 1
- 0
.github/workflows/ci.yml

@ -187,6 +187,7 @@ jobs:
echo "ark-ff = { git = 'https://github.com/arkworks-rs/algebra' }"
echo "ark-poly = { git = 'https://github.com/arkworks-rs/algebra' }"
echo "ark-serialize = { git = 'https://github.com/arkworks-rs/algebra' }"
echo "ark-algebra-bench-templates = { git = 'https://github.com/arkworks-rs/algebra' }"
echo "ark-algebra-test-templates = { git = 'https://github.com/arkworks-rs/algebra' }"
echo "ark-r1cs-std = { path = 'r1cs-std' }"
} >> Cargo.toml

+ 1
- 0
Cargo.toml

@ -77,6 +77,7 @@ ark-std = { git = "https://github.com/arkworks-rs/std" }
ark-ec = { git = "https://github.com/arkworks-rs/algebra" }
ark-ff = { git = "https://github.com/arkworks-rs/algebra" }
ark-poly = { git = "https://github.com/arkworks-rs/algebra" }
ark-algebra-bench-templates = { git = "https://github.com/arkworks-rs/algebra" }
ark-test-curves = { git = "https://github.com/arkworks-rs/algebra" }
ark-bls12-381 = { git = "https://github.com/arkworks-rs/curves" }
ark-bls12-377 = { git = "https://github.com/arkworks-rs/curves" }

+ 1
- 1
src/groups/curves/short_weierstrass/bls12/mod.rs

@ -43,7 +43,7 @@ impl G1PreparedVar

{

let y = self.0.y.value()?;
let infinity = self.0.infinity.value()?;
let g = infinity
.then_some(GroupAffine::zero())
.then_some(GroupAffine::identity())
.unwrap_or(GroupAffine::new(x, y))
.into();
Ok(g)

+ 25
- 26
src/groups/curves/short_weierstrass/mnt4/mod.rs

@ -291,33 +291,32 @@ impl G2PreparedVar

{

t: Fp2G::<P>::one(),
};
for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() {
let mut tmp = *value;
let skip_extraneous_bits = 64 - value.leading_zeros();
let mut v = Vec::with_capacity(16);
for i in 0..64 {
if idx == 0 && (i == 0 || i >= skip_extraneous_bits) {
continue;
}
v.push(tmp & 1 == 1);
tmp >>= 1;
}
for bit in v.iter().rev() {
let (r2, coeff) = PairingVar::<P>::doubling_step_for_flipped_miller_loop(&r)?;
g2p.double_coefficients.push(coeff);
r = r2;
if *bit {
let (r2, coeff) = PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x, &q.y, &r,
)?;
g2p.addition_coefficients.push(coeff);
r = r2;
}
tmp >>= 1;
for bit in P::ATE_LOOP_COUNT.iter().skip(1) {
let (r2, coeff) = PairingVar::<P>::doubling_step_for_flipped_miller_loop(&r)?;
g2p.double_coefficients.push(coeff);
r = r2;
let add_coeff;
let r_temp;
match bit {
1 => {
(r_temp, add_coeff) =
PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x, &q.y, &r,
)?;
},
-1 => {
(r_temp, add_coeff) =
PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x,
&q.y.negate()?,
&r,
)?;
},
_ => continue,
}
g2p.addition_coefficients.push(add_coeff);
r = r_temp;
}
if P::ATE_IS_LOOP_COUNT_NEG {

+ 25
- 26
src/groups/curves/short_weierstrass/mnt6/mod.rs

@ -291,33 +291,32 @@ impl G2PreparedVar

{

t: Fp3G::<P>::one(),
};
for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() {
let mut tmp = *value;
let skip_extraneous_bits = 64 - value.leading_zeros();
let mut v = Vec::with_capacity(16);
for i in 0..64 {
if idx == 0 && (i == 0 || i >= skip_extraneous_bits) {
continue;
}
v.push(tmp & 1 == 1);
tmp >>= 1;
}
for bit in v.iter().rev() {
let (r2, coeff) = PairingVar::<P>::doubling_step_for_flipped_miller_loop(&r)?;
g2p.double_coefficients.push(coeff);
r = r2;
if *bit {
let (r2, coeff) = PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x, &q.y, &r,
)?;
g2p.addition_coefficients.push(coeff);
r = r2;
}
tmp >>= 1;
for bit in P::ATE_LOOP_COUNT.iter().skip(1) {
let (r2, coeff) = PairingVar::<P>::doubling_step_for_flipped_miller_loop(&r)?;
g2p.double_coefficients.push(coeff);
r = r2;
let add_coeff;
let r_temp;
match bit {
1 => {
(r_temp, add_coeff) =
PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x, &q.y, &r,
)?;
},
-1 => {
(r_temp, add_coeff) =
PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
&q.x,
&q.y.negate()?,
&r,
)?;
},
_ => continue,
}
g2p.addition_coefficients.push(add_coeff);
r = r_temp;
}
if P::ATE_IS_LOOP_COUNT_NEG {

+ 9
- 5
src/groups/curves/short_weierstrass/mod.rs

@ -2,7 +2,7 @@ use ark_ec::{
short_weierstrass::{
Affine as SWAffine, Projective as SWProjective, SWCurveConfig as SWModelParameters,
},
AffineCurve, ProjectiveCurve,
AffineRepr, CurveGroup,
};
use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero};
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
@ -94,7 +94,7 @@ where
/// constraint system.
pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
Ok(match self.infinity.value()? {
true => SWAffine::zero(),
true => SWAffine::identity(),
false => SWAffine::new(self.x.value()?, self.y.value()?),
})
}
@ -137,7 +137,7 @@ where
let result = if let Some(z_inv) = z.inverse() {
SWAffine::new(x * &z_inv, y * &z_inv)
} else {
SWAffine::zero()
SWAffine::identity()
};
Ok(result.into())
}
@ -209,7 +209,7 @@ where
let (x, y, z) = match f() {
Ok(ge) => {
let ge = ge.into_affine();
if ge.is_zero() {
if ge.is_identity() {
(
Ok(P::BaseField::zero()),
Ok(P::BaseField::one()),
@ -782,7 +782,11 @@ where
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError> {
Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode)
Self::new_variable(
cs,
|| f().map(|b| SWProjective::from((*b.borrow()).clone())),
mode,
)
}
}

+ 8
- 5
src/groups/curves/short_weierstrass/non_zero_affine.rs

@ -1,4 +1,6 @@
use super::*;
use ark_ec::Group;
use ark_std::ops::Add;
/// An affine representation of a prime order curve point that is guaranteed
/// to *not* be the point at infinity.
@ -43,8 +45,7 @@ where
#[tracing::instrument(target = "r1cs", skip(self, other))]
pub fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
if [self, other].is_constant() {
let result =
(self.value()?.into_projective() + other.value()?.into_projective()).into_affine();
let result = self.value()?.add(other.value()?).into_affine();
Ok(Self::new(F::constant(result.x), F::constant(result.y)))
} else {
let (x1, y1) = (&self.x, &self.y);
@ -70,9 +71,11 @@ where
#[tracing::instrument(target = "r1cs", skip(self))]
pub fn double(&self) -> Result<Self, SynthesisError> {
if [self].is_constant() {
let result = self.value()?.into_projective().double().into_affine();
let result = SWProjective::<P>::from(self.value()?)
.double()
.into_affine();
// Panic if the result is zero.
assert!(!result.is_zero());
assert!(!result.is_identity());
Ok(Self::new(F::constant(result.x), F::constant(result.y)))
} else {
let (x1, y1) = (&self.x, &self.y);
@ -236,7 +239,7 @@ mod test_non_zero_affine {
},
R1CSVar,
};
use ark_ec::{models::short_weierstrass::SWCurveConfig, ProjectiveCurve};
use ark_ec::{models::short_weierstrass::SWCurveConfig, CurveGroup};
use ark_relations::r1cs::ConstraintSystem;
use ark_std::{vec::Vec, One};
use ark_test_curves::bls12_381::{g1::Parameters as G1Parameters, Fq};

+ 8
- 5
src/groups/curves/twisted_edwards/mod.rs

@ -3,10 +3,9 @@ use ark_ec::{
Affine as TEAffine, MontCurveConfig as MontgomeryModelParameter,
Projective as TEProjective, TECurveConfig as TEModelParameters,
},
AffineCurve, ProjectiveCurve,
AffineRepr, CurveGroup, Group,
};
use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero};
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use crate::{prelude::*, ToConstraintFieldGadget, Vec};
@ -85,7 +84,7 @@ mod montgomery_affine_impl {
p: &TEAffine<P>,
) -> Result<(P::BaseField, P::BaseField), SynthesisError> {
let montgomery_point: GroupAffine<P> = if p.y == P::BaseField::one() {
GroupAffine::zero()
GroupAffine::identity()
} else if p.x == P::BaseField::zero() {
GroupAffine::new(P::BaseField::zero(), P::BaseField::zero())
} else {
@ -543,7 +542,7 @@ where
if bits.len() == 2 {
let mut table = [multiples[0], multiples[1], multiples[0] + multiples[1]];
TEProjective::batch_normalization(&mut table);
TEProjective::normalize_batch(&mut table);
let x_s = [zero.x, table[0].x, table[1].x, table[2].x];
let y_s = [zero.y, table[0].y, table[1].y, table[2].y];
@ -675,7 +674,11 @@ where
f: impl FnOnce() -> Result<Point, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError> {
Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode)
Self::new_variable(
cs,
|| f().map(|b| TEProjective::<P>::from((*b.borrow()).clone())),
mode,
)
}
}

+ 2
- 2
src/groups/mod.rs

@ -1,9 +1,9 @@
use crate::prelude::*;
use ark_ec::ProjectiveCurve;
use ark_ff::Field;
use ark_relations::r1cs::{Namespace, SynthesisError};
use core::ops::{Add, AddAssign, Sub, SubAssign};
use ark_ec::CurveGroup;
use core::{borrow::Borrow, fmt::Debug};
/// This module contains implementations of arithmetic for various curve models.
@ -25,7 +25,7 @@ pub trait GroupOpsBounds<'a, F, T: 'a>:
/// A variable that represents a curve point for
/// the curve `C`.
pub trait CurveVar<C: ProjectiveCurve, ConstraintF: Field>:
pub trait CurveVar<C: CurveGroup, ConstraintF: Field>:
'static
+ Sized
+ Clone

+ 18
- 8
src/pairing/mnt4/mod.rs

@ -10,7 +10,6 @@ use crate::{
},
};
use ark_ec::mnt4::{MNT4Parameters, MNT4};
use ark_ff::BitIteratorBE;
use core::marker::PhantomData;
@ -105,10 +104,8 @@ impl PairingVar

{

// code below gets executed for all bits (EXCEPT the MSB itself) of
// mnt6_param_p (skipping leading zeros) in MSB to LSB order
for (dbl_idx, bit) in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT)
.skip(1)
.enumerate()
{
let y_over_twist_neg = &q.y_over_twist.negate()?;
for (dbl_idx, bit) in P::ATE_LOOP_COUNT.iter().skip(1).enumerate() {
let dc = &q.double_coefficients[dbl_idx];
let g_rr_at_p = Fp4G::<P>::new(
@ -118,16 +115,29 @@ impl PairingVar

{

f = f.square()? * &g_rr_at_p;
if bit {
let g_rq_at_p;
// Compute l_{R,Q}(P) if bit == 1, and l_{R,-Q}(P) if bit == -1
if *bit == 1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;
let g_rq_at_p = Fp4G::<P>::new(
g_rq_at_p = Fp4G::<P>::new(
&ac.c_rz * &p.y_twist,
(&q.y_over_twist * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
);
f *= &g_rq_at_p;
} else if *bit == -1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;
g_rq_at_p = Fp4G::<P>::new(
&ac.c_rz * &p.y_twist,
(y_over_twist_neg * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
);
} else {
continue;
}
f *= &g_rq_at_p;
}
if P::ATE_IS_LOOP_COUNT_NEG {

+ 20
- 10
src/pairing/mnt6/mod.rs

@ -10,7 +10,6 @@ use crate::{
},
};
use ark_ec::mnt6::{MNT6Parameters, MNT6};
use ark_ff::fields::BitIteratorBE;
use core::marker::PhantomData;
/// Specifies the constraints for computing a pairing in a MNT6 bilinear group.
@ -100,29 +99,40 @@ impl PairingVar

{

// code below gets executed for all bits (EXCEPT the MSB itself) of
// mnt6_param_p (skipping leading zeros) in MSB to LSB order
for (dbl_idx, bit) in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT)
.skip(1)
.enumerate()
{
let y_over_twist_neg = &q.y_over_twist.negate()?;
for (dbl_idx, bit) in P::ATE_LOOP_COUNT.iter().skip(1).enumerate() {
let dc = &q.double_coefficients[dbl_idx];
let g_rr_at_p = Fp6Var::new(
let g_rr_at_p = Fp6G::<P>::new(
&dc.c_l - &dc.c_4c - &dc.c_j * &p.x_twist,
&dc.c_h * &p.y_twist,
);
f = f.square()? * &g_rr_at_p;
if bit {
let g_rq_at_p;
// Compute l_{R,Q}(P) if bit == 1, and l_{R,-Q}(P) if bit == -1
if *bit == 1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;
let g_rq_at_p = Fp6Var::new(
g_rq_at_p = Fp6G::<P>::new(
&ac.c_rz * &p.y_twist,
(&q.y_over_twist * &ac.c_rz + &(&l1_coeff * &ac.c_l1)).negate()?,
(&q.y_over_twist * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
);
f *= &g_rq_at_p;
} else if *bit == -1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;
g_rq_at_p = Fp6G::<P>::new(
&ac.c_rz * &p.y_twist,
(y_over_twist_neg * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
);
} else {
continue;
}
f *= &g_rq_at_p;
}
if P::ATE_IS_LOOP_COUNT_NEG {

+ 9
- 7
src/pairing/mod.rs

@ -1,5 +1,6 @@
use crate::prelude::*;
use ark_ec::PairingEngine;
use ark_ec::pairing::Pairing;
use ark_ec::CurveGroup;
use ark_ff::Field;
use ark_relations::r1cs::SynthesisError;
use core::fmt::Debug;
@ -13,22 +14,23 @@ pub mod mnt6;
/// Specifies the constraints for computing a pairing in the yybilinear group
/// `E`.
pub trait PairingVar<E: PairingEngine, ConstraintF: Field = <E as PairingEngine>::Fq> {
pub trait PairingVar<E: Pairing, ConstraintF: Field = <<E as Pairing>::G1 as CurveGroup>::BaseField>
{
/// An variable representing an element of `G1`.
/// This is the R1CS equivalent of `E::G1Projective`.
type G1Var: CurveVar<E::G1Projective, ConstraintF>
+ AllocVar<E::G1Projective, ConstraintF>
type G1Var: CurveVar<E::G1, ConstraintF>
+ AllocVar<E::G1, ConstraintF>
+ AllocVar<E::G1Affine, ConstraintF>;
/// An variable representing an element of `G2`.
/// This is the R1CS equivalent of `E::G2Projective`.
type G2Var: CurveVar<E::G2Projective, ConstraintF>
+ AllocVar<E::G2Projective, ConstraintF>
type G2Var: CurveVar<E::G2, ConstraintF>
+ AllocVar<E::G2, ConstraintF>
+ AllocVar<E::G2Affine, ConstraintF>;
/// An variable representing an element of `GT`.
/// This is the R1CS equivalent of `E::GT`.
type GTVar: FieldVar<E::Fqk, ConstraintF>;
type GTVar: FieldVar<E::TargetField, ConstraintF>;
/// An variable representing cached precomputation that can speed up
/// pairings computations. This is the R1CS equivalent of

+ 17
- 17
tests/arithmetic_tests.rs

@ -1,5 +1,5 @@
use ark_bls12_381::Bls12_381;
use ark_ec::PairingEngine;
use ark_ec::{pairing::Pairing, CurveGroup};
use ark_ff::{BigInteger, PrimeField};
use ark_mnt4_298::MNT4_298;
use ark_mnt4_753::MNT4_753;
@ -673,48 +673,48 @@ macro_rules! nonnative_test {
nonnative_test!(
MNT46Small,
<MNT4_298 as PairingEngine>::Fr,
<MNT6_298 as PairingEngine>::Fr
<MNT4_298 as Pairing>::ScalarField,
<MNT6_298 as Pairing>::ScalarField
);
nonnative_test!(
MNT64Small,
<MNT6_298 as PairingEngine>::Fr,
<MNT4_298 as PairingEngine>::Fr
<MNT6_298 as Pairing>::ScalarField,
<MNT4_298 as Pairing>::ScalarField
);
nonnative_test!(
MNT46Big,
<MNT4_753 as PairingEngine>::Fr,
<MNT6_753 as PairingEngine>::Fr
<MNT4_753 as Pairing>::ScalarField,
<MNT6_753 as Pairing>::ScalarField
);
nonnative_test!(
MNT64Big,
<MNT6_753 as PairingEngine>::Fr,
<MNT4_753 as PairingEngine>::Fr
<MNT6_753 as Pairing>::ScalarField,
<MNT4_753 as Pairing>::ScalarField
);
nonnative_test!(
BLS12MNT4Small,
<Bls12_381 as PairingEngine>::Fr,
<MNT4_298 as PairingEngine>::Fr
<Bls12_381 as Pairing>::ScalarField,
<MNT4_298 as Pairing>::ScalarField
);
nonnative_test!(
BLS12,
<Bls12_381 as PairingEngine>::Fq,
<Bls12_381 as PairingEngine>::Fr
<<Bls12_381 as Pairing>::G1 as CurveGroup>::BaseField,
<Bls12_381 as Pairing>::ScalarField
);
#[cfg(not(ci))]
nonnative_test!(
MNT6BigMNT4Small,
<MNT6_753 as PairingEngine>::Fr,
<MNT4_298 as PairingEngine>::Fr
<MNT6_753 as Pairing>::ScalarField,
<MNT4_298 as Pairing>::ScalarField
);
nonnative_test!(
PallasFrMNT6Fr,
ark_pallas::Fr,
<MNT6_753 as PairingEngine>::Fr
<MNT6_753 as Pairing>::ScalarField
);
nonnative_test!(
MNT6FrPallasFr,
<MNT6_753 as PairingEngine>::Fr,
<MNT6_753 as Pairing>::ScalarField,
ark_pallas::Fr
);
nonnative_test!(PallasFqFr, ark_pallas::Fq, ark_pallas::Fr);

Loading…
Cancel
Save