ecgadget.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #include "libsnark/gadgetlib1/gadgets/basic_gadgets.hpp"
  2. using namespace libsnark;
  3. // Double the EC point (inx,iny) to yield (outx,outy). The input point
  4. // must not be the point at infinity.
  5. template<typename FieldT>
  6. class ec_double_gadget : public gadget<FieldT> {
  7. private:
  8. pb_variable<FieldT> lambda, inxsq;
  9. public:
  10. const pb_variable<FieldT> outx, outy, inx, iny;
  11. ec_double_gadget(protoboard<FieldT> &pb,
  12. const pb_variable<FieldT> &outx,
  13. const pb_variable<FieldT> &outy,
  14. const pb_variable<FieldT> &inx,
  15. const pb_variable<FieldT> &iny) :
  16. gadget<FieldT>(pb, "ec_double_gadget"), outx(outx), outy(outy),
  17. inx(inx), iny(iny)
  18. {
  19. // Allocate variables to protoboard
  20. // The strings (like "x") are only for debugging purposes
  21. lambda.allocate(this->pb, "lambda");
  22. inxsq.allocate(this->pb, "inxsq");
  23. }
  24. void generate_r1cs_constraints()
  25. {
  26. // inxsq = inx * inx
  27. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(inx, inx, inxsq));
  28. // 2 * iny * lambda = 3 * inxsq - 3 (a = -3 on our curve)
  29. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2 * iny, lambda, 3 * inxsq - 3));
  30. // outx = lambda^2 - 2 * inx
  31. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + 2 * inx));
  32. // outy = lambda * (inx - outx) - iny
  33. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - outx, outy + iny));
  34. }
  35. void generate_r1cs_witness()
  36. {
  37. this->pb.val(inxsq) = this->pb.val(inx) * this->pb.val(inx);
  38. this->pb.val(lambda) = (this->pb.val(inxsq) * 3 - 3) * (this->pb.val(iny) * 2).inverse();
  39. this->pb.val(outx) = this->pb.val(lambda).squared() - this->pb.val(inx) * 2;
  40. this->pb.val(outy) = this->pb.val(lambda) * (this->pb.val(inx) - this->pb.val(outx)) - this->pb.val(iny);
  41. }
  42. };
  43. // Add nothing, G, H, or G+H to the EC point (inx,iny) to yield
  44. // (outx,outy). The input point must not be the point at infinity.
  45. // The two input bits addG and addH control what is added.
  46. template<typename FieldT>
  47. class ec_add_GH_gadget : public gadget<FieldT> {
  48. private:
  49. pb_variable<FieldT> lambda, sumx, sumy, move;
  50. const FieldT Gx, Gy, Hx, Hy, GHx, GHy;
  51. public:
  52. const pb_variable<FieldT> outx, outy, inx, iny, addG, addH;
  53. ec_add_GH_gadget(protoboard<FieldT> &pb,
  54. const pb_variable<FieldT> &outx,
  55. const pb_variable<FieldT> &outy,
  56. const pb_variable<FieldT> &inx,
  57. const pb_variable<FieldT> &iny,
  58. const pb_variable<FieldT> &addG,
  59. const pb_variable<FieldT> &addH) :
  60. gadget<FieldT>(pb, "ec_add_GH_gadget"),
  61. Gx(0),
  62. Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
  63. Hx(1),
  64. Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279"),
  65. GHx("2864090850787705444524344020850508438903451433901276387624248428140647539638"),
  66. GHy("3350168998338968221269367365107720885864670493693161027931048546881356285970"),
  67. outx(outx), outy(outy), inx(inx), iny(iny), addG(addG), addH(addH)
  68. {
  69. // Allocate variables to protoboard
  70. // The strings (like "x") are only for debugging purposes
  71. lambda.allocate(this->pb, "lambda");
  72. sumx.allocate(this->pb, "sumx");
  73. sumy.allocate(this->pb, "sumy");
  74. move.allocate(this->pb, "move");
  75. }
  76. void generate_r1cs_constraints()
  77. {
  78. // Strategy: if addG = addH = 0, we compute some nonsense but throw
  79. // it away later. Otherwise, the coordinates of addG * G + addH * H
  80. // are a _linear_ function of addG and addH (since G and H are global
  81. // constants)
  82. // In particular:
  83. // G = (0, 11977228949870389393715360594190192321220966033310912010610740966317727761886)
  84. // H = (1, 21803877843449984883423225223478944275188924769286999517937427649571474907279)
  85. // G+H = (2864090850787705444524344020850508438903451433901276387624248428140647539638, 3350168998338968221269367365107720885864670493693161027931048546881356285970)
  86. // so the point to add is ( (GHx - Hx) * addG + (GHx - Gx) * addH + (Gx + Hx - GHx), (GHy - Hy) * addG + (GHy - Gy) * addH + (Gy + Hy - GHy))
  87. // = (2864090850787705444524344020850508438903451433901276387624248428140647539637 * addG + 2864090850787705444524344020850508438903451433901276387624248428140647539638 * addH - 2864090850787705444524344020850508438903451433901276387624248428140647539637, -18453708845111016662153857858371223389324254275593838490006379102690118621309 * addG - 8627059951531421172445993229082471435356295539617750982679692419436371475916 * addH + 30430937794981406055869218452561415710545220308904750500617120069007846383195)
  88. // (addx - inx) * lambda = addy - iny
  89. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>((GHx - Hx) * addG + (GHx - Gx) * addH + (Gx + Hx - GHx) - inx, lambda, (GHy - Hy) * addG + (GHy - Gy) * addH + (Gy + Hy - GHy) - iny));
  90. // sumx = lambda^2 - (addx + inx)
  91. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, sumx + (GHx - Hx) * addG + (GHx - Gx) * addH + (Gx + Hx - GHx) + inx));
  92. // sumy = lambda * (inx - sumx) - iny
  93. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, inx - sumx, sumy + iny));
  94. // Now we want to conditionally move the sum. We want that
  95. // outx = (addG || addH) ? sumx : inx
  96. // outy = (addG || addH) ? sumy : iny
  97. // so we compute move = addG || addH, and then
  98. // outx = inx + (sumx - inx) * move
  99. // outy = iny + (sumy - iny) * move
  100. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1 - addG, 1 - addH, 1 - move));
  101. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, move, outx - inx));
  102. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, move, outy - iny));
  103. }
  104. void generate_r1cs_witness()
  105. {
  106. FieldT addxval = (GHx - Hx) * this->pb.val(addG) + (GHx - Gx) * this->pb.val(addH) + (Gx + Hx - GHx);
  107. FieldT addyval = (GHy - Hy) * this->pb.val(addG) + (GHy - Gy) * this->pb.val(addH) + (Gy + Hy - GHy);
  108. this->pb.val(lambda) = (addyval - this->pb.val(iny)) * (addxval - this->pb.val(inx)).inverse();
  109. this->pb.val(sumx) = this->pb.val(lambda).squared() - (addxval + this->pb.val(inx));
  110. this->pb.val(sumy) = this->pb.val(lambda) * (this->pb.val(inx) - this->pb.val(sumx)) - this->pb.val(iny);
  111. bool aG = this->pb.val(addG) != FieldT(0);
  112. bool aH = this->pb.val(addH) != FieldT(0);
  113. this->pb.val(move) = aG || aH;
  114. this->pb.val(outx) = this->pb.val(aG || aH ? sumx : inx);
  115. this->pb.val(outy) = this->pb.val(aG || aH ? sumy : iny);
  116. }
  117. };
  118. // Compute a*G + b*H as (outx, outy), given a and b as bit vectors.
  119. // a and b must be of the same size. The _caller_ is responsible for
  120. // proving that the elements of avec and bvec are bits.
  121. template<typename FieldT>
  122. class ec_pedersen_vec_gadget : public gadget<FieldT> {
  123. private:
  124. pb_variable_array<FieldT> accumx, accumy, daccumx, daccumy;
  125. pb_variable<FieldT> lambda;
  126. std::vector<ec_double_gadget<FieldT> > doublers;
  127. std::vector<ec_add_GH_gadget<FieldT> > adders;
  128. const FieldT Cx, Cy, CGx, CGy, CHx, CHy, CGHx, CGHy;
  129. FieldT m2nCx, m2nCy;
  130. // Compute m2nC = -2^n * C. We can precomute the answers for values
  131. // of n we expect to get.
  132. void compute_m2nC(FieldT &m2nCx, FieldT &m2nCy, size_t n)
  133. {
  134. if (n == 253) {
  135. m2nCx = FieldT("2630025903576807331238993847875694711243784786568881628418508626984487096258");
  136. m2nCy = FieldT("17628834417659968531880949658739649785248429713924280788649629869316127047701");
  137. } else {
  138. // Invariant: m2iC = -2^i * C
  139. FieldT m2iCx = Cx;
  140. FieldT m2iCy = -Cy;
  141. size_t i = 0;
  142. while (i < n) {
  143. FieldT xsq = m2iCx.squared();
  144. FieldT lambda = (xsq * 3 - 3) * (m2iCy * 2).inverse();
  145. FieldT m2iCxo = lambda.squared() - m2iCx * 2;
  146. FieldT m2iCyo = lambda * (m2iCx - m2iCxo) - m2iCy;
  147. m2iCx = m2iCxo;
  148. m2iCy = m2iCyo;
  149. ++i;
  150. }
  151. m2nCx = m2iCx;
  152. m2nCy = m2iCy;
  153. }
  154. }
  155. public:
  156. const pb_variable<FieldT> outx, outy;
  157. const pb_variable_array<FieldT> avec, bvec;
  158. ec_pedersen_vec_gadget(protoboard<FieldT> &pb,
  159. const pb_variable<FieldT> &outx,
  160. const pb_variable<FieldT> &outy,
  161. const pb_variable_array<FieldT> &avec,
  162. const pb_variable_array<FieldT> &bvec) :
  163. gadget<FieldT>(pb, "ec_pedersen_vec_gadget"),
  164. Cx(2),
  165. Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
  166. CGx("4998993376791159436553350546778310121346937620672073819457843493128326049156"),
  167. CGy("11119675827304465476900978353730540420130346377889406728458325551400357147144"),
  168. CHx("19614539896004018833724771305328960655474424364705508053472946746883341111010"),
  169. CHy("9853241351900213537247225242092949438866383394579783148395572971112906592855"),
  170. CGHx("10755582242294898568680134375159803731902153202607833320871336755950640390928"),
  171. CGHy("3110667473759844579409644567672992116704859238881299917617768683686288881761"),
  172. outx(outx), outy(outy), avec(avec), bvec(bvec)
  173. {
  174. // Allocate variables to protoboard
  175. // The strings (like "x") are only for debugging purposes
  176. size_t numbits = avec.size();
  177. accumx.allocate(this->pb, numbits, "accumx");
  178. accumy.allocate(this->pb, numbits, "accumy");
  179. daccumx.allocate(this->pb, numbits-1, "daccumx");
  180. daccumy.allocate(this->pb, numbits-1, "daccumy");
  181. lambda.allocate(this->pb, "lambda");
  182. for (size_t i = 0; i < numbits-1; ++i) {
  183. doublers.emplace_back(this->pb, daccumx[i], daccumy[i],
  184. accumx[i], accumy[i]);
  185. adders.emplace_back(this->pb, accumx[i+1], accumy[i+1],
  186. daccumx[i], daccumy[i], avec[numbits-2-i], bvec[numbits-2-i]);
  187. }
  188. compute_m2nC(m2nCx, m2nCy, numbits-1);
  189. }
  190. void generate_r1cs_constraints()
  191. {
  192. // Strategy: We do a basic double-and-add, using the variant of a
  193. // single double and an add of one of (O, G, H, G+H) at each step.
  194. // *However*, there's a twist. Our doubling and adding routines
  195. // don't handle the point at infinity O as the point to add to or to
  196. // double. (Adding O as one of the four options above is fine.)
  197. // Normally, the double-and-add algorithm starts with an accumulator
  198. // of O, and that won't work for us. So instead, we start the
  199. // accumulator at a different base point C, whose discrete log
  200. // with respect to the (G,H) basis is unknown. Then we'll end up
  201. // with an extra 2^n * C in the accumulator (where n is the number
  202. // of doublings we do), so at the end, we'll add the (constant!)
  203. // point -2^n * C to get the final result. That the discrete log of
  204. // C is unknown means we won't encounter O along the way, either (if
  205. // we did, we could compute the DL of C in the (G,H) basis).
  206. // For the first bit, we just precompute C, C+G, C+H, C+G+H and use
  207. // the top bit of a and b to choose which one to start with.
  208. // accumx[0] = Cx + (CGx - Cx) * avec[numbits-1] + (CHx - Cx) *
  209. // bvec[numbits-1] + (CGHx - CGx - CHx + Cx) *
  210. // avec[numbits-1]*bvec[numbits-1] (and similarly for y)
  211. // We could possibly optimize this later by computing the a*b
  212. // product once, but then we'd have to pass a large linear
  213. // combination to a gadget, which it probably doesn't like? It
  214. // would save just one constraint, so probably not so important?
  215. size_t numbits = avec.size();
  216. 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])));
  217. 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])));
  218. // After that, for each remaining bit of a and b, we use the
  219. // ec_double_gadget and the ec_add_GH_gadget to accumulate
  220. // the answer.
  221. for (size_t i = 0; i < numbits-1; ++i) {
  222. doublers[i].generate_r1cs_constraints();
  223. adders[i].generate_r1cs_constraints();
  224. }
  225. // Finally, we add the constant point -2^n * C to the result, where
  226. // n = numbits-1
  227. // (m2nCx - accumx[numbits-1]) * lambda = m2nCy - accumy[numbits-1]
  228. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(m2nCx - accumx[numbits-1], lambda, m2nCy - accumy[numbits-1]));
  229. // outx = lambda^2 - (m2nCx + accumx[numbits-1])
  230. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, lambda, outx + m2nCx + accumx[numbits-1]));
  231. // outy = lambda * (accumx[numbits-1] - outx) - accumy[numbits-1]
  232. this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lambda, accumx[numbits-1] - outx, outy + accumy[numbits-1]));
  233. }
  234. void generate_r1cs_witness()
  235. {
  236. size_t numbits = avec.size();
  237. 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]);
  238. 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]);
  239. for (size_t i = 0; i < numbits-1; ++i) {
  240. doublers[i].generate_r1cs_witness();
  241. adders[i].generate_r1cs_witness();
  242. }
  243. this->pb.val(lambda) = (m2nCy - this->pb.val(accumy[numbits-1])) *
  244. (m2nCx - this->pb.val(accumx[numbits-1])).inverse();
  245. this->pb.val(outx) = this->pb.val(lambda).squared() -
  246. (m2nCx + this->pb.val(accumx[numbits-1]));
  247. this->pb.val(outy) = this->pb.val(lambda) *
  248. (this->pb.val(accumx[numbits-1]) - this->pb.val(outx))
  249. - this->pb.val(accumy[numbits-1]);
  250. }
  251. };
  252. // Compute a*G + b*H as (outx, outy), given a and b as field elements.
  253. template<typename FieldT>
  254. class ec_pedersen_gadget : public gadget<FieldT> {
  255. private:
  256. pb_variable_array<FieldT> avec, bvec;
  257. std::vector<packing_gadget<FieldT> > packers;
  258. std::vector<ec_pedersen_vec_gadget<FieldT> > vecgadget;
  259. public:
  260. const pb_variable<FieldT> outx, outy, a, b;
  261. ec_pedersen_gadget(protoboard<FieldT> &pb,
  262. const pb_variable<FieldT> &outx,
  263. const pb_variable<FieldT> &outy,
  264. const pb_variable<FieldT> &a,
  265. const pb_variable<FieldT> &b) :
  266. gadget<FieldT>(pb, "ec_pedersen_gadget"),
  267. outx(outx), outy(outy), a(a), b(b)
  268. {
  269. // Allocate variables to protoboard
  270. // The strings (like "x") are only for debugging purposes
  271. FieldT minus1(-1);
  272. size_t numbits = FieldT::num_bits;
  273. avec.allocate(this->pb, numbits, "a");
  274. bvec.allocate(this->pb, numbits, "b");
  275. packers.emplace_back(this->pb, avec, a);
  276. packers.emplace_back(this->pb, bvec, b);
  277. vecgadget.emplace_back(this->pb, outx, outy, avec, bvec);
  278. }
  279. void generate_r1cs_constraints()
  280. {
  281. packers[0].generate_r1cs_constraints(true);
  282. packers[1].generate_r1cs_constraints(true);
  283. vecgadget[0].generate_r1cs_constraints();
  284. }
  285. void generate_r1cs_witness()
  286. {
  287. packers[0].generate_r1cs_witness_from_packed();
  288. packers[1].generate_r1cs_witness_from_packed();
  289. vecgadget[0].generate_r1cs_witness();
  290. }
  291. };