Skip to main content

ark_r1cs_std/groups/curves/short_weierstrass/mnt6/
mod.rs

1use ark_ec::mnt6::{
2    g2::{AteAdditionCoefficients, AteDoubleCoefficients},
3    G1Prepared, G2Prepared, MNT6Config,
4};
5use ark_ff::Field;
6use ark_relations::gr1cs::{Namespace, SynthesisError};
7use ark_std::vec::Vec;
8
9use crate::{
10    fields::{fp::FpVar, fp3::Fp3Var},
11    groups::curves::short_weierstrass::ProjectiveVar,
12    pairing::mnt6::PairingVar,
13    prelude::*,
14};
15use core::borrow::Borrow;
16use educe::Educe;
17
18/// Represents a projective point in G1.
19pub type G1Var<P> = ProjectiveVar<<P as MNT6Config>::G1Config, FpVar<<P as MNT6Config>::Fp>>;
20
21/// Represents a projective point in G2.
22pub type G2Var<P> = ProjectiveVar<<P as MNT6Config>::G2Config, Fp3G<P>>;
23
24/// Represents the cached precomputation that can be performed on a G1 element
25/// which enables speeding up pairing computation.
26#[derive(Educe)]
27#[educe(Clone, Debug)]
28pub struct G1PreparedVar<P: MNT6Config> {
29    #[doc(hidden)]
30    pub x: FpVar<P::Fp>,
31    #[doc(hidden)]
32    pub y: FpVar<P::Fp>,
33    #[doc(hidden)]
34    pub x_twist: Fp3Var<P::Fp3Config>,
35    #[doc(hidden)]
36    pub y_twist: Fp3Var<P::Fp3Config>,
37}
38
39impl<P: MNT6Config> G1PreparedVar<P> {
40    /// Returns the value assigned to `self` in the underlying constraint
41    /// system.
42    pub fn value(&self) -> Result<G1Prepared<P>, SynthesisError> {
43        let x = self.x.value()?;
44        let y = self.y.value()?;
45        let x_twist = self.x_twist.value()?;
46        let y_twist = self.y_twist.value()?;
47        Ok(G1Prepared {
48            x,
49            y,
50            x_twist,
51            y_twist,
52        })
53    }
54
55    /// Constructs `Self` from a `G1Var`.
56    #[tracing::instrument(target = "gr1cs")]
57    pub fn from_group_var(q: &G1Var<P>) -> Result<Self, SynthesisError> {
58        let q = q.to_affine()?;
59        let zero = FpVar::<P::Fp>::zero();
60        let x_twist = Fp3Var::new(q.x.clone(), zero.clone(), zero.clone()) * P::TWIST;
61        let y_twist = Fp3Var::new(q.y.clone(), zero.clone(), zero) * P::TWIST;
62        let result = G1PreparedVar {
63            x: q.x,
64            y: q.y,
65            x_twist,
66            y_twist,
67        };
68        Ok(result)
69    }
70}
71
72impl<P: MNT6Config> AllocVar<G1Prepared<P>, P::Fp> for G1PreparedVar<P> {
73    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
74    fn new_variable<T: Borrow<G1Prepared<P>>>(
75        cs: impl Into<Namespace<P::Fp>>,
76        f: impl FnOnce() -> Result<T, SynthesisError>,
77        mode: AllocationMode,
78    ) -> Result<Self, SynthesisError> {
79        let ns = cs.into();
80        let cs = ns.cs();
81
82        let g1_prep = f().map(|b| *b.borrow());
83
84        let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?;
85        let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?;
86        let x_twist = Fp3Var::new_variable(
87            ark_relations::ns!(cs, "x_twist"),
88            || g1_prep.map(|g| g.x_twist),
89            mode,
90        )?;
91        let y_twist = Fp3Var::new_variable(
92            ark_relations::ns!(cs, "y_twist"),
93            || g1_prep.map(|g| g.y_twist),
94            mode,
95        )?;
96        Ok(Self {
97            x,
98            y,
99            x_twist,
100            y_twist,
101        })
102    }
103}
104
105impl<P: MNT6Config> ToBytesGadget<P::Fp> for G1PreparedVar<P> {
106    #[inline]
107    #[tracing::instrument(target = "gr1cs")]
108    fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
109        let mut x = self.x.to_bytes_le()?;
110        let mut y = self.y.to_bytes_le()?;
111        let mut x_twist = self.x_twist.to_bytes_le()?;
112        let mut y_twist = self.y_twist.to_bytes_le()?;
113
114        x.append(&mut y);
115        x.append(&mut x_twist);
116        x.append(&mut y_twist);
117        Ok(x)
118    }
119
120    #[tracing::instrument(target = "gr1cs")]
121    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
122        let mut x = self.x.to_non_unique_bytes_le()?;
123        let mut y = self.y.to_non_unique_bytes_le()?;
124        let mut x_twist = self.x_twist.to_non_unique_bytes_le()?;
125        let mut y_twist = self.y_twist.to_non_unique_bytes_le()?;
126
127        x.append(&mut y);
128        x.append(&mut x_twist);
129        x.append(&mut y_twist);
130        Ok(x)
131    }
132}
133
134type Fp3G<P> = Fp3Var<<P as MNT6Config>::Fp3Config>;
135
136/// Represents the cached precomputation that can be performed on a G2 element
137/// which enables speeding up pairing computation.
138#[derive(Educe)]
139#[educe(Clone, Debug)]
140pub struct G2PreparedVar<P: MNT6Config> {
141    #[doc(hidden)]
142    pub x: Fp3Var<P::Fp3Config>,
143    #[doc(hidden)]
144    pub y: Fp3Var<P::Fp3Config>,
145    #[doc(hidden)]
146    pub x_over_twist: Fp3Var<P::Fp3Config>,
147    #[doc(hidden)]
148    pub y_over_twist: Fp3Var<P::Fp3Config>,
149    #[doc(hidden)]
150    pub double_coefficients: Vec<AteDoubleCoefficientsVar<P>>,
151    #[doc(hidden)]
152    pub addition_coefficients: Vec<AteAdditionCoefficientsVar<P>>,
153}
154
155impl<P: MNT6Config> AllocVar<G2Prepared<P>, P::Fp> for G2PreparedVar<P> {
156    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
157    fn new_variable<T: Borrow<G2Prepared<P>>>(
158        cs: impl Into<Namespace<P::Fp>>,
159        f: impl FnOnce() -> Result<T, SynthesisError>,
160        mode: AllocationMode,
161    ) -> Result<Self, SynthesisError> {
162        let ns = cs.into();
163        let cs = ns.cs();
164
165        let g2_prep = f().map(|b| b.borrow().clone());
166        let g2 = g2_prep.as_ref().map_err(|e| *e);
167
168        let x = Fp3Var::new_variable(ark_relations::ns!(cs, "x"), || g2.map(|g| g.x), mode)?;
169        let y = Fp3Var::new_variable(ark_relations::ns!(cs, "y"), || g2.map(|g| g.y), mode)?;
170        let x_over_twist = Fp3Var::new_variable(
171            ark_relations::ns!(cs, "x_over_twist"),
172            || g2.map(|g| g.x_over_twist),
173            mode,
174        )?;
175        let y_over_twist = Fp3Var::new_variable(
176            ark_relations::ns!(cs, "y_over_twist"),
177            || g2.map(|g| g.y_over_twist),
178            mode,
179        )?;
180        let double_coefficients = Vec::new_variable(
181            ark_relations::ns!(cs, "double coeffs"),
182            || g2.map(|g| g.double_coefficients.clone()),
183            mode,
184        )?;
185        let addition_coefficients = Vec::new_variable(
186            ark_relations::ns!(cs, "add coeffs"),
187            || g2.map(|g| g.addition_coefficients.clone()),
188            mode,
189        )?;
190        Ok(Self {
191            x,
192            y,
193            x_over_twist,
194            y_over_twist,
195            double_coefficients,
196            addition_coefficients,
197        })
198    }
199}
200
201impl<P: MNT6Config> ToBytesGadget<P::Fp> for G2PreparedVar<P> {
202    #[inline]
203    #[tracing::instrument(target = "gr1cs")]
204    fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
205        let mut x = self.x.to_bytes_le()?;
206        let mut y = self.y.to_bytes_le()?;
207        let mut x_over_twist = self.x_over_twist.to_bytes_le()?;
208        let mut y_over_twist = self.y_over_twist.to_bytes_le()?;
209
210        x.append(&mut y);
211        x.append(&mut x_over_twist);
212        x.append(&mut y_over_twist);
213
214        for coeff in self.double_coefficients.iter() {
215            x.extend_from_slice(&coeff.to_bytes_le()?);
216        }
217        for coeff in self.addition_coefficients.iter() {
218            x.extend_from_slice(&coeff.to_bytes_le()?);
219        }
220        Ok(x)
221    }
222
223    #[tracing::instrument(target = "gr1cs")]
224    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
225        let mut x = self.x.to_non_unique_bytes_le()?;
226        let mut y = self.y.to_non_unique_bytes_le()?;
227        let mut x_over_twist = self.x_over_twist.to_non_unique_bytes_le()?;
228        let mut y_over_twist = self.y_over_twist.to_non_unique_bytes_le()?;
229
230        x.append(&mut y);
231        x.append(&mut x_over_twist);
232        x.append(&mut y_over_twist);
233
234        for coeff in self.double_coefficients.iter() {
235            x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
236        }
237        for coeff in self.addition_coefficients.iter() {
238            x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
239        }
240        Ok(x)
241    }
242}
243
244impl<P: MNT6Config> G2PreparedVar<P> {
245    /// Returns the value assigned to `self` in the underlying constraint
246    /// system.
247    pub fn value(&self) -> Result<G2Prepared<P>, SynthesisError> {
248        let x = self.x.value()?;
249        let y = self.y.value()?;
250        let x_over_twist = self.x_over_twist.value()?;
251        let y_over_twist = self.y_over_twist.value()?;
252        let double_coefficients = self
253            .double_coefficients
254            .iter()
255            .map(|coeff| coeff.value())
256            .collect::<Result<Vec<_>, SynthesisError>>()?;
257        let addition_coefficients = self
258            .addition_coefficients
259            .iter()
260            .map(|coeff| coeff.value())
261            .collect::<Result<Vec<_>, SynthesisError>>()?;
262        Ok(G2Prepared {
263            x,
264            y,
265            x_over_twist,
266            y_over_twist,
267            double_coefficients,
268            addition_coefficients,
269        })
270    }
271
272    /// Constructs `Self` from a `G2Var`.
273    #[tracing::instrument(target = "gr1cs")]
274    pub fn from_group_var(q: &G2Var<P>) -> Result<Self, SynthesisError> {
275        let q = q.to_affine()?;
276        let twist_inv = P::TWIST.inverse().unwrap();
277
278        let mut g2p = G2PreparedVar {
279            x: q.x.clone(),
280            y: q.y.clone(),
281            x_over_twist: &q.x * twist_inv,
282            y_over_twist: &q.y * twist_inv,
283            double_coefficients: vec![],
284            addition_coefficients: vec![],
285        };
286
287        let mut r = G2ProjectiveExtendedVar {
288            x: q.x.clone(),
289            y: q.y.clone(),
290            z: Fp3G::<P>::one(),
291            t: Fp3G::<P>::one(),
292        };
293
294        for bit in P::ATE_LOOP_COUNT.iter().skip(1) {
295            let (r2, coeff) = PairingVar::<P>::doubling_step_for_flipped_miller_loop(&r)?;
296            g2p.double_coefficients.push(coeff);
297            r = r2;
298
299            let add_coeff;
300            let r_temp;
301            match bit {
302                1 => {
303                    (r_temp, add_coeff) =
304                        PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
305                            &q.x, &q.y, &r,
306                        )?;
307                },
308                -1 => {
309                    (r_temp, add_coeff) =
310                        PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
311                            &q.x,
312                            &q.y.negate()?,
313                            &r,
314                        )?;
315                },
316                _ => continue,
317            }
318            g2p.addition_coefficients.push(add_coeff);
319            r = r_temp;
320        }
321
322        if P::ATE_IS_LOOP_COUNT_NEG {
323            let rz_inv = r.z.inverse()?;
324            let rz2_inv = rz_inv.square()?;
325            let rz3_inv = &rz_inv * &rz2_inv;
326
327            let minus_r_affine_x = &r.x * &rz2_inv;
328            let minus_r_affine_y = r.y.negate()? * &rz3_inv;
329
330            let add_result = PairingVar::<P>::mixed_addition_step_for_flipped_miller_loop(
331                &minus_r_affine_x,
332                &minus_r_affine_y,
333                &r,
334            )?;
335            g2p.addition_coefficients.push(add_result.1);
336        }
337
338        Ok(g2p)
339    }
340}
341
342#[doc(hidden)]
343#[derive(Educe)]
344#[educe(Clone, Debug)]
345pub struct AteDoubleCoefficientsVar<P: MNT6Config> {
346    pub c_h: Fp3Var<P::Fp3Config>,
347    pub c_4c: Fp3Var<P::Fp3Config>,
348    pub c_j: Fp3Var<P::Fp3Config>,
349    pub c_l: Fp3Var<P::Fp3Config>,
350}
351
352impl<P: MNT6Config> AllocVar<AteDoubleCoefficients<P>, P::Fp> for AteDoubleCoefficientsVar<P> {
353    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
354    fn new_variable<T: Borrow<AteDoubleCoefficients<P>>>(
355        cs: impl Into<Namespace<P::Fp>>,
356        f: impl FnOnce() -> Result<T, SynthesisError>,
357        mode: AllocationMode,
358    ) -> Result<Self, SynthesisError> {
359        let ns = cs.into();
360        let cs = ns.cs();
361
362        let c_prep = f().map(|c| c.borrow().clone());
363        let c = c_prep.as_ref().map_err(|e| *e);
364
365        let c_h = Fp3Var::new_variable(ark_relations::ns!(cs, "c_h"), || c.map(|c| c.c_h), mode)?;
366        let c_4c =
367            Fp3Var::new_variable(ark_relations::ns!(cs, "c_4c"), || c.map(|c| c.c_4c), mode)?;
368        let c_j = Fp3Var::new_variable(ark_relations::ns!(cs, "c_j"), || c.map(|c| c.c_j), mode)?;
369        let c_l = Fp3Var::new_variable(ark_relations::ns!(cs, "c_l"), || c.map(|c| c.c_l), mode)?;
370        Ok(Self {
371            c_h,
372            c_4c,
373            c_j,
374            c_l,
375        })
376    }
377}
378
379impl<P: MNT6Config> ToBytesGadget<P::Fp> for AteDoubleCoefficientsVar<P> {
380    #[inline]
381    #[tracing::instrument(target = "gr1cs")]
382    fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
383        let mut c_h = self.c_h.to_bytes_le()?;
384        let mut c_4c = self.c_4c.to_bytes_le()?;
385        let mut c_j = self.c_j.to_bytes_le()?;
386        let mut c_l = self.c_l.to_bytes_le()?;
387
388        c_h.append(&mut c_4c);
389        c_h.append(&mut c_j);
390        c_h.append(&mut c_l);
391        Ok(c_h)
392    }
393
394    #[tracing::instrument(target = "gr1cs")]
395    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
396        let mut c_h = self.c_h.to_non_unique_bytes_le()?;
397        let mut c_4c = self.c_4c.to_non_unique_bytes_le()?;
398        let mut c_j = self.c_j.to_non_unique_bytes_le()?;
399        let mut c_l = self.c_l.to_non_unique_bytes_le()?;
400
401        c_h.append(&mut c_4c);
402        c_h.append(&mut c_j);
403        c_h.append(&mut c_l);
404        Ok(c_h)
405    }
406}
407
408impl<P: MNT6Config> AteDoubleCoefficientsVar<P> {
409    /// Returns the value assigned to `self` in the underlying constraint
410    /// system.
411    pub fn value(&self) -> Result<AteDoubleCoefficients<P>, SynthesisError> {
412        let c_h = self.c_h.value()?;
413        let c_4c = self.c_4c.value()?;
414        let c_j = self.c_j.value()?;
415        let c_l = self.c_l.value()?;
416        Ok(AteDoubleCoefficients {
417            c_h,
418            c_4c,
419            c_j,
420            c_l,
421        })
422    }
423}
424
425#[doc(hidden)]
426#[derive(Educe)]
427#[educe(Clone, Debug)]
428pub struct AteAdditionCoefficientsVar<P: MNT6Config> {
429    pub c_l1: Fp3Var<P::Fp3Config>,
430    pub c_rz: Fp3Var<P::Fp3Config>,
431}
432
433impl<P: MNT6Config> AllocVar<AteAdditionCoefficients<P>, P::Fp> for AteAdditionCoefficientsVar<P> {
434    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
435    fn new_variable<T: Borrow<AteAdditionCoefficients<P>>>(
436        cs: impl Into<Namespace<P::Fp>>,
437        f: impl FnOnce() -> Result<T, SynthesisError>,
438        mode: AllocationMode,
439    ) -> Result<Self, SynthesisError> {
440        let ns = cs.into();
441        let cs = ns.cs();
442
443        let c_prep = f().map(|c| c.borrow().clone());
444        let c = c_prep.as_ref().map_err(|e| *e);
445
446        let c_l1 =
447            Fp3Var::new_variable(ark_relations::ns!(cs, "c_l1"), || c.map(|c| c.c_l1), mode)?;
448        let c_rz =
449            Fp3Var::new_variable(ark_relations::ns!(cs, "c_rz"), || c.map(|c| c.c_rz), mode)?;
450        Ok(Self { c_l1, c_rz })
451    }
452}
453
454impl<P: MNT6Config> ToBytesGadget<P::Fp> for AteAdditionCoefficientsVar<P> {
455    #[inline]
456    #[tracing::instrument(target = "gr1cs")]
457    fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
458        let mut c_l1 = self.c_l1.to_bytes_le()?;
459        let mut c_rz = self.c_rz.to_bytes_le()?;
460
461        c_l1.append(&mut c_rz);
462        Ok(c_l1)
463    }
464
465    #[tracing::instrument(target = "gr1cs")]
466    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
467        let mut c_l1 = self.c_l1.to_non_unique_bytes_le()?;
468        let mut c_rz = self.c_rz.to_non_unique_bytes_le()?;
469
470        c_l1.append(&mut c_rz);
471        Ok(c_l1)
472    }
473}
474
475impl<P: MNT6Config> AteAdditionCoefficientsVar<P> {
476    /// Returns the value assigned to `self` in the underlying constraint
477    /// system.
478    pub fn value(&self) -> Result<AteAdditionCoefficients<P>, SynthesisError> {
479        let c_l1 = self.c_l1.value()?;
480        let c_rz = self.c_rz.value()?;
481        Ok(AteAdditionCoefficients { c_l1, c_rz })
482    }
483}
484
485#[doc(hidden)]
486pub struct G2ProjectiveExtendedVar<P: MNT6Config> {
487    pub x: Fp3Var<P::Fp3Config>,
488    pub y: Fp3Var<P::Fp3Config>,
489    pub z: Fp3Var<P::Fp3Config>,
490    pub t: Fp3Var<P::Fp3Config>,
491}