Skip to content

Commit

Permalink
affine versioned pairing supporting for BitVM
Browse files Browse the repository at this point in the history
  • Loading branch information
PayneJoe committed Jul 2, 2024
1 parent 5a781ae commit 95b1196
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 12 deletions.
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 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq {
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 warning on line 93 in ec/src/pairing.rs

View workflow job for this annotation

GitHub Actions / Check no_std

unused variable: `a`

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 / Test assembly

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

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 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 / 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 @@ 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<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

0 comments on commit 95b1196

Please sign in to comment.