Skip to main content

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

1use ark_ec::mnt4::{
2    g2::{AteAdditionCoefficients, AteDoubleCoefficients},
3    G1Prepared, G2Prepared, MNT4Config,
4};
5use ark_ff::Field;
6use ark_relations::gr1cs::{Namespace, SynthesisError};
7use ark_std::vec::Vec;
8
9use crate::{
10    fields::{fp::FpVar, fp2::Fp2Var},
11    groups::curves::short_weierstrass::ProjectiveVar,
12    pairing::mnt4::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 MNT4Config>::G1Config, FpVar<<P as MNT4Config>::Fp>>;
20
21/// Represents a projective point in G2.
22pub type G2Var<P> = ProjectiveVar<<P as MNT4Config>::G2Config, Fp2G<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: MNT4Config> {
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: Fp2Var<P::Fp2Config>,
35    #[doc(hidden)]
36    pub y_twist: Fp2Var<P::Fp2Config>,
37}
38
39impl<P: MNT4Config> AllocVar<G1Prepared<P>, P::Fp> for G1PreparedVar<P> {
40    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
41    fn new_variable<T: Borrow<G1Prepared<P>>>(
42        cs: impl Into<Namespace<P::Fp>>,
43        f: impl FnOnce() -> Result<T, SynthesisError>,
44        mode: AllocationMode,
45    ) -> Result<Self, SynthesisError> {
46        let ns = cs.into();
47        let cs = ns.cs();
48
49        let g1_prep = f().map(|b| *b.borrow());
50
51        let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?;
52        let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?;
53        let x_twist = Fp2Var::new_variable(
54            ark_relations::ns!(cs, "x_twist"),
55            || g1_prep.map(|g| g.x_twist),
56            mode,
57        )?;
58        let y_twist = Fp2Var::new_variable(
59            ark_relations::ns!(cs, "y_twist"),
60            || g1_prep.map(|g| g.y_twist),
61            mode,
62        )?;
63        Ok(Self {
64            x,
65            y,
66            x_twist,
67            y_twist,
68        })
69    }
70}
71
72impl<P: MNT4Config> G1PreparedVar<P> {
73    /// Returns the value assigned to `self` in the underlying constraint
74    /// system.
75    pub fn value(&self) -> Result<G1Prepared<P>, SynthesisError> {
76        let (x, y, x_twist, y_twist) = (
77            self.x.value()?,
78            self.y.value()?,
79            self.x_twist.value()?,
80            self.y_twist.value()?,
81        );
82        Ok(G1Prepared {
83            x,
84            y,
85            x_twist,
86            y_twist,
87        })
88    }
89
90    /// Constructs `Self` from a `G1Var`.
91    #[tracing::instrument(target = "gr1cs")]
92    pub fn from_group_var(q: &G1Var<P>) -> Result<Self, SynthesisError> {
93        let q = q.to_affine()?;
94        let x_twist = Fp2Var::new(&q.x * P::TWIST.c0, &q.x * P::TWIST.c1);
95        let y_twist = Fp2Var::new(&q.y * P::TWIST.c0, &q.y * P::TWIST.c1);
96        Ok(G1PreparedVar {
97            x: q.x,
98            y: q.y,
99            x_twist,
100            y_twist,
101        })
102    }
103}
104
105impl<P: MNT4Config> 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 Fp2G<P> = Fp2Var<<P as MNT4Config>::Fp2Config>;
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: MNT4Config> {
141    #[doc(hidden)]
142    pub x: Fp2Var<P::Fp2Config>,
143    #[doc(hidden)]
144    pub y: Fp2Var<P::Fp2Config>,
145    #[doc(hidden)]
146    pub x_over_twist: Fp2Var<P::Fp2Config>,
147    #[doc(hidden)]
148    pub y_over_twist: Fp2Var<P::Fp2Config>,
149    #[doc(hidden)]
150    pub double_coefficients: Vec<AteDoubleCoefficientsVar<P>>,
151    #[doc(hidden)]
152    pub addition_coefficients: Vec<AteAdditionCoefficientsVar<P>>,
153}
154
155impl<P: MNT4Config> 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 = Fp2Var::new_variable(ark_relations::ns!(cs, "x"), || g2.map(|g| g.x), mode)?;
169        let y = Fp2Var::new_variable(ark_relations::ns!(cs, "y"), || g2.map(|g| g.y), mode)?;
170        let x_over_twist = Fp2Var::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 = Fp2Var::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: MNT4Config> 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 {
215            x.extend_from_slice(&coeff.to_bytes_le()?);
216        }
217        for coeff in &self.addition_coefficients {
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 {
235            x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
236        }
237        for coeff in &self.addition_coefficients {
238            x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
239        }
240        Ok(x)
241    }
242}
243
244impl<P: MNT4Config> 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<AteDoubleCoefficients<P>>, _>>()?;
257        let addition_coefficients = self
258            .addition_coefficients
259            .iter()
260            .map(|coeff| coeff.value())
261            .collect::<Result<Vec<AteAdditionCoefficients<P>>, _>>()?;
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 twist_inv = P::TWIST.inverse().unwrap();
276        let q = q.to_affine()?;
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: Fp2G::<P>::one(),
291            t: Fp2G::<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: MNT4Config> {
346    pub c_h: Fp2Var<P::Fp2Config>,
347    pub c_4c: Fp2Var<P::Fp2Config>,
348    pub c_j: Fp2Var<P::Fp2Config>,
349    pub c_l: Fp2Var<P::Fp2Config>,
350}
351
352impl<P: MNT4Config> 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 = Fp2Var::new_variable(ark_relations::ns!(cs, "c_h"), || c.map(|c| c.c_h), mode)?;
366        let c_4c =
367            Fp2Var::new_variable(ark_relations::ns!(cs, "c_4c"), || c.map(|c| c.c_4c), mode)?;
368        let c_j = Fp2Var::new_variable(ark_relations::ns!(cs, "c_j"), || c.map(|c| c.c_j), mode)?;
369        let c_l = Fp2Var::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: MNT4Config> 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: MNT4Config> 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, c_4c, c_j, c_l) = (
413            self.c_l.value()?,
414            self.c_4c.value()?,
415            self.c_j.value()?,
416            self.c_l.value()?,
417        );
418        Ok(AteDoubleCoefficients {
419            c_h,
420            c_4c,
421            c_j,
422            c_l,
423        })
424    }
425}
426
427#[doc(hidden)]
428#[derive(Educe)]
429#[educe(Clone, Debug)]
430pub struct AteAdditionCoefficientsVar<P: MNT4Config> {
431    pub c_l1: Fp2Var<P::Fp2Config>,
432    pub c_rz: Fp2Var<P::Fp2Config>,
433}
434
435impl<P: MNT4Config> AllocVar<AteAdditionCoefficients<P>, P::Fp> for AteAdditionCoefficientsVar<P> {
436    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
437    fn new_variable<T: Borrow<AteAdditionCoefficients<P>>>(
438        cs: impl Into<Namespace<P::Fp>>,
439        f: impl FnOnce() -> Result<T, SynthesisError>,
440        mode: AllocationMode,
441    ) -> Result<Self, SynthesisError> {
442        let ns = cs.into();
443        let cs = ns.cs();
444
445        let c_prep = f().map(|c| c.borrow().clone());
446        let c = c_prep.as_ref().map_err(|e| *e);
447
448        let c_l1 =
449            Fp2Var::new_variable(ark_relations::ns!(cs, "c_l1"), || c.map(|c| c.c_l1), mode)?;
450        let c_rz =
451            Fp2Var::new_variable(ark_relations::ns!(cs, "c_rz"), || c.map(|c| c.c_rz), mode)?;
452        Ok(Self { c_l1, c_rz })
453    }
454}
455
456impl<P: MNT4Config> ToBytesGadget<P::Fp> for AteAdditionCoefficientsVar<P> {
457    #[inline]
458    #[tracing::instrument(target = "gr1cs")]
459    fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
460        let mut c_l1 = self.c_l1.to_bytes_le()?;
461        let mut c_rz = self.c_rz.to_bytes_le()?;
462
463        c_l1.append(&mut c_rz);
464        Ok(c_l1)
465    }
466
467    #[tracing::instrument(target = "gr1cs")]
468    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
469        let mut c_l1 = self.c_l1.to_non_unique_bytes_le()?;
470        let mut c_rz = self.c_rz.to_non_unique_bytes_le()?;
471
472        c_l1.append(&mut c_rz);
473        Ok(c_l1)
474    }
475}
476
477impl<P: MNT4Config> AteAdditionCoefficientsVar<P> {
478    /// Returns the value assigned to `self` in the underlying constraint
479    /// system.
480    pub fn value(&self) -> Result<AteAdditionCoefficients<P>, SynthesisError> {
481        let (c_l1, c_rz) = (self.c_l1.value()?, self.c_rz.value()?);
482        Ok(AteAdditionCoefficients { c_l1, c_rz })
483    }
484}
485
486#[doc(hidden)]
487pub struct G2ProjectiveExtendedVar<P: MNT4Config> {
488    pub x: Fp2Var<P::Fp2Config>,
489    pub y: Fp2Var<P::Fp2Config>,
490    pub z: Fp2Var<P::Fp2Config>,
491    pub t: Fp2Var<P::Fp2Config>,
492}