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