ark-r1cs-std/r1cs-std/src/pairing/mnt4/mod.rs
r1cs-std/src/pairing/mnt4/mod.rsRaw
1use r1cs_core::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 algebra::{
13 curves::mnt4::{MNT4Parameters, MNT4},
14 fields::BitIteratorBE,
15};
16use core::marker::PhantomData;
17
18/// Specifies the constraints for computing a pairing in a MNT4 bilinear group.
19pub struct PairingVar<P: MNT4Parameters>(PhantomData<P>);
20
21type Fp2G<P> = Fp2Var<<P as MNT4Parameters>::Fp2Params>;
22type Fp4G<P> = Fp4Var<<P as MNT4Parameters>::Fp4Params>;
23/// A variable corresponding to `algebra_core::mnt4::GT`.
24pub type GTVar<P> = Fp4G<P>;
25
26impl<P: MNT4Parameters> PairingVar<P> {
27 #[tracing::instrument(target = "r1cs", skip(r))]
28 pub(crate) fn doubling_step_for_flipped_miller_loop(
29 r: &G2ProjectiveExtendedVar<P>,
30 ) -> Result<(G2ProjectiveExtendedVar<P>, AteDoubleCoefficientsVar<P>), SynthesisError> {
31 let a = r.t.square()?;
32 let b = r.x.square()?;
33 let c = r.y.square()?;
34 let d = c.square()?;
35 let e = (&r.x + &c).square()? - &b - &d;
36 let f = (b.double()? + &b) + &a * P::TWIST_COEFF_A;
37 let g = f.square()?;
38
39 let d_eight = d.double()?.double()?.double()?;
40
41 let e2 = e.double()?;
42 let x = &g - &e2.double()?;
43
44 let y = &f * (&e2 - &x) - &d_eight;
45 let z = (&r.y + &r.z).square()? - &c - &r.z.square()?;
46 let t = z.square()?;
47
48 let r2 = G2ProjectiveExtendedVar { x, y, z, t };
49 let c_h = (&r2.z + &r.t).square()? - &r2.t - &a;
50 let c_4c = c.double()?.double()?;
51 let c_j = (&f + &r.t).square()? - &g - &a;
52 let c_l = (&f + &r.x).square()? - &g - &b;
53 let coeff = AteDoubleCoefficientsVar {
54 c_h,
55 c_4c,
56 c_j,
57 c_l,
58 };
59
60 Ok((r2, coeff))
61 }
62
63 #[tracing::instrument(target = "r1cs", skip(r))]
64 pub(crate) fn mixed_addition_step_for_flipped_miller_loop(
65 x: &Fp2G<P>,
66 y: &Fp2G<P>,
67 r: &G2ProjectiveExtendedVar<P>,
68 ) -> Result<(G2ProjectiveExtendedVar<P>, AteAdditionCoefficientsVar<P>), SynthesisError> {
69 let a = y.square()?;
70 let b = &r.t * x;
71 let d = ((&r.z + y).square()? - &a - &r.t) * &r.t;
72 let h = &b - &r.x;
73 let i = h.square()?;
74 let e = i.double()?.double()?;
75 let j = &h * &e;
76 let v = &r.x * &e;
77 let ry2 = r.y.double()?;
78 let l1 = &d - &ry2;
79
80 let x = l1.square()? - &j - &v.double()?;
81 let y = &l1 * &(&v - &x) - j * &ry2;
82 let z = (&r.z + &h).square()? - &r.t - &i;
83 let t = z.square()?;
84
85 let r2 = G2ProjectiveExtendedVar {
86 x,
87 y,
88 z: z.clone(),
89 t,
90 };
91 let coeff = AteAdditionCoefficientsVar { c_l1: l1, c_rz: z };
92
93 Ok((r2, coeff))
94 }
95
96 #[tracing::instrument(target = "r1cs", skip(p, q))]
97 pub(crate) fn ate_miller_loop(
98 p: &G1PreparedVar<P>,
99 q: &G2PreparedVar<P>,
100 ) -> Result<Fp4G<P>, SynthesisError> {
101 let l1_coeff = Fp2G::<P>::new(p.x.clone(), FpVar::<P::Fp>::zero()) - &q.x_over_twist;
102
103 let mut f = Fp4G::<P>::one();
104
105 let mut dbl_idx: usize = 0;
106 let mut add_idx: usize = 0;
107
108 // code below gets executed for all bits (EXCEPT the MSB itself) of
109 // mnt6_param_p (skipping leading zeros) in MSB to LSB order
110 for bit in BitIteratorBE::without_leading_zeros(P::ATE_LOOP_COUNT).skip(1) {
111 let dc = &q.double_coefficients[dbl_idx];
112 dbl_idx += 1;
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}