2 *****************************************************************************
4 Implementation of interfaces for Fp6 gadgets.
6 See fp6_2over3_gadgets.hpp .
8 *****************************************************************************
9 * @author This file is part of libsnark, developed by SCIPR Lab
10 * and contributors (see AUTHORS).
11 * @copyright MIT license (see LICENSE file)
12 *****************************************************************************/
14 #ifndef FP6_GADGETS_TCC_
15 #define FP6_GADGETS_TCC_
20 template<typename Fp6T>
21 Fp6_2over3_variable<Fp6T>::Fp6_2over3_variable(
22 protoboard<FieldT> &pb, const std::string &annotation_prefix)
23 : gadget<FieldT>(pb, annotation_prefix)
24 , c0(pb, FMT(annotation_prefix, " c0"))
25 , c1(pb, FMT(annotation_prefix, " c1"))
29 template<typename Fp6T>
30 Fp6_2over3_variable<Fp6T>::Fp6_2over3_variable(
31 protoboard<FieldT> &pb,
33 const std::string &annotation_prefix)
34 : gadget<FieldT>(pb, annotation_prefix)
35 , c0(pb, el.coeffs[0], FMT(annotation_prefix, " c0"))
36 , c1(pb, el.coeffs[1], FMT(annotation_prefix, " c1"))
40 template<typename Fp6T>
41 Fp6_2over3_variable<Fp6T>::Fp6_2over3_variable(
42 protoboard<FieldT> &pb,
43 const Fp3_variable<Fp3T> &c0,
44 const Fp3_variable<Fp3T> &c1,
45 const std::string &annotation_prefix)
46 : gadget<FieldT>(pb, annotation_prefix), c0(c0), c1(c1)
50 template<typename Fp6T>
51 void Fp6_2over3_variable<Fp6T>::generate_r1cs_equals_const_constraints(
54 c0.generate_r1cs_equals_const_constraints(el.coeffs[0]);
55 c1.generate_r1cs_equals_const_constraints(el.coeffs[1]);
58 template<typename Fp6T>
59 void Fp6_2over3_variable<Fp6T>::generate_r1cs_witness(const Fp6T &el)
61 c0.generate_r1cs_witness(el.coeffs[0]);
62 c1.generate_r1cs_witness(el.coeffs[1]);
65 template<typename Fp6T> Fp6T Fp6_2over3_variable<Fp6T>::get_element()
68 el.coeffs[0] = c0.get_element();
69 el.coeffs[1] = c1.get_element();
73 template<typename Fp6T>
74 Fp6_2over3_variable<Fp6T> Fp6_2over3_variable<Fp6T>::Frobenius_map(
75 const size_t power) const
77 pb_linear_combination<FieldT> new_c0c0, new_c0c1, new_c0c2, new_c1c0,
79 new_c0c0.assign(this->pb, c0.c0);
80 new_c0c1.assign(this->pb, c0.c1 * Fp3T::Frobenius_coeffs_c1[power % 3]);
81 new_c0c2.assign(this->pb, c0.c2 * Fp3T::Frobenius_coeffs_c2[power % 3]);
82 new_c1c0.assign(this->pb, c1.c0 * Fp6T::Frobenius_coeffs_c1[power % 6]);
85 c1.c1 * (Fp6T::Frobenius_coeffs_c1[power % 6] *
86 Fp3T::Frobenius_coeffs_c1[power % 3]));
89 c1.c2 * (Fp6T::Frobenius_coeffs_c1[power % 6] *
90 Fp3T::Frobenius_coeffs_c2[power % 3]));
92 return Fp6_2over3_variable<Fp6T>(
99 FMT(this->annotation_prefix, " Frobenius_map_c0")),
105 FMT(this->annotation_prefix, " Frobenius_map_c1")),
106 FMT(this->annotation_prefix, " Frobenius_map"));
109 template<typename Fp6T> void Fp6_2over3_variable<Fp6T>::evaluate() const
115 template<typename Fp6T>
116 Fp6_2over3_mul_gadget<Fp6T>::Fp6_2over3_mul_gadget(
117 protoboard<FieldT> &pb,
118 const Fp6_2over3_variable<Fp6T> &A,
119 const Fp6_2over3_variable<Fp6T> &B,
120 const Fp6_2over3_variable<Fp6T> &result,
121 const std::string &annotation_prefix)
122 : gadget<FieldT>(pb, annotation_prefix), A(A), B(B), result(result)
125 Karatsuba multiplication for Fp6 as a quadratic extension of Fp3:
128 result.c0 = v0 + non_residue * v1
129 result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
130 where "non_residue * elem" := (non_residue * elem.c2, elem.c0, elem.c1)
132 Enforced with 3 Fp3_mul_gadget's that ensure that:
135 (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1
138 "Multiplication and Squaring on Pairing-Friendly Fields"
139 Devegili, OhEigeartaigh, Scott, Dahab
141 v1.reset(new Fp3_variable<Fp3T>(pb, FMT(annotation_prefix, " v1")));
143 compute_v1.reset(new Fp3_mul_gadget<Fp3T>(
144 pb, A.c1, B.c1, *v1, FMT(annotation_prefix, " compute_v1")));
146 v0_c0.assign(pb, result.c0.c0 - Fp6T::non_residue * v1->c2);
147 v0_c1.assign(pb, result.c0.c1 - v1->c0);
148 v0_c2.assign(pb, result.c0.c2 - v1->c1);
149 v0.reset(new Fp3_variable<Fp3T>(
150 pb, v0_c0, v0_c1, v0_c2, FMT(annotation_prefix, " v0")));
152 compute_v0.reset(new Fp3_mul_gadget<Fp3T>(
153 pb, A.c0, B.c0, *v0, FMT(annotation_prefix, " compute_v0")));
155 Ac0_plus_Ac1_c0.assign(pb, A.c0.c0 + A.c1.c0);
156 Ac0_plus_Ac1_c1.assign(pb, A.c0.c1 + A.c1.c1);
157 Ac0_plus_Ac1_c2.assign(pb, A.c0.c2 + A.c1.c2);
158 Ac0_plus_Ac1.reset(new Fp3_variable<Fp3T>(
163 FMT(annotation_prefix, " Ac0_plus_Ac1")));
165 Bc0_plus_Bc1_c0.assign(pb, B.c0.c0 + B.c1.c0);
166 Bc0_plus_Bc1_c1.assign(pb, B.c0.c1 + B.c1.c1);
167 Bc0_plus_Bc1_c2.assign(pb, B.c0.c2 + B.c1.c2);
168 Bc0_plus_Bc1.reset(new Fp3_variable<Fp3T>(
173 FMT(annotation_prefix, " Bc0_plus_Bc1")));
175 result_c1_plus_v0_plus_v1_c0.assign(pb, result.c1.c0 + v0->c0 + v1->c0);
176 result_c1_plus_v0_plus_v1_c1.assign(pb, result.c1.c1 + v0->c1 + v1->c1);
177 result_c1_plus_v0_plus_v1_c2.assign(pb, result.c1.c2 + v0->c2 + v1->c2);
178 result_c1_plus_v0_plus_v1.reset(new Fp3_variable<Fp3T>(
180 result_c1_plus_v0_plus_v1_c0,
181 result_c1_plus_v0_plus_v1_c1,
182 result_c1_plus_v0_plus_v1_c2,
183 FMT(annotation_prefix, " result_c1_plus_v0_plus_v1")));
185 compute_result_c1.reset(new Fp3_mul_gadget<Fp3T>(
189 *result_c1_plus_v0_plus_v1,
190 FMT(annotation_prefix, " compute_result_c1")));
193 template<typename Fp6T>
194 void Fp6_2over3_mul_gadget<Fp6T>::generate_r1cs_constraints()
196 compute_v0->generate_r1cs_constraints();
197 compute_v1->generate_r1cs_constraints();
198 compute_result_c1->generate_r1cs_constraints();
201 template<typename Fp6T>
202 void Fp6_2over3_mul_gadget<Fp6T>::generate_r1cs_witness()
204 compute_v0->generate_r1cs_witness();
205 compute_v1->generate_r1cs_witness();
207 Ac0_plus_Ac1_c0.evaluate(this->pb);
208 Ac0_plus_Ac1_c1.evaluate(this->pb);
209 Ac0_plus_Ac1_c2.evaluate(this->pb);
211 Bc0_plus_Bc1_c0.evaluate(this->pb);
212 Bc0_plus_Bc1_c1.evaluate(this->pb);
213 Bc0_plus_Bc1_c2.evaluate(this->pb);
215 compute_result_c1->generate_r1cs_witness();
217 const Fp6T Aval = A.get_element();
218 const Fp6T Bval = B.get_element();
219 const Fp6T Rval = Aval * Bval;
221 result.generate_r1cs_witness(Rval);
223 result_c1_plus_v0_plus_v1_c0.evaluate(this->pb);
224 result_c1_plus_v0_plus_v1_c1.evaluate(this->pb);
225 result_c1_plus_v0_plus_v1_c2.evaluate(this->pb);
227 compute_result_c1->generate_r1cs_witness();
230 template<typename Fp6T>
231 Fp6_2over3_mul_by_2345_gadget<Fp6T>::Fp6_2over3_mul_by_2345_gadget(
232 protoboard<FieldT> &pb,
233 const Fp6_2over3_variable<Fp6T> &A,
234 const Fp6_2over3_variable<Fp6T> &B,
235 const Fp6_2over3_variable<Fp6T> &result,
236 const std::string &annotation_prefix)
237 : gadget<FieldT>(pb, annotation_prefix), A(A), B(B), result(result)
240 Karatsuba multiplication for Fp6 as a quadratic extension of Fp3:
243 result.c0 = v0 + non_residue * v1
244 result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
245 where "non_residue * elem" := (non_residue * elem.c2, elem.c0, elem.c1)
247 We know that B.c0.c0 = B.c0.c1 = 0
249 Enforced with 2 Fp3_mul_gadget's that ensure that:
251 (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1
253 And one multiplication (three direct constraints) that enforces A.c0 *
254 B.c0 = v0, where B.c0.c0 = B.c0.c1 = 0.
256 Note that (u + v * X + t * X^2) * (0 + 0 * X + z * X^2) =
257 (v * z * non_residue + t * z * non_residue * X + u * z * X^2)
260 "Multiplication and Squaring on Pairing-Friendly Fields"
261 Devegili, OhEigeartaigh, Scott, Dahab
263 v1.reset(new Fp3_variable<Fp3T>(pb, FMT(annotation_prefix, " v1")));
264 compute_v1.reset(new Fp3_mul_gadget<Fp3T>(
265 pb, A.c1, B.c1, *v1, FMT(annotation_prefix, " compute_v1")));
267 /* we inline result.c0 in v0 as follows: v0 = (result.c0.c0 -
268 * Fp6T::non_residue * v1->c2, result.c0.c1 - v1->c0, result.c0.c2 - v1->c1)
270 v0.reset(new Fp3_variable<Fp3T>(pb, FMT(annotation_prefix, " v0")));
272 Ac0_plus_Ac1_c0.assign(pb, A.c0.c0 + A.c1.c0);
273 Ac0_plus_Ac1_c1.assign(pb, A.c0.c1 + A.c1.c1);
274 Ac0_plus_Ac1_c2.assign(pb, A.c0.c2 + A.c1.c2);
275 Ac0_plus_Ac1.reset(new Fp3_variable<Fp3T>(
280 FMT(annotation_prefix, " Ac0_plus_Ac1")));
282 Bc0_plus_Bc1_c0.assign(pb, B.c0.c0 + B.c1.c0);
283 Bc0_plus_Bc1_c1.assign(pb, B.c0.c1 + B.c1.c1);
284 Bc0_plus_Bc1_c2.assign(pb, B.c0.c2 + B.c1.c2);
285 Bc0_plus_Bc1.reset(new Fp3_variable<Fp3T>(
290 FMT(annotation_prefix, " Bc0_plus_Bc1")));
292 result_c1_plus_v0_plus_v1_c0.assign(pb, result.c1.c0 + v0->c0 + v1->c0);
293 result_c1_plus_v0_plus_v1_c1.assign(pb, result.c1.c1 + v0->c1 + v1->c1);
294 result_c1_plus_v0_plus_v1_c2.assign(pb, result.c1.c2 + v0->c2 + v1->c2);
295 result_c1_plus_v0_plus_v1.reset(new Fp3_variable<Fp3T>(
297 result_c1_plus_v0_plus_v1_c0,
298 result_c1_plus_v0_plus_v1_c1,
299 result_c1_plus_v0_plus_v1_c2,
300 FMT(annotation_prefix, " result_c1_plus_v0_plus_v1")));
302 compute_result_c1.reset(new Fp3_mul_gadget<Fp3T>(
306 *result_c1_plus_v0_plus_v1,
307 FMT(annotation_prefix, " compute_result_c1")));
310 template<typename Fp6T>
311 void Fp6_2over3_mul_by_2345_gadget<Fp6T>::generate_r1cs_constraints()
313 compute_v1->generate_r1cs_constraints();
314 this->pb.add_r1cs_constraint(
315 r1cs_constraint<FieldT>(
317 Fp3T::non_residue * B.c0.c2,
318 result.c0.c0 - Fp6T::non_residue * v1->c2),
319 FMT(this->annotation_prefix, " v0.c0"));
320 this->pb.add_r1cs_constraint(
321 r1cs_constraint<FieldT>(
322 A.c0.c2, Fp3T::non_residue * B.c0.c2, result.c0.c1 - v1->c0),
323 FMT(this->annotation_prefix, " v0.c1"));
324 this->pb.add_r1cs_constraint(
325 r1cs_constraint<FieldT>(A.c0.c0, B.c0.c2, result.c0.c2 - v1->c1),
326 FMT(this->annotation_prefix, " v0.c2"));
327 compute_result_c1->generate_r1cs_constraints();
330 template<typename Fp6T>
331 void Fp6_2over3_mul_by_2345_gadget<Fp6T>::generate_r1cs_witness()
333 compute_v1->generate_r1cs_witness();
335 const Fp3T A_c0_val = A.c0.get_element();
336 const Fp3T B_c0_val = B.c0.get_element();
337 assert(B_c0_val.coeffs[0].is_zero());
338 assert(B_c0_val.coeffs[1].is_zero());
340 const Fp3T v0_val = A_c0_val * B_c0_val;
341 v0->generate_r1cs_witness(v0_val);
343 Ac0_plus_Ac1_c0.evaluate(this->pb);
344 Ac0_plus_Ac1_c1.evaluate(this->pb);
345 Ac0_plus_Ac1_c2.evaluate(this->pb);
347 Bc0_plus_Bc1_c0.evaluate(this->pb);
348 Bc0_plus_Bc1_c1.evaluate(this->pb);
349 Bc0_plus_Bc1_c2.evaluate(this->pb);
351 compute_result_c1->generate_r1cs_witness();
353 const Fp6T Aval = A.get_element();
354 const Fp6T Bval = B.get_element();
355 const Fp6T Rval = Aval * Bval;
357 result.generate_r1cs_witness(Rval);
359 result_c1_plus_v0_plus_v1_c0.evaluate(this->pb);
360 result_c1_plus_v0_plus_v1_c1.evaluate(this->pb);
361 result_c1_plus_v0_plus_v1_c2.evaluate(this->pb);
363 compute_result_c1->generate_r1cs_witness();
366 template<typename Fp6T>
367 Fp6_2over3_sqr_gadget<Fp6T>::Fp6_2over3_sqr_gadget(
368 protoboard<FieldT> &pb,
369 const Fp6_2over3_variable<Fp6T> &A,
370 const Fp6_2over3_variable<Fp6T> &result,
371 const std::string &annotation_prefix)
372 : gadget<FieldT>(pb, annotation_prefix), A(A), result(result)
374 // We can't do better than 3 Fp3_mul_gadget's for squaring, so we just use
376 mul.reset(new Fp6_2over3_mul_gadget<Fp6T>(
377 pb, A, A, result, FMT(annotation_prefix, " mul")));
380 template<typename Fp6T>
381 void Fp6_2over3_sqr_gadget<Fp6T>::generate_r1cs_constraints()
383 mul->generate_r1cs_constraints();
386 template<typename Fp6T>
387 void Fp6_2over3_sqr_gadget<Fp6T>::generate_r1cs_witness()
389 mul->generate_r1cs_witness();
392 template<typename Fp6T>
393 Fp6_2over3_cyclotomic_sqr_gadget<Fp6T>::Fp6_2over3_cyclotomic_sqr_gadget(
394 protoboard<FieldT> &pb,
395 const Fp6_2over3_variable<Fp6T> &A,
396 const Fp6_2over3_variable<Fp6T> &result,
397 const std::string &annotation_prefix)
398 : gadget<FieldT>(pb, annotation_prefix), A(A), result(result)
401 my_Fp2 a = my_Fp2(c0.c0, c1.c1);
402 my_Fp2 b = my_Fp2(c1.c0, c0.c2);
403 my_Fp2 c = my_Fp2(c0.c1, c1.c2);
405 my_Fp2 asq = a.squared();
406 my_Fp2 bsq = b.squared();
407 my_Fp2 csq = c.squared();
409 result.c0.c0 = 3 * asq_a - 2 * a_a;
410 result.c1.c1 = 3 * asq_b + 2 * a_b;
412 result.c0.c1 = 3 * bsq_a - 2 * c_a;
413 result.c1.c2 = 3 * bsq_b + 2 * c_b;
415 result.c0.c2 = 3 * csq_a - 2 * b_b;
416 result.c1.c0 = 3 * my_Fp3::non_residue * csq_b + 2 * b_a;
418 return Fp6_2over3_model<n, mbodulus>(my_Fp3(A_a, C_a, B_b),
419 my_Fp3(B_a, A_b, C_b))
421 a.reset(new Fp2_variable<Fp2T>(
422 pb, A.c0.c0, A.c1.c1, FMT(annotation_prefix, " a")));
423 b.reset(new Fp2_variable<Fp2T>(
424 pb, A.c1.c0, A.c0.c2, FMT(annotation_prefix, " b")));
425 c.reset(new Fp2_variable<Fp2T>(
426 pb, A.c0.c1, A.c1.c2, FMT(annotation_prefix, " c")));
428 asq_c0.assign(pb, (result.c0.c0 + 2 * a->c0) * FieldT(3).inverse());
429 asq_c1.assign(pb, (result.c1.c1 - 2 * a->c1) * FieldT(3).inverse());
431 bsq_c0.assign(pb, (result.c0.c1 + 2 * c->c0) * FieldT(3).inverse());
432 bsq_c1.assign(pb, (result.c1.c2 - 2 * c->c1) * FieldT(3).inverse());
434 csq_c0.assign(pb, (result.c0.c2 + 2 * b->c1) * FieldT(3).inverse());
437 (result.c1.c0 - 2 * b->c0) * (FieldT(3) * Fp2T::non_residue).inverse());
439 asq.reset(new Fp2_variable<Fp2T>(
440 pb, asq_c0, asq_c1, FMT(annotation_prefix, " asq")));
441 bsq.reset(new Fp2_variable<Fp2T>(
442 pb, bsq_c0, bsq_c1, FMT(annotation_prefix, " bsq")));
443 csq.reset(new Fp2_variable<Fp2T>(
444 pb, csq_c0, csq_c1, FMT(annotation_prefix, " csq")));
446 compute_asq.reset(new Fp2_sqr_gadget<Fp2T>(
447 pb, *a, *asq, FMT(annotation_prefix, " compute_asq")));
448 compute_bsq.reset(new Fp2_sqr_gadget<Fp2T>(
449 pb, *b, *bsq, FMT(annotation_prefix, " compute_bsq")));
450 compute_csq.reset(new Fp2_sqr_gadget<Fp2T>(
451 pb, *c, *csq, FMT(annotation_prefix, " compute_csq")));
454 template<typename Fp6T>
455 void Fp6_2over3_cyclotomic_sqr_gadget<Fp6T>::generate_r1cs_constraints()
457 compute_asq->generate_r1cs_constraints();
458 compute_bsq->generate_r1cs_constraints();
459 compute_csq->generate_r1cs_constraints();
462 template<typename Fp6T>
463 void Fp6_2over3_cyclotomic_sqr_gadget<Fp6T>::generate_r1cs_witness()
465 const Fp6T Aval = A.get_element();
466 const Fp6T Rval = Aval.cyclotomic_squared();
468 result.generate_r1cs_witness(Rval);
474 compute_asq->generate_r1cs_witness();
475 compute_bsq->generate_r1cs_witness();
476 compute_csq->generate_r1cs_witness();
479 } // namespace libsnark
481 #endif // FP6_GADGETS_TCC_