arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/r1cs-std/src/pairing/mnt4/mod.rs
r1cs-std/src/pairing/mnt4/mod.rsRaw
  1use r1cs_core::{ConstraintSystem, SynthesisError};
  2
  3use super::PairingGadget as PG;
  4
  5use crate::{
  6    fields::{fp::FpGadget, fp2::Fp2Gadget, fp4::Fp4Gadget, FieldGadget},
  7    groups::mnt4::{
  8        AteAdditionCoefficientsGadget, AteDoubleCoefficientsGadget, G1Gadget, G1PreparedGadget,
  9        G2Gadget, G2PreparedGadget, G2ProjectiveExtendedGadget,
 10    },
 11};
 12use algebra::{
 13    curves::mnt4::{MNT4Parameters, MNT4},
 14    fields::BitIterator,
 15};
 16use core::marker::PhantomData;
 17
 18pub struct PairingGadget<P: MNT4Parameters>(PhantomData<P>);
 19
 20type Fp2G<P> = Fp2Gadget<<P as MNT4Parameters>::Fp2Params, <P as MNT4Parameters>::Fp>;
 21type Fp4G<P> = Fp4Gadget<<P as MNT4Parameters>::Fp4Params, <P as MNT4Parameters>::Fp>;
 22pub type GTGadget<P> = Fp4G<P>;
 23
 24impl<P: MNT4Parameters> PairingGadget<P> {
 25    pub(crate) fn doubling_step_for_flipped_miller_loop<CS: ConstraintSystem<P::Fp>>(
 26        mut cs: CS,
 27        r: &G2ProjectiveExtendedGadget<P>,
 28    ) -> Result<
 29        (
 30            G2ProjectiveExtendedGadget<P>,
 31            AteDoubleCoefficientsGadget<P>,
 32        ),
 33        SynthesisError,
 34    > {
 35        let a = r.t.square(cs.ns(|| "r.t^2"))?;
 36        let b = r.x.square(cs.ns(|| "r.x^2"))?;
 37        let c = r.y.square(cs.ns(|| "r.y^2"))?;
 38        let d = c.square(cs.ns(|| "c^2"))?;
 39        let mut e = r.x.add(cs.ns(|| "r.x + c"), &c)?;
 40        e.square_in_place(cs.ns(|| "(r.x + c)^2"))?;
 41        e.sub_in_place(cs.ns(|| "(r.x + c)^2 - b"), &b)?;
 42        e.sub_in_place(cs.ns(|| "(r.x + c)^2 - b - d"), &d)?;
 43
 44        let mut f = b.double(cs.ns(|| "b + b"))?;
 45        f.add_in_place(cs.ns(|| "b + b + b"), &b)?;
 46        let twist_a = a.mul_by_constant(cs.ns(|| "TWIST_COEFF_A * a"), &P::TWIST_COEFF_A)?;
 47        f.add_in_place(cs.ns(|| "(b + b + b) + (TWIST_COEFF_A * a)"), &twist_a)?;
 48        let g = f.square(cs.ns(|| "f^2"))?;
 49
 50        let d_eight = d
 51            .double(cs.ns(|| "2 * d"))?
 52            .double(cs.ns(|| "4 * d"))?
 53            .double(cs.ns(|| "8 * d"))?;
 54
 55        let e2 = e.double(cs.ns(|| "2 * e"))?;
 56        let e4 = e2.double(cs.ns(|| "4 * e"))?;
 57        let x = g.sub(cs.ns(|| "- (e + e + e + e) + g"), &e4)?;
 58
 59        let mut y = e2.sub(cs.ns(|| "e + e - x"), &x)?;
 60        y.mul_in_place(cs.ns(|| "f * (e + e - x)"), &f)?;
 61        y.sub_in_place(cs.ns(|| "- d_eight + f * (e + e - x)"), &d_eight)?;
 62        let mut z = r.y.add(cs.ns(|| "r.y + r.z"), &r.z)?;
 63        z.square_in_place(cs.ns(|| "(r.y + r.z)^2"))?;
 64        z.sub_in_place(cs.ns(|| "(r.y + r.z)^2 - c"), &c)?;
 65        let z2 = r.z.square(cs.ns(|| "r.z^2"))?;
 66        z.sub_in_place(cs.ns(|| "(r.y + r.z)^2 - c - r.z^2"), &z2)?;
 67        let t = z.square(cs.ns(|| "z^2"))?;
 68
 69        let r2 = G2ProjectiveExtendedGadget { x, y, z, t };
 70
 71        let c_h =
 72            r2.z.add(cs.ns(|| "r2.z + r.t"), &r.t)?
 73                .square(cs.ns(|| "(r2.z + r.t)^2"))?
 74                .sub(cs.ns(|| "(r2.z + r.t)^2 - r2.t"), &r2.t)?
 75                .sub(cs.ns(|| "(r2.z + r.t)^2 - r2.t - a"), &a)?;
 76        let c_4c = c.double(cs.ns(|| "2 * c"))?.double(cs.ns(|| "4 * c"))?;
 77        let mut c_j = f.add(cs.ns(|| "f + r.t"), &r.t)?;
 78        c_j.square_in_place(cs.ns(|| "(f + r.t)^2"))?;
 79        c_j.sub_in_place(cs.ns(|| "(f + r.t)^2 - g"), &g)?;
 80        c_j.sub_in_place(cs.ns(|| "(f + r.t)^2 - g - a"), &a)?;
 81        let mut c_l = f.add(cs.ns(|| "f + r.x"), &r.x)?;
 82        c_l.square_in_place(cs.ns(|| "(f + r.x)^2"))?;
 83        c_l.sub_in_place(cs.ns(|| "(f + r.x)^2 - g"), &g)?;
 84        c_l.sub_in_place(cs.ns(|| "(f + r.x)^2 - g - b"), &b)?;
 85        let coeff = AteDoubleCoefficientsGadget {
 86            c_h,
 87            c_4c,
 88            c_j,
 89            c_l,
 90        };
 91
 92        Ok((r2, coeff))
 93    }
 94
 95    pub(crate) fn mixed_addition_step_for_flipped_miller_loop<CS: ConstraintSystem<P::Fp>>(
 96        mut cs: CS,
 97        x: &Fp2G<P>,
 98        y: &Fp2G<P>,
 99        r: &G2ProjectiveExtendedGadget<P>,
100    ) -> Result<
101        (
102            G2ProjectiveExtendedGadget<P>,
103            AteAdditionCoefficientsGadget<P>,
104        ),
105        SynthesisError,
106    > {
107        let a = y.square(cs.ns(|| "y^2"))?;
108        let b = r.t.mul(cs.ns(|| "r.t * x"), &x)?;
109        let mut d = r.z.add(cs.ns(|| "r.z + y"), &y)?;
110        d.square_in_place(cs.ns(|| "(r.z + y)^2"))?;
111        d.sub_in_place(cs.ns(|| "(r.z + y)^2 - a"), &a)?;
112        d.sub_in_place(cs.ns(|| "(r.z + y)^2 - a - r.t"), &r.t)?;
113        d.mul_in_place(cs.ns(|| "((r.z + y)^2 - a - r.t) * r.t"), &r.t)?;
114        let h = b.sub(cs.ns(|| "b - r.x"), &r.x)?;
115        let i = h.square(cs.ns(|| "h^2"))?;
116        let e = i.double(cs.ns(|| "2 * i"))?.double(cs.ns(|| "4 * i"))?;
117        let j = h.mul(cs.ns(|| "h * e"), &e)?;
118        let v = r.x.mul(cs.ns(|| "r.x * e"), &e)?;
119        let ry2 = r.y.double(cs.ns(|| "r.y + r.y"))?;
120        let l1 = d.sub(cs.ns(|| "d - (r.y + r.y)"), &ry2)?;
121
122        let v2 = v.double(cs.ns(|| "v + v"))?;
123        let x = l1
124            .square(cs.ns(|| "l1^2"))?
125            .sub(cs.ns(|| "l1^2 - j"), &j)?
126            .sub(cs.ns(|| "l1^2 - j - (v + v)"), &v2)?;
127        let v_minus_x = v.sub(cs.ns(|| "v - x"), &x)?;
128        let j_ry2 = j.mul(cs.ns(|| "j * (r.y + r.y)"), &ry2)?;
129        let y = l1
130            .mul(cs.ns(|| "l1 * (v - x)"), &v_minus_x)?
131            .sub(cs.ns(|| "l1 * (v - x) - (j * (r.y + r.y)"), &j_ry2)?;
132        let mut z = r.z.add(cs.ns(|| "r.z + h"), &h)?;
133        z.square_in_place(cs.ns(|| "(r.z + h)^2"))?;
134        z.sub_in_place(cs.ns(|| "(r.z + h)^2 - r.t"), &r.t)?;
135        z.sub_in_place(cs.ns(|| "(r.z + h)^2 - r.t - i"), &i)?;
136        let t = z.square(cs.ns(|| "z^2"))?;
137
138        let r2 = G2ProjectiveExtendedGadget {
139            x,
140            y,
141            z: z.clone(),
142            t,
143        };
144        let coeff = AteAdditionCoefficientsGadget { c_l1: l1, c_rz: z };
145
146        Ok((r2, coeff))
147    }
148
149    pub fn ate_miller_loop<CS: ConstraintSystem<P::Fp>>(
150        mut cs: CS,
151        p: &G1PreparedGadget<P>,
152        q: &G2PreparedGadget<P>,
153    ) -> Result<Fp4G<P>, SynthesisError> {
154        let mut l1_coeff = Fp2G::<P>::new(p.x.clone(), FpGadget::<P::Fp>::zero(cs.ns(|| "zero"))?);
155        l1_coeff.sub_in_place(cs.ns(|| "l1_coeff"), &q.x_over_twist)?;
156
157        let mut f = Fp4G::<P>::one(cs.ns(|| "one"))?;
158
159        let mut dbl_idx: usize = 0;
160        let mut add_idx: usize = 0;
161
162        let mut found_one = false;
163
164        for (j, bit) in BitIterator::new(P::ATE_LOOP_COUNT).enumerate() {
165            // code below gets executed for all bits (EXCEPT the MSB itself) of
166            // mnt6_param_p (skipping leading zeros) in MSB to LSB order
167            if !found_one && bit {
168                found_one = true;
169                continue;
170            } else if !found_one {
171                continue;
172            }
173
174            let mut cs = cs.ns(|| format!("bit {}", j));
175
176            let dc = &q.double_coefficients[dbl_idx];
177            dbl_idx += 1;
178
179            let c_j_x_twist = dc.c_j.mul(cs.ns(|| "dc.c_j * p.x_twist"), &p.x_twist)?;
180            let c0 = dc.c_l.sub(cs.ns(|| "-dc.c_4c + dc.c_l"), &dc.c_4c)?.sub(
181                cs.ns(|| "-dc.c_4c - (dc.c_j * p.x_twist) + dc.c_l"),
182                &c_j_x_twist,
183            )?;
184            let c1 = dc.c_h.mul(cs.ns(|| "dc.c_h * p.y_twist"), &p.y_twist)?;
185            let g_rr_at_p = Fp4G::<P>::new(c0, c1);
186
187            f = f
188                .square(cs.ns(|| "f^2"))?
189                .mul(cs.ns(|| "f^2 * g_rr_at_p"), &g_rr_at_p)?;
190
191            if bit {
192                let ac = &q.addition_coefficients[add_idx];
193                add_idx += 1;
194
195                let l1_coeff_c_l1 = l1_coeff.mul(cs.ns(|| "l1_coeff * ac.c_l1"), &ac.c_l1)?;
196                let g_rq_at_p = Fp4G::<P>::new(
197                    ac.c_rz.mul(cs.ns(|| "ac.c_rz * p.y_twist"), &p.y_twist)?,
198                    q.y_over_twist
199                        .mul(cs.ns(|| "q.y_over_twist * ac.c_rz"), &ac.c_rz)?
200                        .add(
201                            cs.ns(|| "q.y_over_twist * ac.c_rz + (l1_coeff * ac.c_l1)"),
202                            &l1_coeff_c_l1,
203                        )?
204                        .negate(cs.ns(|| "-(q.y_over_twist * ac.c_rz + (l1_coeff * ac.c_l1))"))?,
205                );
206                f.mul_in_place(cs.ns(|| "f *= g_rq_at_p"), &g_rq_at_p)?;
207            }
208        }
209
210        if P::ATE_IS_LOOP_COUNT_NEG {
211            let ac = &q.addition_coefficients[add_idx];
212
213            let l1_coeff_c_l1 = l1_coeff.mul(cs.ns(|| "l1_coeff * ac.c_l1"), &ac.c_l1)?;
214            let g_rnegr_at_p = Fp4G::<P>::new(
215                ac.c_rz.mul(cs.ns(|| "ac.c_rz * p.y_twist"), &p.y_twist)?,
216                q.y_over_twist
217                    .mul(cs.ns(|| "q.y_over_twist * ac.c_rz"), &ac.c_rz)?
218                    .add(
219                        cs.ns(|| "q.y_over_twist * ac.c_rz + (l1_coeff * ac.c_l1)"),
220                        &l1_coeff_c_l1,
221                    )?
222                    .negate(cs.ns(|| "-(q.y_over_twist * ac.c_rz + (l1_coeff * ac.c_l1))"))?,
223            );
224            f = f
225                .mul(cs.ns(|| "f * g_rnegr_at_p"), &g_rnegr_at_p)?
226                .inverse(cs.ns(|| "inverse f"))?;
227        }
228
229        Ok(f)
230    }
231
232    pub fn final_exponentiation<CS: ConstraintSystem<P::Fp>>(
233        mut cs: CS,
234        value: &Fp4G<P>,
235    ) -> Result<GTGadget<P>, SynthesisError> {
236        let value_inv = value.inverse(cs.ns(|| "value inverse"))?;
237        let value_to_first_chunk = Self::final_exponentiation_first_chunk(
238            cs.ns(|| "value_to_first_chunk"),
239            value,
240            &value_inv,
241        )?;
242        let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(
243            cs.ns(|| "value_inv_to_first_chunk"),
244            &value_inv,
245            value,
246        )?;
247        Self::final_exponentiation_last_chunk(
248            cs.ns(|| "final_exp_last_chunk"),
249            &value_to_first_chunk,
250            &value_inv_to_first_chunk,
251        )
252    }
253
254    fn final_exponentiation_first_chunk<CS: ConstraintSystem<P::Fp>>(
255        mut cs: CS,
256        elt: &Fp4G<P>,
257        elt_inv: &Fp4G<P>,
258    ) -> Result<Fp4G<P>, SynthesisError> {
259        // (q^2-1)
260
261        // elt_q2 = elt^(q^2)
262        let mut elt_q2 = elt.clone();
263        elt_q2.frobenius_map_in_place(cs.ns(|| "frobenius 2"), 2)?;
264        // elt_q2_over_elt = elt^(q^2-1)
265        elt_q2.mul(cs.ns(|| "elt_q2 * elt_inv"), elt_inv)
266    }
267
268    fn final_exponentiation_last_chunk<CS: ConstraintSystem<P::Fp>>(
269        mut cs: CS,
270        elt: &Fp4G<P>,
271        elt_inv: &Fp4G<P>,
272    ) -> Result<Fp4G<P>, SynthesisError> {
273        let elt_clone = elt.clone();
274        let elt_inv_clone = elt_inv.clone();
275
276        let mut elt_q = elt.clone();
277        elt_q.frobenius_map_in_place(cs.ns(|| "frobenius 1"), 1)?;
278
279        let w1_part = elt_q.cyclotomic_exp(cs.ns(|| "w1_part"), &P::FINAL_EXPONENT_LAST_CHUNK_1)?;
280        let w0_part;
281        if P::FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG {
282            w0_part = elt_inv_clone
283                .cyclotomic_exp(cs.ns(|| "w0_part"), &P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?;
284        } else {
285            w0_part = elt_clone
286                .cyclotomic_exp(cs.ns(|| "w0_part"), &P::FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0)?;
287        }
288
289        w1_part.mul(cs.ns(|| "w1_part * w0_part"), &w0_part)
290    }
291}
292
293impl<P: MNT4Parameters> PG<MNT4<P>, P::Fp> for PairingGadget<P> {
294    type G1Gadget = G1Gadget<P>;
295    type G2Gadget = G2Gadget<P>;
296    type G1PreparedGadget = G1PreparedGadget<P>;
297    type G2PreparedGadget = G2PreparedGadget<P>;
298    type GTGadget = GTGadget<P>;
299
300    fn miller_loop<CS: ConstraintSystem<P::Fp>>(
301        mut cs: CS,
302        ps: &[Self::G1PreparedGadget],
303        qs: &[Self::G2PreparedGadget],
304    ) -> Result<Self::GTGadget, SynthesisError> {
305        let mut result = Fp4G::<P>::one(cs.ns(|| "one"))?;
306        for (i, (p, q)) in ps.iter().zip(qs.iter()).enumerate() {
307            let miller =
308                Self::ate_miller_loop(cs.ns(|| format!("ate miller loop iteration {}", i)), p, q)?;
309            result.mul_in_place(
310                cs.ns(|| format!("mul ate miller loop iteration {}", i)),
311                &miller,
312            )?;
313        }
314
315        Ok(result)
316    }
317
318    fn final_exponentiation<CS: ConstraintSystem<P::Fp>>(
319        cs: CS,
320        r: &Self::GTGadget,
321    ) -> Result<Self::GTGadget, SynthesisError> {
322        Self::final_exponentiation(cs, r)
323    }
324
325    fn prepare_g1<CS: ConstraintSystem<P::Fp>>(
326        cs: CS,
327        p: &Self::G1Gadget,
328    ) -> Result<Self::G1PreparedGadget, SynthesisError> {
329        Self::G1PreparedGadget::from_affine(cs, p)
330    }
331
332    fn prepare_g2<CS: ConstraintSystem<P::Fp>>(
333        cs: CS,
334        q: &Self::G2Gadget,
335    ) -> Result<Self::G2PreparedGadget, SynthesisError> {
336        Self::G2PreparedGadget::from_affine(cs, q)
337    }
338}