mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-11 08:21:30 +01:00
Update to arkworks libraries (#3)
Co-authored-by: Nicholas Ward <npward@berkeley.edu>
This commit is contained in:
167
src/pairing/bls12/mod.rs
Normal file
167
src/pairing/bls12/mod.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
|
||||
use super::PairingVar as PG;
|
||||
|
||||
use crate::{
|
||||
fields::{fp::FpVar, fp12::Fp12Var, fp2::Fp2Var, FieldVar},
|
||||
groups::bls12::{G1AffineVar, G1PreparedVar, G1Var, G2PreparedVar, G2Var},
|
||||
};
|
||||
use ark_ec::bls12::{Bls12, Bls12Parameters, TwistType};
|
||||
use ark_ff::fields::BitIteratorBE;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Specifies the constraints for computing a pairing in a BLS12 bilinear group.
|
||||
pub struct PairingVar<P: Bls12Parameters>(PhantomData<P>);
|
||||
|
||||
type Fp2V<P> = Fp2Var<<P as Bls12Parameters>::Fp2Params>;
|
||||
|
||||
impl<P: Bls12Parameters> PairingVar<P> {
|
||||
// Evaluate the line function at point p.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn ell(
|
||||
f: &mut Fp12Var<P::Fp12Params>,
|
||||
coeffs: &(Fp2V<P>, Fp2V<P>),
|
||||
p: &G1AffineVar<P>,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let zero = FpVar::<P::Fp>::zero();
|
||||
|
||||
match P::TWIST_TYPE {
|
||||
TwistType::M => {
|
||||
let c0 = coeffs.0.clone();
|
||||
let mut c1 = coeffs.1.clone();
|
||||
let c2 = Fp2V::<P>::new(p.y.clone(), zero);
|
||||
|
||||
c1.c0 = c1.c0 * &p.x;
|
||||
c1.c1 = c1.c1 * &p.x;
|
||||
*f = f.mul_by_014(&c0, &c1, &c2)?;
|
||||
Ok(())
|
||||
}
|
||||
TwistType::D => {
|
||||
let c0 = Fp2V::<P>::new(p.y.clone(), zero);
|
||||
let mut c1 = coeffs.0.clone();
|
||||
let c2 = coeffs.1.clone();
|
||||
|
||||
c1.c0 = c1.c0 * &p.x;
|
||||
c1.c1 = c1.c1 * &p.x;
|
||||
*f = f.mul_by_034(&c0, &c1, &c2)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn exp_by_x(f: &Fp12Var<P::Fp12Params>) -> Result<Fp12Var<P::Fp12Params>, SynthesisError> {
|
||||
let mut result = f.optimized_cyclotomic_exp(P::X)?;
|
||||
if P::X_IS_NEGATIVE {
|
||||
result = result.unitary_inverse()?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Bls12Parameters> PG<Bls12<P>, P::Fp> for PairingVar<P> {
|
||||
type G1Var = G1Var<P>;
|
||||
type G2Var = G2Var<P>;
|
||||
type G1PreparedVar = G1PreparedVar<P>;
|
||||
type G2PreparedVar = G2PreparedVar<P>;
|
||||
type GTVar = Fp12Var<P::Fp12Params>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn miller_loop(
|
||||
ps: &[Self::G1PreparedVar],
|
||||
qs: &[Self::G2PreparedVar],
|
||||
) -> Result<Self::GTVar, SynthesisError> {
|
||||
let mut pairs = vec![];
|
||||
for (p, q) in ps.iter().zip(qs.iter()) {
|
||||
pairs.push((p, q.ell_coeffs.iter()));
|
||||
}
|
||||
let mut f = Self::GTVar::one();
|
||||
|
||||
for i in BitIteratorBE::new(P::X).skip(1) {
|
||||
f.square_in_place()?;
|
||||
|
||||
for &mut (p, ref mut coeffs) in pairs.iter_mut() {
|
||||
Self::ell(&mut f, coeffs.next().unwrap(), &p.0)?;
|
||||
}
|
||||
|
||||
if i {
|
||||
for &mut (p, ref mut coeffs) in pairs.iter_mut() {
|
||||
Self::ell(&mut f, &coeffs.next().unwrap(), &p.0)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if P::X_IS_NEGATIVE {
|
||||
f = f.unitary_inverse()?;
|
||||
}
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn final_exponentiation(f: &Self::GTVar) -> Result<Self::GTVar, SynthesisError> {
|
||||
// Computing the final exponentation following
|
||||
// https://eprint.iacr.org/2016/130.pdf.
|
||||
// We don't use their "faster" formula because it is difficult to make
|
||||
// it work for curves with odd `P::X`.
|
||||
// Hence we implement the slower algorithm from Table 1 below.
|
||||
|
||||
let f1 = f.frobenius_map(6)?;
|
||||
|
||||
f.inverse().and_then(|mut f2| {
|
||||
// f2 = f^(-1);
|
||||
// r = f^(p^6 - 1)
|
||||
let mut r = f1;
|
||||
r *= &f2;
|
||||
|
||||
// f2 = f^(p^6 - 1)
|
||||
f2 = r.clone();
|
||||
// r = f^((p^6 - 1)(p^2))
|
||||
r.frobenius_map_in_place(2)?;
|
||||
|
||||
// r = f^((p^6 - 1)(p^2) + (p^6 - 1))
|
||||
// r = f^((p^6 - 1)(p^2 + 1))
|
||||
r *= &f2;
|
||||
|
||||
// Hard part of the final exponentation is below:
|
||||
// From https://eprint.iacr.org/2016/130.pdf, Table 1
|
||||
let mut y0 = r.cyclotomic_square()?;
|
||||
y0 = y0.unitary_inverse()?;
|
||||
|
||||
let mut y5 = Self::exp_by_x(&r)?;
|
||||
|
||||
let mut y1 = y5.cyclotomic_square()?;
|
||||
let mut y3 = y0 * &y5;
|
||||
y0 = Self::exp_by_x(&y3)?;
|
||||
let y2 = Self::exp_by_x(&y0)?;
|
||||
let mut y4 = Self::exp_by_x(&y2)?;
|
||||
y4 *= &y1;
|
||||
y1 = Self::exp_by_x(&y4)?;
|
||||
y3 = y3.unitary_inverse()?;
|
||||
y1 *= &y3;
|
||||
y1 *= &r;
|
||||
y3 = r.clone();
|
||||
y3 = y3.unitary_inverse()?;
|
||||
y0 *= &r;
|
||||
y0.frobenius_map_in_place(3)?;
|
||||
y4 *= &y3;
|
||||
y4.frobenius_map_in_place(1)?;
|
||||
y5 *= &y2;
|
||||
y5.frobenius_map_in_place(2)?;
|
||||
y5 *= &y0;
|
||||
y5 *= &y4;
|
||||
y5 *= &y1;
|
||||
Ok(y5)
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g1(p: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError> {
|
||||
Self::G1PreparedVar::from_group_var(p)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError> {
|
||||
Self::G2PreparedVar::from_group_var(q)
|
||||
}
|
||||
}
|
||||
222
src/pairing/mnt4/mod.rs
Normal file
222
src/pairing/mnt4/mod.rs
Normal file
@@ -0,0 +1,222 @@
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
|
||||
use super::PairingVar as PG;
|
||||
|
||||
use crate::{
|
||||
fields::{fp::FpVar, fp2::Fp2Var, fp4::Fp4Var, FieldVar},
|
||||
groups::mnt4::{
|
||||
AteAdditionCoefficientsVar, AteDoubleCoefficientsVar, G1PreparedVar, G1Var, G2PreparedVar,
|
||||
G2ProjectiveExtendedVar, G2Var,
|
||||
},
|
||||
};
|
||||
use ark_ec::mnt4::{MNT4Parameters, MNT4};
|
||||
use ark_ff::BitIteratorBE;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Specifies the constraints for computing a pairing in a MNT4 bilinear group.
|
||||
pub struct PairingVar<P: MNT4Parameters>(PhantomData<P>);
|
||||
|
||||
type Fp2G<P> = Fp2Var<<P as MNT4Parameters>::Fp2Params>;
|
||||
type Fp4G<P> = Fp4Var<<P as MNT4Parameters>::Fp4Params>;
|
||||
/// A variable corresponding to `ark_ec::mnt4::GT`.
|
||||
pub type GTVar<P> = Fp4G<P>;
|
||||
|
||||
impl<P: MNT4Parameters> PairingVar<P> {
|
||||
#[tracing::instrument(target = "r1cs", skip(r))]
|
||||
pub(crate) fn doubling_step_for_flipped_miller_loop(
|
||||
r: &G2ProjectiveExtendedVar<P>,
|
||||
) -> Result<(G2ProjectiveExtendedVar<P>, AteDoubleCoefficientsVar<P>), SynthesisError> {
|
||||
let a = r.t.square()?;
|
||||
let b = r.x.square()?;
|
||||
let c = r.y.square()?;
|
||||
let d = c.square()?;
|
||||
let e = (&r.x + &c).square()? - &b - &d;
|
||||
let f = (b.double()? + &b) + &a * P::TWIST_COEFF_A;
|
||||
let g = f.square()?;
|
||||
|
||||
let d_eight = d.double()?.double()?.double()?;
|
||||
|
||||
let e2 = e.double()?;
|
||||
let x = &g - &e2.double()?;
|
||||
|
||||
let y = &f * (&e2 - &x) - &d_eight;
|
||||
let z = (&r.y + &r.z).square()? - &c - &r.z.square()?;
|
||||
let t = z.square()?;
|
||||
|
||||
let r2 = G2ProjectiveExtendedVar { x, y, z, t };
|
||||
let c_h = (&r2.z + &r.t).square()? - &r2.t - &a;
|
||||
let c_4c = c.double()?.double()?;
|
||||
let c_j = (&f + &r.t).square()? - &g - &a;
|
||||
let c_l = (&f + &r.x).square()? - &g - &b;
|
||||
let coeff = AteDoubleCoefficientsVar {
|
||||
c_h,
|
||||
c_4c,
|
||||
c_j,
|
||||
c_l,
|
||||
};
|
||||
|
||||
Ok((r2, coeff))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(r))]
|
||||
pub(crate) fn mixed_addition_step_for_flipped_miller_loop(
|
||||
x: &Fp2G<P>,
|
||||
y: &Fp2G<P>,
|
||||
r: &G2ProjectiveExtendedVar<P>,
|
||||
) -> Result<(G2ProjectiveExtendedVar<P>, AteAdditionCoefficientsVar<P>), SynthesisError> {
|
||||
let a = y.square()?;
|
||||
let b = &r.t * x;
|
||||
let d = ((&r.z + y).square()? - &a - &r.t) * &r.t;
|
||||
let h = &b - &r.x;
|
||||
let i = h.square()?;
|
||||
let e = i.double()?.double()?;
|
||||
let j = &h * &e;
|
||||
let v = &r.x * &e;
|
||||
let ry2 = r.y.double()?;
|
||||
let l1 = &d - &ry2;
|
||||
|
||||
let x = l1.square()? - &j - &v.double()?;
|
||||
let y = &l1 * &(&v - &x) - j * &ry2;
|
||||
let z = (&r.z + &h).square()? - &r.t - &i;
|
||||
let t = z.square()?;
|
||||
|
||||
let r2 = G2ProjectiveExtendedVar {
|
||||
x,
|
||||
y,
|
||||
z: z.clone(),
|
||||
t,
|
||||
};
|
||||
let coeff = AteAdditionCoefficientsVar { c_l1: l1, c_rz: z };
|
||||
|
||||
Ok((r2, coeff))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(p, q))]
|
||||
pub(crate) fn ate_miller_loop(
|
||||
p: &G1PreparedVar<P>,
|
||||
q: &G2PreparedVar<P>,
|
||||
) -> Result<Fp4G<P>, SynthesisError> {
|
||||
let l1_coeff = Fp2G::<P>::new(p.x.clone(), FpVar::<P::Fp>::zero()) - &q.x_over_twist;
|
||||
|
||||
let mut f = Fp4G::<P>::one();
|
||||
|
||||
let mut dbl_idx: usize = 0;
|
||||
let mut add_idx: usize = 0;
|
||||
|
||||
// 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 in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT).skip(1) {
|
||||
let dc = &q.double_coefficients[dbl_idx];
|
||||
dbl_idx += 1;
|
||||
|
||||
let g_rr_at_p = Fp4G::<P>::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 {
|
||||
let ac = &q.addition_coefficients[add_idx];
|
||||
add_idx += 1;
|
||||
|
||||
let g_rq_at_p = Fp4G::<P>::new(
|
||||
&ac.c_rz * &p.y_twist,
|
||||
(&q.y_over_twist * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
|
||||
);
|
||||
f *= &g_rq_at_p;
|
||||
}
|
||||
}
|
||||
|
||||
if P::ATE_IS_LOOP_COUNT_NEG {
|
||||
let ac = &q.addition_coefficients[add_idx];
|
||||
|
||||
let g_rnegr_at_p = Fp4G::<P>::new(
|
||||
&ac.c_rz * &p.y_twist,
|
||||
(&q.y_over_twist * &ac.c_rz + &l1_coeff * &ac.c_l1).negate()?,
|
||||
);
|
||||
f = (&f * &g_rnegr_at_p).inverse()?;
|
||||
}
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(value))]
|
||||
pub(crate) fn final_exponentiation(value: &Fp4G<P>) -> Result<GTVar<P>, SynthesisError> {
|
||||
let value_inv = value.inverse()?;
|
||||
let value_to_first_chunk = Self::final_exponentiation_first_chunk(value, &value_inv)?;
|
||||
let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(&value_inv, value)?;
|
||||
Self::final_exponentiation_last_chunk(&value_to_first_chunk, &value_inv_to_first_chunk)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
|
||||
fn final_exponentiation_first_chunk(
|
||||
elt: &Fp4G<P>,
|
||||
elt_inv: &Fp4G<P>,
|
||||
) -> Result<Fp4G<P>, SynthesisError> {
|
||||
// (q^2-1)
|
||||
|
||||
// elt_q2 = elt^(q^2)
|
||||
let elt_q2 = elt.unitary_inverse()?;
|
||||
// elt_q2_over_elt = elt^(q^2-1)
|
||||
Ok(elt_q2 * elt_inv)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
|
||||
fn final_exponentiation_last_chunk(
|
||||
elt: &Fp4G<P>,
|
||||
elt_inv: &Fp4G<P>,
|
||||
) -> Result<Fp4G<P>, SynthesisError> {
|
||||
let elt_clone = elt.clone();
|
||||
let elt_inv_clone = elt_inv.clone();
|
||||
|
||||
let mut elt_q = elt.clone();
|
||||
elt_q.frobenius_map_in_place(1)?;
|
||||
|
||||
let w1_part = elt_q.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_1)?;
|
||||
let w0_part = if P::FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG {
|
||||
elt_inv_clone.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
|
||||
} else {
|
||||
elt_clone.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
|
||||
};
|
||||
|
||||
Ok(w1_part * &w0_part)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: MNT4Parameters> PG<MNT4<P>, P::Fp> for PairingVar<P> {
|
||||
type G1Var = G1Var<P>;
|
||||
type G2Var = G2Var<P>;
|
||||
type G1PreparedVar = G1PreparedVar<P>;
|
||||
type G2PreparedVar = G2PreparedVar<P>;
|
||||
type GTVar = GTVar<P>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn miller_loop(
|
||||
ps: &[Self::G1PreparedVar],
|
||||
qs: &[Self::G2PreparedVar],
|
||||
) -> Result<Self::GTVar, SynthesisError> {
|
||||
let mut result = Fp4G::<P>::one();
|
||||
for (p, q) in ps.iter().zip(qs) {
|
||||
result *= Self::ate_miller_loop(p, q)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn final_exponentiation(r: &Self::GTVar) -> Result<Self::GTVar, SynthesisError> {
|
||||
Self::final_exponentiation(r)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g1(p: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError> {
|
||||
Self::G1PreparedVar::from_group_var(p)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError> {
|
||||
Self::G2PreparedVar::from_group_var(q)
|
||||
}
|
||||
}
|
||||
217
src/pairing/mnt6/mod.rs
Normal file
217
src/pairing/mnt6/mod.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
|
||||
use super::PairingVar as PG;
|
||||
|
||||
use crate::{
|
||||
fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var, FieldVar},
|
||||
groups::mnt6::{
|
||||
AteAdditionCoefficientsVar, AteDoubleCoefficientsVar, G1PreparedVar, G1Var, G2PreparedVar,
|
||||
G2ProjectiveExtendedVar, G2Var,
|
||||
},
|
||||
};
|
||||
use ark_ec::mnt6::{MNT6Parameters, MNT6};
|
||||
use ark_ff::fields::BitIteratorBE;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Specifies the constraints for computing a pairing in a MNT6 bilinear group.
|
||||
pub struct PairingVar<P: MNT6Parameters>(PhantomData<P>);
|
||||
|
||||
type Fp3G<P> = Fp3Var<<P as MNT6Parameters>::Fp3Params>;
|
||||
type Fp6G<P> = Fp6Var<<P as MNT6Parameters>::Fp6Params>;
|
||||
/// A variable corresponding to `ark_ec::mnt6::GT`.
|
||||
pub type GTVar<P> = Fp6G<P>;
|
||||
|
||||
impl<P: MNT6Parameters> PairingVar<P> {
|
||||
#[tracing::instrument(target = "r1cs", skip(r))]
|
||||
pub(crate) fn doubling_step_for_flipped_miller_loop(
|
||||
r: &G2ProjectiveExtendedVar<P>,
|
||||
) -> Result<(G2ProjectiveExtendedVar<P>, AteDoubleCoefficientsVar<P>), SynthesisError> {
|
||||
let a = r.t.square()?;
|
||||
let b = r.x.square()?;
|
||||
let c = r.y.square()?;
|
||||
let d = c.square()?;
|
||||
let e = (&r.x + &c).square()? - &b - &d;
|
||||
let f = b.double()? + &b + &(&a * P::TWIST_COEFF_A);
|
||||
let g = f.square()?;
|
||||
|
||||
let d_eight = d.double()?.double()?.double()?;
|
||||
|
||||
let e2 = e.double()?;
|
||||
let x = &g - e2.double()?;
|
||||
let y = &f * (e2 - &x) - d_eight;
|
||||
let z = (&r.y + &r.z).square()? - &c - &r.z.square()?;
|
||||
let t = z.square()?;
|
||||
|
||||
let r2 = G2ProjectiveExtendedVar { x, y, z, t };
|
||||
let coeff = AteDoubleCoefficientsVar {
|
||||
c_h: (&r2.z + &r.t).square()? - &r2.t - &a,
|
||||
c_4c: c.double()?.double()?,
|
||||
c_j: (&f + &r.t).square()? - &g - &a,
|
||||
c_l: (&f + &r.x).square()? - &g - &b,
|
||||
};
|
||||
|
||||
Ok((r2, coeff))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(r))]
|
||||
pub(crate) fn mixed_addition_step_for_flipped_miller_loop(
|
||||
x: &Fp3G<P>,
|
||||
y: &Fp3G<P>,
|
||||
r: &G2ProjectiveExtendedVar<P>,
|
||||
) -> Result<(G2ProjectiveExtendedVar<P>, AteAdditionCoefficientsVar<P>), SynthesisError> {
|
||||
let a = y.square()?;
|
||||
let b = &r.t * x;
|
||||
let d = ((&r.z + y).square()? - &a - &r.t) * &r.t;
|
||||
let h = &b - &r.x;
|
||||
let i = h.square()?;
|
||||
let e = i.double()?.double()?;
|
||||
let j = &h * &e;
|
||||
let v = &r.x * &e;
|
||||
let ry2 = r.y.double()?;
|
||||
let l1 = &d - &ry2;
|
||||
|
||||
let x = l1.square()? - &j - &v.double()?;
|
||||
let y = &l1 * &(&v - &x) - &j * ry2;
|
||||
let z = (&r.z + &h).square()? - &r.t - &i;
|
||||
let t = z.square()?;
|
||||
|
||||
let r2 = G2ProjectiveExtendedVar {
|
||||
x,
|
||||
y,
|
||||
z: z.clone(),
|
||||
t,
|
||||
};
|
||||
let coeff = AteAdditionCoefficientsVar { c_l1: l1, c_rz: z };
|
||||
|
||||
Ok((r2, coeff))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(p, q))]
|
||||
pub(crate) fn ate_miller_loop(
|
||||
p: &G1PreparedVar<P>,
|
||||
q: &G2PreparedVar<P>,
|
||||
) -> Result<Fp6G<P>, SynthesisError> {
|
||||
let zero = FpVar::<P::Fp>::zero();
|
||||
let l1_coeff = Fp3Var::new(p.x.clone(), zero.clone(), zero) - &q.x_over_twist;
|
||||
|
||||
let mut f = Fp6G::<P>::one();
|
||||
|
||||
let mut dbl_idx: usize = 0;
|
||||
let mut add_idx: usize = 0;
|
||||
|
||||
// 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 in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT).skip(1) {
|
||||
let dc = &q.double_coefficients[dbl_idx];
|
||||
dbl_idx += 1;
|
||||
|
||||
let g_rr_at_p = Fp6Var::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 {
|
||||
let ac = &q.addition_coefficients[add_idx];
|
||||
add_idx += 1;
|
||||
|
||||
let g_rq_at_p = Fp6Var::new(
|
||||
&ac.c_rz * &p.y_twist,
|
||||
(&q.y_over_twist * &ac.c_rz + &(&l1_coeff * &ac.c_l1)).negate()?,
|
||||
);
|
||||
f *= &g_rq_at_p;
|
||||
}
|
||||
}
|
||||
|
||||
if P::ATE_IS_LOOP_COUNT_NEG {
|
||||
let ac = &q.addition_coefficients[add_idx];
|
||||
|
||||
let g_rnegr_at_p = Fp6Var::new(
|
||||
&ac.c_rz * &p.y_twist,
|
||||
(&q.y_over_twist * &ac.c_rz + &(l1_coeff * &ac.c_l1)).negate()?,
|
||||
);
|
||||
f = (f * &g_rnegr_at_p).inverse()?;
|
||||
}
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub(crate) fn final_exponentiation(value: &Fp6G<P>) -> Result<GTVar<P>, SynthesisError> {
|
||||
let value_inv = value.inverse()?;
|
||||
let value_to_first_chunk = Self::final_exponentiation_first_chunk(value, &value_inv)?;
|
||||
let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(&value_inv, value)?;
|
||||
Self::final_exponentiation_last_chunk(&value_to_first_chunk, &value_inv_to_first_chunk)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
|
||||
fn final_exponentiation_first_chunk(
|
||||
elt: &Fp6G<P>,
|
||||
elt_inv: &Fp6G<P>,
|
||||
) -> Result<Fp6G<P>, SynthesisError> {
|
||||
// (q^3-1)*(q+1)
|
||||
|
||||
// elt_q3 = elt^(q^3)
|
||||
let elt_q3 = elt.unitary_inverse()?;
|
||||
// elt_q3_over_elt = elt^(q^3-1)
|
||||
let elt_q3_over_elt = elt_q3 * elt_inv;
|
||||
// alpha = elt^((q^3-1) * q)
|
||||
let alpha = elt_q3_over_elt.frobenius_map(1)?;
|
||||
// beta = elt^((q^3-1)*(q+1)
|
||||
Ok(alpha * &elt_q3_over_elt)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
|
||||
fn final_exponentiation_last_chunk(
|
||||
elt: &Fp6G<P>,
|
||||
elt_inv: &Fp6G<P>,
|
||||
) -> Result<Fp6G<P>, SynthesisError> {
|
||||
let elt_q = elt.frobenius_map(1)?;
|
||||
|
||||
let w1_part = elt_q.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_1)?;
|
||||
let w0_part = if P::FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG {
|
||||
elt_inv.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
|
||||
} else {
|
||||
elt.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
|
||||
};
|
||||
|
||||
Ok(w1_part * &w0_part)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: MNT6Parameters> PG<MNT6<P>, P::Fp> for PairingVar<P> {
|
||||
type G1Var = G1Var<P>;
|
||||
type G2Var = G2Var<P>;
|
||||
type G1PreparedVar = G1PreparedVar<P>;
|
||||
type G2PreparedVar = G2PreparedVar<P>;
|
||||
type GTVar = GTVar<P>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn miller_loop(
|
||||
ps: &[Self::G1PreparedVar],
|
||||
qs: &[Self::G2PreparedVar],
|
||||
) -> Result<Self::GTVar, SynthesisError> {
|
||||
let mut result = Fp6G::<P>::one();
|
||||
for (p, q) in ps.iter().zip(qs) {
|
||||
result *= Self::ate_miller_loop(p, q)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn final_exponentiation(r: &Self::GTVar) -> Result<Self::GTVar, SynthesisError> {
|
||||
Self::final_exponentiation(r)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g1(p: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError> {
|
||||
Self::G1PreparedVar::from_group_var(p)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError> {
|
||||
Self::G2PreparedVar::from_group_var(q)
|
||||
}
|
||||
}
|
||||
84
src/pairing/mod.rs
Normal file
84
src/pairing/mod.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use crate::prelude::*;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_ff::Field;
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use core::fmt::Debug;
|
||||
|
||||
/// This module implements pairings for BLS12 bilinear groups.
|
||||
pub mod bls12;
|
||||
/// This module implements pairings for MNT4 bilinear groups.
|
||||
pub mod mnt4;
|
||||
/// This module implements pairings for MNT6 bilinear groups.
|
||||
pub mod mnt6;
|
||||
|
||||
/// Specifies the constraints for computing a pairing in the yybilinear group
|
||||
/// `E`.
|
||||
pub trait PairingVar<E: PairingEngine, ConstraintF: Field = <E as PairingEngine>::Fq> {
|
||||
/// An variable representing an element of `G1`.
|
||||
/// This is the R1CS equivalent of `E::G1Projective`.
|
||||
type G1Var: CurveVar<E::G1Projective, ConstraintF>
|
||||
+ AllocVar<E::G1Projective, ConstraintF>
|
||||
+ AllocVar<E::G1Affine, ConstraintF>;
|
||||
|
||||
/// An variable representing an element of `G2`.
|
||||
/// This is the R1CS equivalent of `E::G2Projective`.
|
||||
type G2Var: CurveVar<E::G2Projective, ConstraintF>
|
||||
+ AllocVar<E::G2Projective, ConstraintF>
|
||||
+ AllocVar<E::G2Affine, ConstraintF>;
|
||||
|
||||
/// An variable representing an element of `GT`.
|
||||
/// This is the R1CS equivalent of `E::GT`.
|
||||
type GTVar: FieldVar<E::Fqk, ConstraintF>;
|
||||
|
||||
/// An variable representing cached precomputation that can speed up
|
||||
/// pairings computations. This is the R1CS equivalent of
|
||||
/// `E::G1Prepared`.
|
||||
type G1PreparedVar: ToBytesGadget<ConstraintF>
|
||||
+ AllocVar<E::G1Prepared, ConstraintF>
|
||||
+ Clone
|
||||
+ Debug;
|
||||
/// An variable representing cached precomputation that can speed up
|
||||
/// pairings computations. This is the R1CS equivalent of
|
||||
/// `E::G2Prepared`.
|
||||
type G2PreparedVar: ToBytesGadget<ConstraintF>
|
||||
+ AllocVar<E::G2Prepared, ConstraintF>
|
||||
+ Clone
|
||||
+ Debug;
|
||||
|
||||
/// Computes a multi-miller loop between elements
|
||||
/// of `p` and `q`.
|
||||
fn miller_loop(
|
||||
p: &[Self::G1PreparedVar],
|
||||
q: &[Self::G2PreparedVar],
|
||||
) -> Result<Self::GTVar, SynthesisError>;
|
||||
|
||||
/// Computes a final exponentiation over `p`.
|
||||
fn final_exponentiation(p: &Self::GTVar) -> Result<Self::GTVar, SynthesisError>;
|
||||
|
||||
/// Computes a pairing over `p` and `q`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn pairing(
|
||||
p: Self::G1PreparedVar,
|
||||
q: Self::G2PreparedVar,
|
||||
) -> Result<Self::GTVar, SynthesisError> {
|
||||
let tmp = Self::miller_loop(&[p], &[q])?;
|
||||
Self::final_exponentiation(&tmp)
|
||||
}
|
||||
|
||||
/// Computes a product of pairings over the elements in `p` and `q`.
|
||||
#[must_use]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn product_of_pairings(
|
||||
p: &[Self::G1PreparedVar],
|
||||
q: &[Self::G2PreparedVar],
|
||||
) -> Result<Self::GTVar, SynthesisError> {
|
||||
let miller_result = Self::miller_loop(p, q)?;
|
||||
Self::final_exponentiation(&miller_result)
|
||||
}
|
||||
|
||||
/// Performs the precomputation to generate `Self::G1PreparedVar`.
|
||||
fn prepare_g1(q: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError>;
|
||||
|
||||
/// Performs the precomputation to generate `Self::G2PreparedVar`.
|
||||
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError>;
|
||||
}
|
||||
Reference in New Issue
Block a user