ecgadget.hpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. #include "libsnark_headers.hpp"
  2. using namespace libsnark;
  3. // Double a public EC point (inx,iny) to yield (outx,outy). The input
  4. // point must not be the point at infinity.
  5. template<typename FieldT>
  6. static void ec_double_point(FieldT &outx, FieldT &outy,
  7. const FieldT &inx, const FieldT &iny)
  8. {
  9. FieldT xsq = inx.squared();
  10. FieldT lambda = (xsq * 3 - 3) * (iny * 2).inverse();
  11. outx = lambda.squared() - inx * 2;
  12. outy = lambda * (inx - outx) - iny;
  13. }
  14. // Add public EC points (inx, iny) and (addx, addy) to yield (outx, outy).
  15. // inx and addx must not be equal.
  16. template<typename FieldT>
  17. static void ec_add_points(FieldT &outx, FieldT &outy,
  18. const FieldT &inx, const FieldT &iny,
  19. const FieldT &addx, const FieldT &addy)
  20. {
  21. FieldT lambda = (addy - iny) * (addx - inx).inverse();
  22. outx = lambda.squared() - (addx + inx);
  23. outy = lambda * (inx - outx) - iny;
  24. }
  25. // Double the EC point (inx,iny) to yield (outx,outy). The input point
  26. // must not be the point at infinity.
  27. template<typename FieldT>
  28. class ec_double_gadget : public gadget<FieldT> {
  29. private:
  30. pb_variable<FieldT> lambda, inxsq;
  31. public:
  32. const pb_variable<FieldT> outx, outy, inx, iny;
  33. ec_double_gadget(protoboard<FieldT> &pb,
  34. const pb_variable<FieldT> &outx,
  35. const pb_variable<FieldT> &outy,
  36. const pb_variable<FieldT> &inx,
  37. const pb_variable<FieldT> &iny) :
  38. gadget<FieldT>(pb, "ec_double_gadget"), outx(outx), outy(outy),
  39. inx(inx), iny(iny)
  40. {
  41. // Allocate variables to protoboard
  42. // The strings (like "x") are only for debugging purposes
  43. lambda.allocate(this->pb, "lambda");
  44. inxsq.allocate(this->pb, "inxsq");
  45. }
  46. void generate_r1cs_constraints()
  47. {
  48. // inxsq = inx * inx
  49. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(inx, inx, inxsq));
  50. // 2 * iny * lambda = 3 * inxsq - 3 (a = -3 on our curve)
  51. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2 * iny, lambda, 3 * inxsq - 3));
  52. // outx = lambda^2 - 2 * inx
  53. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + 2 * inx));
  54. // outy = lambda * (inx - outx) - iny
  55. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  56. }
  57. void generate_r1cs_witness()
  58. {
  59. this->pb.val(inxsq) = this->pb.val(inx) * this->pb.val(inx);
  60. this->pb.val(lambda) = (this->pb.val(inxsq) * 3 - 3) * (this->pb.val(iny) * 2).inverse();
  61. this->pb.val(outx) = this->pb.val(lambda).squared() - this->pb.val(inx) * 2;
  62. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.val(inx) - this->pb.val(outx)) - this->pb.val(iny);
  63. }
  64. };
  65. // Add the EC point (addx,addy) to the EC point (inx,iny) to yield
  66. // (outx,outy). The input point must not be the point at infinity.
  67. template<typename FieldT>
  68. class ec_add_gadget : public gadget<FieldT> {
  69. private:
  70. pb_variable<FieldT> lambda;
  71. public:
  72. const pb_variable<FieldT> outx, outy;
  73. const pb_linear_combination<FieldT> inx, iny, addx, addy;
  74. ec_add_gadget(protoboard<FieldT> &pb,
  75. const pb_variable<FieldT> &outx,
  76. const pb_variable<FieldT> &outy,
  77. const pb_linear_combination<FieldT> &inx,
  78. const pb_linear_combination<FieldT> &iny,
  79. const pb_linear_combination<FieldT> &addx,
  80. const pb_linear_combination<FieldT> &addy) :
  81. gadget<FieldT>(pb, "ec_add_gadget"),
  82. outx(outx), outy(outy), inx(inx), iny(iny), addx(addx), addy(addy)
  83. {
  84. // Allocate variables to protoboard
  85. // The strings (like "x") are only for debugging purposes
  86. lambda.allocate(this->pb, "lambda");
  87. }
  88. void generate_r1cs_constraints()
  89. {
  90. // (addx - inx) * lambda = addy - iny
  91. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(addx - inx, lambda, addy - iny));
  92. // outx = lambda^2 - (addx + inx)
  93. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + addx + inx));
  94. // outy = lambda * (inx - outx) - iny
  95. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  96. }
  97. void generate_r1cs_witness()
  98. {
  99. 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();
  100. this->pb.val(outx) = this->pb.val(lambda).squared() - (this->pb.lc_val(addx) + this->pb.lc_val(inx));
  101. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(outx)) - this->pb.lc_val(iny);
  102. }
  103. };
  104. // Add the public EC point P to the EC point (inx,iny) to yield
  105. // (outx,outy). The input point must not be the point at infinity.
  106. template<typename FieldT>
  107. class ec_public_add_gadget : public gadget<FieldT> {
  108. private:
  109. pb_variable<FieldT> lambda;
  110. public:
  111. const pb_variable<FieldT> outx, outy;
  112. const pb_linear_combination<FieldT> inx, iny;
  113. const FieldT Px, Py;
  114. ec_public_add_gadget(protoboard<FieldT> &pb,
  115. const pb_variable<FieldT> &outx,
  116. const pb_variable<FieldT> &outy,
  117. const pb_linear_combination<FieldT> &inx,
  118. const pb_linear_combination<FieldT> &iny,
  119. const FieldT &Px, const FieldT &Py) :
  120. gadget<FieldT>(pb, "ec_public_add_gadget"),
  121. outx(outx), outy(outy), inx(inx), iny(iny), Px(Px), Py(Py)
  122. {
  123. // Allocate variables to protoboard
  124. // The strings (like "x") are only for debugging purposes
  125. lambda.allocate(this->pb, "lambda");
  126. }
  127. void generate_r1cs_constraints()
  128. {
  129. // (Px - inx) * lambda = Py - iny
  130. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Px - inx, lambda, Py - iny));
  131. // outx = lambda^2 - (Px + inx)
  132. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + Px + inx));
  133. // outy = lambda * (inx - outx) - iny
  134. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  135. }
  136. void generate_r1cs_witness()
  137. {
  138. this->pb.val(lambda) = (Py - this->pb.lc_val(iny)) * (Px - this->pb.lc_val(inx)).inverse();
  139. this->pb.val(outx) = this->pb.val(lambda).squared() - (Px + this->pb.lc_val(inx));
  140. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(outx)) - this->pb.lc_val(iny);
  141. }
  142. };
  143. // Add nothing or the public EC point P to the EC point (inx,iny) to
  144. // yield (outx,outy). The input point must not be the point at
  145. // infinity. The input bit do_add controls whether the addition is
  146. // done.
  147. template<typename FieldT>
  148. class ec_conditional_add_gadget : public gadget<FieldT> {
  149. private:
  150. pb_variable<FieldT> sumx, sumy;
  151. std::vector<ec_public_add_gadget<FieldT> > adder;
  152. public:
  153. const pb_variable<FieldT> outx, outy;
  154. const pb_linear_combination<FieldT> inx, iny;
  155. const pb_variable<FieldT> do_add;
  156. const FieldT Px, Py;
  157. ec_conditional_add_gadget(protoboard<FieldT> &pb,
  158. const pb_variable<FieldT> &outx,
  159. const pb_variable<FieldT> &outy,
  160. const pb_linear_combination<FieldT> &inx,
  161. const pb_linear_combination<FieldT> &iny,
  162. const pb_variable<FieldT> &do_add,
  163. const FieldT &Px, const FieldT &Py) :
  164. gadget<FieldT>(pb, "ec_conditional_add_gadget"),
  165. outx(outx), outy(outy), inx(inx), iny(iny), do_add(do_add),
  166. Px(Px), Py(Py)
  167. {
  168. // Allocate variables to protoboard
  169. // The strings (like "x") are only for debugging purposes
  170. sumx.allocate(this->pb, "sumx");
  171. sumy.allocate(this->pb, "sumy");
  172. adder.emplace_back(this->pb, sumx, sumy, inx, iny, Px, Py);
  173. }
  174. void generate_r1cs_constraints()
  175. {
  176. // Strategy: we always do the addition, but if do_add = 0, we throw
  177. // it away later.
  178. adder[0].generate_r1cs_constraints();
  179. // Now we want to conditionally move the sum. We want that
  180. // outx = do_add ? sumx : inx
  181. // outy = do_add ? sumy : iny
  182. // so we compute
  183. // outx = inx + (sumx - inx) * do_add
  184. // outy = iny + (sumy - iny) * do_add
  185. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, do_add, outx - inx));
  186. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, do_add, outy - iny));
  187. }
  188. void generate_r1cs_witness()
  189. {
  190. adder[0].generate_r1cs_witness();
  191. bool move = this->pb.val(do_add) != FieldT(0);
  192. this->pb.val(outx) = move ? this->pb.val(sumx) : this->pb.lc_val(inx);
  193. this->pb.val(outy) = move ? this->pb.val(sumy) : this->pb.lc_val(iny);
  194. }
  195. };
  196. // Add nothing, or one of the public EC points P1, P2, or P3 to the EC
  197. // point (inx,iny) to yield (outx,outy). The input point must not be
  198. // the point at infinity. The two input bits add1 and add2 control what
  199. // is added. Typically, P3 will equal P1+P2, in which case this gadget
  200. // does two conditional adds simultaneously in just 6 constraints.
  201. template<typename FieldT>
  202. class ec_add_P123_gadget : public gadget<FieldT> {
  203. private:
  204. pb_variable<FieldT> lambda, sumx, sumy, move;
  205. public:
  206. const pb_variable<FieldT> outx, outy;
  207. const pb_linear_combination<FieldT> inx, iny;
  208. const pb_variable<FieldT> add1, add2;
  209. const FieldT P1x, P1y, P2x, P2y, P3x, P3y;
  210. ec_add_P123_gadget(protoboard<FieldT> &pb,
  211. const pb_variable<FieldT> &outx,
  212. const pb_variable<FieldT> &outy,
  213. const pb_linear_combination<FieldT> &inx,
  214. const pb_linear_combination<FieldT> &iny,
  215. const pb_variable<FieldT> &add1,
  216. const pb_variable<FieldT> &add2,
  217. const FieldT &P1x, const FieldT &P1y,
  218. const FieldT &P2x, const FieldT &P2y,
  219. const FieldT &P3x, const FieldT &P3y) :
  220. gadget<FieldT>(pb, "ec_add_P123_gadget"),
  221. outx(outx), outy(outy), inx(inx), iny(iny), add1(add1), add2(add2),
  222. P1x(P1x), P1y(P1y), P2x(P2x), P2y(P2y), P3x(P3x), P3y(P3y)
  223. {
  224. // Allocate variables to protoboard
  225. // The strings (like "x") are only for debugging purposes
  226. lambda.allocate(this->pb, "lambda");
  227. sumx.allocate(this->pb, "sumx");
  228. sumy.allocate(this->pb, "sumy");
  229. move.allocate(this->pb, "move");
  230. }
  231. void generate_r1cs_constraints()
  232. {
  233. // Strategy: if add1 = add2 = 0, we compute some nonsense but throw
  234. // it away later. Otherwise, the coordinates of the point to add
  235. // are a _linear_ function of add1 and add2 (since P1, P2, and P3
  236. // are public constants)
  237. // In particular, the point to add is ( (P3x - P2x) * add1 + (P3x -
  238. // P1x) * add2 + (P1x + P2x - P3x), (P3y - P2y) * add1 + (P3y - P1y) *
  239. // add2 + (P1y + P2y - P3y))
  240. // (addx - inx) * lambda = addy - iny
  241. 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));
  242. // sumx = lambda^2 - (addx + inx)
  243. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, sumx + (P3x - P2x) * add1 + (P3x - P1x) * add2 + (P1x + P2x - P3x) + inx));
  244. // sumy = lambda * (inx - sumx) - iny
  245. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - sumx, sumy + iny));
  246. // Now we want to conditionally move the sum. We want that
  247. // outx = (add1 || add2) ? sumx : inx
  248. // outy = (add1 || add2) ? sumy : iny
  249. // so we compute move = add1 || add2, and then
  250. // outx = inx + (sumx - inx) * move
  251. // outy = iny + (sumy - iny) * move
  252. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1 - add1, 1 - add2, 1 - move));
  253. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, move, outx - inx));
  254. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, move, outy - iny));
  255. }
  256. void generate_r1cs_witness()
  257. {
  258. FieldT addxval = (P3x - P2x) * this->pb.val(add1) + (P3x - P1x) * this->pb.val(add2) + (P1x + P2x - P3x);
  259. FieldT addyval = (P3y - P2y) * this->pb.val(add1) + (P3y - P1y) * this->pb.val(add2) + (P1y + P2y - P3y);
  260. this->pb.val(lambda) = (addyval - this->pb.lc_val(iny)) * (addxval - this->pb.lc_val(inx)).inverse();
  261. this->pb.val(sumx) = this->pb.val(lambda).squared() - (addxval + this->pb.lc_val(inx));
  262. this->pb.val(sumy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(sumx)) - this->pb.lc_val(iny);
  263. bool a1 = this->pb.val(add1) != FieldT(0);
  264. bool a2 = this->pb.val(add2) != FieldT(0);
  265. this->pb.val(move) = a1 || a2;
  266. this->pb.val(outx) = (a1 || a2) ? this->pb.val(sumx) : this->pb.lc_val(inx);
  267. this->pb.val(outy) = (a1 || a2) ? this->pb.val(sumy) : this->pb.lc_val(iny);
  268. }
  269. };
  270. // Compute a*P as (outx, outy) for a given public point P, given a
  271. // as a bit vector. The _caller_ is responsible for proving that the
  272. // elements of avec are bits.
  273. template<typename FieldT>
  274. class ec_scalarmul_vec_gadget : public gadget<FieldT> {
  275. private:
  276. FieldT Cx, Cy, CPx, CPy;
  277. pb_variable_array<FieldT> accumx, accumy;
  278. std::vector<ec_add_P123_gadget<FieldT> > conddoubleadders;
  279. std::vector<ec_conditional_add_gadget<FieldT> > condsingleadders;
  280. std::vector<ec_public_add_gadget<FieldT> > singleadders;
  281. public:
  282. const pb_variable<FieldT> outx, outy;
  283. const pb_variable_array<FieldT> avec;
  284. const FieldT Px, Py;
  285. // Strategy: We compute (in public) (powers of 2) times P, and then
  286. // conditionally add them into an accumulator. Because our adder cannot
  287. // handle the point at infinity O, we start the accumulator with a value
  288. // of C, whose discrete log with respect to P should be unknown, so that
  289. // we won't encounter O along the way. (Also, a should not be 0 or
  290. // the group order.) We actually start the accumulator with either
  291. // C or C+P depending on avec[0], so we get the first conditional add
  292. // "for free". Then we use the ec_add_P123_gadget to do the conditional
  293. // adds two at a time (at a cost of 6 constraints per pair, as opposed to
  294. // 5 for a single conditional add). If the length of avec is even, then
  295. // there will be one left over, and we do a single conditional add for
  296. // that one. Finally, we add the public point -C.
  297. ec_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  298. const pb_variable<FieldT> &outx,
  299. const pb_variable<FieldT> &outy,
  300. const pb_variable_array<FieldT> &avec,
  301. const FieldT &Px, const FieldT &Py) :
  302. gadget<FieldT>(pb, "ec_pedersen_vec_gadget"),
  303. // Precomputed coordinates of C
  304. Cx(2),
  305. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  306. outx(outx), outy(outy), avec(avec), Px(Px), Py(Py)
  307. {
  308. size_t numbits = avec.size();
  309. accumx.allocate(this->pb, numbits/2+1, "accumx");
  310. accumy.allocate(this->pb, numbits/2+1, "accumy");
  311. ec_add_points(CPx, CPy, Cx, Cy, Px, Py);
  312. FieldT twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py;
  313. size_t i = 1;
  314. ec_double_point(twoiPx, twoiPy, Px, Py);
  315. while(i < numbits) {
  316. // Invariants: i is odd, and twoiP = 2^i * P
  317. // Compute twoi1P = 2^{i+1} * P = 2 * twoiP and
  318. // twoi3P = 2^i * 3 * P = 3 * twoiP
  319. ec_double_point(twoi1Px, twoi1Py, twoiPx, twoiPy);
  320. ec_add_points(twoi3Px, twoi3Py, twoi1Px, twoi1Py, twoiPx, twoiPy);
  321. if (i == numbits-1) {
  322. // There's only one bit of avec left; use a single conditional
  323. // add.
  324. condsingleadders.emplace_back(this->pb,
  325. accumx[(i+1)/2], accumy[(i+1)/2],
  326. accumx[(i-1)/2], accumy[(i-1)/2],
  327. avec[i],
  328. twoiPx, twoiPy);
  329. } else {
  330. conddoubleadders.emplace_back(this->pb,
  331. accumx[(i+1)/2], accumy[(i+1)/2],
  332. accumx[(i-1)/2], accumy[(i-1)/2],
  333. avec[i], avec[i+1],
  334. twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py);
  335. }
  336. ec_double_point(twoiPx, twoiPy, twoi1Px, twoi1Py);
  337. i += 2;
  338. }
  339. // If numbits is even, the output so far is in accum[(numbits)/2].
  340. // If numbits is odd, it is in accum[(numbits-1)/2]. So in either
  341. // case, it is in accum[numbits/2].
  342. singleadders.emplace_back(this->pb,
  343. outx, outy, accumx[numbits/2], accumy[numbits/2],
  344. Cx, -Cy);
  345. }
  346. void generate_r1cs_constraints()
  347. {
  348. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cx + (CPx-Cx) * avec[0], 1, accumx[0]));
  349. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cy + (CPy-Cy) * avec[0], 1, accumy[0]));
  350. for (auto&& gadget : conddoubleadders) {
  351. gadget.generate_r1cs_constraints();
  352. }
  353. for (auto&& gadget : condsingleadders) {
  354. gadget.generate_r1cs_constraints();
  355. }
  356. for (auto&& gadget : singleadders) {
  357. gadget.generate_r1cs_constraints();
  358. }
  359. }
  360. void generate_r1cs_witness()
  361. {
  362. this->pb.val(accumx[0]) = Cx + (CPx-Cx) * this->pb.val(avec[0]);
  363. this->pb.val(accumy[0]) = Cy + (CPy-Cy) * this->pb.val(avec[0]);
  364. for (auto&& gadget : conddoubleadders) {
  365. gadget.generate_r1cs_witness();
  366. }
  367. for (auto&& gadget : condsingleadders) {
  368. gadget.generate_r1cs_witness();
  369. }
  370. for (auto&& gadget : singleadders) {
  371. gadget.generate_r1cs_witness();
  372. }
  373. }
  374. };
  375. // Compute a*P as (outx, outy) for a given public point P, given a
  376. // as a field element.
  377. template<typename FieldT>
  378. class ec_scalarmul_gadget : public gadget<FieldT> {
  379. private:
  380. pb_variable_array<FieldT> avec;
  381. std::vector<packing_gadget<FieldT> > packers;
  382. std::vector<ec_scalarmul_vec_gadget<FieldT> > vecgadget;
  383. public:
  384. const pb_variable<FieldT> outx, outy, a;
  385. const FieldT Px, Py;
  386. ec_scalarmul_gadget(protoboard<FieldT> &pb,
  387. const pb_variable<FieldT> &outx,
  388. const pb_variable<FieldT> &outy,
  389. const pb_variable<FieldT> &a,
  390. const FieldT &Px, const FieldT &Py) :
  391. gadget<FieldT>(pb, "ec_scalarmul_gadget"),
  392. outx(outx), outy(outy), a(a), Px(Px), Py(Py)
  393. {
  394. // Allocate variables to protoboard
  395. // The strings (like "x") are only for debugging purposes
  396. size_t numbits = FieldT::num_bits;
  397. avec.allocate(this->pb, numbits, "avec");
  398. packers.emplace_back(this->pb, avec, a);
  399. vecgadget.emplace_back(this->pb, outx, outy, avec, Px, Py);
  400. }
  401. void generate_r1cs_constraints()
  402. {
  403. packers[0].generate_r1cs_constraints(true);
  404. vecgadget[0].generate_r1cs_constraints();
  405. }
  406. void generate_r1cs_witness()
  407. {
  408. packers[0].generate_r1cs_witness_from_packed();
  409. vecgadget[0].generate_r1cs_witness();
  410. }
  411. };
  412. // Compute a*G + b*H as (outx, outy), given a and b as bit vectors.
  413. // a and b must be of the same size. The _caller_ is responsible for
  414. // proving that the elements of avec and bvec are bits.
  415. template<typename FieldT>
  416. class ec_pedersen_vec_gadget : public gadget<FieldT> {
  417. private:
  418. pb_variable_array<FieldT> accumx, accumy, daccumx, daccumy;
  419. pb_variable<FieldT> lambda;
  420. std::vector<ec_double_gadget<FieldT> > doublers;
  421. std::vector<ec_add_P123_gadget<FieldT> > adders;
  422. const FieldT Cx, Cy, CGx, CGy, CHx, CHy, CGHx, CGHy;
  423. FieldT m2nCx, m2nCy;
  424. // Compute m2nC = -2^n * C. We can precomute the answers for values
  425. // of n we expect to get.
  426. void compute_m2nC(FieldT &m2nCx, FieldT &m2nCy, size_t n)
  427. {
  428. if (n == 253) {
  429. // Precomputed coordinates of -2^253*C
  430. m2nCx = FieldT("2630025903576807331238993847875694711243784786568881628418508626984487096258");
  431. m2nCy = FieldT("17628834417659968531880949658739649785248429713924280788649629869316127047701");
  432. } else {
  433. // Invariant: m2iC = -2^i * C
  434. FieldT m2iCx = Cx;
  435. FieldT m2iCy = -Cy;
  436. size_t i = 0;
  437. while (i < n) {
  438. FieldT m2iCxo, m2iCyo;
  439. ec_double_point(m2iCxo, m2iCyo, m2iCx, m2iCy);
  440. m2iCx = m2iCxo;
  441. m2iCy = m2iCyo;
  442. ++i;
  443. }
  444. m2nCx = m2iCx;
  445. m2nCy = m2iCy;
  446. }
  447. }
  448. public:
  449. const pb_variable<FieldT> outx, outy;
  450. const pb_variable_array<FieldT> avec, bvec;
  451. ec_pedersen_vec_gadget(protoboard<FieldT> &pb,
  452. const pb_variable<FieldT> &outx,
  453. const pb_variable<FieldT> &outy,
  454. const pb_variable_array<FieldT> &avec,
  455. const pb_variable_array<FieldT> &bvec) :
  456. gadget<FieldT>(pb, "ec_pedersen_vec_gadget"),
  457. // Precomputed coordinates of C, C+G, C+H, and C+G+H
  458. Cx(2),
  459. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  460. CGx("4998993376791159436553350546778310121346937620672073819457843493128326049156"),
  461. CGy("11119675827304465476900978353730540420130346377889406728458325551400357147144"),
  462. CHx("19614539896004018833724771305328960655474424364705508053472946746883341111010"),
  463. CHy("9853241351900213537247225242092949438866383394579783148395572971112906592855"),
  464. CGHx("10755582242294898568680134375159803731902153202607833320871336755950640390928"),
  465. CGHy("3110667473759844579409644567672992116704859238881299917617768683686288881761"),
  466. outx(outx), outy(outy), avec(avec), bvec(bvec)
  467. {
  468. // Allocate variables to protoboard
  469. // The strings (like "x") are only for debugging purposes
  470. size_t numbits = avec.size();
  471. accumx.allocate(this->pb, numbits, "accumx");
  472. accumy.allocate(this->pb, numbits, "accumy");
  473. daccumx.allocate(this->pb, numbits-1, "daccumx");
  474. daccumy.allocate(this->pb, numbits-1, "daccumy");
  475. lambda.allocate(this->pb, "lambda");
  476. for (size_t i = 0; i < numbits-1; ++i) {
  477. doublers.emplace_back(this->pb, daccumx[i], daccumy[i],
  478. accumx[i], accumy[i]);
  479. adders.emplace_back(this->pb, accumx[i+1], accumy[i+1],
  480. daccumx[i], daccumy[i], avec[numbits-2-i], bvec[numbits-2-i],
  481. // The coordinates of G, H, and G+H
  482. FieldT(0), FieldT("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  483. FieldT(1), FieldT("21803877843449984883423225223478944275188924769286999517937427649571474907279"),
  484. FieldT("2864090850787705444524344020850508438903451433901276387624248428140647539638"),
  485. FieldT("3350168998338968221269367365107720885864670493693161027931048546881356285970"));
  486. }
  487. compute_m2nC(m2nCx, m2nCy, numbits-1);
  488. }
  489. void generate_r1cs_constraints()
  490. {
  491. // Strategy: We do a basic double-and-add, using the variant of a
  492. // single double and an add of one of (O, G, H, G+H) at each step.
  493. // *However*, there's a twist. Our doubling and adding routines
  494. // don't handle the point at infinity O as the point to add to or to
  495. // double. (Adding O as one of the four options above is fine.)
  496. // Normally, the double-and-add algorithm starts with an accumulator
  497. // of O, and that won't work for us. So instead, we start the
  498. // accumulator at a different base point C, whose discrete log
  499. // with respect to the (G,H) basis is unknown. Then we'll end up
  500. // with an extra 2^n * C in the accumulator (where n is the number
  501. // of doublings we do), so at the end, we'll add the (constant!)
  502. // point -2^n * C to get the final result. That the discrete log of
  503. // C is unknown means we won't encounter O along the way, either (if
  504. // we did, we could compute the DL of C in the (G,H) basis).
  505. // For the first bit, we just precompute C, C+G, C+H, C+G+H (the
  506. // values are above) and use the top bit of a and b to choose which
  507. // one to start with.
  508. // accumx[0] = Cx + (CGx - Cx) * avec[numbits-1] + (CHx - Cx) *
  509. // bvec[numbits-1] + (CGHx - CGx - CHx + Cx) *
  510. // avec[numbits-1]*bvec[numbits-1] (and similarly for y)
  511. // We could possibly optimize this later by computing the a*b
  512. // product once, but then we'd have to pass a large linear
  513. // combination to a gadget, which it probably doesn't like? It
  514. // would save just one constraint, so probably not so important?
  515. size_t numbits = avec.size();
  516. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>((CGHx - CGx - CHx + Cx) * avec[numbits-1], bvec[numbits-1], accumx[0] - (Cx + (CGx - Cx) * avec[numbits-1] + (CHx - Cx) * bvec[numbits-1])));
  517. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>((CGHy - CGy - CHy + Cy) * avec[numbits-1], bvec[numbits-1], accumy[0] - (Cy + (CGy - Cy) * avec[numbits-1] + (CHy - Cy) * bvec[numbits-1])));
  518. // After that, for each remaining bit of a and b, we use the
  519. // ec_double_gadget and the ec_add_GH_gadget to accumulate
  520. // the answer.
  521. for (size_t i = 0; i < numbits-1; ++i) {
  522. doublers[i].generate_r1cs_constraints();
  523. adders[i].generate_r1cs_constraints();
  524. }
  525. // Finally, we add the constant point -2^n * C to the result, where
  526. // n = numbits-1
  527. // (m2nCx - accumx[numbits-1]) * lambda = m2nCy - accumy[numbits-1]
  528. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(m2nCx - accumx[numbits-1], lambda, m2nCy - accumy[numbits-1]));
  529. // outx = lambda^2 - (m2nCx + accumx[numbits-1])
  530. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + m2nCx + accumx[numbits-1]));
  531. // outy = lambda * (accumx[numbits-1] - outx) - accumy[numbits-1]
  532. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, accumx[numbits-1] - outx, outy + accumy[numbits-1]));
  533. }
  534. void generate_r1cs_witness()
  535. {
  536. size_t numbits = avec.size();
  537. this->pb.val(accumx[0]) = Cx + (CGx - Cx) * this->pb.val(avec[numbits-1]) + (CHx - Cx) * this->pb.val(bvec[numbits-1]) + (CGHx - CGx - CHx + Cx) * this->pb.val(avec[numbits-1])*this->pb.val(bvec[numbits-1]);
  538. this->pb.val(accumy[0]) = Cy + (CGy - Cy) * this->pb.val(avec[numbits-1]) + (CHy - Cy) * this->pb.val(bvec[numbits-1]) + (CGHy - CGy - CHy + Cy) * this->pb.val(avec[numbits-1])*this->pb.val(bvec[numbits-1]);
  539. for (size_t i = 0; i < numbits-1; ++i) {
  540. doublers[i].generate_r1cs_witness();
  541. adders[i].generate_r1cs_witness();
  542. }
  543. this->pb.val(lambda) = (m2nCy - this->pb.val(accumy[numbits-1])) *
  544. (m2nCx - this->pb.val(accumx[numbits-1])).inverse();
  545. this->pb.val(outx) = this->pb.val(lambda).squared() -
  546. (m2nCx + this->pb.val(accumx[numbits-1]));
  547. this->pb.val(outy) = this->pb.val(lambda) *
  548. (this->pb.val(accumx[numbits-1]) - this->pb.val(outx))
  549. - this->pb.val(accumy[numbits-1]);
  550. }
  551. };
  552. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  553. template<typename FieldT>
  554. class ec_old_pedersen_gadget : public gadget<FieldT> {
  555. private:
  556. pb_variable_array<FieldT> avec, bvec;
  557. std::vector<packing_gadget<FieldT> > packers;
  558. std::vector<ec_pedersen_vec_gadget<FieldT> > vecgadget;
  559. public:
  560. const pb_variable<FieldT> outx, outy, a, b;
  561. ec_old_pedersen_gadget(protoboard<FieldT> &pb,
  562. const pb_variable<FieldT> &outx,
  563. const pb_variable<FieldT> &outy,
  564. const pb_variable<FieldT> &a,
  565. const pb_variable<FieldT> &b) :
  566. gadget<FieldT>(pb, "ec_old_pedersen_gadget"),
  567. outx(outx), outy(outy), a(a), b(b)
  568. {
  569. // Allocate variables to protoboard
  570. // The strings (like "x") are only for debugging purposes
  571. size_t numbits = FieldT::num_bits;
  572. avec.allocate(this->pb, numbits, "avec");
  573. bvec.allocate(this->pb, numbits, "bvec");
  574. packers.emplace_back(this->pb, avec, a);
  575. packers.emplace_back(this->pb, bvec, b);
  576. vecgadget.emplace_back(this->pb, outx, outy, avec, bvec);
  577. }
  578. void generate_r1cs_constraints()
  579. {
  580. packers[0].generate_r1cs_constraints(true);
  581. packers[1].generate_r1cs_constraints(true);
  582. vecgadget[0].generate_r1cs_constraints();
  583. }
  584. void generate_r1cs_witness()
  585. {
  586. packers[0].generate_r1cs_witness_from_packed();
  587. packers[1].generate_r1cs_witness_from_packed();
  588. vecgadget[0].generate_r1cs_witness();
  589. }
  590. };
  591. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  592. template<typename FieldT>
  593. class ec_pedersen_gadget : public gadget<FieldT> {
  594. private:
  595. pb_variable<FieldT> aoutx, aouty, boutx, bouty;
  596. std::vector<ec_scalarmul_gadget<FieldT> > mulgadgets;
  597. std::vector<ec_add_gadget<FieldT> > addgadget;
  598. const FieldT Gx, Gy, Hx, Hy;
  599. public:
  600. const pb_variable<FieldT> outx, outy, a, b;
  601. ec_pedersen_gadget(protoboard<FieldT> &pb,
  602. const pb_variable<FieldT> &outx,
  603. const pb_variable<FieldT> &outy,
  604. const pb_variable<FieldT> &a,
  605. const pb_variable<FieldT> &b) :
  606. gadget<FieldT>(pb, "ec_pedersen_gadget"),
  607. outx(outx), outy(outy), a(a), b(b),
  608. // Precomputed coordinates of G and H
  609. Gx(0),
  610. Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  611. Hx(1),
  612. Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279")
  613. {
  614. // Allocate variables to protoboard
  615. // The strings (like "x") are only for debugging purposes
  616. aoutx.allocate(this->pb, "aoutx");
  617. aouty.allocate(this->pb, "aouty");
  618. boutx.allocate(this->pb, "boutx");
  619. bouty.allocate(this->pb, "bouty");
  620. mulgadgets.emplace_back(this->pb, aoutx, aouty, a, Gx, Gy);
  621. mulgadgets.emplace_back(this->pb, boutx, bouty, b, Hx, Hy);
  622. addgadget.emplace_back(this->pb, outx, outy, aoutx, aouty, boutx, bouty);
  623. }
  624. void generate_r1cs_constraints()
  625. {
  626. mulgadgets[0].generate_r1cs_constraints();
  627. mulgadgets[1].generate_r1cs_constraints();
  628. addgadget[0].generate_r1cs_constraints();
  629. }
  630. void generate_r1cs_witness()
  631. {
  632. mulgadgets[0].generate_r1cs_witness();
  633. mulgadgets[1].generate_r1cs_witness();
  634. addgadget[0].generate_r1cs_witness();
  635. }
  636. };