From e13200883680bd2b5b3d8875b9c9d2a58a5eb231 Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 11 Mar 2023 14:59:30 +0100 Subject: [PATCH 01/11] Split the computaion of f_{u+1, Q}(P) into f_{u, Q}(P)*l_{Q, uQ}(P)} --- ec/src/models/bw6/g2.rs | 3 ++- ec/src/models/bw6/mod.rs | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 02430bead..71f643a2e 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -56,7 +56,7 @@ impl From> for G2Prepared

{ }; } - // f_{u+1,Q}(P) + // f_{u,Q}(P) let mut ell_coeffs_1 = vec![]; let mut r = G2HomProjective::

{ x: q.x, @@ -71,6 +71,7 @@ impl From> for G2Prepared

{ ell_coeffs_1.push(r.add_in_place(&q)); } } + ell_coeffs_1.push(r.add_in_place(&q)); // f_{u^3-u^2-u,Q}(P) let mut ell_coeffs_2 = vec![]; diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index 62c053fb8..f22f8a3e9 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -26,6 +26,7 @@ pub enum TwistType { pub trait BW6Config: 'static + Eq + Sized { const X: ::BigInt; const X_IS_NEGATIVE: bool; + // X const ATE_LOOP_COUNT_1: &'static [u64]; const ATE_LOOP_COUNT_1_IS_NEGATIVE: bool; const ATE_LOOP_COUNT_2: &'static [i8]; @@ -72,7 +73,8 @@ pub trait BW6Config: 'static + Eq + Sized { }) .unzip::<_, _, Vec<_>, Vec<_>>(); - let mut f_1 = cfg_chunks_mut!(pairs_1, 4) + // compute f_u which we can later re-use for the 2nd loop + let mut f_u = cfg_chunks_mut!(pairs_1, 4) .map(|pairs| { let mut f = as Pairing>::TargetField::one(); for i in BitIteratorBE::without_leading_zeros(Self::ATE_LOOP_COUNT_1).skip(1) { @@ -91,8 +93,21 @@ pub trait BW6Config: 'static + Eq + Sized { .product::< as Pairing>::TargetField>(); if Self::ATE_LOOP_COUNT_1_IS_NEGATIVE { - f_1.cyclotomic_inverse_in_place(); + f_u.cyclotomic_inverse_in_place(); } + + // f_1(P) = f_(u+1)(P) = f_u(P) * l([u]q, q)(P) + let f_1 = cfg_chunks_mut!(pairs_1, 4) + .map(|pairs| { + // TODO remove clone? + let mut f = f_u.clone(); + for (p, coeffs) in pairs.iter_mut() { + BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); + } + f + }) + .product::< as Pairing>::TargetField>(); + let mut f_2 = cfg_chunks_mut!(pairs_2, 4) .map(|pairs| { let mut f = < as Pairing>::TargetField>::one(); From 364d1a6ce0934842e8fff946fb1165cd38f6afe3 Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 11 Mar 2023 15:15:05 +0100 Subject: [PATCH 02/11] Get the point [u]Q and assert it's correct --- ec/src/models/bw6/g2.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 71f643a2e..d4eca487f 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -46,6 +46,17 @@ impl Default for G2Prepared

{ } } +// impl into G2Affine from G2HomProjective +impl From> for G2Affine

{ + fn from(q: G2HomProjective

) -> Self { + let z_inv = q.z.inverse().unwrap(); + let x = q.x * &z_inv; + let y = q.y * &z_inv; + // TODO: change this to `new_unchecked` + G2Affine::

::new(x, y) + } +} + impl From> for G2Prepared

