diff --git a/ec/src/models/bn/g2.rs b/ec/src/models/bn/g2.rs index b8444b2a2..db0d74e68 100644 --- a/ec/src/models/bn/g2.rs +++ b/ec/src/models/bn/g2.rs @@ -40,6 +40,88 @@ pub struct G2HomProjective { z: Fp2, } +impl G2Prepared

{ + fn affine_double_in_place(t: &mut G2Affine

, three_div_two: &P::Fp) -> EllCoeff

{ + // for affine coordinates + // slope: alpha = 3 * x^2 / 2 * y + let mut alpha = t.x.square(); + alpha /= t.y; + alpha.mul_assign_by_fp(&three_div_two); + let bias = t.y - alpha * t.x; + + // update T + // T.x = alpha^2 - 2 * t.x + // T.y = -bias - alpha * T.x + let tx = alpha.square() - t.x.double(); + t.y = -bias - alpha * tx; + t.x = tx; + + (Fp2::::ONE, alpha, -bias) + } + + fn affine_add_in_place(t: &mut G2Affine

, q: &G2Affine

) -> EllCoeff

{ + // alpha = (t.y - q.y) / (t.x - q.x) + // bias = t.y - alpha * t.x + let alpha = (t.y - q.y) / (t.x - q.x); + let bias = t.y - alpha * t.x; + + // update T + // T.x = alpha^2 - t.x - q.x + // T.y = -bias - alpha * T.x + let tx = alpha.square() - t.x - q.x; + t.y = -bias - alpha * tx; + t.x = tx; + + (Fp2::::ONE, alpha, -bias) + } + + /// !!! this method cannot be used directly for users, so we need reuse the `from` trait already exists + fn from_affine(q: G2Affine

) -> Self { + if q.infinity { + G2Prepared { + ell_coeffs: vec![], + infinity: true, + } + } else { + // let two_inv = P::Fp::one().double().inverse().unwrap(); + let two_inv = P::Fp::one().double().inverse().unwrap(); + let three_div_two = (P::Fp::one().double() + P::Fp::one()) * two_inv; + + let mut ell_coeffs = vec![]; + let mut r = q.clone(); + + let neg_q = -q; + + for bit in P::ATE_LOOP_COUNT.iter().rev().skip(1) { + ell_coeffs.push(Self::affine_double_in_place(&mut r, &three_div_two)); + + match bit { + 1 => ell_coeffs.push(Self::affine_add_in_place(&mut r, &q)), + -1 => ell_coeffs.push(Self::affine_add_in_place(&mut r, &neg_q)), + _ => continue, + } + } + + let q1 = mul_by_char::

(q); + let mut q2 = mul_by_char::

(q1); + + if P::X_IS_NEGATIVE { + r.y = -r.y; + } + + q2.y = -q2.y; + + ell_coeffs.push(Self::affine_add_in_place(&mut r, &q1)); + ell_coeffs.push(Self::affine_add_in_place(&mut r, &q2)); + + Self { + ell_coeffs, + infinity: false, + } + } + } +} + impl G2HomProjective

