ark_r1cs_std/pairing/mnt6/
mod.rs

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