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>
jump to
@@ -187,6 +187,7 @@ echo "ark-ec = { git = 'https://github.com/arkworks-rs/algebra' }"
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
@@ -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" }
@@ -43,7 +43,7 @@ let x = self.0.x.value()?;
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)
@@ -291,33 +291,32 @@ z: Fp2G::<P>::one(),
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 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; - 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; + 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 {
@@ -291,33 +291,32 @@ z: Fp3G::<P>::one(),
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 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; - 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; + 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 {
@@ -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 @@ /// Returns the value assigned to `self` in the underlying
/// 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 @@ let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?);
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 @@
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 @@ cs: impl Into<Namespace<<P::BaseField as Field>::BasePrimeField>>,
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, + ) } }
@@ -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 @@ /// Performs an addition without checking that other != ±self.
#[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 @@ /// the output is guaranteed to not be the point at infinity.
#[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 @@ CurveVar,
}, 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};
@@ -3,10 +3,9 @@ twisted_edwards::{
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 @@ pub fn from_edwards_to_coords(
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 @@ for (bits, multiples) in bits.chunks(2).zip(multiples.chunks(2)) {
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 @@ cs: impl Into<Namespace<<P::BaseField as Field>::BasePrimeField>>,
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, + ) } }
@@ -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 @@ }
/// 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
@@ -10,7 +10,6 @@ G2ProjectiveExtendedVar, G2Var,
}, }; use ark_ec::mnt4::{MNT4Parameters, MNT4}; -use ark_ff::BitIteratorBE; use core::marker::PhantomData;@@ -105,10 +104,8 @@ let mut add_idx: usize = 0;
// 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 @@ );
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 {
@@ -10,7 +10,6 @@ G2ProjectiveExtendedVar, G2Var,
}, }; 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 @@ let mut add_idx: usize = 0;
// 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; + + 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()?, + ); + } else 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()?, + (y_over_twist_neg * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?, ); - f *= &g_rq_at_p; + } else { + continue; } + + f *= &g_rq_at_p; } if P::ATE_IS_LOOP_COUNT_NEG {
@@ -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
@@ -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 @@ }
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);