ecgadget.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. #include "libsnark_headers.hpp"
  2. using namespace libsnark;
  3. // There are two types of values:
  4. // _constants_ are values known at circuit generation time; they
  5. // are global constants known to everyone
  6. // _variables_ are values that change in each use of the circuit;
  7. // they have two subtypes:
  8. //
  9. // _public variables_ are values known to both the prover
  10. // and verifier but change in each use of the circuit
  11. // _private variables_ are values known only to the prover
  12. // and change in each use of the circuit
  13. // Double a constant EC point (inx,iny) to yield (outx,outy). The input
  14. // point must not be the point at infinity.
  15. template<typename FieldT>
  16. static void ec_double_point(FieldT &outx, FieldT &outy,
  17. const FieldT &inx, const FieldT &iny)
  18. {
  19. FieldT xsq = inx.squared();
  20. FieldT lambda = (xsq * 3 - 3) * (iny * 2).inverse();
  21. outx = lambda.squared() - inx * 2;
  22. outy = lambda * (inx - outx) - iny;
  23. }
  24. // Add constant EC points (inx, iny) and (addx, addy) to yield (outx, outy).
  25. // inx and addx must not be equal.
  26. template<typename FieldT>
  27. static void ec_add_points(FieldT &outx, FieldT &outy,
  28. const FieldT &inx, const FieldT &iny,
  29. const FieldT &addx, const FieldT &addy)
  30. {
  31. FieldT lambda = (addy - iny) * (addx - inx).inverse();
  32. outx = lambda.squared() - (addx + inx);
  33. outy = lambda * (inx - outx) - iny;
  34. }
  35. // Double the variable EC point (inx,iny) to yield (outx,outy). The
  36. // input point must not be the point at infinity.
  37. template<typename FieldT>
  38. class ec_double_gadget : public gadget<FieldT> {
  39. private:
  40. pb_variable<FieldT> lambda, inxsq;
  41. public:
  42. const pb_variable<FieldT> outx, outy, inx, iny;
  43. ec_double_gadget(protoboard<FieldT> &pb,
  44. const pb_variable<FieldT> &outx,
  45. const pb_variable<FieldT> &outy,
  46. const pb_linear_combination<FieldT> &inx,
  47. const pb_linear_combination<FieldT> &iny) :
  48. gadget<FieldT>(pb, "ec_double_gadget"), outx(outx), outy(outy),
  49. inx(inx), iny(iny)
  50. {
  51. // Allocate variables to protoboard
  52. // The strings (like "x") are only for debugging purposes
  53. lambda.allocate(this->pb, "lambda");
  54. inxsq.allocate(this->pb, "inxsq");
  55. }
  56. void generate_r1cs_constraints()
  57. {
  58. // inxsq = inx * inx
  59. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(inx, inx, inxsq));
  60. // 2 * iny * lambda = 3 * inxsq - 3 (a = -3 on our curve)
  61. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2 * iny, lambda, 3 * inxsq - 3));
  62. // outx = lambda^2 - 2 * inx
  63. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + 2 * inx));
  64. // outy = lambda * (inx - outx) - iny
  65. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  66. }
  67. void generate_r1cs_witness()
  68. {
  69. this->pb.val(inxsq) = this->pb.lc_val(inx) * this->pb.lc_val(inx);
  70. this->pb.val(lambda) = (this->pb.val(inxsq) * 3 - 3) * (this->pb.lc_val(iny) * 2).inverse();
  71. this->pb.val(outx) = this->pb.val(lambda).squared() - this->pb.lc_val(inx) * 2;
  72. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(outx)) - this->pb.lc_val(iny);
  73. }
  74. };
  75. // Add the variable EC point (addx,addy) to the variable EC point
  76. // (inx,iny) to yield (outx,outy). The input point must not be the
  77. // point at infinity.
  78. template<typename FieldT>
  79. class ec_add_gadget : public gadget<FieldT> {
  80. private:
  81. pb_variable<FieldT> lambda;
  82. public:
  83. const pb_variable<FieldT> outx, outy;
  84. const pb_linear_combination<FieldT> inx, iny, addx, addy;
  85. ec_add_gadget(protoboard<FieldT> &pb,
  86. const pb_variable<FieldT> &outx,
  87. const pb_variable<FieldT> &outy,
  88. const pb_linear_combination<FieldT> &inx,
  89. const pb_linear_combination<FieldT> &iny,
  90. const pb_linear_combination<FieldT> &addx,
  91. const pb_linear_combination<FieldT> &addy) :
  92. gadget<FieldT>(pb, "ec_add_gadget"),
  93. outx(outx), outy(outy), inx(inx), iny(iny), addx(addx), addy(addy)
  94. {
  95. // Allocate variables to protoboard
  96. // The strings (like "x") are only for debugging purposes
  97. lambda.allocate(this->pb, "lambda");
  98. }
  99. void generate_r1cs_constraints()
  100. {
  101. // (addx - inx) * lambda = addy - iny
  102. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(addx - inx, lambda, addy - iny));
  103. // outx = lambda^2 - (addx + inx)
  104. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + addx + inx));
  105. // outy = lambda * (inx - outx) - iny
  106. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  107. }
  108. void generate_r1cs_witness()
  109. {
  110. this->pb.val(lambda) = (this->pb.lc_val(addy) - this->pb.lc_val(iny)) * (this->pb.lc_val(addx) - this->pb.lc_val(inx)).inverse();
  111. this->pb.val(outx) = this->pb.val(lambda).squared() - (this->pb.lc_val(addx) + this->pb.lc_val(inx));
  112. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(outx)) - this->pb.lc_val(iny);
  113. }
  114. };
  115. // Add the variable EC point P to the constant EC point (inx,iny) to
  116. // yield (outx,outy). The input point must not be the point at
  117. // infinity.
  118. template<typename FieldT>
  119. class ec_constant_add_gadget : public gadget<FieldT> {
  120. private:
  121. pb_variable<FieldT> lambda;
  122. public:
  123. const pb_variable<FieldT> outx, outy;
  124. const pb_linear_combination<FieldT> inx, iny;
  125. const FieldT Px, Py;
  126. ec_constant_add_gadget(protoboard<FieldT> &pb,
  127. const pb_variable<FieldT> &outx,
  128. const pb_variable<FieldT> &outy,
  129. const pb_linear_combination<FieldT> &inx,
  130. const pb_linear_combination<FieldT> &iny,
  131. const FieldT &Px, const FieldT &Py) :
  132. gadget<FieldT>(pb, "ec_constant_add_gadget"),
  133. outx(outx), outy(outy), inx(inx), iny(iny), Px(Px), Py(Py)
  134. {
  135. // Allocate variables to protoboard
  136. // The strings (like "x") are only for debugging purposes
  137. lambda.allocate(this->pb, "lambda");
  138. }
  139. void generate_r1cs_constraints()
  140. {
  141. // (Px - inx) * lambda = Py - iny
  142. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Px - inx, lambda, Py - iny));
  143. // outx = lambda^2 - (Px + inx)
  144. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + Px + inx));
  145. // outy = lambda * (inx - outx) - iny
  146. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  147. }
  148. void generate_r1cs_witness()
  149. {
  150. this->pb.val(lambda) = (Py - this->pb.lc_val(iny)) * (Px - this->pb.lc_val(inx)).inverse();
  151. this->pb.val(outx) = this->pb.val(lambda).squared() - (Px + this->pb.lc_val(inx));
  152. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(outx)) - this->pb.lc_val(iny);
  153. }
  154. };
  155. // Add nothing or the constant EC point P to the variable EC point
  156. // (inx,iny) to yield (outx,outy). The input point must not be the
  157. // point at infinity. The input bit do_add controls whether the
  158. // addition is done.
  159. template<typename FieldT>
  160. class ec_conditional_constant_add_gadget : public gadget<FieldT> {
  161. private:
  162. pb_variable<FieldT> sumx, sumy;
  163. std::vector<ec_constant_add_gadget<FieldT> > adder;
  164. public:
  165. const pb_variable<FieldT> outx, outy;
  166. const pb_linear_combination<FieldT> inx, iny;
  167. const pb_variable<FieldT> do_add;
  168. const FieldT Px, Py;
  169. ec_conditional_constant_add_gadget(protoboard<FieldT> &pb,
  170. const pb_variable<FieldT> &outx,
  171. const pb_variable<FieldT> &outy,
  172. const pb_linear_combination<FieldT> &inx,
  173. const pb_linear_combination<FieldT> &iny,
  174. const pb_variable<FieldT> &do_add,
  175. const FieldT &Px, const FieldT &Py) :
  176. gadget<FieldT>(pb, "ec_conditional_constant_add_gadget"),
  177. outx(outx), outy(outy), inx(inx), iny(iny), do_add(do_add),
  178. Px(Px), Py(Py)
  179. {
  180. // Allocate variables to protoboard
  181. // The strings (like "x") are only for debugging purposes
  182. sumx.allocate(this->pb, "sumx");
  183. sumy.allocate(this->pb, "sumy");
  184. adder.emplace_back(this->pb, sumx, sumy, inx, iny, Px, Py);
  185. }
  186. void generate_r1cs_constraints()
  187. {
  188. // Strategy: we always do the addition, but if do_add = 0, we throw
  189. // it away later.
  190. adder[0].generate_r1cs_constraints();
  191. // Now we want to conditionally move the sum. We want that
  192. // outx = do_add ? sumx : inx
  193. // outy = do_add ? sumy : iny
  194. // so we compute
  195. // outx = inx + (sumx - inx) * do_add
  196. // outy = iny + (sumy - iny) * do_add
  197. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, do_add, outx - inx));
  198. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, do_add, outy - iny));
  199. }
  200. void generate_r1cs_witness()
  201. {
  202. adder[0].generate_r1cs_witness();
  203. bool move = this->pb.val(do_add) != FieldT(0);
  204. this->pb.val(outx) = move ? this->pb.val(sumx) : this->pb.lc_val(inx);
  205. this->pb.val(outy) = move ? this->pb.val(sumy) : this->pb.lc_val(iny);
  206. }
  207. };
  208. // Add nothing, or one of the constant EC points P1, P2, or P3 to the EC
  209. // point (inx,iny) to yield (outx,outy). The input point must not be
  210. // the point at infinity. The two input bits add1 and add2 control what
  211. // is added. Typically, P3 will equal P1+P2, in which case this gadget
  212. // does two conditional constant adds simultaneously in just 6 constraints.
  213. template<typename FieldT>
  214. class ec_add_P123_gadget : public gadget<FieldT> {
  215. private:
  216. pb_variable<FieldT> lambda, sumx, sumy, move;
  217. public:
  218. const pb_variable<FieldT> outx, outy;
  219. const pb_linear_combination<FieldT> inx, iny;
  220. const pb_variable<FieldT> add1, add2;
  221. const FieldT P1x, P1y, P2x, P2y, P3x, P3y;
  222. ec_add_P123_gadget(protoboard<FieldT> &pb,
  223. const pb_variable<FieldT> &outx,
  224. const pb_variable<FieldT> &outy,
  225. const pb_linear_combination<FieldT> &inx,
  226. const pb_linear_combination<FieldT> &iny,
  227. const pb_variable<FieldT> &add1,
  228. const pb_variable<FieldT> &add2,
  229. const FieldT &P1x, const FieldT &P1y,
  230. const FieldT &P2x, const FieldT &P2y,
  231. const FieldT &P3x, const FieldT &P3y) :
  232. gadget<FieldT>(pb, "ec_add_P123_gadget"),
  233. outx(outx), outy(outy), inx(inx), iny(iny), add1(add1), add2(add2),
  234. P1x(P1x), P1y(P1y), P2x(P2x), P2y(P2y), P3x(P3x), P3y(P3y)
  235. {
  236. // Allocate variables to protoboard
  237. // The strings (like "x") are only for debugging purposes
  238. lambda.allocate(this->pb, "lambda");
  239. sumx.allocate(this->pb, "sumx");
  240. sumy.allocate(this->pb, "sumy");
  241. move.allocate(this->pb, "move");
  242. }
  243. void generate_r1cs_constraints()
  244. {
  245. // Strategy: if add1 = add2 = 0, we compute some nonsense but throw
  246. // it away later. Otherwise, the coordinates of the point to add
  247. // are a _linear_ function of add1 and add2 (since P1, P2, and P3
  248. // are public constants)
  249. // In particular, the point to add is ( (P3x - P2x) * add1 + (P3x -
  250. // P1x) * add2 + (P1x + P2x - P3x), (P3y - P2y) * add1 + (P3y - P1y) *
  251. // add2 + (P1y + P2y - P3y))
  252. // (addx - inx) * lambda = addy - iny
  253. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>((P3x - P2x) * add1 + (P3x - P1x) * add2 + (P1x + P2x - P3x) - inx, lambda, (P3y - P2y) * add1 + (P3y - P1y) * add2 + (P1y + P2y - P3y) - iny));
  254. // sumx = lambda^2 - (addx + inx)
  255. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, sumx + (P3x - P2x) * add1 + (P3x - P1x) * add2 + (P1x + P2x - P3x) + inx));
  256. // sumy = lambda * (inx - sumx) - iny
  257. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - sumx, sumy + iny));
  258. // Now we want to conditionally move the sum. We want that
  259. // outx = (add1 || add2) ? sumx : inx
  260. // outy = (add1 || add2) ? sumy : iny
  261. // so we compute move = add1 || add2, and then
  262. // outx = inx + (sumx - inx) * move
  263. // outy = iny + (sumy - iny) * move
  264. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1 - add1, 1 - add2, 1 - move));
  265. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, move, outx - inx));
  266. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, move, outy - iny));
  267. }
  268. void generate_r1cs_witness()
  269. {
  270. FieldT addxval = (P3x - P2x) * this->pb.val(add1) + (P3x - P1x) * this->pb.val(add2) + (P1x + P2x - P3x);
  271. FieldT addyval = (P3y - P2y) * this->pb.val(add1) + (P3y - P1y) * this->pb.val(add2) + (P1y + P2y - P3y);
  272. this->pb.val(lambda) = (addyval - this->pb.lc_val(iny)) * (addxval - this->pb.lc_val(inx)).inverse();
  273. this->pb.val(sumx) = this->pb.val(lambda).squared() - (addxval + this->pb.lc_val(inx));
  274. this->pb.val(sumy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(sumx)) - this->pb.lc_val(iny);
  275. bool a1 = this->pb.val(add1) != FieldT(0);
  276. bool a2 = this->pb.val(add2) != FieldT(0);
  277. this->pb.val(move) = a1 || a2;
  278. this->pb.val(outx) = (a1 || a2) ? this->pb.val(sumx) : this->pb.lc_val(inx);
  279. this->pb.val(outy) = (a1 || a2) ? this->pb.val(sumy) : this->pb.lc_val(iny);
  280. }
  281. };
  282. // Compute a*P as (outx, outy) for a given constant point P, given a
  283. // as a bit vector. The _caller_ is responsible for proving that the
  284. // elements of avec are bits.
  285. template<typename FieldT>
  286. class ec_constant_scalarmul_vec_gadget : public gadget<FieldT> {
  287. private:
  288. FieldT Cx, Cy, CPx, CPy;
  289. pb_variable_array<FieldT> accumx, accumy;
  290. std::vector<ec_add_P123_gadget<FieldT> > conddoubleadders;
  291. std::vector<ec_conditional_constant_add_gadget<FieldT> > condsingleadders;
  292. std::vector<ec_constant_add_gadget<FieldT> > singleadders;
  293. public:
  294. const pb_variable<FieldT> outx, outy;
  295. const pb_variable_array<FieldT> avec;
  296. const FieldT Px, Py;
  297. // Strategy: We compute (as compile-time constants) (powers of 2)
  298. // times P, and then conditionally add them into an accumulator.
  299. // Because our adder cannot handle the point at infinity O, we start
  300. // the accumulator with a value of C, whose discrete log with respect
  301. // to P should be unknown, so that we won't encounter O along the way.
  302. // (Also, a should not be 0 or the group order.) We actually start
  303. // the accumulator with either C or C+P depending on avec[0], so we
  304. // get the first conditional add "for free". Then we use the
  305. // ec_add_P123_gadget to do the conditional adds two at a time (at a
  306. // cost of 6 constraints per pair, as opposed to 5 for a single
  307. // conditional add). If the length of avec is even, then there will
  308. // be one left over, and we do a single conditional add for that one.
  309. // Finally, we add the public point -C.
  310. ec_constant_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  311. const pb_variable<FieldT> &outx,
  312. const pb_variable<FieldT> &outy,
  313. const pb_variable_array<FieldT> &avec,
  314. const FieldT &Px, const FieldT &Py) :
  315. gadget<FieldT>(pb, "ec_constant_scalarmul_vec_gadget"),
  316. // Precomputed coordinates of C
  317. Cx(2),
  318. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  319. outx(outx), outy(outy), avec(avec), Px(Px), Py(Py)
  320. {
  321. size_t numbits = avec.size();
  322. accumx.allocate(this->pb, numbits/2+1, "accumx");
  323. accumy.allocate(this->pb, numbits/2+1, "accumy");
  324. ec_add_points(CPx, CPy, Cx, Cy, Px, Py);
  325. FieldT twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py;
  326. size_t i = 1;
  327. ec_double_point(twoiPx, twoiPy, Px, Py);
  328. while(i < numbits) {
  329. // Invariants: i is odd, and twoiP = 2^i * P
  330. // Compute twoi1P = 2^{i+1} * P = 2 * twoiP and
  331. // twoi3P = 2^i * 3 * P = 3 * twoiP
  332. ec_double_point(twoi1Px, twoi1Py, twoiPx, twoiPy);
  333. ec_add_points(twoi3Px, twoi3Py, twoi1Px, twoi1Py, twoiPx, twoiPy);
  334. if (i == numbits-1) {
  335. // There's only one bit of avec left; use a single conditional
  336. // add.
  337. condsingleadders.emplace_back(this->pb,
  338. accumx[(i+1)/2], accumy[(i+1)/2],
  339. accumx[(i-1)/2], accumy[(i-1)/2],
  340. avec[i],
  341. twoiPx, twoiPy);
  342. } else {
  343. conddoubleadders.emplace_back(this->pb,
  344. accumx[(i+1)/2], accumy[(i+1)/2],
  345. accumx[(i-1)/2], accumy[(i-1)/2],
  346. avec[i], avec[i+1],
  347. twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py);
  348. }
  349. ec_double_point(twoiPx, twoiPy, twoi1Px, twoi1Py);
  350. i += 2;
  351. }
  352. // If numbits is even, the output so far is in accum[(numbits)/2].
  353. // If numbits is odd, it is in accum[(numbits-1)/2]. So in either
  354. // case, it is in accum[numbits/2].
  355. singleadders.emplace_back(this->pb,
  356. outx, outy, accumx[numbits/2], accumy[numbits/2],
  357. Cx, -Cy);
  358. }
  359. void generate_r1cs_constraints()
  360. {
  361. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cx + (CPx-Cx) * avec[0], 1, accumx[0]));
  362. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cy + (CPy-Cy) * avec[0], 1, accumy[0]));
  363. for (auto&& gadget : conddoubleadders) {
  364. gadget.generate_r1cs_constraints();
  365. }
  366. for (auto&& gadget : condsingleadders) {
  367. gadget.generate_r1cs_constraints();
  368. }
  369. for (auto&& gadget : singleadders) {
  370. gadget.generate_r1cs_constraints();
  371. }
  372. }
  373. void generate_r1cs_witness()
  374. {
  375. this->pb.val(accumx[0]) = Cx + (CPx-Cx) * this->pb.val(avec[0]);
  376. this->pb.val(accumy[0]) = Cy + (CPy-Cy) * this->pb.val(avec[0]);
  377. for (auto&& gadget : conddoubleadders) {
  378. gadget.generate_r1cs_witness();
  379. }
  380. for (auto&& gadget : condsingleadders) {
  381. gadget.generate_r1cs_witness();
  382. }
  383. for (auto&& gadget : singleadders) {
  384. gadget.generate_r1cs_witness();
  385. }
  386. }
  387. };
  388. // Compute a*P as (outx, outy) for a given constant point P, given a
  389. // as a field element.
  390. template<typename FieldT>
  391. class ec_constant_scalarmul_gadget : public gadget<FieldT> {
  392. private:
  393. pb_variable_array<FieldT> avec;
  394. std::vector<packing_gadget<FieldT> > packers;
  395. std::vector<ec_constant_scalarmul_vec_gadget<FieldT> > vecgadget;
  396. public:
  397. const pb_variable<FieldT> outx, outy, a;
  398. const FieldT Px, Py;
  399. ec_constant_scalarmul_gadget(protoboard<FieldT> &pb,
  400. const pb_variable<FieldT> &outx,
  401. const pb_variable<FieldT> &outy,
  402. const pb_variable<FieldT> &a,
  403. const FieldT &Px, const FieldT &Py) :
  404. gadget<FieldT>(pb, "ec_constant_scalarmul_gadget"),
  405. outx(outx), outy(outy), a(a), Px(Px), Py(Py)
  406. {
  407. // Allocate variables to protoboard
  408. // The strings (like "x") are only for debugging purposes
  409. size_t numbits = FieldT::num_bits;
  410. avec.allocate(this->pb, numbits, "avec");
  411. packers.emplace_back(this->pb, avec, a);
  412. vecgadget.emplace_back(this->pb, outx, outy, avec, Px, Py);
  413. }
  414. void generate_r1cs_constraints()
  415. {
  416. packers[0].generate_r1cs_constraints(true);
  417. vecgadget[0].generate_r1cs_constraints();
  418. }
  419. void generate_r1cs_witness()
  420. {
  421. packers[0].generate_r1cs_witness_from_packed();
  422. vecgadget[0].generate_r1cs_witness();
  423. }
  424. };
  425. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  426. template<typename FieldT>
  427. class ec_pedersen_gadget : public gadget<FieldT> {
  428. private:
  429. pb_variable<FieldT> aoutx, aouty, boutx, bouty;
  430. std::vector<ec_constant_scalarmul_gadget<FieldT> > mulgadgets;
  431. std::vector<ec_add_gadget<FieldT> > addgadget;
  432. const FieldT Gx, Gy, Hx, Hy;
  433. public:
  434. const pb_variable<FieldT> outx, outy, a, b;
  435. ec_pedersen_gadget(protoboard<FieldT> &pb,
  436. const pb_variable<FieldT> &outx,
  437. const pb_variable<FieldT> &outy,
  438. const pb_variable<FieldT> &a,
  439. const pb_variable<FieldT> &b) :
  440. gadget<FieldT>(pb, "ec_pedersen_gadget"),
  441. outx(outx), outy(outy), a(a), b(b),
  442. // Precomputed coordinates of G and H
  443. Gx(0),
  444. Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  445. Hx(1),
  446. Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279")
  447. {
  448. // Allocate variables to protoboard
  449. // The strings (like "x") are only for debugging purposes
  450. aoutx.allocate(this->pb, "aoutx");
  451. aouty.allocate(this->pb, "aouty");
  452. boutx.allocate(this->pb, "boutx");
  453. bouty.allocate(this->pb, "bouty");
  454. mulgadgets.emplace_back(this->pb, aoutx, aouty, a, Gx, Gy);
  455. mulgadgets.emplace_back(this->pb, boutx, bouty, b, Hx, Hy);
  456. addgadget.emplace_back(this->pb, outx, outy, aoutx, aouty, boutx, bouty);
  457. }
  458. void generate_r1cs_constraints()
  459. {
  460. mulgadgets[0].generate_r1cs_constraints();
  461. mulgadgets[1].generate_r1cs_constraints();
  462. addgadget[0].generate_r1cs_constraints();
  463. }
  464. void generate_r1cs_witness()
  465. {
  466. mulgadgets[0].generate_r1cs_witness();
  467. mulgadgets[1].generate_r1cs_witness();
  468. addgadget[0].generate_r1cs_witness();
  469. }
  470. };