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

affine versioned pairing supporting for BitVM #846

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
106 changes: 99 additions & 7 deletions ec/src/models/bn/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,88 @@ pub struct G2HomProjective<P: BnConfig> {
z: Fp2<P::Fp2Config>,
}

impl<P: BnConfig> G2Prepared<P> {
fn affine_double_in_place(t: &mut G2Affine<P>, three_div_two: &P::Fp) -> EllCoeff<P> {
// 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::<P::Fp2Config>::ONE, alpha, -bias)
}

fn affine_add_in_place(t: &mut G2Affine<P>, q: &G2Affine<P>) -> EllCoeff<P> {
// 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::<P::Fp2Config>::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<P>) -> 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::<P>(q);
let mut q2 = mul_by_char::<P>(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<P: BnConfig> G2HomProjective<P> {
pub fn double_in_place(&mut self, two_inv: &P::Fp) -> EllCoeff<P> {
// Formula for line function when working with
Expand Down Expand Up @@ -96,8 +178,24 @@ impl<P: BnConfig> Default for G2Prepared<P> {
}
}

/// !!! affine mode is for the purpose of verifying pairings
impl<P: BnConfig> From<G2Affine<P>> for G2Prepared<P> {
fn from(q: G2Affine<P>) -> 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<P: BnConfig> From<G2Projective<P>> for G2Prepared<P> {
fn from(q: G2Projective<P>) -> Self {
let q = q.into_affine();
if q.infinity {
G2Prepared {
ell_coeffs: vec![],
Expand Down Expand Up @@ -144,12 +242,6 @@ impl<P: BnConfig> From<G2Affine<P>> for G2Prepared<P> {
}
}

impl<P: BnConfig> From<G2Projective<P>> for G2Prepared<P> {
fn from(q: G2Projective<P>) -> Self {
q.into_affine().into()
}
}

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

impl<'a, P: BnConfig> From<&'a G2Projective<P>> for G2Prepared<P> {
fn from(q: &'a G2Projective<P>) -> Self {
q.into_affine().into()
(*q).into()
}
}

Expand Down
103 changes: 102 additions & 1 deletion ec/src/models/bn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,71 @@ pub trait BnConfig: 'static + Sized {
MillerLoopOutput(f)
}

fn multi_miller_loop_affine(
a: impl IntoIterator<Item = impl Into<G1Prepared<Self>>>,
b: impl IntoIterator<Item = impl Into<G2Prepared<Self>>>,
) -> MillerLoopOutput<Bn<Self>> {
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::<Vec<_>>();

let mut f = cfg_chunks_mut!(pairs, 4)
.map(|pairs| {
let mut f = <Bn<Self> 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::<Self>::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::<Self>::ell_affine(
&mut f,
&coeffs.next().unwrap(),
&coeff_1,
&coeff_2,
);
}
}
}
f
})
.product::<<Bn<Self> as Pairing>::TargetField>();

if Self::X_IS_NEGATIVE {
f.cyclotomic_inverse_in_place();
}

for (coeff_1, coeff_2, coeffs) in &mut pairs {
Bn::<Self>::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2);
}

for (coeff_1, coeff_2, coeffs) in &mut pairs {
Bn::<Self>::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2);
}

MillerLoopOutput(f)
}

#[allow(clippy::let_and_return)]
fn final_exponentiation(f: MillerLoopOutput<Bn<Self>>) -> Option<PairingOutput<Bn<Self>>> {
// Easy part: result = elt^((q^6-1)*(q^2+1)).
Expand Down Expand Up @@ -180,7 +245,7 @@ pub use self::{
pub struct Bn<P: BnConfig>(PhantomData<fn() -> P>);

impl<P: BnConfig> Bn<P> {
/// 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<P::Fp12Config>, coeffs: &g2::EllCoeff<P>, p: &G1Affine<P>) {
let mut c0 = coeffs.0;
let mut c1 = coeffs.1;
Expand All @@ -200,6 +265,35 @@ impl<P: BnConfig> Bn<P> {
}
}

/// 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<P::Fp12Config>, coeffs: &g2::EllCoeff<P>, 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<P::Fp12Config>) -> Fp12<P::Fp12Config> {
f = f.cyclotomic_exp(P::X);
if !P::X_IS_NEGATIVE {
Expand Down Expand Up @@ -227,6 +321,13 @@ impl<P: BnConfig> Pairing for Bn<P> {
P::multi_miller_loop(a, b)
}

fn multi_miller_loop_affine(
a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
) -> MillerLoopOutput<Self> {
P::multi_miller_loop_affine(a, b)
}

fn final_exponentiation(f: MillerLoopOutput<Self>) -> Option<PairingOutput<Self>> {
P::final_exponentiation(f)
}
Expand Down
24 changes: 24 additions & 0 deletions ec/src/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
) -> MillerLoopOutput<Self>;

/// 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<Item = impl Into<Self::G1Prepared>>,

Check failure on line 93 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Test (stable)

unused variable: `a`

Check warning on line 93 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Check no_std

unused variable: `a`

Check warning on line 93 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Test assembly

unused variable: `a`
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,

Check failure on line 94 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Test (stable)

unused variable: `b`

Check warning on line 94 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Check no_std

unused variable: `b`

Check warning on line 94 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Test assembly

unused variable: `b`
) -> MillerLoopOutput<Self> {
unimplemented!()
}

/// Computes the Miller loop over `a` and `b`.
fn miller_loop(
a: impl Into<Self::G1Prepared>,
Expand All @@ -108,13 +116,29 @@
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<Item = impl Into<Self::G1Prepared>>,
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
) -> PairingOutput<Self> {
Self::final_exponentiation(Self::multi_miller_loop_affine(a, b)).unwrap()
}

/// Performs multiple pairing operations
fn pairing(
p: impl Into<Self::G1Prepared>,
q: impl Into<Self::G2Prepared>,
) -> PairingOutput<Self> {
Self::multi_pairing([p], [q])
}

/// Performs multiple pairing operations, where the line functions are in affine mode
fn pairing_affine(
p: impl Into<Self::G1Prepared>,
q: impl Into<Self::G2Prepared>,
) -> PairingOutput<Self> {
Self::multi_pairing_affine([p], [q])
}
}

/// Represents the target group of a pairing. This struct is a
Expand Down
Loading
Loading