{ fn from(q: G2Affine

) -> Self { if q.infinity { @@ -71,9 +82,13 @@ impl From> for G2Prepared

{ ell_coeffs_1.push(r.add_in_place(&q)); } } - ell_coeffs_1.push(r.add_in_place(&q)); + ell_coeffs_1.push(r.clone().add_in_place(&q)); // f_{u^3-u^2-u,Q}(P) + let qu: G2Affine

= r.into(); + assert_eq!(qu, q.mul_bigint(&P::ATE_LOOP_COUNT_1)); + assert_eq!(qu, q.mul_bigint(&P::X)); + let mut ell_coeffs_2 = vec![]; let mut r = G2HomProjective::

{ x: q.x, @@ -81,14 +96,14 @@ impl From> for G2Prepared

{ z: P::Fp::one(), }; - let negq = -q; + let neg_q = -q; for bit in P::ATE_LOOP_COUNT_2.iter().rev().skip(1) { ell_coeffs_2.push(r.double_in_place()); match bit { 1 => ell_coeffs_2.push(r.add_in_place(&q)), - -1 => ell_coeffs_2.push(r.add_in_place(&negq)), + -1 => ell_coeffs_2.push(r.add_in_place(&neg_q)), _ => continue, } } From 0f4af9c18513caa2ac75da1008df8417bc93384d Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 11 Mar 2023 16:09:51 +0100 Subject: [PATCH 03/11] Re-use f_u for the second loop --- ec/src/models/bw6/g2.rs | 13 ++++--------- ec/src/models/bw6/mod.rs | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index d4eca487f..32a5e979b 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -84,26 +84,21 @@ impl From> for G2Prepared

{ } ell_coeffs_1.push(r.clone().add_in_place(&q)); - // f_{u^3-u^2-u,Q}(P) + // f_{u^2-u-1,Q}(P) let qu: G2Affine

= r.into(); assert_eq!(qu, q.mul_bigint(&P::ATE_LOOP_COUNT_1)); assert_eq!(qu, q.mul_bigint(&P::X)); let mut ell_coeffs_2 = vec![]; - let mut r = G2HomProjective::

{ - x: q.x, - y: q.y, - z: P::Fp::one(), - }; - let neg_q = -q; + let neg_qu = -qu; for bit in P::ATE_LOOP_COUNT_2.iter().rev().skip(1) { ell_coeffs_2.push(r.double_in_place()); match bit { - 1 => ell_coeffs_2.push(r.add_in_place(&q)), - -1 => ell_coeffs_2.push(r.add_in_place(&neg_q)), + 1 => ell_coeffs_2.push(r.add_in_place(&qu)), + -1 => ell_coeffs_2.push(r.add_in_place(&neg_qu)), _ => continue, } } diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index f22f8a3e9..56f67193e 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -29,6 +29,7 @@ pub trait BW6Config: 'static + Eq + Sized { // X const ATE_LOOP_COUNT_1: &'static [u64]; const ATE_LOOP_COUNT_1_IS_NEGATIVE: bool; + // X^2 - X - 1 const ATE_LOOP_COUNT_2: &'static [i8]; const ATE_LOOP_COUNT_2_IS_NEGATIVE: bool; const TWIST_TYPE: TwistType; @@ -92,6 +93,9 @@ pub trait BW6Config: 'static + Eq + Sized { }) .product::< as Pairing>::TargetField>(); + let f_u_inv = f_u.cyclotomic_inverse().unwrap(); + + // TODO: figure if Self::ATE_LOOP_COUNT_1_IS_NEGATIVE { f_u.cyclotomic_inverse_in_place(); } @@ -99,8 +103,7 @@ pub trait BW6Config: 'static + Eq + Sized { // f_1(P) = f_(u+1)(P) = f_u(P) * l([u]q, q)(P) let f_1 = cfg_chunks_mut!(pairs_1, 4) .map(|pairs| { - // TODO remove clone? - let mut f = f_u.clone(); + let mut f = f_u; for (p, coeffs) in pairs.iter_mut() { BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); } @@ -110,18 +113,22 @@ pub trait BW6Config: 'static + Eq + Sized { let mut f_2 = cfg_chunks_mut!(pairs_2, 4) .map(|pairs| { - let mut f = < as Pairing>::TargetField>::one(); + let mut f = f_u; for i in (1..Self::ATE_LOOP_COUNT_2.len()).rev() { - if i != Self::ATE_LOOP_COUNT_2.len() - 1 { - f.square_in_place(); - } + f.square_in_place(); for (p, ref mut coeffs) in pairs.iter_mut() { BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); } let bit = Self::ATE_LOOP_COUNT_2[i - 1]; - if bit == 1 || bit == -1 { + if bit == 1 { + f *= &f_u; + for &mut (p, ref mut coeffs) in pairs.iter_mut() { + BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); + } + } else if bit == -1 { + f *= &f_u_inv; for &mut (p, ref mut coeffs) in pairs.iter_mut() { BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); } From dbab89a75e7abeb091a6d5afdab52cd733068a48 Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 11 Mar 2023 16:36:17 +0100 Subject: [PATCH 04/11] Add TODOs, remove unnecessary asserts --- ec/src/models/bw6/g2.rs | 11 ++++++----- ec/src/models/bw6/mod.rs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 32a5e979b..8deab04ec 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -84,15 +84,16 @@ impl From> for G2Prepared

{ } ell_coeffs_1.push(r.clone().add_in_place(&q)); - // f_{u^2-u-1,Q}(P) + // TODO: this is probably the slowest part + // While G2 preparation is overall faster due to shortened 2nd loop, + // The inversion could probably be avoided by using Hom(P) + Hom(Q) addition, + // instead of mixed addition as is currently done. let qu: G2Affine

= r.into(); - assert_eq!(qu, q.mul_bigint(&P::ATE_LOOP_COUNT_1)); - assert_eq!(qu, q.mul_bigint(&P::X)); + let neg_qu = -qu; let mut ell_coeffs_2 = vec![]; - let neg_qu = -qu; - + // f_{u^2-u-1,[u]Q}(P) for bit in P::ATE_LOOP_COUNT_2.iter().rev().skip(1) { ell_coeffs_2.push(r.double_in_place()); diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index 56f67193e..b2c822447 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -95,7 +95,7 @@ pub trait BW6Config: 'static + Eq + Sized { let f_u_inv = f_u.cyclotomic_inverse().unwrap(); - // TODO: figure + // TODO: is it enough to get the inverse of f_1, or does f_u also need to get inverted? if Self::ATE_LOOP_COUNT_1_IS_NEGATIVE { f_u.cyclotomic_inverse_in_place(); } From 678ccbabc83c354d3cbaefd35f7c4b2c7733a60d Mon Sep 17 00:00:00 2001 From: mmagician Date: Sat, 11 Mar 2023 16:36:45 +0100 Subject: [PATCH 05/11] Use unchecked Affine point creation --- ec/src/models/bw6/g2.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 8deab04ec..9306347cc 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -52,8 +52,7 @@ impl From> for G2Affine

{ let z_inv = q.z.inverse().unwrap(); let x = q.x * &z_inv; let y = q.y * &z_inv; - // TODO: change this to `new_unchecked` - G2Affine::

::new(x, y) + G2Affine::

::new_unchecked(x, y) } } From 851b2350e0382b483f93ff27e24039d23526fb35 Mon Sep 17 00:00:00 2001 From: mmagician Date: Sun, 12 Mar 2023 00:43:23 +0100 Subject: [PATCH 06/11] Swap the sign of r = [u]Q if loop count 1 is negative --- ec/src/models/bw6/g2.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 9306347cc..7e9f914e4 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -81,14 +81,29 @@ impl From> for G2Prepared

{ ell_coeffs_1.push(r.add_in_place(&q)); } } - ell_coeffs_1.push(r.clone().add_in_place(&q)); - // TODO: this is probably the slowest part // While G2 preparation is overall faster due to shortened 2nd loop, // The inversion could probably be avoided by using Hom(P) + Hom(Q) addition, // instead of mixed addition as is currently done. - let qu: G2Affine

= r.into(); - let neg_qu = -qu; + let r_affine: G2Affine

= r.into(); + let qu; + let neg_qu; + + // Swap the signs of `qu`, `r` & `neg_qu` if the loop count is negative. + if P::ATE_LOOP_COUNT_1_IS_NEGATIVE { + qu = -r_affine; + neg_qu = r_affine; + } else { + qu = r_affine; + neg_qu = -qu; + } + + r = G2HomProjective::

{ + x: qu.x, + y: qu.y, + z: P::Fp::one(), + }; + ell_coeffs_1.push(r.clone().add_in_place(&q)); let mut ell_coeffs_2 = vec![]; From c7d8a69edae7054e863826c82fe3f4ee0db095ed Mon Sep 17 00:00:00 2001 From: mmagician Date: Sun, 12 Mar 2023 00:44:37 +0100 Subject: [PATCH 07/11] Set f_u as its inverse if loop count 1 is negative --- ec/src/models/bw6/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index b2c822447..e836615ca 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -93,11 +93,14 @@ pub trait BW6Config: 'static + Eq + Sized { }) .product::< as Pairing>::TargetField>(); - let f_u_inv = f_u.cyclotomic_inverse().unwrap(); + let f_u_inv; // TODO: is it enough to get the inverse of f_1, or does f_u also need to get inverted? if Self::ATE_LOOP_COUNT_1_IS_NEGATIVE { + f_u_inv = f_u; f_u.cyclotomic_inverse_in_place(); + } else { + f_u_inv = f_u.cyclotomic_inverse().unwrap(); } // f_1(P) = f_(u+1)(P) = f_u(P) * l([u]q, q)(P) From dcdb744948d2e31f42f4eae7e285181659e82d70 Mon Sep 17 00:00:00 2001 From: mmagician Date: Mon, 4 Sep 2023 14:06:17 +0200 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: Pratyush Mishra --- ec/src/models/bw6/g2.rs | 13 ++++--------- ec/src/models/bw6/mod.rs | 6 ++---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index ea792a425..71c18aebc 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -86,17 +86,12 @@ impl From> for G2Prepared

{ // The inversion could probably be avoided by using Hom(P) + Hom(Q) addition, // instead of mixed addition as is currently done. let r_affine: G2Affine

= r.into(); - let qu; - let neg_qu; - // Swap the signs of `qu`, `r` & `neg_qu` if the loop count is negative. - if P::ATE_LOOP_COUNT_1_IS_NEGATIVE { - qu = -r_affine; - neg_qu = r_affine; + let (qu, neg_qu) = if P::ATE_LOOP_COUNT_1_IS_NEGATIVE { + (-r_affine, r_affine) } else { - qu = r_affine; - neg_qu = -qu; - } + (r_affine, -r_affine) + }; r = G2HomProjective::

{ x: qu.x, diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index 7b7fe075c..e245e01ab 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -129,11 +129,9 @@ pub trait BW6Config: 'static + Eq + Sized { // f_1(P) = f_(u+1)(P) = f_u(P) * l([u]q, q)(P) let mut f_1 = cfg_chunks_mut!(pairs_1, 4) .map(|pairs| { - let mut f = f_u; - for (p, coeffs) in pairs.iter_mut() { + pairs.iter_mut().fold(f_u, |mut f, (p, coeffs)| { BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); - } - f + }) }) .product::< as Pairing>::TargetField>(); From 2a678f47a82dfc769b40114d7900ebabc0274665 Mon Sep 17 00:00:00 2001 From: mmagician Date: Mon, 4 Sep 2023 14:15:14 +0200 Subject: [PATCH 09/11] Fix code review suggestion --- ec/src/models/bw6/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index e245e01ab..04b7493c3 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -129,8 +129,9 @@ pub trait BW6Config: 'static + Eq + Sized { // f_1(P) = f_(u+1)(P) = f_u(P) * l([u]q, q)(P) let mut f_1 = cfg_chunks_mut!(pairs_1, 4) .map(|pairs| { - pairs.iter_mut().fold(f_u, |mut f, (p, coeffs)| { + pairs.iter_mut().fold(f_u, |mut f, (p, coeffs)| { BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); + f }) }) .product::< as Pairing>::TargetField>(); From 2557df1a4f552b7a6c5db175fb76eadbfd346f3c Mon Sep 17 00:00:00 2001 From: mmagician Date: Mon, 4 Sep 2023 14:19:50 +0200 Subject: [PATCH 10/11] move the for f update loop outside of the if-else checking the 2NAF bit --- ec/src/models/bw6/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index 04b7493c3..feeefd692 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -149,14 +149,13 @@ pub trait BW6Config: 'static + Eq + Sized { let bit = Self::ATE_LOOP_COUNT_2[i - 1]; if bit == 1 { f *= &f_u; - for &mut (p, ref mut coeffs) in pairs.iter_mut() { - BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); - } } else if bit == -1 { f *= &f_u_inv; - for &mut (p, ref mut coeffs) in pairs.iter_mut() { - BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); - } + } else { + continue; + } + for &mut (p, ref mut coeffs) in pairs.iter_mut() { + BW6::::ell(&mut f, &coeffs.next().unwrap(), &p.0); } } f From 874ddfbc9206723edfe09cbe85f0eaa52baf9cc3 Mon Sep 17 00:00:00 2001 From: mmagician Date: Mon, 4 Sep 2023 14:42:37 +0200 Subject: [PATCH 11/11] remove old TODO --- ec/src/models/bw6/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index feeefd692..fef57148d 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -118,7 +118,6 @@ pub trait BW6Config: 'static + Eq + Sized { let f_u_inv; - // TODO: is it enough to get the inverse of f_1, or does f_u also need to get inverted? if Self::ATE_LOOP_COUNT_1_IS_NEGATIVE { f_u_inv = f_u; f_u.cyclotomic_inverse_in_place();