You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 lines
17 KiB

4 years ago
  1. // Copyright (c) 2018 Jordi Baylina
  2. // License: LGPL-3.0+
  3. //
  4. const Contract = require("./evmasm");
  5. const G2 = require("snarkjs").bn128.G2;
  6. const bigInt = require("snarkjs").bigInt;
  7. function toHex256(a) {
  8. let S = a.toString(16);
  9. while (S.length < 64) S="0"+S;
  10. return "0x" + S;
  11. }
  12. function createCode(P, w) {
  13. const C = new Contract();
  14. const NPOINTS = 1 << (w-1);
  15. const VAR_POS = C.allocMem(32);
  16. const VAR_POINTS = C.allocMem( (NPOINTS)*4*32);
  17. const savedP = C.allocMem(32);
  18. const savedZ3 = C.allocMem(32);
  19. // Check selector
  20. C.push("0x0100000000000000000000000000000000000000000000000000000000");
  21. C.push(0);
  22. C.calldataload();
  23. C.div();
  24. C.push("b65c7c74"); // mulexp(uint256)
  25. C.eq();
  26. C.jmpi("start");
  27. C.invalid();
  28. C.label("start");
  29. storeVals();
  30. C.push( Math.floor(255/w)*w ); // pos := 255
  31. C.push(VAR_POS);
  32. C.mstore();
  33. C.push("21888242871839275222246405745257275088696311157297823662689037894645226208583");
  34. C.push(0);
  35. C.push(0);
  36. C.push(0);
  37. C.push(0);
  38. C.push(0);
  39. C.push(0);
  40. C.label("begin_loop"); // ACC_X ACC_Y ACC_Z q
  41. C.internalCall("double");
  42. // g = (e>>pos)&MASK
  43. C.push(4);
  44. C.calldataload(); // e ACC_X ACC_Y ACC_Z q
  45. C.push(VAR_POS);
  46. C.mload(); // pos e ACC_X ACC_Y ACC_Z q
  47. C.shr();
  48. C.push(NPOINTS-1);
  49. C.and(); // g ACC_X ACC_Y ACC_Z q
  50. C.internalCall("add"); // acc_x acc_y acc_z
  51. C.push(VAR_POS);
  52. C.mload(); // pos acc_x acc_y acc_z
  53. C.dup(0); // pos pos acc_x acc_y acc_z
  54. C.push(0); // 0 pos pos acc_x acc_y acc_z
  55. C.eq(); // eq pos acc_x acc_y acc_z
  56. C.jmpi("after_loop"); // pos acc_x acc_y acc_z
  57. C.push(w); // 5 pos acc_x acc_y acc_z
  58. C.sub(); // pos acc_x acc_y acc_z
  59. C.push(VAR_POS);
  60. C.mstore(); // acc_x acc_y acc_z
  61. C.jmp("begin_loop");
  62. C.label("after_loop"); // pos acc_x acc_y acc_z
  63. C.pop(); // acc_x acc_y acc_z
  64. C.internalCall("affine"); // acc_x acc_y
  65. C.push(0);
  66. C.mstore();
  67. C.push(20);
  68. C.mstore();
  69. C.push(40);
  70. C.mstore();
  71. C.push(60);
  72. C.mstore();
  73. C.push("0x80");
  74. C.push("0x00");
  75. C.return();
  76. double();
  77. addPoint();
  78. affine();
  79. return C.createTxData();
  80. function add(a,b,q) {
  81. C.dup(q);
  82. C.dup(a+1 + 1);
  83. C.dup(b+1 + 2);
  84. C.addmod();
  85. C.dup(q + 1);
  86. C.dup(a + 2);
  87. C.dup(b + 3);
  88. C.addmod();
  89. }
  90. function sub(a,b,q) {
  91. C.dup(q); // q
  92. C.dup(a+1 + 1); // ai q
  93. C.dub(q + 2); // q ai q
  94. C.dup(b+1 + 3); // bi q ai q
  95. C.sub(); // -bi ai q
  96. C.addmod(); // ci
  97. C.dup(q + 1); // q ci
  98. C.dup(a + 2); // ar q ci
  99. C.dup(q + 3); // q ar q ci
  100. C.dup(b + 4); // br q ar q ci
  101. C.sub(); // -br ar q ci
  102. C.addmod(); // cr ci
  103. }
  104. function mul(a, b, q) {
  105. C.dup(q); // q
  106. C.dup(q + 1); // q q
  107. C.dup(a + 2); // ar q q
  108. C.dup(b+1 + 3); // bi ar q q
  109. C.mulmod(); // ci1 q
  110. C.dup(q + 2); // q ci1 q
  111. C.dup(a+1 + 3); // ai q ci1 q
  112. C.dup(b + 4); // ar ai q ci1 q
  113. C.mulmod(); // ci2 ci1 q
  114. C.addmod(); // ci
  115. C.dup(q + 1); // q ci
  116. C.dup(q + 2); // q q ci
  117. C.dup(q + 3); // q q q ci
  118. C.dup(a+1 + 4); // ai q q ci
  119. C.dup(b+1 + 5); // bi ai q q ci
  120. C.mulmod(); // cr2 q q ci
  121. C.sub(); // -cr2 q ci
  122. C.dup(q + 3); // q -cr2 q ci
  123. C.dup(a + 4); // ar q -cr2 q ci
  124. C.dup(b + 5); // br ar q -cr2 q ci
  125. C.mulmod(); // cr1 -cr2 q ci
  126. C.addmod(); // cr ci
  127. }
  128. function square(a, q) {
  129. C.dup(q); // q
  130. C.dup(q + 1); // q q
  131. C.dup(a + 2); // ar q q
  132. C.dup(a+1 + 3); // ai ar q q
  133. C.mulmod(); // arai q
  134. C.dup(0); // arai arai q
  135. C.addmod(); // ci
  136. C.dup(q + 1); // q ci
  137. C.dup(q + 2); // q q ci
  138. C.dup(q + 3); // q q q ci
  139. C.dup(a+1 + 4); // ai q q ci
  140. C.dup(a+1 + 5); // ai ai q q ci
  141. C.mulmod(); // cr2 q q ci
  142. C.sub(); // -cr2 q ci
  143. C.dup(q + 3); // q -cr2 q ci
  144. C.dup(a + 4); // ar q -cr2 q ci
  145. C.dup(a + 5); // br ar q -cr2 q ci
  146. C.mulmod(); // cr1 -cr2 q ci
  147. C.addmod(); // cr ci
  148. }
  149. function add1(a, q) {
  150. C.dup(a+1); // im
  151. C.dup(1 + q); // q
  152. C.dup(2 + a); // re q im
  153. C.push(1); // 1 re q im
  154. C.addmod();
  155. }
  156. function cmp(a, b) {
  157. C.dup(a);
  158. C.dup(b);
  159. C.eq();
  160. C.dup(a+1);
  161. C.dup(a+1);
  162. C.and();
  163. }
  164. function rm(a) {
  165. if (a>0) C.swap(a);
  166. C.pop();
  167. if (a>0) C.swap(a);
  168. C.pop();
  169. }
  170. function double() {
  171. C.label("double"); // xR, xI, yR, yI, zR zI, q
  172. C.dup(4);
  173. C.iszero();
  174. C.dup(6);
  175. C.iszero();
  176. C.and();
  177. C.jumpi("enddouble"); // X Y Z q
  178. // Z3 = 2*Y*Z // Remove Z
  179. mul(2, 4, 6); // yz X Y Z q
  180. rm(6); // X Y yz q
  181. add(4, 4, 6); // 2yz X Y yz q
  182. rm(6); // X Y Z3 q
  183. // A = X^2
  184. square(0,6); // A X Y Z3 q
  185. // B = Y^2 // Remove Y
  186. square(4,8); // B A X Y Z3 q
  187. rm(6); // A X B Z3 q
  188. // C = B^2
  189. square(4,8); // C A X B Z3 q
  190. // D = (X+B)^2-A-C // Remove X, Remove B
  191. add(4,6, 10); // X+B C A X B Z3 q
  192. rm(6); // C A X+B B Z3 q
  193. rm(6); // A X+B C Z3 q
  194. square(2,8); // (X+B)^2 A X+B C Z3 q
  195. rm(4); // A (X+B)^2 C Z3 q
  196. sub(2, 0, 8); // (X+B)^2-A A (X+B)^2 C Z3 q
  197. rm(4); // A (X+B)^2-A C Z3 q
  198. sub(2, 4, 8); // (X+B)^2-A-C A (X+B)^2-A C Z3 q
  199. rm(4); // A D C Z3 q
  200. // D = D+D
  201. add(2,2, 8); // D+D A D C Z3 q
  202. rm(4); // A D C Z3 q
  203. // E=A+A+A
  204. add(0, 0, 8); // 2A A D C Z3 q
  205. add(0, 2, 10); // 3A 2A A D C Z3 q
  206. rm(4); // 2A 3A D C Z3 q
  207. rm(0); // E D C Z3 q
  208. // F=E^2
  209. square(0, 8); // F E D C Z3 q
  210. // X3= F - 2*D // Remove F
  211. add(4, 4, 10); // 2D F E D C Z3 q
  212. sub(2, 0, 12); // F-2D 2D F E D C Z3 q
  213. rm(4); // 2D X3 E D C Z3 q
  214. rm(0); // X3 E D C Z3 q
  215. // Y3 = E * (D - X3) - 8 * C // Remove D C E
  216. sub(4, 0, 10); // D-X3 X3 E D C Z3 q
  217. rm(6); // X3 E D-X3 C Z3 q
  218. mul(2, 4, 10); // E*(D-X3) X3 E D-X3 C Z3 q
  219. rm(6); // X3 E E*(D-X3) C Z3 q
  220. rm(2); // X3 E*(D-X3) C Z3 q
  221. add(4, 4, 8); // 2C X3 E*(D-X3) C Z3 q
  222. rm(6); // X3 E*(D-X3) 2C Z3 q
  223. add(4, 4, 8); // 4C X3 E*(D-X3) 2C Z3 q
  224. rm(6); // X3 E*(D-X3) 4C Z3 q
  225. add(4, 4, 8); // 8C X3 E*(D-X3) 4C Z3 q
  226. rm(6); // X3 E*(D-X3) 8C Z3 q
  227. sub(2, 4, 8); // E*(D-X3)-8C X3 E*(D-X3) 8C Z3 q
  228. rm(6); // X3 E*(D-X3) Y3 Z3 q
  229. rm(2); // X3 Y3 Z3 q
  230. C.label("enddouble");
  231. C.returnCall();
  232. }
  233. function addPoint() { // p, xR, xI, yR, yI, zR zI, q
  234. C.dup(0); // p p X2 Y2 Z2 q
  235. C.push(savedP);
  236. C.mstore();
  237. C.iszero(); // X2 Y2 Z2 q
  238. C.jumpi("endpadd");
  239. C.dup(4);
  240. C.iszero();
  241. C.dup(6);
  242. C.iszero();
  243. C.and();
  244. C.jumpi("returnP"); // X2 Y2 Z2 q
  245. // lastZ3 = (Z2+1)^2 - Z2^2
  246. add1(4, 6); // Z2+1 X2 Y2 Z2 q
  247. square(0, 8); // (Z2+1)^2 Z2+1 X2 Y2 Z2 q
  248. rm(2); // (Z2+1)^2 X2 Y2 Z2 q
  249. square(6, 8); // Z2^2 (Z2+1)^2 X2 Y2 Z2 q
  250. sub(2, 0, 10); // (Z2+1)^2-Z2^2 Z2^2 (Z2+1)^2 X2 Y2 Z2 q
  251. saveZ3(); // Z2^2 (Z2+1)^2 X2 Y2 Z2 q
  252. rm(2); // Z2^2 X2 Y2 Z2 q
  253. // U2 = X2
  254. // S2 = Y2 // Z2^2 U2 S2 Z2 q
  255. // U1 = X1 * Z2^2
  256. loadX(); // X1 Z2^2 U2 S2 Z2 q
  257. mul(0, 2, 10); // X1*Z2^2 X1 Z2^2 U2 S2 Z2 q
  258. rm(2); // X1*Z2^2 Z2^2 U2 S2 Z2 q
  259. mul(2, 8, 10); // Z2^3 U1 Z2^2 U2 S2 Z2 q
  260. rm(4); // U1 Z2^3 U2 S2 Z2 q
  261. rm(8); // Z2^3 U2 S2 U1 q
  262. // S1 = Y1 * Z1^3
  263. loadY(); // Y1 Z2^3 U2 S2 U1 q
  264. mul(0, 2, 10); // S1 Y1 Z2^3 U2 S2 U1 q
  265. rm(4); // Y1 S1 U2 S2 U1 q
  266. rm(0); // S1 U2 S2 U1 q
  267. cmp(0, 4); // c1 S1 U2 S2 U1 q
  268. cmp(3, 7); // c2 c1 S1 U2 S2 U1 q
  269. C.and(); // c2&c1 S1 U2 S2 U1 q
  270. C.jumpi("double1"); // S1 U2 S2 U1 q
  271. // Returns the double
  272. // H = U2-U1 // Remove U2
  273. C.sub(4, 8, 10); // H S1 U2 S2 U1 q
  274. rm(4); // S1 H S2 U1 q
  275. // // r = 2 * (S2-S1) // Remove S2
  276. C.sub(4, 4, 8); // S1-S2 S1 H S2 U1 q
  277. rm(6); // S1 H S1-S2 U1 q
  278. C.add(4, 4, 8); // 2*(S1-S2) S1 H S1-S2 U1 q
  279. rm(6); // S1 H r U1 q
  280. // I = (2 * H)^2
  281. C.add(2, 2, 8); // 2*H S1 H r U1 q
  282. C.square(0, 10); // (2*H)^2 2*H S1 H r U1 q
  283. rm(2); // I S1 H r U1 q
  284. // V = U1 * I
  285. mul(8, 0, 10); // V I S1 H r U1 q
  286. rm(10); // I S1 H r V q
  287. // J = H * I // Remove I
  288. mul(4, 0, 10); // J I S1 H r V q
  289. rm(2); // J S1 H r V q
  290. // X3 = r^2 - J - 2 * V
  291. // S1J2 = (S1*J)*2 // Remove S1
  292. mul(2, 0, 10); // S1*J J S1 H r V q
  293. rm(4); // J S1*J H r V q
  294. add(2,2, 10); // (S1*J)*2 J S1*J H r V q
  295. rm(4); // J S1J2 H r V q
  296. // X3 = r^2 - J - 2 * V
  297. square(6, 10); // r^2 J S1J2 H r V q
  298. sub(0, 2, 12); // r^2-J r^2 J S1J2 H r V q
  299. rm(2); // r^2-J J S1J2 H r V q
  300. rm(2); // r^2-J S1J2 H r V q
  301. add(8, 8, 10); // 2*V r^2-J S1J2 H r V q
  302. sub(2, 0, 12); // r^2-J-2*V 2*V r^2-J S1J2 H r V q
  303. rm(4); // 2*V X3 S1J2 H r V q
  304. rm(0); // X3 S1J2 H r V q
  305. // Y3 = r * (V-X3)-S1J2
  306. sub(8, 0, 10); // V-X3 X3 S1J2 H r V q
  307. rm(10); // X3 S1J2 H r V-X3 q
  308. mul(6, 8, 10); // r*(V-X3) X3 S1J2 H r V-X3 q
  309. rm(8); // X3 S1J2 H r*(V-X3) V-X3 q
  310. rm(8); // S1J2 H r*(V-X3) X3 q
  311. sub(4, 0, 8); // Y3 S1J2 H r*(V-X3) X3 q
  312. rm(6); // S1J2 H Y3 X3 q
  313. rm(0); // H Y3 X3 q
  314. // Z3 = lastZ * H
  315. loadZ3(); // lastZ3 H Y3 X3 q
  316. mul(0, 2, 8); // Z3 lastZ3 H Y3 X3 q
  317. rm(4); // lastZ3 Z3 Y3 X3 q
  318. rm(0); // Z3 Y3 X3 q
  319. C.swap(1);
  320. C.swap(5);
  321. C.swap(1);
  322. C.swap(4); // X3 Y3 Z3 q
  323. // returns the point in memory
  324. C.label("returnP"); // X Y Z q
  325. rm(0);
  326. rm(0);
  327. rm(0);
  328. C.push(0);
  329. C.push(1);
  330. loadX();
  331. loadY();
  332. C.jump("endpadd");
  333. C.label("double1"); // S1 U2 S2 U1 q
  334. rm(0);
  335. rm(0);
  336. rm(0);
  337. rm(0);
  338. C.push(0);
  339. C.push(1);
  340. loadX();
  341. loadY();
  342. C.jump("double");
  343. C.label("endpadd");
  344. C.returnCall();
  345. function loadX() {
  346. C.push(savedP);
  347. C.mload(); // p
  348. C.push(32);
  349. C.mul(); // P*32
  350. C.push(VAR_POINTS+32);
  351. C.add(); // P*32+32
  352. C.dup(); // P*32+32 P*32+32
  353. C.mload(); // im P*32+32
  354. C.swap(1); // P*32+32 im
  355. C.push(0x20); // 32 P*32+32 im
  356. C.sub(); // P*32 im
  357. C.mload(); // re im
  358. }
  359. function loadY() {
  360. C.push(savedP);
  361. C.mload(); // p
  362. C.push(32);
  363. C.mul(); // P*32
  364. C.push(VAR_POINTS+32*3);
  365. C.add(); // P*32+32
  366. C.dup(); // P*32+32 P*32+32
  367. C.mload(); // im P*32+32
  368. C.swap(1); // P*32+32 im
  369. C.push(0x20); // 32 P*32+32 im
  370. C.sub(); // P*32 im
  371. C.mload(); // re im
  372. }
  373. function loadZ3() {
  374. C.push(savedZ3+32);
  375. C.mload(); // p
  376. C.push(savedZ3);
  377. C.mload();
  378. }
  379. function saveZ3() {
  380. C.push(savedZ3);
  381. C.mstore();
  382. C.push(savedZ3+32);
  383. C.mstore();
  384. }
  385. }
  386. function affine() { // X Y Z q
  387. // If Z2=0 return 0
  388. C.label("affine");
  389. C.dup(4);
  390. C.dup(5 + 1);
  391. C.or();
  392. C.jumpi("notZero"); // X Y Z q
  393. rm(0);
  394. rm(0);
  395. C.push(0);
  396. C.push(0);
  397. C.jmp("endAffine");
  398. C.label("notZero");
  399. inverse2(4,6); // Z_inv X Y Z q
  400. square(2, 8); // Z2_inv Z_inv X Y Z q
  401. mul(0, 2, 10); // Z3_inv Z2_inv Z_inv X Y Z q
  402. rm(4); // Z2_inv Z3_inv X Y Z q
  403. C.push(1);
  404. C.push(0); // 1 Z2_inv Z3_inv X Y Z q
  405. rm(10); // Z2_inv Z3_inv X Y 1 q
  406. mul(2, 6, 10); // YI Z2_inv Z3_inv X Y 1 q
  407. rm(8); // Z2_inv Z3_inv X YI 1 q
  408. mul(0, 4, 10); // XI Z2_inv Z3_inv X YI 1 q
  409. rm(6); // Z2_inv Z3_inv XI YI 1 q
  410. rm(0); // Z3_inv XI YI 1 q
  411. rm(0); // XI YI 1 q
  412. C.label("endAffine");
  413. C.returnCall();
  414. }
  415. function inverse2(a, q) {
  416. C.dup(q); // q
  417. C.dup(q + 1); // q q
  418. C.push(2); // 2 q q
  419. C.sub(); // q-2 q
  420. C.dup(q + 2); // q q-2 q
  421. C.dup(q + 3); // q q q-2 q
  422. C.dup(a + 4); // ar q q q-2 q
  423. C.dup(a + 5); // ar ar q q q-2 q
  424. C.mulmod(); // t0 q q-2 q
  425. C.dup(q + 4); // q t0 q q-2 q
  426. C.dup(a+1 + 5); // ai q t0 q q-2 q
  427. C.dup(a+1 + 6); // ai ai q t0 q q-2 q
  428. C.mulmod(); // t1 t0 q q-2 q
  429. C.addmod(); // t2 q-2 q
  430. C.expmod(); // t3
  431. C.dup(q + 1); // q t3
  432. C.dup(q + 2); // q q t3
  433. C.dup(q + 3); // q q q t3
  434. C.dup(1); // t3 q q q t3
  435. C.sub(); // -t3 q q t3
  436. C.dup(a+1 + 3); // ai -t3 q q t3
  437. C.mulmod(); // ii q t3
  438. C.swap(2); // t3 q ii
  439. C.dup(a + 3); // ar t3 q ii
  440. C.mulmod(); // ir ii
  441. }
  442. function storeVals() {
  443. C.push(VAR_POINTS); // p
  444. for (let i=0; i<NPOINTS; i++) {
  445. const MP = G2.affine(G2.mulScalar(P, bigInt(i)));
  446. for (let j=0; j<2; j++) {
  447. for (let k=0; k<2; k++) {
  448. C.push(toHex256(MP[j][k])); // MP[0][0] p
  449. C.dup(1); // p MP[0][0] p
  450. C.mstore(); // p
  451. C.push(32); // 32 p
  452. C.add(); // p+32
  453. }
  454. }
  455. }
  456. }
  457. }
  458. module.exports.abi = [
  459. {
  460. "constant": true,
  461. "inputs": [
  462. {
  463. "name": "escalar",
  464. "type": "uint256"
  465. }
  466. ],
  467. "name": "mulexp",
  468. "outputs": [
  469. {
  470. "name": "",
  471. "type": "uint256"
  472. },
  473. {
  474. "name": "",
  475. "type": "uint256"
  476. }
  477. ],
  478. "payable": false,
  479. "stateMutability": "pure",
  480. "type": "function"
  481. }
  482. ];
  483. module.exports.createCode = createCode;