arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/r1cs-std/src/pairing/mnt6/mod.rs
r1cs-std/src/pairing/mnt6/mod.rsRaw
  1use r1cs_core::SynthesisError;
  2
  3use super::PairingVar as PG;
  4
  5use crate::{
  6    fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var, FieldVar},
  7    groups::mnt6::{
  8        AteAdditionCoefficientsVar, AteDoubleCoefficientsVar, G1PreparedVar, G1Var, G2PreparedVar,
  9        G2ProjectiveExtendedVar, G2Var,
 10    },
 11};
 12use algebra::{
 13    curves::mnt6::{MNT6Parameters, MNT6},
 14    fields::BitIteratorBE,
 15};
 16use core::marker::PhantomData;
 17
 18pub struct PairingVar<P: MNT6Parameters>(PhantomData<P>);
 19
 20type Fp3G<P> = Fp3Var<<P as MNT6Parameters>::Fp3Params>;
 21type Fp6G<P> = Fp6Var<<P as MNT6Parameters>::Fp6Params>;
 22pub type GTVar<P> = Fp6G<P>;
 23
 24impl<P: MNT6Parameters> PairingVar<P> {
 25    #[tracing::instrument(target = "r1cs", skip(r))]
 26    pub(crate) fn doubling_step_for_flipped_miller_loop(
 27        r: &G2ProjectiveExtendedVar<P>,
 28    ) -> Result<(G2ProjectiveExtendedVar<P>, AteDoubleCoefficientsVar<P>), SynthesisError> {
 29        let a = r.t.square()?;
 30        let b = r.x.square()?;
 31        let c = r.y.square()?;
 32        let d = c.square()?;
 33        let e = (&r.x + &c).square()? - &b - &d;
 34        let f = b.double()? + &b + &(&a * P::TWIST_COEFF_A);
 35        let g = f.square()?;
 36
 37        let d_eight = d.double()?.double()?.double()?;
 38
 39        let e2 = e.double()?;
 40        let x = &g - e2.double()?;
 41        let y = &f * (e2 - &x) - d_eight;
 42        let z = (&r.y + &r.z).square()? - &c - &r.z.square()?;
 43        let t = z.square()?;
 44
 45        let r2 = G2ProjectiveExtendedVar { x, y, z, t };
 46        let coeff = AteDoubleCoefficientsVar {
 47            c_h: (&r2.z + &r.t).square()? - &r2.t - &a,
 48            c_4c: c.double()?.double()?,
 49            c_j: (&f + &r.t).square()? - &g - &a,
 50            c_l: (&f + &r.x).square()? - &g - &b,
 51        };
 52
 53        Ok((r2, coeff))
 54    }
 55
 56    #[tracing::instrument(target = "r1cs", skip(r))]
 57    pub(crate) fn mixed_addition_step_for_flipped_miller_loop(
 58        x: &Fp3G<P>,
 59        y: &Fp3G<P>,
 60        r: &G2ProjectiveExtendedVar<P>,
 61    ) -> Result<(G2ProjectiveExtendedVar<P>, AteAdditionCoefficientsVar<P>), SynthesisError> {
 62        let a = y.square()?;
 63        let b = &r.t * x;
 64        let d = ((&r.z + y).square()? - &a - &r.t) * &r.t;
 65        let h = &b - &r.x;
 66        let i = h.square()?;
 67        let e = i.double()?.double()?;
 68        let j = &h * &e;
 69        let v = &r.x * &e;
 70        let ry2 = r.y.double()?;
 71        let l1 = &d - &ry2;
 72
 73        let x = l1.square()? - &j - &v.double()?;
 74        let y = &l1 * &(&v - &x) - &j * ry2;
 75        let z = (&r.z + &h).square()? - &r.t - &i;
 76        let t = z.square()?;
 77
 78        let r2 = G2ProjectiveExtendedVar {
 79            x,
 80            y,
 81            z: z.clone(),
 82            t,
 83        };
 84        let coeff = AteAdditionCoefficientsVar { c_l1: l1, c_rz: z };
 85
 86        Ok((r2, coeff))
 87    }
 88
 89    #[tracing::instrument(target = "r1cs", skip(p, q))]
 90    pub fn ate_miller_loop(
 91        p: &G1PreparedVar<P>,
 92        q: &G2PreparedVar<P>,
 93    ) -> Result<Fp6G<P>, SynthesisError> {
 94        let zero = FpVar::<P::Fp>::zero();
 95        let l1_coeff = Fp3Var::new(p.x.clone(), zero.clone(), zero) - &q.x_over_twist;
 96
 97        let mut f = Fp6G::<P>::one();
 98
 99        let mut dbl_idx: usize = 0;
100        let mut add_idx: usize = 0;
101
102        // code below gets executed for all bits (EXCEPT the MSB itself) of
103        // mnt6_param_p (skipping leading zeros) in MSB to LSB order
104        for bit in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT).skip(1) {
105            let dc = &q.double_coefficients[dbl_idx];
106            dbl_idx += 1;
107
108            let g_rr_at_p = Fp6Var::new(
109                &dc.c_l - &dc.c_4c - &dc.c_j * &p.x_twist,
110                &dc.c_h * &p.y_twist,
111            );
112
113            f = f.square()? * &g_rr_at_p;
114
115            if bit {
116                let ac = &q.addition_coefficients[add_idx];
117                add_idx += 1;
118
119                let g_rq_at_p = Fp6Var::new(
120                    &ac.c_rz * &p.y_twist,
121                    (&q.y_over_twist * &ac.c_rz + &(&l1_coeff * &ac.c_l1)).negate()?,
122                );
123                f *= &g_rq_at_p;
124            }
125        }
126
127        if P::ATE_IS_LOOP_COUNT_NEG {
128            let ac = &q.addition_coefficients[add_idx];
129
130            let g_rnegr_at_p = Fp6Var::new(
131                &ac.c_rz * &p.y_twist,
132                (&q.y_over_twist * &ac.c_rz + &(l1_coeff * &ac.c_l1)).negate()?,
133            );
134            f = (f * &g_rnegr_at_p).inverse()?;
135        }
136
137        Ok(f)
138    }
139
140    #[tracing::instrument(target = "r1cs")]
141    pub fn final_exponentiation(value: &Fp6G<P>) -> Result<GTVar<P>, SynthesisError> {
142        let value_inv = value.inverse()?;
143        let value_to_first_chunk = Self::final_exponentiation_first_chunk(value, &value_inv)?;
144        let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(&value_inv, value)?;
145        Self::final_exponentiation_last_chunk(&value_to_first_chunk, &value_inv_to_first_chunk)
146    }
147
148    #[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
149    fn final_exponentiation_first_chunk(
150        elt: &Fp6G<P>,
151        elt_inv: &Fp6G<P>,
152    ) -> Result<Fp6G<P>, SynthesisError> {
153        // (q^3-1)*(q+1)
154
155        // elt_q3 = elt^(q^3)
156        let elt_q3 = elt.unitary_inverse()?;
157        // elt_q3_over_elt = elt^(q^3-1)
158        let elt_q3_over_elt = elt_q3 * elt_inv;
159        // alpha = elt^((q^3-1) * q)
160        let alpha = elt_q3_over_elt.frobenius_map(1)?;
161        // beta = elt^((q^3-1)*(q+1)
162        Ok(alpha * &elt_q3_over_elt)
163    }
164
165    #[tracing::instrument(target = "r1cs", skip(elt, elt_inv))]
166    fn final_exponentiation_last_chunk(
167        elt: &Fp6G<P>,
168        elt_inv: &Fp6G<P>,
169    ) -> Result<Fp6G<P>, SynthesisError> {
170        let elt_q = elt.frobenius_map(1)?;
171
172        let w1_part = elt_q.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_1)?;
173        let w0_part = if P::FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG {
174            elt_inv.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
175        } else {
176            elt.cyclotomic_exp(&P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?
177        };
178
179        Ok(w1_part * &w0_part)
180    }
181}
182
183impl<P: MNT6Parameters> PG<MNT6<P>, P::Fp> for PairingVar<P> {
184    type G1Var = G1Var<P>;
185    type G2Var = G2Var<P>;
186    type G1PreparedVar = G1PreparedVar<P>;
187    type G2PreparedVar = G2PreparedVar<P>;
188    type GTVar = GTVar<P>;
189
190    #[tracing::instrument(target = "r1cs")]
191    fn miller_loop(
192        ps: &[Self::G1PreparedVar],
193        qs: &[Self::G2PreparedVar],
194    ) -> Result<Self::GTVar, SynthesisError> {
195        let mut result = Fp6G::<P>::one();
196        for (p, q) in ps.iter().zip(qs) {
197            result *= Self::ate_miller_loop(p, q)?;
198        }
199
200        Ok(result)
201    }
202
203    #[tracing::instrument(target = "r1cs")]
204    fn final_exponentiation(r: &Self::GTVar) -> Result<Self::GTVar, SynthesisError> {
205        Self::final_exponentiation(r)
206    }
207
208    #[tracing::instrument(target = "r1cs")]
209    fn prepare_g1(p: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError> {
210        Self::G1PreparedVar::from_group_var(p)
211    }
212
213    #[tracing::instrument(target = "r1cs")]
214    fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError> {
215        Self::G2PreparedVar::from_group_var(q)
216    }
217}