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.

608 lines
19 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. Copyright 2019 0KIMS association.
  3. This file is part of websnark (Web Assembly zkSnark Prover).
  4. websnark is a free software: you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. websnark is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with websnark. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. /* globals WebAssembly, Blob, Worker, navigator, Promise, window */
  16. const bigInt = require("big-integer");
  17. const groth16_wasm = require("../build/groth16_wasm.js");
  18. const assert = require("assert");
  19. const inBrowser = (typeof window !== "undefined");
  20. let NodeWorker;
  21. let NodeCrypto;
  22. if (!inBrowser) {
  23. NodeWorker = require("worker_threads").Worker;
  24. NodeCrypto = require("crypto");
  25. }
  26. class Deferred {
  27. constructor() {
  28. this.promise = new Promise((resolve, reject)=> {
  29. this.reject = reject;
  30. this.resolve = resolve;
  31. });
  32. }
  33. }
  34. /*
  35. function delay(ms) {
  36. return new Promise(resolve => setTimeout(resolve, ms));
  37. }
  38. */
  39. function thread(self) {
  40. let instance;
  41. let memory;
  42. let i32;
  43. async function init(data) {
  44. const code = new Uint8Array(data.code);
  45. const wasmModule = await WebAssembly.compile(code);
  46. memory = new WebAssembly.Memory({initial:data.init});
  47. i32 = new Uint32Array(memory.buffer);
  48. instance = await WebAssembly.instantiate(wasmModule, {
  49. env: {
  50. "memory": memory
  51. }
  52. });
  53. }
  54. function alloc(length) {
  55. while (i32[0] & 3) i32[0]++; // Return always aligned pointers
  56. const res = i32[0];
  57. i32[0] += length;
  58. while (i32[0] > memory.buffer.byteLength) {
  59. memory.grow(100);
  60. }
  61. i32 = new Uint32Array(memory.buffer);
  62. return res;
  63. }
  64. function putBin(b) {
  65. const p = alloc(b.byteLength);
  66. const s32 = new Uint32Array(b);
  67. i32.set(s32, p/4);
  68. return p;
  69. }
  70. function getBin(p, l) {
  71. return memory.buffer.slice(p, p+l);
  72. }
  73. self.onmessage = function(e) {
  74. let data;
  75. if (e.data) {
  76. data = e.data;
  77. } else {
  78. data = e;
  79. }
  80. if (data.command == "INIT") {
  81. init(data).then(function() {
  82. self.postMessage(data.result);
  83. });
  84. } else if (data.command == "G1_MULTIEXP") {
  85. const oldAlloc = i32[0];
  86. const pScalars = putBin(data.scalars);
  87. const pPoints = putBin(data.points);
  88. const pRes = alloc(96);
  89. instance.exports.g1_zero(pRes);
  90. instance.exports.g1_multiexp2(pScalars, pPoints, data.n, 7, pRes);
  91. data.result = getBin(pRes, 96);
  92. i32[0] = oldAlloc;
  93. self.postMessage(data.result, [data.result]);
  94. } else if (data.command == "G2_MULTIEXP") {
  95. const oldAlloc = i32[0];
  96. const pScalars = putBin(data.scalars);
  97. const pPoints = putBin(data.points);
  98. const pRes = alloc(192);
  99. instance.exports.g2_zero(pRes);
  100. instance.exports.g2_multiexp(pScalars, pPoints, data.n, 7, pRes);
  101. data.result = getBin(pRes, 192);
  102. i32[0] = oldAlloc;
  103. self.postMessage(data.result, [data.result]);
  104. } else if (data.command == "CALC_H") {
  105. const oldAlloc = i32[0];
  106. const pSignals = putBin(data.signals);
  107. const pPolsA = putBin(data.polsA);
  108. const pPolsB = putBin(data.polsB);
  109. const nSignals = data.nSignals;
  110. const domainSize = data.domainSize;
  111. const pSignalsM = alloc(nSignals*32);
  112. const pPolA = alloc(domainSize*32);
  113. const pPolB = alloc(domainSize*32);
  114. const pPolA2 = alloc(domainSize*32*2);
  115. const pPolB2 = alloc(domainSize*32*2);
  116. instance.exports.fft_toMontgomeryN(pSignals, pSignalsM, nSignals);
  117. instance.exports.pol_zero(pPolA, domainSize);
  118. instance.exports.pol_zero(pPolB, domainSize);
  119. instance.exports.pol_constructLC(pPolsA, pSignalsM, nSignals, pPolA);
  120. instance.exports.pol_constructLC(pPolsB, pSignalsM, nSignals, pPolB);
  121. instance.exports.fft_copyNInterleaved(pPolA, pPolA2, domainSize);
  122. instance.exports.fft_copyNInterleaved(pPolB, pPolB2, domainSize);
  123. instance.exports.fft_ifft(pPolA, domainSize, 0);
  124. instance.exports.fft_ifft(pPolB, domainSize, 0);
  125. instance.exports.fft_fft(pPolA, domainSize, 1);
  126. instance.exports.fft_fft(pPolB, domainSize, 1);
  127. instance.exports.fft_copyNInterleaved(pPolA, pPolA2+32, domainSize);
  128. instance.exports.fft_copyNInterleaved(pPolB, pPolB2+32, domainSize);
  129. instance.exports.fft_mulN(pPolA2, pPolB2, domainSize*2, pPolA2);
  130. instance.exports.fft_ifft(pPolA2, domainSize*2, 0);
  131. instance.exports.fft_fromMontgomeryN(pPolA2+domainSize*32, pPolA2+domainSize*32, domainSize);
  132. data.result = getBin(pPolA2+domainSize*32, domainSize*32);
  133. i32[0] = oldAlloc;
  134. self.postMessage(data.result, [data.result]);
  135. } else if (data.command == "TERMINATE") {
  136. process.exit();
  137. }
  138. };
  139. }
  140. async function build() {
  141. const groth16 = new Groth16();
  142. groth16.q = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208583");
  143. groth16.r = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
  144. groth16.n64 = Math.floor((groth16.q.minus(1).bitLength() - 1)/64) +1;
  145. groth16.n32 = groth16.n64*2;
  146. groth16.n8 = groth16.n64*8;
  147. groth16.memory = new WebAssembly.Memory({initial:1000});
  148. groth16.i32 = new Uint32Array(groth16.memory.buffer);
  149. const wasmModule = await WebAssembly.compile(groth16_wasm.code);
  150. groth16.instance = await WebAssembly.instantiate(wasmModule, {
  151. env: {
  152. "memory": groth16.memory
  153. }
  154. });
  155. groth16.pq = groth16_wasm.pq;
  156. groth16.pr = groth16_wasm.pr;
  157. groth16.pr0 = groth16.alloc(192);
  158. groth16.pr1 = groth16.alloc(192);
  159. groth16.workers = [];
  160. groth16.pendingDeferreds = [];
  161. groth16.working = [];
  162. let concurrency;
  163. if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) {
  164. concurrency = navigator.hardwareConcurrency;
  165. } else {
  166. concurrency = 8;
  167. }
  168. function getOnMsg(i) {
  169. return function(e) {
  170. let data;
  171. if ((e)&&(e.data)) {
  172. data = e.data;
  173. } else {
  174. data = e;
  175. }
  176. groth16.working[i]=false;
  177. groth16.pendingDeferreds[i].resolve(data);
  178. groth16.processWorks();
  179. };
  180. }
  181. for (let i = 0; i<concurrency; i++) {
  182. if (inBrowser) {
  183. const blob = new Blob(["(", thread.toString(), ")(self);"], { type: "text/javascript" });
  184. const url = URL.createObjectURL(blob);
  185. groth16.workers[i] = new Worker(url);
  186. groth16.workers[i].onmessage = getOnMsg(i);
  187. } else {
  188. groth16.workers[i] = new NodeWorker("(" + thread.toString()+ ")(require('worker_threads').parentPort);", {eval: true});
  189. groth16.workers[i].on("message", getOnMsg(i));
  190. }
  191. groth16.working[i]=false;
  192. }
  193. const initPromises = [];
  194. for (let i=0; i<groth16.workers.length;i++) {
  195. const copyCode = groth16_wasm.code.buffer.slice(0);
  196. initPromises.push(groth16.postAction(i, {
  197. command: "INIT",
  198. init: 1000,
  199. code: copyCode
  200. }, [copyCode]));
  201. }
  202. await Promise.all(initPromises);
  203. return groth16;
  204. }
  205. class Groth16 {
  206. constructor() {
  207. this.actionQueue = [];
  208. }
  209. postAction(workerId, e, transfers, _deferred) {
  210. assert(this.working[workerId] == false);
  211. this.working[workerId] = true;
  212. this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred();
  213. this.workers[workerId].postMessage(e, transfers);
  214. return this.pendingDeferreds[workerId].promise;
  215. }
  216. processWorks() {
  217. for (let i=0; (i<this.workers.length)&&(this.actionQueue.length > 0); i++) {
  218. if (this.working[i] == false) {
  219. const work = this.actionQueue.shift();
  220. this.postAction(i, work.data, work.transfers, work.deferred);
  221. }
  222. }
  223. }
  224. queueAction(actionData, transfers) {
  225. const d = new Deferred();
  226. this.actionQueue.push({
  227. data: actionData,
  228. transfers: transfers,
  229. deferred: d
  230. });
  231. this.processWorks();
  232. return d.promise;
  233. }
  234. alloc(length) {
  235. while (this.i32[0] & 3) this.i32[0]++; // Return always aligned pointers
  236. const res = this.i32[0];
  237. this.i32[0] += length;
  238. return res;
  239. }
  240. putBin(p, b) {
  241. const s32 = new Uint32Array(b);
  242. this.i32.set(s32, p/4);
  243. }
  244. getBin(p, l) {
  245. return this.memory.buffer.slice(p, p+l);
  246. }
  247. bin2int(b) {
  248. const i32 = new Uint32Array(b);
  249. let acc = bigInt(i32[7]);
  250. for (let i=6; i>=0; i--) {
  251. acc = acc.shiftLeft(32);
  252. acc = acc.add(i32[i]);
  253. }
  254. return acc.toString();
  255. }
  256. bin2g1(b) {
  257. return [
  258. this.bin2int(b.slice(0,32)),
  259. this.bin2int(b.slice(32,64)),
  260. this.bin2int(b.slice(64,96)),
  261. ];
  262. }
  263. bin2g2(b) {
  264. return [
  265. [
  266. this.bin2int(b.slice(0,32)),
  267. this.bin2int(b.slice(32,64))
  268. ],
  269. [
  270. this.bin2int(b.slice(64,96)),
  271. this.bin2int(b.slice(96,128))
  272. ],
  273. [
  274. this.bin2int(b.slice(128,160)),
  275. this.bin2int(b.slice(160,192))
  276. ],
  277. ];
  278. }
  279. async g1_multiexp(scalars, points) {
  280. const nPoints = scalars.byteLength /32;
  281. const nPointsPerThread = Math.floor(nPoints / this.workers.length);
  282. const opPromises = [];
  283. for (let i=0; i<this.workers.length; i++) {
  284. const th_nPoints =
  285. i < this.workers.length -1 ?
  286. nPointsPerThread :
  287. nPoints - (nPointsPerThread * (this.workers.length -1));
  288. const scalars_th = scalars.slice(i*nPointsPerThread*32, i*nPointsPerThread*32 + th_nPoints*32);
  289. const points_th = points.slice(i*nPointsPerThread*64, i*nPointsPerThread*64 + th_nPoints*64);
  290. opPromises.push(
  291. this.queueAction({
  292. command: "G1_MULTIEXP",
  293. scalars: scalars_th,
  294. points: points_th,
  295. n: th_nPoints
  296. }, [scalars_th, points_th])
  297. );
  298. }
  299. const results = await Promise.all(opPromises);
  300. this.instance.exports.g1_zero(this.pr0);
  301. for (let i=0; i<results.length; i++) {
  302. this.putBin(this.pr1, results[i]);
  303. this.instance.exports.g1_add(this.pr0, this.pr1, this.pr0);
  304. }
  305. return this.getBin(this.pr0, 96);
  306. }
  307. async g2_multiexp(scalars, points) {
  308. const nPoints = scalars.byteLength /32;
  309. const nPointsPerThread = Math.floor(nPoints / this.workers.length);
  310. const opPromises = [];
  311. for (let i=0; i<this.workers.length; i++) {
  312. const th_nPoints =
  313. i < this.workers.length -1 ?
  314. nPointsPerThread :
  315. nPoints - (nPointsPerThread * (this.workers.length -1));
  316. const scalars_th = scalars.slice(i*nPointsPerThread*32, i*nPointsPerThread*32 + th_nPoints*32);
  317. const points_th = points.slice(i*nPointsPerThread*128, i*nPointsPerThread*128 + th_nPoints*128);
  318. opPromises.push(
  319. this.queueAction({
  320. command: "G2_MULTIEXP",
  321. scalars: scalars_th,
  322. points: points_th,
  323. n: th_nPoints
  324. }, [scalars_th, points_th])
  325. );
  326. }
  327. const results = await Promise.all(opPromises);
  328. this.instance.exports.g2_zero(this.pr0);
  329. for (let i=0; i<results.length; i++) {
  330. this.putBin(this.pr1, results[i]);
  331. this.instance.exports.g2_add(this.pr0, this.pr1, this.pr0);
  332. }
  333. return this.getBin(this.pr0, 192);
  334. }
  335. g1_affine(p) {
  336. this.putBin(this.pr0, p);
  337. this.instance.exports.g1_affine(this.pr0, this.pr0);
  338. return this.getBin(this.pr0, 96);
  339. }
  340. g2_affine(p) {
  341. this.putBin(this.pr0, p);
  342. this.instance.exports.g2_affine(this.pr0, this.pr0);
  343. return this.getBin(this.pr0, 192);
  344. }
  345. g1_fromMontgomery(p) {
  346. this.putBin(this.pr0, p);
  347. this.instance.exports.g1_fromMontgomery(this.pr0, this.pr0);
  348. return this.getBin(this.pr0, 96);
  349. }
  350. g2_fromMontgomery(p) {
  351. this.putBin(this.pr0, p);
  352. this.instance.exports.g2_fromMontgomery(this.pr0, this.pr0);
  353. return this.getBin(this.pr0, 192);
  354. }
  355. loadPoint1(b) {
  356. const p = this.alloc(96);
  357. this.putBin(p, b);
  358. this.instance.exports.f1m_one(p+64);
  359. return p;
  360. }
  361. loadPoint2(b) {
  362. const p = this.alloc(192);
  363. this.putBin(p, b);
  364. this.instance.exports.f2m_one(p+128);
  365. return p;
  366. }
  367. terminate() {
  368. for (let i=0; i<this.workers.length; i++) {
  369. this.workers[i].postMessage({command: "TERMINATE"});
  370. }
  371. }
  372. async calcH(signals, polsA, polsB, nSignals, domainSize) {
  373. return this.queueAction({
  374. command: "CALC_H",
  375. signals: signals,
  376. polsA: polsA,
  377. polsB: polsB,
  378. nSignals: nSignals,
  379. domainSize: domainSize
  380. }, [signals, polsA, polsB]);
  381. }
  382. async proof(signals, pkey) {
  383. const pkey32 = new Uint32Array(pkey);
  384. const nSignals = pkey32[0];
  385. const nPublic = pkey32[1];
  386. const domainSize = pkey32[2];
  387. const pPolsA = pkey32[3];
  388. const pPolsB = pkey32[4];
  389. const pPointsA = pkey32[5];
  390. const pPointsB1 = pkey32[6];
  391. const pPointsB2 = pkey32[7];
  392. const pPointsC = pkey32[8];
  393. const pHExps = pkey32[9];
  394. const polsA = pkey.slice(pPolsA, pPolsA + pPolsB);
  395. const polsB = pkey.slice(pPolsB, pPolsB + pPointsA);
  396. const pointsA = pkey.slice(pPointsA, pPointsA + nSignals*64);
  397. const pointsB1 = pkey.slice(pPointsB1, pPointsB1 + nSignals*64);
  398. const pointsB2 = pkey.slice(pPointsB2, pPointsB2 + nSignals*128);
  399. const pointsC = pkey.slice(pPointsC, pPointsC + (nSignals-nPublic-1)*64);
  400. const pointsHExps = pkey.slice(pHExps, pHExps + domainSize*64);
  401. const alfa1 = pkey.slice(10*4, 10*4 + 64);
  402. const beta1 = pkey.slice(10*4 + 64, 10*4 + 128);
  403. const delta1 = pkey.slice(10*4 + 128, 10*4 + 192);
  404. const beta2 = pkey.slice(10*4 + 192, 10*4 + 320);
  405. const delta2 = pkey.slice(10*4 + 320, 10*4 + 448);
  406. const pH = this.calcH(signals.slice(0), polsA, polsB, nSignals, domainSize).then( (h) => {
  407. /* Debug code to print the result of h
  408. for (let i=0; i<domainSize; i++) {
  409. const a = this.bin2int(h.slice(i*32, i*32+32));
  410. console.log(i + " -> " + a.toString());
  411. }
  412. */
  413. return this.g1_multiexp(h, pointsHExps);
  414. });
  415. const pA = this.g1_multiexp(signals.slice(0), pointsA);
  416. const pB1 = this.g1_multiexp(signals.slice(0), pointsB1);
  417. const pB2 = this.g2_multiexp(signals.slice(0), pointsB2);
  418. const pC = this.g1_multiexp(signals.slice((nPublic+1)*32), pointsC);
  419. const res = await Promise.all([pA, pB1, pB2, pC, pH]);
  420. const pi_a = this.alloc(96);
  421. const pi_b = this.alloc(192);
  422. const pi_c = this.alloc(96);
  423. const pib1 = this.alloc(96);
  424. this.putBin(pi_a, res[0]);
  425. this.putBin(pib1, res[1]);
  426. this.putBin(pi_b, res[2]);
  427. this.putBin(pi_c, res[3]);
  428. const pAlfa1 = this.loadPoint1(alfa1);
  429. const pBeta1 = this.loadPoint1(beta1);
  430. const pDelta1 = this.loadPoint1(delta1);
  431. const pBeta2 = this.loadPoint2(beta2);
  432. const pDelta2 = this.loadPoint2(delta2);
  433. let rnd = new Uint32Array(8);
  434. const aux1 = this.alloc(96);
  435. const aux2 = this.alloc(192);
  436. const pr = this.alloc(32);
  437. const ps = this.alloc(32);
  438. if (inBrowser) {
  439. window.crypto.getRandomValues(rnd);
  440. this.putBin(pr, rnd);
  441. window.crypto.getRandomValues(rnd);
  442. this.putBin(ps, rnd);
  443. } else {
  444. const br = NodeCrypto.randomBytes(32);
  445. this.putBin(pr, br);
  446. const bs = NodeCrypto.randomBytes(32);
  447. this.putBin(ps, bs);
  448. }
  449. /// Uncoment it to debug and check it works
  450. // this.instance.exports.f1m_zero(pr);
  451. // this.instance.exports.f1m_zero(ps);
  452. // pi_a = pi_a + Alfa1 + r*Delta1
  453. this.instance.exports.g1_add(pAlfa1, pi_a, pi_a);
  454. this.instance.exports.g1_timesScalar(pDelta1, pr, 32, aux1);
  455. this.instance.exports.g1_add(aux1, pi_a, pi_a);
  456. // pi_b = pi_b + Beta2 + s*Delta2
  457. this.instance.exports.g2_add(pBeta2, pi_b, pi_b);
  458. this.instance.exports.g2_timesScalar(pDelta2, ps, 32, aux2);
  459. this.instance.exports.g2_add(aux2, pi_b, pi_b);
  460. // pib1 = pib1 + Beta1 + s*Delta1
  461. this.instance.exports.g1_add(pBeta1, pib1, pib1);
  462. this.instance.exports.g1_timesScalar(pDelta1, ps, 32, aux1);
  463. this.instance.exports.g1_add(aux1, pib1, pib1);
  464. // pi_c = pi_c + pH
  465. this.putBin(aux1, res[4]);
  466. this.instance.exports.g1_add(aux1, pi_c, pi_c);
  467. // pi_c = pi_c + s*pi_a
  468. this.instance.exports.g1_timesScalar(pi_a, ps, 32, aux1);
  469. this.instance.exports.g1_add(aux1, pi_c, pi_c);
  470. // pi_c = pi_c + r*pib1
  471. this.instance.exports.g1_timesScalar(pib1, pr, 32, aux1);
  472. this.instance.exports.g1_add(aux1, pi_c, pi_c);
  473. // pi_c = pi_c - r*s*delta1
  474. const prs = this.alloc(64);
  475. this.instance.exports.int_mul(pr, ps, prs);
  476. this.instance.exports.g1_timesScalar(pDelta1, prs, 64, aux1);
  477. this.instance.exports.g1_neg(aux1, aux1);
  478. this.instance.exports.g1_add(aux1, pi_c, pi_c);
  479. this.instance.exports.g1_affine(pi_a, pi_a);
  480. this.instance.exports.g2_affine(pi_b, pi_b);
  481. this.instance.exports.g1_affine(pi_c, pi_c);
  482. this.instance.exports.g1_fromMontgomery(pi_a, pi_a);
  483. this.instance.exports.g2_fromMontgomery(pi_b, pi_b);
  484. this.instance.exports.g1_fromMontgomery(pi_c, pi_c);
  485. return {
  486. pi_a: this.bin2g1(this.getBin(pi_a, 96)),
  487. pi_b: this.bin2g2(this.getBin(pi_b, 192)),
  488. pi_c: this.bin2g1(this.getBin(pi_c, 96)),
  489. };
  490. }
  491. }
  492. module.exports = build;