2 *****************************************************************************
4 Implementation of interfaces for Fp3 gadgets.
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 FP3_GADGETS_TCC_
15 #define FP3_GADGETS_TCC_
20 template<typename Fp3T>
21 Fp3_variable<Fp3T>::Fp3_variable(
22 protoboard<FieldT> &pb, const std::string &annotation_prefix)
23 : gadget<FieldT>(pb, annotation_prefix)
25 pb_variable<FieldT> c0_var, c1_var, c2_var;
26 c0_var.allocate(pb, FMT(annotation_prefix, " c0"));
27 c1_var.allocate(pb, FMT(annotation_prefix, " c1"));
28 c2_var.allocate(pb, FMT(annotation_prefix, " c2"));
30 c0 = pb_linear_combination<FieldT>(c0_var);
31 c1 = pb_linear_combination<FieldT>(c1_var);
32 c2 = pb_linear_combination<FieldT>(c2_var);
34 all_vars.emplace_back(c0);
35 all_vars.emplace_back(c1);
36 all_vars.emplace_back(c2);
39 template<typename Fp3T>
40 Fp3_variable<Fp3T>::Fp3_variable(
41 protoboard<FieldT> &pb,
43 const std::string &annotation_prefix)
44 : gadget<FieldT>(pb, annotation_prefix)
46 c0.assign(pb, el.coeffs[0]);
47 c1.assign(pb, el.coeffs[1]);
48 c2.assign(pb, el.coeffs[2]);
54 all_vars.emplace_back(c0);
55 all_vars.emplace_back(c1);
56 all_vars.emplace_back(c2);
59 template<typename Fp3T>
60 Fp3_variable<Fp3T>::Fp3_variable(
61 protoboard<FieldT> &pb,
63 const pb_linear_combination<FieldT> &coeff,
64 const std::string &annotation_prefix)
65 : gadget<FieldT>(pb, annotation_prefix)
67 c0.assign(pb, el.coeffs[0] * coeff);
68 c1.assign(pb, el.coeffs[1] * coeff);
69 c2.assign(pb, el.coeffs[2] * coeff);
71 all_vars.emplace_back(c0);
72 all_vars.emplace_back(c1);
73 all_vars.emplace_back(c2);
76 template<typename Fp3T>
77 Fp3_variable<Fp3T>::Fp3_variable(
78 protoboard<FieldT> &pb,
79 const pb_linear_combination<FieldT> &c0,
80 const pb_linear_combination<FieldT> &c1,
81 const pb_linear_combination<FieldT> &c2,
82 const std::string &annotation_prefix)
83 : gadget<FieldT>(pb, annotation_prefix), c0(c0), c1(c1), c2(c2)
85 all_vars.emplace_back(c0);
86 all_vars.emplace_back(c1);
87 all_vars.emplace_back(c2);
90 template<typename Fp3T>
91 void Fp3_variable<Fp3T>::generate_r1cs_equals_const_constraints(const Fp3T &el)
93 this->pb.add_r1cs_constraint(
94 r1cs_constraint<FieldT>(1, el.coeffs[0], c0),
95 FMT(this->annotation_prefix, " c0"));
96 this->pb.add_r1cs_constraint(
97 r1cs_constraint<FieldT>(1, el.coeffs[1], c1),
98 FMT(this->annotation_prefix, " c1"));
99 this->pb.add_r1cs_constraint(
100 r1cs_constraint<FieldT>(1, el.coeffs[2], c2),
101 FMT(this->annotation_prefix, " c2"));
104 template<typename Fp3T>
105 void Fp3_variable<Fp3T>::generate_r1cs_witness(const Fp3T &el)
107 this->pb.lc_val(c0) = el.coeffs[0];
108 this->pb.lc_val(c1) = el.coeffs[1];
109 this->pb.lc_val(c2) = el.coeffs[2];
112 template<typename Fp3T> Fp3T Fp3_variable<Fp3T>::get_element()
115 el.coeffs[0] = this->pb.lc_val(c0);
116 el.coeffs[1] = this->pb.lc_val(c1);
117 el.coeffs[2] = this->pb.lc_val(c2);
121 template<typename Fp3T>
122 Fp3_variable<Fp3T> Fp3_variable<Fp3T>::operator*(const FieldT &coeff) const
124 pb_linear_combination<FieldT> new_c0, new_c1, new_c2;
125 new_c0.assign(this->pb, this->c0 * coeff);
126 new_c1.assign(this->pb, this->c1 * coeff);
127 new_c2.assign(this->pb, this->c2 * coeff);
128 return Fp3_variable<Fp3T>(
133 FMT(this->annotation_prefix, " operator*"));
136 template<typename Fp3T>
137 Fp3_variable<Fp3T> Fp3_variable<Fp3T>::operator+(
138 const Fp3_variable<Fp3T> &other) const
140 pb_linear_combination<FieldT> new_c0, new_c1, new_c2;
141 new_c0.assign(this->pb, this->c0 + other.c0);
142 new_c1.assign(this->pb, this->c1 + other.c1);
143 new_c2.assign(this->pb, this->c2 + other.c2);
144 return Fp3_variable<Fp3T>(
149 FMT(this->annotation_prefix, " operator+"));
152 template<typename Fp3T>
153 Fp3_variable<Fp3T> Fp3_variable<Fp3T>::operator+(const Fp3T &fp3_const) const
155 pb_linear_combination<FieldT> new_c0, new_c1, new_c2;
156 new_c0.assign(this->pb, this->c0 + fp3_const.coeffs[0]);
157 new_c1.assign(this->pb, this->c1 + fp3_const.coeffs[1]);
158 new_c2.assign(this->pb, this->c2 + fp3_const.coeffs[2]);
159 return Fp3_variable<Fp3T>(
164 FMT(this->annotation_prefix, " operator+"));
167 template<typename Fp3T> Fp3_variable<Fp3T> Fp3_variable<Fp3T>::mul_by_X() const
169 pb_linear_combination<FieldT> new_c0, new_c1, new_c2;
170 new_c0.assign(this->pb, this->c2 * Fp3T::non_residue);
171 new_c1.assign(this->pb, this->c0);
172 new_c2.assign(this->pb, this->c1);
173 return Fp3_variable<Fp3T>(
178 FMT(this->annotation_prefix, " mul_by_X"));
181 template<typename Fp3T> void Fp3_variable<Fp3T>::evaluate() const
183 c0.evaluate(this->pb);
184 c1.evaluate(this->pb);
185 c2.evaluate(this->pb);
188 template<typename Fp3T> bool Fp3_variable<Fp3T>::is_constant() const
190 return (c0.is_constant() && c1.is_constant() && c2.is_constant());
193 template<typename Fp3T> size_t Fp3_variable<Fp3T>::size_in_bits()
195 return 3 * FieldT::size_in_bits();
198 template<typename Fp3T> size_t Fp3_variable<Fp3T>::num_variables() { return 3; }
200 template<typename Fp3T>
201 Fp3_mul_gadget<Fp3T>::Fp3_mul_gadget(
202 protoboard<FieldT> &pb,
203 const Fp3_variable<Fp3T> &A,
204 const Fp3_variable<Fp3T> &B,
205 const Fp3_variable<Fp3T> &result,
206 const std::string &annotation_prefix)
207 : gadget<FieldT>(pb, annotation_prefix), A(A), B(B), result(result)
209 v0.allocate(pb, FMT(annotation_prefix, " v0"));
210 v4.allocate(pb, FMT(annotation_prefix, " v4"));
213 template<typename Fp3T> void Fp3_mul_gadget<Fp3T>::generate_r1cs_constraints()
218 v1 = (A.c0 + A.c1 + A.c2) * (B.c0 + B.c1 + B.c2)
219 v2 = (A.c0 - A.c1 + A.c2) * (B.c0 - B.c1 + B.c2)
220 v3 = (A.c0 + 2*A.c1 + 4*A.c2) * (B.c0 + 2*B.c1 + 4*B.c2)
222 result.c0 = v0 + non_residue * (v0/2 - v1/2 - v2/6 + v3/6 - 2*v4)
223 result.c1 = -(1/2) v0 + v1 - (1/3) v2 - (1/6) v3 + 2 v4 +
224 non_residue*v4 result.c2 = -v0 + (1/2) v1 + (1/2) v2 - v4
226 Enforced with 5 constraints. Doing so requires some care, as we first
227 compute two of the v_i explicitly, and then "inline" result.c1/c2/c3
228 in computations of teh remaining three v_i.
230 Concretely, we first compute v0 and v4 explicitly, via 2 constraints:
233 Then we use the following 3 additional constraints:
234 v1 = result.c1 + result.c2 + (result.c0 - v0)/non_residue + v0 + v4
235 - non_residue v4 v2 = -result.c1 + result.c2 + v0 + (-result.c0 +
236 v0)/non_residue + v4 + non_residue v4 v3 = 2 * result.c1 + 4 result.c2 +
237 (8*(result.c0 - v0))/non_residue + v0 + 16 * v4 - 2 * non_residue * v4
240 "Multiplication and Squaring on Pairing-Friendly Fields"
241 Devegili, OhEigeartaigh, Scott, Dahab
243 NOTE: the expressions above were cherry-picked from the Mathematica
244 result of the following command:
246 (# -> Solve[{c0 == v0 + non_residue*(v0/2 - v1/2 - v2/6 + v3/6 - 2 v4),
247 c1 == -(1/2) v0 + v1 - (1/3) v2 - (1/6) v3 + 2 v4 +
248 non_residue*v4, c2 == -v0 + (1/2) v1 + (1/2) v2 - v4}, #] //
249 FullSimplify) & /@ Subsets[{v0, v1, v2, v3, v4}, {3}]
251 this->pb.add_r1cs_constraint(
252 r1cs_constraint<FieldT>(A.c0, B.c0, v0),
253 FMT(this->annotation_prefix, " v0"));
254 this->pb.add_r1cs_constraint(
255 r1cs_constraint<FieldT>(A.c2, B.c2, v4),
256 FMT(this->annotation_prefix, " v4"));
258 const FieldT beta = Fp3T::non_residue;
260 this->pb.add_r1cs_constraint(
261 r1cs_constraint<FieldT>(
264 result.c1 + result.c2 + result.c0 * beta.inverse() +
265 v0 * (FieldT(1) - beta.inverse()) + v4 * (FieldT(1) - beta)),
266 FMT(this->annotation_prefix, " v1"));
267 this->pb.add_r1cs_constraint(
268 r1cs_constraint<FieldT>(
271 -result.c1 + result.c2 + v0 * (FieldT(1) + beta.inverse()) -
272 result.c0 * beta.inverse() + v4 * (FieldT(1) + beta)),
273 FMT(this->annotation_prefix, " v2"));
274 this->pb.add_r1cs_constraint(
275 r1cs_constraint<FieldT>(
276 A.c0 + 2 * A.c1 + 4 * A.c2,
277 B.c0 + 2 * B.c1 + 4 * B.c2,
278 2 * result.c1 + 4 * result.c2 +
279 result.c0 * (FieldT(8) * beta.inverse()) +
280 v0 * (FieldT(1) - FieldT(8) * beta.inverse()) +
281 v4 * (FieldT(16) - FieldT(2) * beta)),
282 FMT(this->annotation_prefix, " v3"));
285 template<typename Fp3T> void Fp3_mul_gadget<Fp3T>::generate_r1cs_witness()
287 this->pb.val(v0) = this->pb.lc_val(A.c0) * this->pb.lc_val(B.c0);
288 this->pb.val(v4) = this->pb.lc_val(A.c2) * this->pb.lc_val(B.c2);
290 const Fp3T Aval = A.get_element();
291 const Fp3T Bval = B.get_element();
292 const Fp3T Rval = Aval * Bval;
293 result.generate_r1cs_witness(Rval);
296 template<typename Fp3T>
297 Fp3_mul_by_lc_gadget<Fp3T>::Fp3_mul_by_lc_gadget(
298 protoboard<FieldT> &pb,
299 const Fp3_variable<Fp3T> &A,
300 const pb_linear_combination<FieldT> &lc,
301 const Fp3_variable<Fp3T> &result,
302 const std::string &annotation_prefix)
303 : gadget<FieldT>(pb, annotation_prefix), A(A), lc(lc), result(result)
307 template<typename Fp3T>
308 void Fp3_mul_by_lc_gadget<Fp3T>::generate_r1cs_constraints()
310 this->pb.add_r1cs_constraint(
311 r1cs_constraint<FieldT>(A.c0, lc, result.c0),
312 FMT(this->annotation_prefix, " result.c0"));
313 this->pb.add_r1cs_constraint(
314 r1cs_constraint<FieldT>(A.c1, lc, result.c1),
315 FMT(this->annotation_prefix, " result.c1"));
316 this->pb.add_r1cs_constraint(
317 r1cs_constraint<FieldT>(A.c2, lc, result.c2),
318 FMT(this->annotation_prefix, " result.c2"));
321 template<typename Fp3T> void Fp3_mul_by_lc_gadget<Fp3T>::generate_r1cs_witness()
323 this->pb.lc_val(result.c0) = this->pb.lc_val(A.c0) * this->pb.lc_val(lc);
324 this->pb.lc_val(result.c1) = this->pb.lc_val(A.c1) * this->pb.lc_val(lc);
325 this->pb.lc_val(result.c2) = this->pb.lc_val(A.c2) * this->pb.lc_val(lc);
328 template<typename Fp3T>
329 Fp3_sqr_gadget<Fp3T>::Fp3_sqr_gadget(
330 protoboard<FieldT> &pb,
331 const Fp3_variable<Fp3T> &A,
332 const Fp3_variable<Fp3T> &result,
333 const std::string &annotation_prefix)
334 : gadget<FieldT>(pb, annotation_prefix), A(A), result(result)
336 mul.reset(new Fp3_mul_gadget<Fp3T>(
337 pb, A, A, result, FMT(annotation_prefix, " mul")));
340 template<typename Fp3T> void Fp3_sqr_gadget<Fp3T>::generate_r1cs_constraints()
342 // We can't do better than 5 constraints for squaring, so we just use
344 mul->generate_r1cs_constraints();
347 template<typename Fp3T> void Fp3_sqr_gadget<Fp3T>::generate_r1cs_witness()
349 mul->generate_r1cs_witness();
352 } // namespace libsnark
354 #endif // FP3_GADGETS_TCC_