ecgadget.hpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  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 variable 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_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 pb_variable<FieldT> P0x, P0y, P1x, P1y;
  213. ec_2_add_gadget(protoboard<FieldT> &pb,
  214. const pb_variable<FieldT> &outx,
  215. const pb_variable<FieldT> &outy,
  216. const pb_linear_combination<FieldT> &inx,
  217. const pb_linear_combination<FieldT> &iny,
  218. const pb_variable<FieldT> &which,
  219. const pb_variable<FieldT> &P0x,
  220. const pb_variable<FieldT> &P0y,
  221. const pb_variable<FieldT> &P1x,
  222. const pb_variable<FieldT> &P1y) :
  223. gadget<FieldT>(pb, "ec_2_add_gadget"),
  224. outx(outx), outy(outy), inx(inx), iny(iny), which(which),
  225. P0x(P0x), P0y(P0y), P1x(P1x), P1y(P1y)
  226. {
  227. // Allocate variables to protoboard
  228. addx.allocate(this->pb, "addx");
  229. addy.allocate(this->pb, "addy");
  230. adder.emplace_back(this->pb, outx, outy, inx, iny, addx, addy);
  231. }
  232. void generate_r1cs_constraints()
  233. {
  234. // Set (addx,addy) = which ? (P0x, P0y) : (P1x, P1y)
  235. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1x - P0x, which, addx - P0x));
  236. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(P1y - P0y, which, addy - P0y));
  237. adder[0].generate_r1cs_constraints();
  238. }
  239. void generate_r1cs_witness()
  240. {
  241. this->pb.val(addx) = this->pb.val(which) ? this->pb.val(P1x) : this->pb.val(P0x);
  242. this->pb.val(addy) = this->pb.val(which) ? this->pb.val(P1y) : this->pb.val(P0y);
  243. adder[0].generate_r1cs_witness();
  244. }
  245. };
  246. #if 0
  247. // Add nothing, or one of the constant EC points P1, P2, or P3 to the EC
  248. // point (inx,iny) to yield (outx,outy). The input point must not be
  249. // the point at infinity. The two input bits add1 and add2 control what
  250. // is added. Typically, P3 will equal P1+P2, in which case this gadget
  251. // does two conditional constant adds simultaneously in just 6 constraints.
  252. template<typename FieldT>
  253. class ec_add_P123_gadget : public gadget<FieldT> {
  254. private:
  255. pb_variable<FieldT> lambda, sumx, sumy, move;
  256. public:
  257. const pb_variable<FieldT> outx, outy;
  258. const pb_linear_combination<FieldT> inx, iny;
  259. const pb_variable<FieldT> add1, add2;
  260. const FieldT P1x, P1y, P2x, P2y, P3x, P3y;
  261. ec_add_P123_gadget(protoboard<FieldT> &pb,
  262. const pb_variable<FieldT> &outx,
  263. const pb_variable<FieldT> &outy,
  264. const pb_linear_combination<FieldT> &inx,
  265. const pb_linear_combination<FieldT> &iny,
  266. const pb_variable<FieldT> &add1,
  267. const pb_variable<FieldT> &add2,
  268. const FieldT &P1x, const FieldT &P1y,
  269. const FieldT &P2x, const FieldT &P2y,
  270. const FieldT &P3x, const FieldT &P3y) :
  271. gadget<FieldT>(pb, "ec_add_P123_gadget"),
  272. outx(outx), outy(outy), inx(inx), iny(iny), add1(add1), add2(add2),
  273. P1x(P1x), P1y(P1y), P2x(P2x), P2y(P2y), P3x(P3x), P3y(P3y)
  274. {
  275. // Allocate variables to protoboard
  276. // The strings (like "x") are only for debugging purposes
  277. lambda.allocate(this->pb, "lambda");
  278. sumx.allocate(this->pb, "sumx");
  279. sumy.allocate(this->pb, "sumy");
  280. move.allocate(this->pb, "move");
  281. }
  282. void generate_r1cs_constraints()
  283. {
  284. // Strategy: if add1 = add2 = 0, we compute some nonsense but throw
  285. // it away later. Otherwise, the coordinates of the point to add
  286. // are a _linear_ function of add1 and add2 (since P1, P2, and P3
  287. // are public constants)
  288. // In particular, the point to add is ( (P3x - P2x) * add1 + (P3x -
  289. // P1x) * add2 + (P1x + P2x - P3x), (P3y - P2y) * add1 + (P3y - P1y) *
  290. // add2 + (P1y + P2y - P3y))
  291. // (addx - inx) * lambda = addy - iny
  292. 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));
  293. // sumx = lambda^2 - (addx + inx)
  294. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, sumx + (P3x - P2x) * add1 + (P3x - P1x) * add2 + (P1x + P2x - P3x) + inx));
  295. // sumy = lambda * (inx - sumx) - iny
  296. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - sumx, sumy + iny));
  297. // Now we want to conditionally move the sum. We want that
  298. // outx = (add1 || add2) ? sumx : inx
  299. // outy = (add1 || add2) ? sumy : iny
  300. // so we compute move = add1 || add2, and then
  301. // outx = inx + (sumx - inx) * move
  302. // outy = iny + (sumy - iny) * move
  303. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1 - add1, 1 - add2, 1 - move));
  304. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, move, outx - inx));
  305. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, move, outy - iny));
  306. }
  307. void generate_r1cs_witness()
  308. {
  309. FieldT addxval = (P3x - P2x) * this->pb.val(add1) + (P3x - P1x) * this->pb.val(add2) + (P1x + P2x - P3x);
  310. FieldT addyval = (P3y - P2y) * this->pb.val(add1) + (P3y - P1y) * this->pb.val(add2) + (P1y + P2y - P3y);
  311. this->pb.val(lambda) = (addyval - this->pb.lc_val(iny)) * (addxval - this->pb.lc_val(inx)).inverse();
  312. this->pb.val(sumx) = this->pb.val(lambda).squared() - (addxval + this->pb.lc_val(inx));
  313. this->pb.val(sumy) = this->pb.val(lambda) * (this->pb.lc_val(inx) - this->pb.val(sumx)) - this->pb.lc_val(iny);
  314. bool a1 = this->pb.val(add1) != FieldT(0);
  315. bool a2 = this->pb.val(add2) != FieldT(0);
  316. this->pb.val(move) = a1 || a2;
  317. this->pb.val(outx) = (a1 || a2) ? this->pb.val(sumx) : this->pb.lc_val(inx);
  318. this->pb.val(outy) = (a1 || a2) ? this->pb.val(sumy) : this->pb.lc_val(iny);
  319. }
  320. };
  321. #endif
  322. // Compute A + s*P as (outx, outy) for an accumulator A, a given
  323. // constant point P, and s given as a bit vector. The _caller_ is
  324. // responsible for proving that the elements of svec are bits. The
  325. // (constant) accumulator excess (AXS) will be updated; when all the
  326. // computations are complete, AXS should be subtracted from the
  327. // accumulator A.
  328. template<typename FieldT>
  329. class ec_constant_scalarmul_vec_accum_gadget : public gadget<FieldT> {
  330. private:
  331. FieldT Cx, Cy;
  332. pb_variable_array<FieldT> accumx, accumy;
  333. std::vector<ec_2_constant_add_gadget<FieldT> > twoadders;
  334. public:
  335. const pb_variable<FieldT> outx, outy;
  336. const pb_variable<FieldT> Ax, Ay;
  337. const pb_variable_array<FieldT> svec;
  338. const FieldT Px, Py;
  339. // Strategy: We compute (as compile-time constants) (powers of 2)
  340. // times P. Based on each bit of s, we add one of the constant points
  341. // C or (2^i * P) + C to the accumulator, and regardless of s, add C
  342. // to the excess.
  343. ec_constant_scalarmul_vec_accum_gadget(protoboard<FieldT> &pb,
  344. const pb_variable<FieldT> &outx,
  345. const pb_variable<FieldT> &outy,
  346. const pb_variable<FieldT> &Ax,
  347. const pb_variable<FieldT> &Ay,
  348. const pb_variable_array<FieldT> &svec,
  349. const FieldT &Px, const FieldT &Py,
  350. FieldT &AXSx, FieldT &AXSy) :
  351. gadget<FieldT>(pb, "ec_constant_scalarmul_vec_accum_gadget"),
  352. // Precomputed coordinates of C
  353. Cx(2),
  354. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  355. outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec), Px(Px), Py(Py)
  356. {
  357. size_t numbits = svec.size();
  358. accumx.allocate(this->pb, numbits-1, "accumx");
  359. accumy.allocate(this->pb, numbits-1, "accumy");
  360. FieldT twoiPx = Px, twoiPy = Py;
  361. size_t i = 0;
  362. while(i < numbits) {
  363. // Invariant: twoiP = 2^i * P
  364. FieldT twoiPCx, twoiPCy;
  365. ec_add_points(twoiPCx, twoiPCy, twoiPx, twoiPy, Cx, Cy);
  366. twoadders.emplace_back(this->pb,
  367. (i == numbits-1 ? outx : accumx[i]),
  368. (i == numbits-1 ? outy : accumy[i]),
  369. (i == 0 ? Ax : accumx[i-1]),
  370. (i == 0 ? Ay : accumy[i-1]),
  371. svec[i], Cx, Cy, twoiPCx, twoiPCy);
  372. FieldT newtwoiPx, newtwoiPy, newAXSx, newAXSy;
  373. ec_double_point(newtwoiPx, newtwoiPy, twoiPx, twoiPy);
  374. twoiPx = newtwoiPx;
  375. twoiPy = newtwoiPy;
  376. i += 1;
  377. ec_add_points(newAXSx, newAXSy, AXSx, AXSy, Cx, Cy);
  378. AXSx = newAXSx;
  379. AXSy = newAXSy;
  380. }
  381. }
  382. void generate_r1cs_constraints()
  383. {
  384. for (auto&& gadget : twoadders) {
  385. gadget.generate_r1cs_constraints();
  386. }
  387. }
  388. void generate_r1cs_witness()
  389. {
  390. for (auto&& gadget : twoadders) {
  391. gadget.generate_r1cs_witness();
  392. }
  393. }
  394. };
  395. // Compute A + s*P as (outx, outy) for an accumulator A, a given
  396. // constant point P, and s given as a field element. The (constant)
  397. // accumulator excess (AXS) will be updated; when all the computations
  398. // are complete, AXS should be subtracted from the accumulator A.
  399. template<typename FieldT>
  400. class ec_constant_scalarmul_accum_gadget : public gadget<FieldT> {
  401. private:
  402. pb_variable_array<FieldT> svec;
  403. std::vector<packing_gadget<FieldT> > packers;
  404. std::vector<ec_constant_scalarmul_vec_accum_gadget<FieldT> > vecgadget;
  405. public:
  406. const pb_variable<FieldT> outx, outy;
  407. const pb_variable<FieldT> Ax, Ay;
  408. const pb_variable<FieldT> s;
  409. const FieldT Px, Py;
  410. ec_constant_scalarmul_accum_gadget(protoboard<FieldT> &pb,
  411. const pb_variable<FieldT> &outx,
  412. const pb_variable<FieldT> &outy,
  413. const pb_variable<FieldT> &Ax,
  414. const pb_variable<FieldT> &Ay,
  415. const pb_variable<FieldT> &s,
  416. const FieldT &Px, const FieldT &Py,
  417. FieldT &AXSx, FieldT &AXSy) :
  418. gadget<FieldT>(pb, "ec_constant_scalarmul_accum_gadget"),
  419. outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s), Px(Px), Py(Py)
  420. {
  421. // Allocate variables to protoboard
  422. // The strings (like "x") are only for debugging purposes
  423. size_t numbits = FieldT::num_bits;
  424. svec.allocate(this->pb, numbits, "svec");
  425. packers.emplace_back(this->pb, svec, s);
  426. vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec, Px, Py, AXSx, AXSy);
  427. }
  428. void generate_r1cs_constraints()
  429. {
  430. packers[0].generate_r1cs_constraints(true);
  431. vecgadget[0].generate_r1cs_constraints();
  432. }
  433. void generate_r1cs_witness()
  434. {
  435. packers[0].generate_r1cs_witness_from_packed();
  436. vecgadget[0].generate_r1cs_witness();
  437. }
  438. };
  439. // Compute s*P as (outx, outy) for a given constant point P, and s given
  440. // as a bit vector. The _caller_ is responsible for proving that the
  441. // elements of svec are bits.
  442. template<typename FieldT>
  443. class ec_constant_scalarmul_vec_gadget : public gadget<FieldT> {
  444. private:
  445. FieldT Cx, Cy, Ax, Ay, AXSx, AXSy;
  446. pb_variable<FieldT> accinx, acciny, accoutx, accouty;
  447. std::vector<ec_constant_scalarmul_vec_accum_gadget<FieldT> > scalarmuls;
  448. std::vector<ec_constant_add_gadget<FieldT> > adders;
  449. public:
  450. const pb_variable<FieldT> outx, outy;
  451. const pb_variable_array<FieldT> svec;
  452. const FieldT Px, Py;
  453. ec_constant_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  454. const pb_variable<FieldT> &outx,
  455. const pb_variable<FieldT> &outy,
  456. const pb_variable_array<FieldT> &svec,
  457. const FieldT &Px, const FieldT &Py) :
  458. gadget<FieldT>(pb, "ec_constant_scalarmul_vec_gadget"),
  459. // Precomputed coordinates of C and A
  460. Cx(2),
  461. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  462. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  463. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302"),
  464. outx(outx), outy(outy), svec(svec), Px(Px), Py(Py)
  465. {
  466. AXSx = Ax;
  467. AXSy = Ay;
  468. accinx.allocate(this->pb, "accinx");
  469. acciny.allocate(this->pb, "acciny");
  470. accoutx.allocate(this->pb, "accoutx");
  471. accouty.allocate(this->pb, "accouty");
  472. scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec, Px, Py, AXSx, AXSy);
  473. adders.emplace_back(pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  474. }
  475. void generate_r1cs_constraints()
  476. {
  477. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  478. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  479. scalarmuls[0].generate_r1cs_constraints();
  480. adders[0].generate_r1cs_constraints();
  481. }
  482. void generate_r1cs_witness()
  483. {
  484. this->pb.val(accinx) = Ax;
  485. this->pb.val(acciny) = Ay;
  486. scalarmuls[0].generate_r1cs_witness();
  487. adders[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 field element.
  492. template<typename FieldT>
  493. class ec_constant_scalarmul_gadget : public gadget<FieldT> {
  494. private:
  495. pb_variable_array<FieldT> svec;
  496. std::vector<packing_gadget<FieldT> > packers;
  497. std::vector<ec_constant_scalarmul_vec_gadget<FieldT> > vecgadget;
  498. public:
  499. const pb_variable<FieldT> outx, outy;
  500. const pb_variable<FieldT> s;
  501. const FieldT Px, Py;
  502. ec_constant_scalarmul_gadget(protoboard<FieldT> &pb,
  503. const pb_variable<FieldT> &outx,
  504. const pb_variable<FieldT> &outy,
  505. const pb_variable<FieldT> &s,
  506. const FieldT &Px, const FieldT &Py) :
  507. gadget<FieldT>(pb, "ec_constant_scalarmul_gadget"),
  508. outx(outx), outy(outy), s(s), Px(Px), Py(Py)
  509. {
  510. // Allocate variables to protoboard
  511. // The strings (like "x") are only for debugging purposes
  512. size_t numbits = FieldT::num_bits;
  513. svec.allocate(this->pb, numbits, "svec");
  514. packers.emplace_back(this->pb, svec, s);
  515. vecgadget.emplace_back(this->pb, outx, outy, svec, Px, Py);
  516. }
  517. void generate_r1cs_constraints()
  518. {
  519. packers[0].generate_r1cs_constraints(true);
  520. vecgadget[0].generate_r1cs_constraints();
  521. }
  522. void generate_r1cs_witness()
  523. {
  524. packers[0].generate_r1cs_witness_from_packed();
  525. vecgadget[0].generate_r1cs_witness();
  526. }
  527. };
  528. // Compute A + s*P as (outx, outy) for an accumulator A, a precomputed
  529. // addition table Ptable for a variable point P, and s given as a bit
  530. // vector. The _caller_ is responsible for proving that the elements of
  531. // svec are bits. The (constant) accumulator excess (AXS) will be
  532. // updated; when all the computations are complete, AXS should be
  533. // subtracted from the accumulator A. The addition table is a variable
  534. // array of length 2*numbits (where numbits is the length of svec) such
  535. // that Ptable[2*i] and Ptable[2*i+1] are the (x,y) coordinates of
  536. // 2^i * P + C.
  537. template<typename FieldT>
  538. class ec_scalarmul_vec_accum_gadget : public gadget<FieldT> {
  539. private:
  540. FieldT Cx, Cy;
  541. pb_variable_array<FieldT> accumx, accumy;
  542. std::vector<ec_2_add_gadget<FieldT> > twoadders;
  543. public:
  544. const pb_variable<FieldT> outx, outy;
  545. const pb_variable<FieldT> Ax, Ay;
  546. const pb_variable_array<FieldT> svec, Ptable;
  547. ec_scalarmul_vec_accum_gadget(protoboard<FieldT> &pb,
  548. const pb_variable<FieldT> &outx,
  549. const pb_variable<FieldT> &outy,
  550. const pb_variable<FieldT> &Ax,
  551. const pb_variable<FieldT> &Ay,
  552. const pb_variable_array<FieldT> &svec,
  553. const pb_variable_array<FieldT> &Ptable,
  554. FieldT &AXSx, FieldT &AXSy) :
  555. gadget<FieldT>(pb, "ec_scalarmul_vec_accum_gadget"),
  556. // Precomputed coordinates of C
  557. Cx(2),
  558. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  559. outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec), Ptable(Ptable)
  560. {
  561. size_t numbits = svec.size();
  562. assert(Ptable.size() == 2*numbits);
  563. accumx.allocate(this->pb, numbits-1, "accumx");
  564. accumy.allocate(this->pb, numbits-1, "accumy");
  565. for (size_t i = 0; i < numbits; ++i) {
  566. twoadders.emplace_back(this->pb,
  567. (i == numbits-1 ? outx : accumx[i]),
  568. (i == numbits-1 ? outy : accumy[i]),
  569. (i == 0 ? Ax : accumx[i-1]),
  570. (i == 0 ? Ay : accumy[i-1]),
  571. svec[i], Cx, Cy, Ptable[2*i], Ptable[2*i+1]);
  572. FieldT newAXSx, newAXSy;
  573. ec_add_points(newAXSx, newAXSy, AXSx, AXSy, Cx, Cy);
  574. AXSx = newAXSx;
  575. AXSy = newAXSy;
  576. }
  577. }
  578. void generate_r1cs_constraints()
  579. {
  580. for (auto&& gadget : twoadders) {
  581. gadget.generate_r1cs_constraints();
  582. }
  583. }
  584. void generate_r1cs_witness()
  585. {
  586. for (auto&& gadget : twoadders) {
  587. gadget.generate_r1cs_witness();
  588. }
  589. }
  590. };
  591. // Compute A + s*P as (outx, outy) for an accumulator A, a precomputed
  592. // addition table Ptable for a variable point P, and s given as a field
  593. // element. The _caller_ is responsible for proving that the elements
  594. // of svec are bits. The (constant) accumulator excess (AXS) will be
  595. // updated; when all the computations are complete, AXS should be
  596. // subtracted from the accumulator A. The addition table is a variable
  597. // array of length 2*numbits (where numbits is the length of the FieldT
  598. // size) such that Ptable[2*i] and Ptable[2*i+1] are the (x,y)
  599. // coordinates of 2^i * P + C.
  600. template<typename FieldT>
  601. class ec_scalarmul_accum_gadget : public gadget<FieldT> {
  602. private:
  603. pb_variable_array<FieldT> svec;
  604. std::vector<packing_gadget<FieldT> > packers;
  605. std::vector<ec_scalarmul_vec_accum_gadget<FieldT> > vecgadget;
  606. public:
  607. const pb_variable<FieldT> outx, outy;
  608. const pb_variable<FieldT> Ax, Ay;
  609. const pb_variable<FieldT> s;
  610. const pb_variable_array<FieldT> Ptable;
  611. ec_scalarmul_accum_gadget(protoboard<FieldT> &pb,
  612. const pb_variable<FieldT> &outx,
  613. const pb_variable<FieldT> &outy,
  614. const pb_variable<FieldT> &Ax,
  615. const pb_variable<FieldT> &Ay,
  616. const pb_variable<FieldT> &s,
  617. const pb_variable_array<FieldT> &Ptable,
  618. FieldT &AXSx, FieldT &AXSy) :
  619. gadget<FieldT>(pb, "ec_scalarmul_accum_gadget"),
  620. outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s), Ptable(Ptable)
  621. {
  622. // Allocate variables to protoboard
  623. // The strings (like "x") are only for debugging purposes
  624. size_t numbits = FieldT::num_bits;
  625. svec.allocate(this->pb, numbits, "svec");
  626. packers.emplace_back(this->pb, svec, s);
  627. vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec, Ptable, AXSx, AXSy);
  628. }
  629. void generate_r1cs_constraints()
  630. {
  631. packers[0].generate_r1cs_constraints(true);
  632. vecgadget[0].generate_r1cs_constraints();
  633. }
  634. void generate_r1cs_witness()
  635. {
  636. packers[0].generate_r1cs_witness_from_packed();
  637. vecgadget[0].generate_r1cs_witness();
  638. }
  639. };
  640. // Compute s*P as (outx, outy) for a precomputed addition table Ptable
  641. // for a variable point P, and s given as a bit vector. The _caller_ is
  642. // responsible for proving that the elements of svec are bits.
  643. // The addition table is a variable array of length 2*numbits (where
  644. // numbits is the length of svec) such that Ptable[2*i] and
  645. // Ptable[2*i+1] are the (x,y) coordinates of 2^i * P + C.
  646. template<typename FieldT>
  647. class ec_scalarmul_vec_gadget : public gadget<FieldT> {
  648. private:
  649. FieldT Cx, Cy, Ax, Ay, AXSx, AXSy;
  650. pb_variable<FieldT> accinx, acciny, accoutx, accouty;
  651. std::vector<ec_scalarmul_vec_accum_gadget<FieldT> > scalarmuls;
  652. std::vector<ec_constant_add_gadget<FieldT> > adders;
  653. public:
  654. const pb_variable<FieldT> outx, outy;
  655. const pb_variable_array<FieldT> svec;
  656. const pb_variable_array<FieldT> Ptable;
  657. ec_scalarmul_vec_gadget(protoboard<FieldT> &pb,
  658. const pb_variable<FieldT> &outx,
  659. const pb_variable<FieldT> &outy,
  660. const pb_variable_array<FieldT> &svec,
  661. const pb_variable_array<FieldT> &Ptable) :
  662. gadget<FieldT>(pb, "ec_scalarmul_vec_gadget"),
  663. // Precomputed coordinates of C and A
  664. Cx(2),
  665. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  666. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  667. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302"),
  668. outx(outx), outy(outy), svec(svec), Ptable(Ptable)
  669. {
  670. AXSx = Ax;
  671. AXSy = Ay;
  672. accinx.allocate(this->pb, "accinx");
  673. acciny.allocate(this->pb, "acciny");
  674. accoutx.allocate(this->pb, "accoutx");
  675. accouty.allocate(this->pb, "accouty");
  676. scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec, Ptable, AXSx, AXSy);
  677. adders.emplace_back(pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  678. }
  679. void generate_r1cs_constraints()
  680. {
  681. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  682. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  683. scalarmuls[0].generate_r1cs_constraints();
  684. adders[0].generate_r1cs_constraints();
  685. }
  686. void generate_r1cs_witness()
  687. {
  688. this->pb.val(accinx) = Ax;
  689. this->pb.val(acciny) = Ay;
  690. scalarmuls[0].generate_r1cs_witness();
  691. adders[0].generate_r1cs_witness();
  692. }
  693. };
  694. // Compute s*P as (outx, outy) for a precomputed addition table Ptable
  695. // for a variable point P, and s given as a field element. The addition
  696. // table is a variable array of length 2*numbits (where numbits is the
  697. // length of the FieldT size) such that Ptable[2*i] and Ptable[2*i+1]
  698. // are the (x,y) coordinates of 2^i * P + C.
  699. template<typename FieldT>
  700. class ec_scalarmul_gadget : public gadget<FieldT> {
  701. private:
  702. pb_variable_array<FieldT> svec;
  703. std::vector<packing_gadget<FieldT> > packers;
  704. std::vector<ec_constant_scalarmul_vec_gadget<FieldT> > vecgadget;
  705. public:
  706. const pb_variable<FieldT> outx, outy;
  707. const pb_variable<FieldT> s;
  708. const pb_variable_array<FieldT> Ptable;
  709. ec_scalarmul_gadget(protoboard<FieldT> &pb,
  710. const pb_variable<FieldT> &outx,
  711. const pb_variable<FieldT> &outy,
  712. const pb_variable<FieldT> &s,
  713. const pb_variable_array<FieldT> &Ptable) :
  714. gadget<FieldT>(pb, "ec_scalarmul_gadget"),
  715. outx(outx), outy(outy), s(s), Ptable(Ptable)
  716. {
  717. // Allocate variables to protoboard
  718. // The strings (like "x") are only for debugging purposes
  719. size_t numbits = FieldT::num_bits;
  720. svec.allocate(this->pb, numbits, "svec");
  721. packers.emplace_back(this->pb, svec, s);
  722. vecgadget.emplace_back(this->pb, outx, outy, svec, Ptable);
  723. }
  724. void generate_r1cs_constraints()
  725. {
  726. packers[0].generate_r1cs_constraints(true);
  727. vecgadget[0].generate_r1cs_constraints();
  728. }
  729. void generate_r1cs_witness()
  730. {
  731. packers[0].generate_r1cs_witness_from_packed();
  732. vecgadget[0].generate_r1cs_witness();
  733. }
  734. };
  735. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  736. template<typename FieldT>
  737. class ec_pedersen_gadget : public gadget<FieldT> {
  738. private:
  739. pb_variable<FieldT> accinx, acciny, accmidx, accmidy, accoutx, accouty;
  740. std::vector<ec_constant_scalarmul_accum_gadget<FieldT> > mulgadgets;
  741. std::vector<ec_constant_add_gadget<FieldT> > addgadget;
  742. const FieldT Gx, Gy, Hx, Hy, Ax, Ay;
  743. public:
  744. const pb_variable<FieldT> outx, outy, a, b;
  745. ec_pedersen_gadget(protoboard<FieldT> &pb,
  746. const pb_variable<FieldT> &outx,
  747. const pb_variable<FieldT> &outy,
  748. const pb_variable<FieldT> &a,
  749. const pb_variable<FieldT> &b) :
  750. gadget<FieldT>(pb, "ec_pedersen_gadget"),
  751. outx(outx), outy(outy), a(a), b(b),
  752. // Precomputed coordinates of G, H, and A
  753. Gx(0),
  754. Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  755. Hx(1),
  756. Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279"),
  757. Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
  758. Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302")
  759. {
  760. // Allocate variables to protoboard
  761. // The strings (like "x") are only for debugging purposes
  762. accinx.allocate(this->pb, "accinx");
  763. acciny.allocate(this->pb, "acciny");
  764. accmidx.allocate(this->pb, "accmidx");
  765. accmidy.allocate(this->pb, "accmidy");
  766. accoutx.allocate(this->pb, "accoutx");
  767. accouty.allocate(this->pb, "accouty");
  768. // Initialize the accumulator
  769. FieldT AXSx = Ax;
  770. FieldT AXSy = Ay;
  771. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
  772. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
  773. // Initialize the gadgets
  774. mulgadgets.emplace_back(this->pb, accmidx, accmidy, accinx, acciny, a, Gx, Gy, AXSx, AXSy);
  775. mulgadgets.emplace_back(this->pb, accoutx, accouty, accmidx, accmidy, b, Hx, Hy, AXSx, AXSy);
  776. // Subtract the accumulator excess to get the result
  777. addgadget.emplace_back(this->pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
  778. }
  779. void generate_r1cs_constraints()
  780. {
  781. this->pb.val(accinx) = Ax;
  782. this->pb.val(acciny) = Ay;
  783. mulgadgets[0].generate_r1cs_constraints();
  784. mulgadgets[1].generate_r1cs_constraints();
  785. addgadget[0].generate_r1cs_constraints();
  786. }
  787. void generate_r1cs_witness()
  788. {
  789. mulgadgets[0].generate_r1cs_witness();
  790. mulgadgets[1].generate_r1cs_witness();
  791. addgadget[0].generate_r1cs_witness();
  792. }
  793. };