{ pub fn double_in_place(&mut self, two_inv: &P::Fp) -> EllCoeff

{ // Formula for line function when working with @@ -96,8 +178,24 @@ impl Default for G2Prepared

{ } } +/// !!! affine mode is for the purpose of verifying pairings impl From> for G2Prepared

{ fn from(q: G2Affine

) -> Self { + if q.infinity { + G2Prepared { + ell_coeffs: vec![], + infinity: true, + } + } else { + Self::from_affine(q) + } + } +} + +/// !!! projective mode is for the purpose of computing pairings +impl From> for G2Prepared

{ + fn from(q: G2Projective

) -> Self { + let q = q.into_affine(); if q.infinity { G2Prepared { ell_coeffs: vec![], @@ -144,12 +242,6 @@ impl From> for G2Prepared

{ } } -impl From> for G2Prepared

{ - fn from(q: G2Projective

) -> Self { - q.into_affine().into() - } -} - impl<'a, P: BnConfig> From<&'a G2Affine

> for G2Prepared

{ fn from(other: &'a G2Affine

) -> Self { (*other).into() @@ -158,7 +250,7 @@ impl<'a, P: BnConfig> From<&'a G2Affine

> for G2Prepared

{ impl<'a, P: BnConfig> From<&'a G2Projective

> for G2Prepared

{ fn from(q: &'a G2Projective

) -> Self { - q.into_affine().into() + (*q).into() } } diff --git a/ec/src/models/bn/mod.rs b/ec/src/models/bn/mod.rs index b09e61b04..ca46c1f3c 100644 --- a/ec/src/models/bn/mod.rs +++ b/ec/src/models/bn/mod.rs @@ -102,6 +102,71 @@ pub trait BnConfig: 'static + Sized { MillerLoopOutput(f) } + fn multi_miller_loop_affine( + a: impl IntoIterator>>, + b: impl IntoIterator>>, + ) -> MillerLoopOutput> { + let mut pairs = a + .into_iter() + .zip_eq(b) + .filter_map(|(p, q)| { + // if input q is projective coordinates, then we will enter `into`` computing pairing mode + // otherwise if input q is affine coordinates, then we will enter `into` verifying pairing mode + let (p, q) = (p.into(), q.into()); + match !p.is_zero() && !q.is_zero() { + true => Some(( + -p.0.x / p.0.y, + p.0.y.inverse().unwrap(), + q.ell_coeffs.into_iter(), + )), + false => None, + } + }) + .collect::>(); + + let mut f = cfg_chunks_mut!(pairs, 4) + .map(|pairs| { + let mut f = as Pairing>::TargetField::one(); + for i in (1..Self::ATE_LOOP_COUNT.len()).rev() { + if i != Self::ATE_LOOP_COUNT.len() - 1 { + f.square_in_place(); + } + + for (coeff_1, coeff_2, coeffs) in pairs.iter_mut() { + Bn::::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2); + } + + let bit = Self::ATE_LOOP_COUNT[i - 1]; + if bit == 1 || bit == -1 { + for (coeff_1, coeff_2, coeffs) in pairs.iter_mut() { + Bn::::ell_affine( + &mut f, + &coeffs.next().unwrap(), + &coeff_1, + &coeff_2, + ); + } + } + } + f + }) + .product::< as Pairing>::TargetField>(); + + if Self::X_IS_NEGATIVE { + f.cyclotomic_inverse_in_place(); + } + + for (coeff_1, coeff_2, coeffs) in &mut pairs { + Bn::::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2); + } + + for (coeff_1, coeff_2, coeffs) in &mut pairs { + Bn::::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2); + } + + MillerLoopOutput(f) + } + #[allow(clippy::let_and_return)] fn final_exponentiation(f: MillerLoopOutput>) -> Option>> { // Easy part: result = elt^((q^6-1)*(q^2+1)). @@ -180,7 +245,7 @@ pub use self::{ pub struct Bn(PhantomData P>); impl Bn

{ - /// Evaluates the line function at point p. + /// Evaluates the line function at point p, where the line function is in projective mode fn ell(f: &mut Fp12, coeffs: &g2::EllCoeff

, p: &G1Affine

) { let mut c0 = coeffs.0; let mut c1 = coeffs.1; @@ -200,6 +265,35 @@ impl Bn

{ } } + /// Evaluates the line function at point p, where the line function is in affine mode + /// input: + /// f, Fq12 + /// coeffs, (1, alpha, bias) + /// x' = -p.x / p.y + /// y' = 1 / p.y + /// output: + /// f = f * f_Q(P)', where f_Q(P)' is a vairant of f_Q(P), f_Q(P) = y' * f_Q(P) + fn ell_affine(f: &mut Fp12, coeffs: &g2::EllCoeff

, xx: &P::Fp, yy: &P::Fp) { + // c0 is a trival value 1 + let c0 = coeffs.0; + let mut c1 = coeffs.1; + let mut c2 = coeffs.2; + + match P::TWIST_TYPE { + TwistType::M => { + c1.mul_assign_by_fp(&xx); + c2.mul_assign_by_fp(&yy); + f.mul_by_014(&c0, &c1, &c2); + }, + // line evaluation is y' * f_Q(P), coefficients are (1, x' * lambda, -y' * bias) + TwistType::D => { + c1.mul_assign_by_fp(&xx); + c2.mul_assign_by_fp(&yy); + f.mul_by_034(&c0, &c1, &(c2)); + }, + } + } + fn exp_by_neg_x(mut f: Fp12) -> Fp12 { f = f.cyclotomic_exp(P::X); if !P::X_IS_NEGATIVE { @@ -227,6 +321,13 @@ impl Pairing for Bn

{ P::multi_miller_loop(a, b) } + fn multi_miller_loop_affine( + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> MillerLoopOutput { + P::multi_miller_loop_affine(a, b) + } + fn final_exponentiation(f: MillerLoopOutput) -> Option> { P::final_exponentiation(f) } diff --git a/ec/src/pairing.rs b/ec/src/pairing.rs index d92bad976..fd2286489 100644 --- a/ec/src/pairing.rs +++ b/ec/src/pairing.rs @@ -88,6 +88,14 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq { b: impl IntoIterator>, ) -> MillerLoopOutput; + /// Computes the product of Miller loops for some number of (G1, G2) pairs, where the line functions are in affine mode + fn multi_miller_loop_affine( + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> MillerLoopOutput { + unimplemented!() + } + /// Computes the Miller loop over `a` and `b`. fn miller_loop( a: impl Into, @@ -108,6 +116,14 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq { Self::final_exponentiation(Self::multi_miller_loop(a, b)).unwrap() } + /// Computes a "product" of pairings, where the line functions are in affine mode + fn multi_pairing_affine( + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> PairingOutput { + Self::final_exponentiation(Self::multi_miller_loop_affine(a, b)).unwrap() + } + /// Performs multiple pairing operations fn pairing( p: impl Into, @@ -115,6 +131,14 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq { ) -> PairingOutput { Self::multi_pairing([p], [q]) } + + /// Performs multiple pairing operations, where the line functions are in affine mode + fn pairing_affine( + p: impl Into, + q: impl Into, + ) -> PairingOutput { + Self::multi_pairing_affine([p], [q]) + } } /// Represents the target group of a pairing. This struct is a diff --git a/test-templates/src/pairing.rs b/test-templates/src/pairing.rs index 755ee22f7..6bdf72eb3 100644 --- a/test-templates/src/pairing.rs +++ b/test-templates/src/pairing.rs @@ -6,8 +6,53 @@ macro_rules! test_pairing { use ark_ec::{pairing::*, CurveGroup, PrimeGroup}; use ark_ff::{CyclotomicMultSubgroup, Field, PrimeField}; use ark_std::{test_rng, One, UniformRand, Zero}; + #[test] - fn test_bilinearity() { + fn test_multi_pairing_bilinearity_affine() { + let mut rng = test_rng(); + let g1: <$Pairing as Pairing>::G1 = UniformRand::rand(&mut rng); + let g2: <$Pairing as Pairing>::G2 = UniformRand::rand(&mut rng); + let s1: <$Pairing as Pairing>::ScalarField = UniformRand::rand(&mut rng); + let s2: <$Pairing as Pairing>::ScalarField = UniformRand::rand(&mut rng); + let s3 = s1 + s2; + + let (p1, p2, p3) = (g1.into_affine(), (g1 * s2).into_affine(), g1.into_affine()); + // affine mode + let (q1, q2, q3) = ( + (g2 * s1).into_affine(), + g2.into_affine(), + (g2 * s3).into_affine(), + ); + let e1 = <$Pairing>::pairing_affine(p1, q1); + let e2 = <$Pairing>::pairing_affine(p2, q2); + let e3 = <$Pairing>::pairing_affine(p3, q3); + let e33 = <$Pairing>::multi_pairing_affine(&[p1, p2], &[q1, q2]); + assert_eq!(e1 + e2, e3); + assert_eq!(e3, e33); + } + + #[test] + fn test_multi_pairing_bilinearity_projective() { + let mut rng = test_rng(); + let g1: <$Pairing as Pairing>::G1 = UniformRand::rand(&mut rng); + let g2: <$Pairing as Pairing>::G2 = UniformRand::rand(&mut rng); + let s1: <$Pairing as Pairing>::ScalarField = UniformRand::rand(&mut rng); + let s2: <$Pairing as Pairing>::ScalarField = UniformRand::rand(&mut rng); + let s3 = s1 + s2; + + let (p1, p2, p3) = (g1.into_affine(), (g1 * s2).into_affine(), g1.into_affine()); + // projective mode + let (q1, q2, q3) = (g2 * s1, g2, g2 * s3); + let e1 = <$Pairing>::pairing(p1, q1); + let e2 = <$Pairing>::pairing(p2, q2); + let e3 = <$Pairing>::pairing(p3, q3); + let e33 = <$Pairing>::multi_pairing(&[p1, p2], &[q1, q2]); + assert_eq!(e1 + e2, e3); + assert_eq!(e3, e33); + } + + #[test] + fn test_bilinearity_projective() { for _ in 0..100 { let mut rng = test_rng(); let a: <$Pairing as Pairing>::G1 = UniformRand::rand(&mut rng); @@ -36,7 +81,51 @@ macro_rules! test_pairing { } #[test] - fn test_multi_pairing() { + fn test_bilinearity_affine() { + for _ in 0..100 { + let mut rng = test_rng(); + let a: <$Pairing as Pairing>::G1 = UniformRand::rand(&mut rng); + let b: <$Pairing as Pairing>::G2 = UniformRand::rand(&mut rng); + let s: <$Pairing as Pairing>::ScalarField = UniformRand::rand(&mut rng); + + let sa = a * s; + let sb = b * s; + + let ans1 = <$Pairing>::pairing_affine(sa, b.into_affine()); + let ans2 = <$Pairing>::pairing_affine(a, sb.into_affine()); + let ans3 = <$Pairing>::pairing_affine(a, b.into_affine()) * s; + + assert_eq!(ans1, ans2); + assert_eq!(ans2, ans3); + + assert_ne!(ans1, PairingOutput::zero()); + assert_ne!(ans2, PairingOutput::zero()); + assert_ne!(ans3, PairingOutput::zero()); + let group_order = <<$Pairing as Pairing>::ScalarField>::characteristic(); + + assert_eq!(ans1.mul_bigint(group_order), PairingOutput::zero()); + assert_eq!(ans2.mul_bigint(group_order), PairingOutput::zero()); + assert_eq!(ans3.mul_bigint(group_order), PairingOutput::zero()); + } + } + + #[test] + fn test_multi_pairing_projective() { + for _ in 0..ITERATIONS { + let rng = &mut test_rng(); + + let a = <$Pairing as Pairing>::G1::rand(rng); + let b = <$Pairing as Pairing>::G2::rand(rng); + let c = <$Pairing as Pairing>::G1::rand(rng); + let d = <$Pairing as Pairing>::G2::rand(rng); + let ans1 = <$Pairing>::pairing(a, b) + &<$Pairing>::pairing(c, d); + let ans2 = <$Pairing>::multi_pairing(&[a, c], &[b, d]); + assert_eq!(ans1, ans2); + } + } + + #[test] + fn test_multi_pairing_affine() { for _ in 0..ITERATIONS { let rng = &mut test_rng(); @@ -44,12 +133,26 @@ macro_rules! test_pairing { let b = <$Pairing as Pairing>::G2::rand(rng).into_affine(); let c = <$Pairing as Pairing>::G1::rand(rng).into_affine(); let d = <$Pairing as Pairing>::G2::rand(rng).into_affine(); - let ans1 = <$Pairing>::pairing(a, b) + &<$Pairing>::pairing(c, d); - let ans2 = <$Pairing>::multi_pairing(&[a, c], &[b, d]); + let ans1 = <$Pairing>::pairing_affine(a, b) + &<$Pairing>::pairing_affine(c, d); + let ans2 = <$Pairing>::multi_pairing_affine(&[a, c], &[b, d]); assert_eq!(ans1, ans2); } } + #[test] + fn test_pairing_affine_vs_projective() { + let rng = &mut test_rng(); + + let a_proj = <$Pairing as Pairing>::G1::rand(rng); + let b_proj = <$Pairing as Pairing>::G2::rand(rng); + let a_affine = a_proj.into_affine(); + let b_affine = b_proj.into_affine(); + + let ans1 = <$Pairing>::multi_pairing(&[a_proj], &[b_proj]); + let ans2 = <$Pairing>::multi_pairing_affine(&[a_affine], &[b_affine]); + assert_eq!(ans1, ans2); + } + #[test] fn test_final_exp() { for _ in 0..ITERATIONS {