ecgadget.hpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  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 the constant EC point P0 or the constant EC point P1 to the
  156. // variable EC point (inx,iny) to yield (outx,outy). The input point
  157. // must not be the point at infinity. The input bit which controls
  158. // which addition is done.
  159. template<typename FieldT>
  160. class ec_2_constant_add_gadget : public gadget<FieldT> {
  161. private:
  162. pb_variable<FieldT> sumx, sumy;
  163. pb_linear_combination<FieldT> addx, addy;
  164. std::vector<ec_add_gadget<FieldT> > adder;
  165. public:
  166. const pb_variable<FieldT> outx, outy;
  167. const pb_linear_combination<FieldT> inx, iny;
  168. const pb_variable<FieldT> which;
  169. const FieldT P0x, P0y, P1x, P1y;
  170. ec_2_constant_add_gadget(protoboard<FieldT> &pb,
  171. const pb_variable<FieldT> &outx,
  172. const pb_variable<FieldT> &outy,
  173. const pb_linear_combination<FieldT> &inx,
  174. const pb_linear_combination<FieldT> &iny,
  175. const pb_variable<FieldT> &which,
  176. const FieldT &P0x, const FieldT &P0y,
  177. const FieldT &P1x, const FieldT &P1y) :
  178. gadget<FieldT>(pb, "ec_2_constant_add_gadget"),
  179. outx(outx), outy(outy), inx(inx), iny(iny), which(which),
  180. P0x(P0x), P0y(P0y), P1x(P1x), P1y(P1y)
  181. {
  182. // Allocate variables to protoboard
  183. addx.assign(pb, which * (P1x-P0x) + P0x);
  184. addy.assign(pb, which * (P1y-P0y) + P0y);
  185. adder.emplace_back(this->pb, outx, outy, inx, iny, addx, addy);
  186. }
  187. void generate_r1cs_constraints()
  188. {
  189. adder[0].generate_r1cs_constraints();
  190. }
  191. void generate_r1cs_witness()
  192. {
  193. addx.evaluate(this->pb);
  194. addy.evaluate(this->pb);
  195. adder[0].generate_r1cs_witness();
  196. }
  197. };
  198. // Add the constant EC point P0 or the variable EC point P1 to the
  199. // variable EC point (inx,iny) to yield (outx,outy). The input point
  200. // must not be the point at infinity. The input bit which controls
  201. // which addition is done.
  202. template<typename FieldT>
  203. class ec_2_1constant_add_gadget : public gadget<FieldT> {
  204. private:
  205. pb_variable<FieldT> sumx, sumy;
  206. pb_variable<FieldT> addx, addy;
  207. std::vector<ec_add_gadget<FieldT> > adder;
  208. public:
  209. const pb_variable<FieldT> outx, outy;
  210. const pb_linear_combination<FieldT> inx, iny;
  211. const pb_variable<FieldT> which;
  212. const FieldT P0x, P0y;
  213. const pb_variable<FieldT> P1x, P1y;
  214. ec_2_1constant_add_gadget(protoboard<FieldT> &pb,
  215. const pb_variable<FieldT> &outx,
  216. const pb_variable<FieldT> &outy,
  217. const pb_linear_combination<FieldT> &inx,
  218. const pb_linear_combination<FieldT> &iny,
  219. const pb_variable<FieldT> &which,
  220. const FieldT &P0x,
  221. const FieldT &P0y,
  222. const pb_variable<FieldT> &P1x,
  223. const pb_variable<FieldT> &P1y) :
  224. gadget<FieldT>(pb, "ec_2_1constant_add_gadget"),
  225. outx(outx), outy(outy), inx(inx), iny(iny), which(which),
  226. P0x(P0x), P0y(P0y), P1x(P1x), P1y(P1y)
  227. {
  228. // Allocate variables to protoboard
  229. addx.allocate(this->pb, "addx");
  230. addy.allocate(this->pb, "addy");
  231. adder.emplace_back(this->pb, outx, outy, inx, iny, addx, addy);
  232. }
  233. void generate_r1cs_constraints()
  234. {
  235. // Set (addx,addy) = which ? (P0x, P0y) : (P1x, P1y)
  236. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1x - P0x, which, addx - P0x));
  237. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1y - P0y, which, addy - P0y));
  238. adder[0].generate_r1cs_constraints();
  239. }
  240. void generate_r1cs_witness()
  241. {
  242. bool whichb = this->pb.val(which) != FieldT(0);
  243. this->pb.val(addx) = whichb ? this->pb.val(P1x) : P0x;
  244. this->pb.val(addy) = whichb ? this->pb.val(P1y) : P0y;
  245. adder[0].generate_r1cs_witness();
  246. }
  247. };
  248. // Add the variable EC point P0 or the variable EC point P1 to the
  249. // variable EC point (inx,iny) to yield (outx,outy). The input point
  250. // must not be the point at infinity. The input bit which controls
  251. // which addition is done.
  252. template<typename FieldT>
  253. class ec_2_add_gadget : public gadget<FieldT> {
  254. private:
  255. pb_variable<FieldT> sumx, sumy;
  256. pb_variable<FieldT> addx, addy;
  257. std::vector<ec_add_gadget<FieldT> > adder;
  258. public:
  259. const pb_variable<FieldT> outx, outy;
  260. const pb_linear_combination<FieldT> inx, iny;
  261. const pb_variable<FieldT> which;
  262. const pb_variable<FieldT> P0x, P0y, P1x, P1y;
  263. ec_2_add_gadget(protoboard<FieldT> &pb,
  264. const pb_variable<FieldT> &outx,
  265. const pb_variable<FieldT> &outy,
  266. const pb_linear_combination<FieldT> &inx,
  267. const pb_linear_combination<FieldT> &iny,
  268. const pb_variable<FieldT> &which,
  269. const pb_variable<FieldT> &P0x,
  270. const pb_variable<FieldT> &P0y,
  271. const pb_variable<FieldT> &P1x,
  272. const pb_variable<FieldT> &P1y) :
  273. gadget<FieldT>(pb, "ec_2_add_gadget"),
  274. outx(outx), outy(outy), inx(inx), iny(iny), which(which),
  275. P0x(P0x), P0y(P0y), P1x(P1x), P1y(P1y)
  276. {
  277. // Allocate variables to protoboard
  278. addx.allocate(this->pb, "addx");
  279. addy.allocate(this->pb, "addy");
  280. adder.emplace_back(this->pb, outx, outy, inx, iny, addx, addy);
  281. }
  282. void generate_r1cs_constraints()
  283. {
  284. // Set (addx,addy) = which ? (P0x, P0y) : (P1x, P1y)
  285. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1x - P0x, which, addx - P0x));
  286. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1y - P0y, which, addy - P0y));
  287. adder[0].generate_r1cs_constraints();
  288. }
  289. void generate_r1cs_witness()
  290. {
  291. bool whichb = this->pb.val(which) != FieldT(0);
  292. this->pb.val(addx) = whichb ? this->pb.val(P1x) : this->pb.val(P0x);
  293. this->pb.val(addy) = whichb ? this->pb.val(P1y) : this->pb.val(P0y);
  294. adder[0].generate_r1cs_witness();
  295. }
  296. };
  297. #if 0
  298. // Add nothing, or one of the constant EC points P1, P2, or P3 to the EC
  299. // point (inx,iny) to yield (outx,outy). The input point must not be
  300. // the point at infinity. The two input bits add1 and add2 control what
  301. // is added. Typically, P3 will equal P1+P2, in which case this gadget
  302. // does two conditional constant adds simultaneously in just 6 constraints.
  303. template<typename FieldT>
  304. class ec_add_P123_gadget : public gadget<FieldT> {
  305. private:
  306. pb_variable<FieldT> lambda, sumx, sumy, move;
  307. public:
  308. const pb_variable<FieldT> outx, outy;
  309. const pb_linear_combination<FieldT> inx, iny;
  310. const pb_variable<FieldT> add1, add2;
  311. const FieldT P1x, P1y, P2x, P2y, P3x, P3y;
  312. ec_add_P123_gadget(protoboard<FieldT> &pb,
  313. const pb_variable<FieldT> &outx,
  314. const pb_variable<FieldT> &outy,
  315. const pb_linear_combination<FieldT> &inx,
  316. const pb_linear_combination<FieldT> &iny,
  317. const pb_variable<FieldT> &add1,
  318. const pb_variable<FieldT> &add2,
  319. const FieldT &P1x, const FieldT &P1y,
  320. const FieldT &P2x, const FieldT &P2y,
  321. const FieldT &P3x, const FieldT &P3y) :
  322. gadget<FieldT>(pb, "ec_add_P123_gadget"),
  323. outx(outx), outy(outy), inx(inx), iny(iny), add1(add1), add2(add2),
  324. P1x(P1x), P1y(P1y), P2x(P2x), P2y(P2y), P3x(P3x), P3y(P3y)
  325. {
  326. // Allocate variables to protoboard
  327. // The strings (like "x") are only for debugging purposes
  328. lambda.allocate(this->pb, "lambda");
  329. sumx.allocate(this->pb, "sumx");
  330. sumy.allocate(this->pb, "sumy");
  331. move.allocate(this->pb, "move");
  332. }
  333. void generate_r1cs_constraints()
  334. {
  335. // Strategy: if add1 = add2 = 0, we compute some nonsense but throw
  336. // it away later. Otherwise, the coordinates of the point to add
  337. // are a _linear_ function of add1 and add2 (since P1, P2, and P3
  338. // are public constants)
  339. // In particular, the point to add is ( (P3x - P2x) * add1 + (P3x -
  340. // P1x) * add2 + (P1x + P2x - P3x), (P3y - P2y) * add1 + (P3y - P1y) *
  341. // add2 + (P1y + P2y - P3y))
  342. // (addx - inx) * lambda = addy - iny
  343. 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));
  344. // sumx = lambda^2 - (addx + inx)
  345. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, sumx + (P3x - P2x) * add1 + (P3x - P1x) * add2 + (P1x + P2x - P3x) + inx));
  346. // sumy = lambda * (inx - sumx) - iny
  347. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - sumx, sumy + iny));
  348. // Now we want to conditionally move the sum. We want that
  349. // outx = (add1 || add2) ? sumx : inx
  350. // outy = (add1 || add2) ? sumy : iny
  351. // so we compute move = add1 || add2, and then
  352. // outx = inx + (sumx - inx) * move
  353. // outy = iny + (sumy - iny) * move
  354. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1 - add1, 1 - add2, 1 - move));
  355. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, move, outx - inx));
  356. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, move, outy - iny));
  357. }
  358. void generate_r1cs_witness()
  359. {
  360. FieldT addxval = (P3x - P2x) * this->pb.val(add1) + (P3x - P1x) * this->pb.val(add2) + (P1x + P2x - P3x);
  361. FieldT addyval = (P3y - P2y) * this->pb.val(add1) + (P3y - P1y) * this->pb.val(add2) + (P1y + P2y - P3y);
  362. this->pb.val(lambda) = (addyval - this->pb.lc_val(iny)) * (addxval - this->pb.lc_val(inx)).inverse();
  363. this->pb.val(sumx) = this->pb.val(lambda).squared() - (addxval + this->pb.lc_val(inx));
  364. this->pb.val(sumy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(sumx)) - this->pb.lc_val(iny);
  365. bool a1 = this->pb.val(add1) != FieldT(0);
  366. bool a2 = this->pb.val(add2) != FieldT(0);
  367. this->pb.val(move) = a1 || a2;
  368. this->pb.val(outx) = (a1 || a2) ? this->pb.val(sumx) : this->pb.lc_val(inx);
  369. this->pb.val(outy) = (a1 || a2) ? this->pb.val(sumy) : this->pb.lc_val(iny);
  370. }
  371. };
  372. #endif
  373. // Compute A + s*P as (outx, outy) for an accumulator A, a given
  374. // constant point P, and s given as a bit vector. The _caller_ is
  375. // responsible for proving that the elements of svec are bits. The
  376. // (constant) accumulator excess (AXS) will be updated; when all the
  377. // computations are complete, AXS should be subtracted from the
  378. // accumulator A.
  379. template<typename FieldT>
  380. class ec_constant_scalarmul_vec_accum_gadget : public gadget<FieldT> {
  381. private:
  382. FieldT Cx, Cy;
  383. pb_variable_array<FieldT> accumx, accumy;
  384. std::vector<ec_2_constant_add_gadget<FieldT> > twoadders;
  385. public:
  386. const pb_variable<FieldT> outx, outy;
  387. const pb_variable<FieldT> Ax, Ay;
  388. const pb_variable_array<FieldT> svec;
  389. const FieldT Px, Py;
  390. // Strategy: We compute (as compile-time constants) (powers of 2)
  391. // times P. Based on each bit of s, we add one of the constant points
  392. // C or (2^i * P) + C to the accumulator, and regardless of s, add C
  393. // to the excess.
  394. ec_constant_scalarmul_vec_accum_gadget(protoboard<FieldT> &pb,
  395. const pb_variable<FieldT> &outx,
  396. const pb_variable<FieldT> &outy,
  397. const pb_variable<FieldT> &Ax,
  398. const pb_variable<FieldT> &Ay,
  399. const pb_variable_array<FieldT> &svec,
  400. const FieldT &Px, const FieldT &Py,
  401. FieldT &AXSx, FieldT &AXSy) :
  402. gadget<FieldT>(pb, "ec_constant_scalarmul_vec_accum_gadget"),
  403. // Precomputed coordinates of C
  404. Cx(2),
  405. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  406. outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec), Px(Px), Py(Py)
  407. {
  408. size_t numbits = svec.size();
  409. accumx.allocate(this->pb, numbits-1, "accumx");
  410. accumy.allocate(this->pb, numbits-1, "accumy");
  411. FieldT twoiPx = Px, twoiPy = Py;
  412. size_t i = 0;
  413. while(i < numbits) {
  414. // Invariant: twoiP = 2^i * P
  415. FieldT twoiPCx, twoiPCy;
  416. ec_add_points(twoiPCx, twoiPCy, twoiPx, twoiPy, Cx, Cy);
  417. twoadders.emplace_back(this->pb,
  418. (i == numbits-1 ? outx : accumx[i]),
  419. (i == numbits-1 ? outy : accumy[i]),
  420. (i == 0 ? Ax : accumx[i-1]),
  421. (i == 0 ? Ay : accumy[i-1]),
  422. svec[i], Cx, Cy, twoiPCx, twoiPCy);
  423. FieldT newtwoiPx, newtwoiPy, newAXSx, newAXSy;
  424. ec_double_point(newtwoiPx, newtwoiPy, twoiPx, twoiPy);
  425. twoiPx = newtwoiPx;
  426. twoiPy = newtwoiPy;
  427. i += 1;
  428. ec_add_points(newAXSx, newAXSy, AXSx, AXSy, Cx, Cy);
  429. AXSx = newAXSx;
  430. AXSy = newAXSy;
  431. }
  432. }
  433. void generate_r1cs_constraints()
  434. {
  435. for (auto&& gadget : twoadders) {
  436. gadget.generate_r1cs_constraints();
  437. }
  438. }
  439. void generate_r1cs_witness()
  440. {
  441. for (auto&& gadget : twoadders) {
  442. gadget.generate_r1cs_witness();
  443. }
  444. }
  445. };
  446. // Compute A + s*P as (outx, outy) for an accumulator A, a given
  447. // constant point P, and s given as a field element. The (constant)
  448. // accumulator excess (AXS) will be updated; when all the computations
  449. // are complete, AXS should be subtracted from the accumulator A.
  450. template<typename FieldT>
  451. class ec_constant_scalarmul_accum_gadget : public gadget<FieldT> {
  452. private:
  453. pb_variable_array<FieldT> svec;
  454. std::vector<packing_gadget<FieldT> > packers;
  455. std::vector<ec_constant_scalarmul_vec_accum_gadget<FieldT> > vecgadget;
  456. public:
  457. const pb_variable<FieldT> outx, outy;
  458. const pb_variable<FieldT> Ax, Ay;
  459. const pb_variable<FieldT> s;
  460. const FieldT Px, Py;
  461. ec_constant_scalarmul_accum_gadget(protoboard<FieldT> &pb,
  462. const pb_variable<FieldT> &outx,
  463. const pb_variable<FieldT> &outy,
  464. const pb_variable<FieldT> &Ax,
  465. const pb_variable<FieldT> &Ay,
  466. const pb_variable<FieldT> &s,
  467. const FieldT &Px, const FieldT &Py,
  468. FieldT &AXSx, FieldT &AXSy) :
  469. gadget<FieldT>(pb, "ec_constant_scalarmul_accum_gadget"),
  470. outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s), Px(Px), Py(Py)
  471. {
  472. // Allocate variables to protoboard
  473. // The strings (like "x") are only for debugging purposes
  474. size_t numbits = FieldT::num_bits;
  475. svec.allocate(this->pb, numbits, "svec");
  476. packers.emplace_back(this->pb, svec, s);
  477. vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec, Px, Py, AXSx, AXSy);
  478. }
  479. void generate_r1cs_constraints()
  480. {
  481. packers[0].generate_r1cs_constraints(true);
  482. vecgadget[0].generate_r1cs_constraints();
  483. }
  484. void generate_r1cs_witness()
  485. {
  486. packers[0].generate_r1cs_witness_from_packed();
  487. vecgadget[0].generate_r1cs_witness();
  488. }
  489. };
  490. // Compute s*P as (outx, outy) for a given constant point P, and s given
  491. // as a bit vector. The _caller_ is responsible for proving that the
  492. // elements of svec are bits.
  493. template<typename FieldT>
  494. class ec_constant_scalarmul_vec_gadget : public gadget<FieldT> {
  495. private:
  496. FieldT Cx, Cy, Ax, Ay, AXSx, AXSy;
  497. pb_variable<FieldT> accinx, acciny, accoutx, accouty;
  498. std::vector<ec_constant_scalarmul_vec_accum_gadget<FieldT> > scalarmuls;
  499. std::vector<ec_constant_add_gadget<FieldT> > adders;
  500. public:
  501. const pb_variable<FieldT> outx, outy;
  502. const pb_variable_array<FieldT> svec;
  503. const FieldT Px, Py;
  504. ec_constant_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  505. const pb_variable<FieldT> &outx,
  506. const pb_variable<FieldT> &outy,
  507. const pb_variable_array<FieldT> &svec,
  508. const FieldT &Px, const FieldT &Py) :
  509. gadget<FieldT>(pb, "ec_constant_scalarmul_vec_gadget"),
  510. // Precomputed coordinates of C and A
  511. Cx(2),
  512. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  513. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  514. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302"),
  515. outx(outx), outy(outy), svec(svec), Px(Px), Py(Py)
  516. {
  517. AXSx = Ax;
  518. AXSy = Ay;
  519. accinx.allocate(this->pb, "accinx");
  520. acciny.allocate(this->pb, "acciny");
  521. accoutx.allocate(this->pb, "accoutx");
  522. accouty.allocate(this->pb, "accouty");
  523. scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec, Px, Py, AXSx, AXSy);
  524. adders.emplace_back(pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  525. }
  526. void generate_r1cs_constraints()
  527. {
  528. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  529. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  530. scalarmuls[0].generate_r1cs_constraints();
  531. adders[0].generate_r1cs_constraints();
  532. }
  533. void generate_r1cs_witness()
  534. {
  535. this->pb.val(accinx) = Ax;
  536. this->pb.val(acciny) = Ay;
  537. scalarmuls[0].generate_r1cs_witness();
  538. adders[0].generate_r1cs_witness();
  539. }
  540. };
  541. // Compute s*P as (outx, outy) for a given constant point P, and s given
  542. // as a field element.
  543. template<typename FieldT>
  544. class ec_constant_scalarmul_gadget : public gadget<FieldT> {
  545. private:
  546. pb_variable_array<FieldT> svec;
  547. std::vector<packing_gadget<FieldT> > packers;
  548. std::vector<ec_constant_scalarmul_vec_gadget<FieldT> > vecgadget;
  549. public:
  550. const pb_variable<FieldT> outx, outy;
  551. const pb_variable<FieldT> s;
  552. const FieldT Px, Py;
  553. ec_constant_scalarmul_gadget(protoboard<FieldT> &pb,
  554. const pb_variable<FieldT> &outx,
  555. const pb_variable<FieldT> &outy,
  556. const pb_variable<FieldT> &s,
  557. const FieldT &Px, const FieldT &Py) :
  558. gadget<FieldT>(pb, "ec_constant_scalarmul_gadget"),
  559. outx(outx), outy(outy), s(s), Px(Px), Py(Py)
  560. {
  561. // Allocate variables to protoboard
  562. // The strings (like "x") are only for debugging purposes
  563. size_t numbits = FieldT::num_bits;
  564. svec.allocate(this->pb, numbits, "svec");
  565. packers.emplace_back(this->pb, svec, s);
  566. vecgadget.emplace_back(this->pb, outx, outy, svec, Px, Py);
  567. }
  568. void generate_r1cs_constraints()
  569. {
  570. packers[0].generate_r1cs_constraints(true);
  571. vecgadget[0].generate_r1cs_constraints();
  572. }
  573. void generate_r1cs_witness()
  574. {
  575. packers[0].generate_r1cs_witness_from_packed();
  576. vecgadget[0].generate_r1cs_witness();
  577. }
  578. };
  579. template<typename FieldT>
  580. class ec_scalarmul_gadget;
  581. // Compute A + s*P as (outx, outy) for an accumulator A, a precomputed
  582. // addition table Ptable for a variable point P, and s given as a bit
  583. // vector. The _caller_ is responsible for proving that the elements of
  584. // svec are bits. The (constant) accumulator excess (AXS) will be
  585. // updated; when all the computations are complete, AXS should be
  586. // subtracted from the accumulator A. The addition table is a variable
  587. // array of length 2*numbits (where numbits is the length of svec) such
  588. // that Ptable[2*i] and Ptable[2*i+1] are the (x,y) coordinates of
  589. // 2^i * P + C. Set Ptable_set_constraints to true (exactly once
  590. // in the event the same Ptable is reused in the same circuit) if
  591. // the Ptable is part of the private input. Set Ptable_fill_values
  592. // to true exactly once per Ptable (again, in case it it reused in the
  593. // same circuit).
  594. template<typename FieldT>
  595. class ec_scalarmul_vec_accum_gadget : public gadget<FieldT> {
  596. private:
  597. FieldT Cx, Cy;
  598. pb_variable_array<FieldT> accumx, accumy;
  599. pb_variable_array<FieldT> twoiPx, twoiPy;
  600. std::vector<ec_constant_add_gadget<FieldT> > cadders;
  601. std::vector<ec_add_gadget<FieldT> > adders;
  602. std::vector<ec_2_1constant_add_gadget<FieldT> > twoadders;
  603. public:
  604. const pb_variable<FieldT> outx, outy;
  605. const pb_variable<FieldT> Ax, Ay;
  606. const pb_variable_array<FieldT> svec;
  607. const pb_variable<FieldT> Px, Py;
  608. const pb_variable_array<FieldT> Ptable;
  609. bool Ptable_set_constraints, Ptable_fill_values;
  610. ec_scalarmul_vec_accum_gadget(protoboard<FieldT> &pb,
  611. const pb_variable<FieldT> &outx,
  612. const pb_variable<FieldT> &outy,
  613. const pb_variable<FieldT> &Ax,
  614. const pb_variable<FieldT> &Ay,
  615. const pb_variable_array<FieldT> &svec,
  616. const pb_variable<FieldT> &Px,
  617. const pb_variable<FieldT> &Py,
  618. const pb_variable_array<FieldT> &Ptable,
  619. bool Ptable_set_constraints,
  620. bool Ptable_fill_values,
  621. FieldT &AXSx, FieldT &AXSy) :
  622. gadget<FieldT>(pb, "ec_scalarmul_vec_accum_gadget"),
  623. // Precomputed coordinates of C
  624. Cx(2),
  625. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  626. outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec),
  627. Px(Px), Py(Py), Ptable(Ptable),
  628. Ptable_set_constraints(Ptable_set_constraints),
  629. Ptable_fill_values(Ptable_fill_values)
  630. {
  631. size_t numbits = svec.size();
  632. assert(Ptable.size() == 2*numbits);
  633. if (Ptable_set_constraints) {
  634. // Create the adders to fill the Ptable with the correct values.
  635. // Ptable[2*i] and Ptable[2*i+1] are the (x,y) coordinates of
  636. // 2^i * P + C.
  637. if (numbits > 0) {
  638. // Add P and C to get Ptable[0,1] = P+C
  639. cadders.emplace_back(this->pb, Ptable[0], Ptable[1],
  640. Px, Py, Cx, Cy);
  641. }
  642. if (numbits > 1) {
  643. // Add P and P+C to get Ptable[2,3] = 2*P+C
  644. adders.emplace_back(this->pb, Ptable[2], Ptable[3],
  645. Px, Py, Ptable[0], Ptable[1]);
  646. }
  647. if (numbits > 2) {
  648. twoiPx.allocate(this->pb, numbits-2, "twoiPx");
  649. twoiPy.allocate(this->pb, numbits-2, "twoiPy");
  650. }
  651. for (size_t i = 2; i < numbits; ++i) {
  652. // Invariant: twoiP[i] = 2^{i+1} * P
  653. // Compute 2^{i-1}*P = (2^{i-1}*P + C) - C
  654. cadders.emplace_back(this->pb,
  655. twoiPx[i-2], twoiPy[i-2],
  656. Ptable[2*(i-1)], Ptable[2*(i-1)+1],
  657. Cx, -Cy);
  658. // Compute 2^{i}*P + C = (2^{i-1}*P + C) + (2^{i-1}*P)
  659. adders.emplace_back(this->pb,
  660. Ptable[2*i], Ptable[2*i+1],
  661. Ptable[2*i-2], Ptable[2*i-1],
  662. twoiPx[i-2], twoiPy[i-2]);
  663. }
  664. }
  665. accumx.allocate(this->pb, numbits-1, "accumx");
  666. accumy.allocate(this->pb, numbits-1, "accumy");
  667. for (size_t i = 0; i < numbits; ++i) {
  668. twoadders.emplace_back(this->pb,
  669. (i == numbits-1 ? outx : accumx[i]),
  670. (i == numbits-1 ? outy : accumy[i]),
  671. (i == 0 ? Ax : accumx[i-1]),
  672. (i == 0 ? Ay : accumy[i-1]),
  673. svec[i], Cx, Cy, Ptable[2*i], Ptable[2*i+1]);
  674. FieldT newAXSx, newAXSy;
  675. ec_add_points(newAXSx, newAXSy, AXSx, AXSy, Cx, Cy);
  676. AXSx = newAXSx;
  677. AXSy = newAXSy;
  678. }
  679. }
  680. void generate_r1cs_constraints()
  681. {
  682. if (Ptable_set_constraints) {
  683. for (auto&& gadget : cadders) {
  684. gadget.generate_r1cs_constraints();
  685. }
  686. for (auto&& gadget : adders) {
  687. gadget.generate_r1cs_constraints();
  688. }
  689. }
  690. for (auto&& gadget : twoadders) {
  691. gadget.generate_r1cs_constraints();
  692. }
  693. }
  694. void generate_r1cs_witness()
  695. {
  696. if (Ptable_set_constraints) {
  697. // We also have to satisfy the constraints we set
  698. size_t numbits = Ptable.size() / 2;
  699. if (numbits > 0) {
  700. cadders[0].generate_r1cs_witness();
  701. }
  702. if (numbits > 1) {
  703. adders[0].generate_r1cs_witness();
  704. }
  705. for (size_t i = 2; i < numbits; ++i) {
  706. cadders[i-1].generate_r1cs_witness();
  707. adders[i-1].generate_r1cs_witness();
  708. }
  709. } else if (Ptable_fill_values) {
  710. // We can just compute the Ptable values manually
  711. ec_scalarmul_gadget<FieldT>::compute_Ptable(this->pb, Ptable, Px, Py);
  712. }
  713. for (auto&& gadget : twoadders) {
  714. gadget.generate_r1cs_witness();
  715. }
  716. }
  717. };
  718. // Compute A + s*P as (outx, outy) for an accumulator A, a precomputed
  719. // addition table Ptable for a variable point P, and s given as a field
  720. // element. The _caller_ is responsible for proving that the elements
  721. // of svec are bits. The (constant) accumulator excess (AXS) will be
  722. // updated; when all the computations are complete, AXS should be
  723. // subtracted from the accumulator A. The addition table is a variable
  724. // array of length 2*numbits (where numbits is the length of the FieldT
  725. // size) such that Ptable[2*i] and Ptable[2*i+1] are the (x,y)
  726. // coordinates of 2^i * P + C. Set Ptable_set_constraints to true
  727. // (exactly once in the event the same Ptable is reused in the same
  728. // circuit) if the Ptable is part of the private input. Set
  729. // Ptable_fill_values to true exactly once per Ptable (again, in case it
  730. // it reused in the same circuit).
  731. template<typename FieldT>
  732. class ec_scalarmul_accum_gadget : public gadget<FieldT> {
  733. private:
  734. pb_variable_array<FieldT> svec;
  735. std::vector<packing_gadget<FieldT> > packers;
  736. std::vector<ec_scalarmul_vec_accum_gadget<FieldT> > vecgadget;
  737. public:
  738. const pb_variable<FieldT> outx, outy;
  739. const pb_variable<FieldT> Ax, Ay;
  740. const pb_variable<FieldT> s;
  741. const pb_variable<FieldT> Px, Py;
  742. const pb_variable_array<FieldT> Ptable;
  743. bool Ptable_set_constraints, Ptable_fill_values;
  744. ec_scalarmul_accum_gadget(protoboard<FieldT> &pb,
  745. const pb_variable<FieldT> &outx,
  746. const pb_variable<FieldT> &outy,
  747. const pb_variable<FieldT> &Ax,
  748. const pb_variable<FieldT> &Ay,
  749. const pb_variable<FieldT> &s,
  750. const pb_variable<FieldT> &Px,
  751. const pb_variable<FieldT> &Py,
  752. const pb_variable_array<FieldT> &Ptable,
  753. bool Ptable_set_constraints,
  754. bool Ptable_fill_values,
  755. FieldT &AXSx, FieldT &AXSy) :
  756. gadget<FieldT>(pb, "ec_scalarmul_accum_gadget"),
  757. outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s),
  758. Px(Px), Py(Py), Ptable(Ptable),
  759. Ptable_set_constraints(Ptable_set_constraints),
  760. Ptable_fill_values(Ptable_fill_values)
  761. {
  762. // Allocate variables to protoboard
  763. // The strings (like "x") are only for debugging purposes
  764. size_t numbits = FieldT::num_bits;
  765. svec.allocate(this->pb, numbits, "svec");
  766. packers.emplace_back(this->pb, svec, s);
  767. vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec,
  768. Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values,
  769. AXSx, AXSy);
  770. }
  771. void generate_r1cs_constraints()
  772. {
  773. packers[0].generate_r1cs_constraints(true);
  774. vecgadget[0].generate_r1cs_constraints();
  775. }
  776. void generate_r1cs_witness()
  777. {
  778. packers[0].generate_r1cs_witness_from_packed();
  779. vecgadget[0].generate_r1cs_witness();
  780. }
  781. };
  782. // Compute s*P as (outx, outy) for a precomputed addition table Ptable
  783. // for a variable point P, and s given as a bit vector. The _caller_ is
  784. // responsible for proving that the elements of svec are bits.
  785. // The addition table is a variable array of length 2*numbits (where
  786. // numbits is the length of svec) such that Ptable[2*i] and
  787. // Ptable[2*i+1] are the (x,y) coordinates of 2^i * P + C. Set
  788. // Ptable_set_constraints to true (exactly once in the event the same
  789. // Ptable is reused in the same circuit) if the Ptable is part of the
  790. // private input. Set Ptable_fill_values to true exactly once per
  791. // Ptable (again, in case it it reused in the same circuit).
  792. template<typename FieldT>
  793. class ec_scalarmul_vec_gadget : public gadget<FieldT> {
  794. private:
  795. FieldT Cx, Cy, Ax, Ay, AXSx, AXSy;
  796. pb_variable<FieldT> accinx, acciny, accoutx, accouty;
  797. std::vector<ec_scalarmul_vec_accum_gadget<FieldT> > scalarmuls;
  798. std::vector<ec_constant_add_gadget<FieldT> > adders;
  799. public:
  800. const pb_variable<FieldT> outx, outy;
  801. const pb_variable_array<FieldT> svec;
  802. const pb_variable<FieldT> Px, Py;
  803. const pb_variable_array<FieldT> Ptable;
  804. bool Ptable_set_constraints, Ptable_fill_values;
  805. ec_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  806. const pb_variable<FieldT> &outx,
  807. const pb_variable<FieldT> &outy,
  808. const pb_variable_array<FieldT> &svec,
  809. const pb_variable<FieldT> &Px,
  810. const pb_variable<FieldT> &Py,
  811. const pb_variable_array<FieldT> &Ptable,
  812. bool Ptable_set_constraints,
  813. bool Ptable_fill_values) :
  814. gadget<FieldT>(pb, "ec_scalarmul_vec_gadget"),
  815. // Precomputed coordinates of C and A
  816. Cx(2),
  817. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  818. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  819. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302"),
  820. outx(outx), outy(outy), svec(svec),
  821. Px(Px), Py(Py), Ptable(Ptable),
  822. Ptable_set_constraints(Ptable_set_constraints),
  823. Ptable_fill_values(Ptable_fill_values)
  824. {
  825. AXSx = Ax;
  826. AXSy = Ay;
  827. accinx.allocate(this->pb, "accinx");
  828. acciny.allocate(this->pb, "acciny");
  829. accoutx.allocate(this->pb, "accoutx");
  830. accouty.allocate(this->pb, "accouty");
  831. scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec,
  832. Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values,
  833. AXSx, AXSy);
  834. adders.emplace_back(pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  835. }
  836. void generate_r1cs_constraints()
  837. {
  838. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  839. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  840. scalarmuls[0].generate_r1cs_constraints();
  841. adders[0].generate_r1cs_constraints();
  842. }
  843. void generate_r1cs_witness()
  844. {
  845. this->pb.val(accinx) = Ax;
  846. this->pb.val(acciny) = Ay;
  847. scalarmuls[0].generate_r1cs_witness();
  848. adders[0].generate_r1cs_witness();
  849. }
  850. };
  851. // Compute s*P as (outx, outy) for a precomputed addition table Ptable
  852. // for a variable point P, and s given as a field element. The addition
  853. // table is a variable array of length 2*numbits (where numbits is the
  854. // length of the FieldT size) such that Ptable[2*i] and Ptable[2*i+1]
  855. // are the (x,y) coordinates of 2^i * P + C. Set Ptable_set_constraints
  856. // to true (exactly once in the event the same Ptable is reused in the
  857. // same circuit) if the Ptable is part of the private input. Set
  858. // Ptable_fill_values to true exactly once per Ptable (again, in case it
  859. // it reused in the same circuit).
  860. template<typename FieldT>
  861. class ec_scalarmul_gadget : public gadget<FieldT> {
  862. private:
  863. pb_variable_array<FieldT> svec;
  864. std::vector<packing_gadget<FieldT> > packers;
  865. std::vector<ec_scalarmul_vec_gadget<FieldT> > vecgadget;
  866. public:
  867. const pb_variable<FieldT> outx, outy;
  868. const pb_variable<FieldT> s;
  869. const pb_variable<FieldT> Px, Py;
  870. const pb_variable_array<FieldT> Ptable;
  871. bool Ptable_set_constraints, Ptable_fill_values;
  872. ec_scalarmul_gadget(protoboard<FieldT> &pb,
  873. const pb_variable<FieldT> &outx,
  874. const pb_variable<FieldT> &outy,
  875. const pb_variable<FieldT> &s,
  876. const pb_variable<FieldT> &Px,
  877. const pb_variable<FieldT> &Py,
  878. const pb_variable_array<FieldT> &Ptable,
  879. bool Ptable_set_constraints,
  880. bool Ptable_fill_values) :
  881. gadget<FieldT>(pb, "ec_scalarmul_gadget"),
  882. outx(outx), outy(outy), s(s),
  883. Px(Px), Py(Py), Ptable(Ptable),
  884. Ptable_set_constraints(Ptable_set_constraints),
  885. Ptable_fill_values(Ptable_fill_values)
  886. {
  887. // Allocate variables to protoboard
  888. // The strings (like "x") are only for debugging purposes
  889. size_t numbits = FieldT::num_bits;
  890. svec.allocate(this->pb, numbits, "svec");
  891. packers.emplace_back(this->pb, svec, s);
  892. vecgadget.emplace_back(this->pb, outx, outy, svec,
  893. Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values);
  894. }
  895. void generate_r1cs_constraints()
  896. {
  897. packers[0].generate_r1cs_constraints(true);
  898. vecgadget[0].generate_r1cs_constraints();
  899. }
  900. void generate_r1cs_witness()
  901. {
  902. packers[0].generate_r1cs_witness_from_packed();
  903. vecgadget[0].generate_r1cs_witness();
  904. }
  905. // Compute the addition table. The addition table is a variable array
  906. // of length 2*numbits such that Ptable[2*i] and Ptable[2*i+1] are the
  907. // (x,y) coordinates of 2^i * P + C.
  908. static void compute_Ptable(protoboard<FieldT> &pb,
  909. const pb_variable_array<FieldT> &Ptable,
  910. const pb_variable<FieldT> &Px,
  911. const pb_variable<FieldT> &Py)
  912. {
  913. const FieldT Cx(2);
  914. const FieldT Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331");
  915. assert(Ptable.size() % 2 == 0);
  916. size_t numbits = Ptable.size() / 2;
  917. FieldT twoiPx = pb.val(Px);
  918. FieldT twoiPy = pb.val(Py);
  919. for (size_t i = 0; i < numbits; ++i) {
  920. // Invariant: (twoiPx, twoiPy) = 2^i * P
  921. // Compute 2^i * P + C
  922. FieldT twoiPCx, twoiPCy;
  923. ec_add_points(twoiPCx, twoiPCy, twoiPx, twoiPy, Cx, Cy);
  924. pb.val(Ptable[2*i]) = twoiPCx;
  925. pb.val(Ptable[2*i+1]) = twoiPCy;
  926. // Compute 2^{i+1} * P
  927. FieldT twoi1Px, twoi1Py;
  928. ec_double_point(twoi1Px, twoi1Py, twoiPx, twoiPy);
  929. twoiPx = twoi1Px;
  930. twoiPy = twoi1Py;
  931. }
  932. }
  933. };
  934. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  935. template<typename FieldT>
  936. class ec_pedersen_gadget : public gadget<FieldT> {
  937. private:
  938. pb_variable<FieldT> accinx, acciny, accmidx, accmidy, accoutx, accouty;
  939. std::vector<ec_constant_scalarmul_accum_gadget<FieldT> > mulgadgets;
  940. std::vector<ec_constant_add_gadget<FieldT> > addgadget;
  941. const FieldT Gx, Gy, Hx, Hy, Ax, Ay;
  942. public:
  943. const pb_variable<FieldT> outx, outy, a, b;
  944. ec_pedersen_gadget(protoboard<FieldT> &pb,
  945. const pb_variable<FieldT> &outx,
  946. const pb_variable<FieldT> &outy,
  947. const pb_variable<FieldT> &a,
  948. const pb_variable<FieldT> &b) :
  949. gadget<FieldT>(pb, "ec_pedersen_gadget"),
  950. outx(outx), outy(outy), a(a), b(b),
  951. // Precomputed coordinates of G, H, and A
  952. Gx(0),
  953. Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  954. Hx(1),
  955. Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279"),
  956. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  957. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302")
  958. {
  959. // Allocate variables to protoboard
  960. // The strings (like "x") are only for debugging purposes
  961. accinx.allocate(this->pb, "accinx");
  962. acciny.allocate(this->pb, "acciny");
  963. accmidx.allocate(this->pb, "accmidx");
  964. accmidy.allocate(this->pb, "accmidy");
  965. accoutx.allocate(this->pb, "accoutx");
  966. accouty.allocate(this->pb, "accouty");
  967. // Initialize the accumulator
  968. FieldT AXSx = Ax;
  969. FieldT AXSy = Ay;
  970. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  971. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  972. // Initialize the gadgets
  973. mulgadgets.emplace_back(this->pb, accmidx, accmidy, accinx, acciny, a, Gx, Gy, AXSx, AXSy);
  974. mulgadgets.emplace_back(this->pb, accoutx, accouty, accmidx, accmidy, b, Hx, Hy, AXSx, AXSy);
  975. // Subtract the accumulator excess to get the result
  976. addgadget.emplace_back(this->pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  977. }
  978. void generate_r1cs_constraints()
  979. {
  980. this->pb.val(accinx) = Ax;
  981. this->pb.val(acciny) = Ay;
  982. mulgadgets[0].generate_r1cs_constraints();
  983. mulgadgets[1].generate_r1cs_constraints();
  984. addgadget[0].generate_r1cs_constraints();
  985. }
  986. void generate_r1cs_witness()
  987. {
  988. mulgadgets[0].generate_r1cs_witness();
  989. mulgadgets[1].generate_r1cs_witness();
  990. addgadget[0].generate_r1cs_witness();
  991. }
  992. };