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}