Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use 2-NAF for representing ATE_LOOP_COUNT in MNT Miller loop #445

Merged
merged 23 commits into from
Aug 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
- Bound `AffineCurve` by
- `Mul<ScalarField, Output = ProjectiveCurve>`
- `for<'a> Mul<&'a ScalarField, Output = ProjectiveCurve>`
- [\#445](https://github.com/arkworks-rs/algebra/pull/445) (`ark-ec`) Change the `ATE_LOOP_COUNT` in MNT4/6 curves to use 2-NAF.
- [\#446](https://github.com/arkworks-rs/algebra/pull/446) (`ark-ff`) Add `CyclotomicMultSubgroup` trait and impl for extension fields

### Features
Expand Down Expand Up @@ -84,6 +85,7 @@
- [\#339](https://github.com/arkworks-rs/algebra/pull/339) (`ark-ff`) Remove duplicated code from `test_field` module and replace its usage with `ark-test-curves` crate.
- [\#352](https://github.com/arkworks-rs/algebra/pull/352) (`ark-ff`) Update `QuadExtField::sqrt` for better performance.
- [\#357](https://github.com/arkworks-rs/algebra/pull/357) (`ark-poly`) Speedup division by vanishing polynomials for dense polynomials.
- [\#445](https://github.com/arkworks-rs/algebra/pull/445) (`ark-ec`) Use 2-NAF for ate pairing in MNT4/6 curves.

### Bugfixes

Expand Down
64 changes: 27 additions & 37 deletions ec/src/models/mnt4/g2.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use ark_std::ops::Neg;

use crate::{
mnt4::MNT4Parameters,
models::mnt4::MNT4,
Expand Down Expand Up @@ -34,51 +36,39 @@ impl<P: MNT4Parameters> Default for G2Prepared<P> {
}

impl<P: MNT4Parameters> From<G2Affine<P>> for G2Prepared<P> {
fn from(g2: G2Affine<P>) -> Self {
fn from(g: G2Affine<P>) -> Self {
let twist_inv = P::TWIST.inverse().unwrap();

let mut g2p = G2Prepared {
x: g2.x,
y: g2.y,
x_over_twist: g2.x * &twist_inv,
y_over_twist: g2.y * &twist_inv,
let mut g_prep = G2Prepared {
x: g.x,
y: g.y,
x_over_twist: g.x * &twist_inv,
y_over_twist: g.y * &twist_inv,
double_coefficients: vec![],
addition_coefficients: vec![],
};

let mut r = G2ProjectiveExtended {
x: g2.x,
y: g2.y,
x: g.x,
y: g.y,
z: <Fp2<P::Fp2Config>>::one(),
t: <Fp2<P::Fp2Config>>::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) = MNT4::<P>::doubling_step_for_flipped_miller_loop(&r);
g2p.double_coefficients.push(coeff);
r = r2;

if *bit {
let (r2, coeff) =
MNT4::<P>::mixed_addition_step_for_flipped_miller_loop(&g2.x, &g2.y, &r);
g2p.addition_coefficients.push(coeff);
r = r2;
}

tmp >>= 1;
}
let neg_g = g.neg();
for bit in P::ATE_LOOP_COUNT.iter().skip(1) {
let (r2, coeff) = MNT4::<P>::doubling_for_flipped_miller_loop(&r);
g_prep.double_coefficients.push(coeff);
r = r2;

let (r_temp, add_coeff) = match bit {
1 => MNT4::<P>::mixed_addition_for_flipped_miller_loop(&g.x, &g.y, &r),
-1 => MNT4::<P>::mixed_addition_for_flipped_miller_loop(&neg_g.x, &neg_g.y, &r),
0 => continue,
_ => unreachable!(),
};
g_prep.addition_coefficients.push(add_coeff);
r = r_temp;
}

if P::ATE_IS_LOOP_COUNT_NEG {
Expand All @@ -89,15 +79,15 @@ impl<P: MNT4Parameters> From<G2Affine<P>> for G2Prepared<P> {
let minus_r_affine_x = r.x * &rz2_inv;
let minus_r_affine_y = -r.y * &rz3_inv;

let add_result = MNT4::<P>::mixed_addition_step_for_flipped_miller_loop(
let add_result = MNT4::<P>::mixed_addition_for_flipped_miller_loop(
&minus_r_affine_x,
&minus_r_affine_y,
&r,
);
g2p.addition_coefficients.push(add_result.1);
g_prep.addition_coefficients.push(add_result.1);
}

g2p
g_prep
}
}

Expand Down
39 changes: 25 additions & 14 deletions ec/src/models/mnt4/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
use ark_ff::{
fp2::{Fp2, Fp2Config},
fp4::{Fp4, Fp4Config},
BitIteratorBE, CyclotomicMultSubgroup, Field, PrimeField,
CyclotomicMultSubgroup, Field, PrimeField,
};
use num_traits::{One, Zero};

Expand All @@ -25,7 +25,7 @@ pub type GT<P> = Fp4<P>;
pub trait MNT4Parameters: 'static {
const TWIST: Fp2<Self::Fp2Config>;
const TWIST_COEFF_A: Fp2<Self::Fp2Config>;
const ATE_LOOP_COUNT: &'static [u64];
const ATE_LOOP_COUNT: &'static [i8];
const ATE_IS_LOOP_COUNT_NEG: bool;
const FINAL_EXPONENT_LAST_CHUNK_1: <Self::Fp as PrimeField>::BigInt;
const FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG: bool;
Expand All @@ -46,7 +46,7 @@ pub trait MNT4Parameters: 'static {
pub struct MNT4<P: MNT4Parameters>(PhantomData<fn() -> P>);

impl<P: MNT4Parameters> MNT4<P> {
fn doubling_step_for_flipped_miller_loop(
fn doubling_for_flipped_miller_loop(
r: &G2ProjectiveExtended<P>,
) -> (G2ProjectiveExtended<P>, AteDoubleCoefficients<P>) {
let a = r.t.square();
Expand Down Expand Up @@ -75,7 +75,7 @@ impl<P: MNT4Parameters> MNT4<P> {
(r2, coeff)
}

fn mixed_addition_step_for_flipped_miller_loop(
fn mixed_addition_for_flipped_miller_loop(
x: &Fp2<P::Fp2Config>,
y: &Fp2<P::Fp2Config>,
r: &G2ProjectiveExtended<P>,
Expand Down Expand Up @@ -110,28 +110,39 @@ impl<P: MNT4Parameters> MNT4<P> {

// code below gets executed for all bits (EXCEPT the MSB itself) of
// mnt6_param_p (skipping leading zeros) in MSB to LSB order

for (bit, dc) in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT)
.skip(1)
.zip(&q.double_coefficients)
{
let y_over_twist_neg = -q.y_over_twist;
weikengchen marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(P::ATE_LOOP_COUNT.len() - 1, q.double_coefficients.len());
for (bit, dc) in P::ATE_LOOP_COUNT.iter().skip(1).zip(&q.double_coefficients) {
let g_rr_at_p = Fp4::new(
-dc.c_4c - &(dc.c_j * &p.x_twist) + &dc.c_l,
dc.c_h * &p.y_twist,
);

f = f.square() * &g_rr_at_p;

if bit {
// Compute l_{R,Q}(P) if bit == 1, and l_{R,-Q}(P) if bit == -1
let g_rq_at_p = if *bit == 1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;

let g_rq_at_p = Fp4::new(
Fp4::new(
ac.c_rz * &p.y_twist,
-(q.y_over_twist * &ac.c_rz + &(l1_coeff * &ac.c_l1)),
);
f *= &g_rq_at_p;
}
)
} else if *bit == -1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;

Fp4::new(
ac.c_rz * &p.y_twist,
-(y_over_twist_neg * &ac.c_rz + &(l1_coeff * &ac.c_l1)),
)
} else if *bit == 0 {
continue;
weikengchen marked this conversation as resolved.
Show resolved Hide resolved
} else {
unreachable!();
};
f *= &g_rq_at_p;
}

if P::ATE_IS_LOOP_COUNT_NEG {
Expand Down
73 changes: 30 additions & 43 deletions ec/src/models/mnt6/g2.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::ops::Neg;

use crate::{
mnt6::MNT6Parameters,
models::mnt6::MNT6,
Expand Down Expand Up @@ -34,70 +36,55 @@ impl<P: MNT6Parameters> Default for G2Prepared<P> {
}

impl<P: MNT6Parameters> From<G2Affine<P>> for G2Prepared<P> {
fn from(g2: G2Affine<P>) -> Self {
fn from(g: G2Affine<P>) -> Self {
let twist_inv = P::TWIST.inverse().unwrap();

let mut g2p = G2Prepared {
x: g2.x,
y: g2.y,
x_over_twist: g2.x * &twist_inv,
y_over_twist: g2.y * &twist_inv,
let mut g_prep = G2Prepared {
x: g.x,
y: g.y,
x_over_twist: g.x * &twist_inv,
y_over_twist: g.y * &twist_inv,
double_coefficients: vec![],
addition_coefficients: vec![],
};

let mut r = G2ProjectiveExtended {
x: g2.x,
y: g2.y,
x: g.x,
y: g.y,
z: <Fp3<P::Fp3Config>>::one(),
t: <Fp3<P::Fp3Config>>::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) = MNT6::<P>::doubling_step_for_flipped_miller_loop(&r);
g2p.double_coefficients.push(coeff);
r = r2;

if *bit {
let (r2, coeff) =
MNT6::<P>::mixed_addition_step_for_flipped_miller_loop(&g2.x, &g2.y, &r);
g2p.addition_coefficients.push(coeff);
r = r2;
}

tmp >>= 1;
}
let neg_g = g.neg();
for bit in P::ATE_LOOP_COUNT.iter().skip(1) {
let (r2, coeff) = MNT6::<P>::doubling_for_flipped_miller_loop(&r);
g_prep.double_coefficients.push(coeff);
r = r2;

let (r_temp, add_coeff) = match bit {
1 => MNT6::<P>::mixed_addition_for_flipper_miller_loop(&g.x, &g.y, &r),
-1 => MNT6::<P>::mixed_addition_for_flipper_miller_loop(&neg_g.x, &neg_g.y, &r),
0 => continue,
_ => unreachable!(),
};
g_prep.addition_coefficients.push(add_coeff);
r = r_temp;
}

if P::ATE_IS_LOOP_COUNT_NEG {
let rz_inv = r.z.inverse().unwrap();
let rz2_inv = rz_inv.square();
let rz3_inv = rz_inv * &rz2_inv;

let minus_r_affine_x = r.x * &rz2_inv;
let minus_r_affine_y = -r.y * &rz3_inv;
let minus_r_x = r.x * &rz2_inv;
let minus_r_y = -r.y * &rz3_inv;

let add_result = MNT6::<P>::mixed_addition_step_for_flipped_miller_loop(
&minus_r_affine_x,
&minus_r_affine_y,
&r,
);
g2p.addition_coefficients.push(add_result.1);
let add_result =
MNT6::<P>::mixed_addition_for_flipper_miller_loop(&minus_r_x, &minus_r_y, &r);
g_prep.addition_coefficients.push(add_result.1);
}

g2p
g_prep
}
}

Expand Down
37 changes: 24 additions & 13 deletions ec/src/models/mnt6/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
use ark_ff::{
fp3::{Fp3, Fp3Config},
fp6_2over3::{Fp6, Fp6Config},
BitIteratorBE, CyclotomicMultSubgroup, Field, PrimeField,
CyclotomicMultSubgroup, Field, PrimeField,
};
use num_traits::{One, Zero};

Expand All @@ -25,7 +25,7 @@ pub type GT<P> = Fp6<P>;
pub trait MNT6Parameters: 'static {
const TWIST: Fp3<Self::Fp3Config>;
const TWIST_COEFF_A: Fp3<Self::Fp3Config>;
const ATE_LOOP_COUNT: &'static [u64];
const ATE_LOOP_COUNT: &'static [i8];
const ATE_IS_LOOP_COUNT_NEG: bool;
const FINAL_EXPONENT_LAST_CHUNK_1: <Self::Fp as PrimeField>::BigInt;
const FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG: bool;
Expand All @@ -46,7 +46,7 @@ pub trait MNT6Parameters: 'static {
pub struct MNT6<P: MNT6Parameters>(PhantomData<fn() -> P>);

impl<P: MNT6Parameters> MNT6<P> {
fn doubling_step_for_flipped_miller_loop(
fn doubling_for_flipped_miller_loop(
r: &G2ProjectiveExtended<P>,
) -> (G2ProjectiveExtended<P>, AteDoubleCoefficients<P>) {
let a = r.t.square();
Expand Down Expand Up @@ -76,7 +76,7 @@ impl<P: MNT6Parameters> MNT6<P> {
(r2, coeff)
}

fn mixed_addition_step_for_flipped_miller_loop(
fn mixed_addition_for_flipper_miller_loop(
x: &Fp3<P::Fp3Config>,
y: &Fp3<P::Fp3Config>,
r: &G2ProjectiveExtended<P>,
Expand Down Expand Up @@ -112,27 +112,38 @@ impl<P: MNT6Parameters> MNT6<P> {

// code below gets executed for all bits (EXCEPT the MSB itself) of
// mnt6_param_p (skipping leading zeros) in MSB to LSB order
for (bit, dc) in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT)
.skip(1)
.zip(&q.double_coefficients)
{
let y_over_twist_neg = -q.y_over_twist;
weikengchen marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(P::ATE_LOOP_COUNT.len() - 1, q.double_coefficients.len());
for (bit, dc) in P::ATE_LOOP_COUNT.iter().skip(1).zip(&q.double_coefficients) {
let g_rr_at_p = Fp6::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 {
// Compute l_{R,Q}(P) if bit == 1, and l_{R,-Q}(P) if bit == -1
let g_rq_at_p = if *bit == 1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;

let g_rq_at_p = Fp6::new(
Fp6::new(
ac.c_rz * &p.y_twist,
-(q.y_over_twist * &ac.c_rz + &(l1_coeff * &ac.c_l1)),
);
f *= &g_rq_at_p;
}
)
} else if *bit == -1 {
let ac = &q.addition_coefficients[add_idx];
add_idx += 1;
Fp6::new(
ac.c_rz * &p.y_twist,
-(y_over_twist_neg * &ac.c_rz + &(l1_coeff * &ac.c_l1)),
)
} else if *bit == 0 {
continue;
} else {
unreachable!();
};
f *= &g_rq_at_p;
}

if P::ATE_IS_LOOP_COUNT_NEG {
Expand Down