From 6512e48540cf98d4bb9e1a4b2badd077867f510a Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 3 Sep 2022 09:15:45 +0200 Subject: [PATCH] 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 Co-authored-by: onewayfunc --- .github/workflows/ci.yml | 1 + Cargo.toml | 1 + .../curves/short_weierstrass/bls12/mod.rs | 2 +- .../curves/short_weierstrass/mnt4/mod.rs | 51 +++++++++---------- .../curves/short_weierstrass/mnt6/mod.rs | 51 +++++++++---------- src/groups/curves/short_weierstrass/mod.rs | 14 +++-- .../short_weierstrass/non_zero_affine.rs | 13 +++-- src/groups/curves/twisted_edwards/mod.rs | 13 +++-- src/groups/mod.rs | 4 +- src/pairing/mnt4/mod.rs | 26 +++++++--- src/pairing/mnt6/mod.rs | 30 +++++++---- src/pairing/mod.rs | 16 +++--- tests/arithmetic_tests.rs | 34 ++++++------- 13 files changed, 144 insertions(+), 112 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8364904..e9c8378 100644 --- a/.github/workflows/ci.yml +++ b/.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 diff --git a/Cargo.toml b/Cargo.toml index d9ad88c..1c72ac8 100644 --- a/Cargo.toml +++ b/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" } diff --git a/src/groups/curves/short_weierstrass/bls12/mod.rs b/src/groups/curves/short_weierstrass/bls12/mod.rs index 7c5a856..99dcd11 100644 --- a/src/groups/curves/short_weierstrass/bls12/mod.rs +++ b/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) diff --git a/src/groups/curves/short_weierstrass/mnt4/mod.rs b/src/groups/curves/short_weierstrass/mnt4/mod.rs index d55476a..51e98c5 100644 --- a/src/groups/curves/short_weierstrass/mnt4/mod.rs +++ b/src/groups/curves/short_weierstrass/mnt4/mod.rs @@ -291,33 +291,32 @@ impl G2PreparedVar

{ t: Fp2G::

::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::

::doubling_step_for_flipped_miller_loop(&r)?; - g2p.double_coefficients.push(coeff); - r = r2; - - if *bit { - let (r2, coeff) = PairingVar::

::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::

::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::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; + }, + -1 => { + (r_temp, add_coeff) = + PairingVar::

::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 { diff --git a/src/groups/curves/short_weierstrass/mnt6/mod.rs b/src/groups/curves/short_weierstrass/mnt6/mod.rs index 50a0c86..b3ab838 100644 --- a/src/groups/curves/short_weierstrass/mnt6/mod.rs +++ b/src/groups/curves/short_weierstrass/mnt6/mod.rs @@ -291,33 +291,32 @@ impl G2PreparedVar

{ t: Fp3G::

::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::

::doubling_step_for_flipped_miller_loop(&r)?; - g2p.double_coefficients.push(coeff); - r = r2; - - if *bit { - let (r2, coeff) = PairingVar::

::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::

::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::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; + }, + -1 => { + (r_temp, add_coeff) = + PairingVar::

::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 { diff --git a/src/groups/curves/short_weierstrass/mod.rs b/src/groups/curves/short_weierstrass/mod.rs index a9111e5..4088f4d 100644 --- a/src/groups/curves/short_weierstrass/mod.rs +++ b/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, 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, mode: AllocationMode, ) -> Result { - 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, + ) } } diff --git a/src/groups/curves/short_weierstrass/non_zero_affine.rs b/src/groups/curves/short_weierstrass/non_zero_affine.rs index baf2321..1c83c3c 100644 --- a/src/groups/curves/short_weierstrass/non_zero_affine.rs +++ b/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 { 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 { if [self].is_constant() { - let result = self.value()?.into_projective().double().into_affine(); + let result = SWProjective::

::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}; diff --git a/src/groups/curves/twisted_edwards/mod.rs b/src/groups/curves/twisted_edwards/mod.rs index 6461a1b..1d2dffd 100644 --- a/src/groups/curves/twisted_edwards/mod.rs +++ b/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

, ) -> Result<(P::BaseField, P::BaseField), SynthesisError> { let montgomery_point: GroupAffine

= 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, mode: AllocationMode, ) -> Result { - Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode) + Self::new_variable( + cs, + || f().map(|b| TEProjective::

::from((*b.borrow()).clone())), + mode, + ) } } diff --git a/src/groups/mod.rs b/src/groups/mod.rs index 881c566..a02e303 100644 --- a/src/groups/mod.rs +++ b/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: +pub trait CurveVar: 'static + Sized + Clone diff --git a/src/pairing/mnt4/mod.rs b/src/pairing/mnt4/mod.rs index 1a2f506..b7ce0b3 100644 --- a/src/pairing/mnt4/mod.rs +++ b/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::

::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::

::new( + g_rq_at_p = Fp4G::

::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::

::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 { diff --git a/src/pairing/mnt6/mod.rs b/src/pairing/mnt6/mod.rs index fa56a91..350eb62 100644 --- a/src/pairing/mnt6/mod.rs +++ b/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::

::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::

::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::

::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 { diff --git a/src/pairing/mod.rs b/src/pairing/mod.rs index 157f26d..958134e 100644 --- a/src/pairing/mod.rs +++ b/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::Fq> { +pub trait PairingVar::G1 as CurveGroup>::BaseField> +{ /// An variable representing an element of `G1`. /// This is the R1CS equivalent of `E::G1Projective`. - type G1Var: CurveVar - + AllocVar + type G1Var: CurveVar + + AllocVar + AllocVar; /// An variable representing an element of `G2`. /// This is the R1CS equivalent of `E::G2Projective`. - type G2Var: CurveVar - + AllocVar + type G2Var: CurveVar + + AllocVar + AllocVar; /// An variable representing an element of `GT`. /// This is the R1CS equivalent of `E::GT`. - type GTVar: FieldVar; + type GTVar: FieldVar; /// An variable representing cached precomputation that can speed up /// pairings computations. This is the R1CS equivalent of diff --git a/tests/arithmetic_tests.rs b/tests/arithmetic_tests.rs index 4ff4361..983a8fc 100644 --- a/tests/arithmetic_tests.rs +++ b/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, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( MNT64Small, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( MNT46Big, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( MNT64Big, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( BLS12MNT4Small, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( BLS12, - ::Fq, - ::Fr + <::G1 as CurveGroup>::BaseField, + ::ScalarField ); #[cfg(not(ci))] nonnative_test!( MNT6BigMNT4Small, - ::Fr, - ::Fr + ::ScalarField, + ::ScalarField ); nonnative_test!( PallasFrMNT6Fr, ark_pallas::Fr, - ::Fr + ::ScalarField ); nonnative_test!( MNT6FrPallasFr, - ::Fr, + ::ScalarField, ark_pallas::Fr ); nonnative_test!(PallasFqFr, ark_pallas::Fq, ark_pallas::Fr);