From fbbc43c54c2630e1b9f6220f32b05a22170f1009 Mon Sep 17 00:00:00 2001
From: poma <Semenov.Roman@mail.ru>
Date: Fri, 12 Jul 2019 19:34:25 +0300
Subject: [PATCH] Snark test

---
 circuits/merkleTree.circom |  3 +-
 test/test_snark.js         | 55 ++++++++++++++++++++++++
 test/utils.js              | 87 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+), 2 deletions(-)
 create mode 100644 test/test_snark.js
 create mode 100644 test/utils.js

diff --git a/circuits/merkleTree.circom b/circuits/merkleTree.circom
index df7b254..f7ffbb6 100644
--- a/circuits/merkleTree.circom
+++ b/circuits/merkleTree.circom
@@ -1,4 +1,3 @@
-include "../node_modules/circomlib/circuits/bitify.circom";
 include "../node_modules/circomlib/circuits/mimcsponge.circom";
 
 // Computes MiMC(left + right)
@@ -71,4 +70,4 @@ template MerkleTree(levels, rounds) {
     }
 
     root === hashers[levels - 1].hash;
-}
\ No newline at end of file
+}
diff --git a/test/test_snark.js b/test/test_snark.js
new file mode 100644
index 0000000..084301d
--- /dev/null
+++ b/test/test_snark.js
@@ -0,0 +1,55 @@
+const fs = require('fs');
+const circom = require("circom");
+const snarkjs = require("snarkjs");
+const circomlib = require('circomlib');
+const bigInt = snarkjs.bigInt;
+const stringifyBigInts = require("../node_modules/websnark/tools/stringifybigint.js").stringifyBigInts;
+const unstringifyBigInts = require("../node_modules/websnark/tools/stringifybigint.js").unstringifyBigInts;
+const utils = require("./utils");
+const merkleTree = require('../lib/MerkleTree');
+const jsStorage = require("../lib/Storage");
+const mimcHasher = require("../lib/MiMC");
+
+function generateDeposit() {
+  let deposit = {
+    secret: utils.rbigint(31),
+    nullifier: utils.rbigint(31),
+  };
+  const preimage = Buffer.concat([deposit.nullifier.leInt2Buff(32), deposit.secret.leInt2Buff(32)]);
+  deposit.commitment = utils.pedersenHash(preimage);
+  return deposit;
+}
+
+(async () => {
+  const dep1 = generateDeposit();
+  const dep2 = generateDeposit();
+  const dep3 = generateDeposit();
+
+  const tree = new merkleTree("", new jsStorage(), new mimcHasher(), 16, 0);
+
+  await tree.insert(dep1.commitment);
+  await tree.insert(dep2.commitment);
+  await tree.insert(dep3.commitment);
+
+  const {root, path_elements, path_index} = await tree.path(1);
+
+  // Circuit input
+  const input = stringifyBigInts({
+    // public
+    root: root,
+    nullifier: dep2.nullifier,
+    receiver: utils.rbigint(20),
+    fee: bigInt(1e17),
+
+    // private
+    secret: dep2.secret,
+    pathElements: path_elements,
+    pathIndex: path_index,
+  });
+
+  console.log("Input:\n", input);
+  console.time("Time");
+  const proof = await utils.snarkProof(input);
+  console.log("Proof:\n", proof);
+  console.timeEnd("Time");
+})();
diff --git a/test/utils.js b/test/utils.js
new file mode 100644
index 0000000..f53474d
--- /dev/null
+++ b/test/utils.js
@@ -0,0 +1,87 @@
+const fs = require('fs');
+const circom = require("circom");
+const snarkjs = require("snarkjs");
+const groth = snarkjs["groth"];
+const crypto = require("crypto");
+const circomlib = require('circomlib');
+const pedersen = circomlib.pedersenHash;
+const babyjub = circomlib.babyJub;
+const mimcsponge = circomlib.mimcsponge;
+const bigInt = snarkjs.bigInt;
+const buildGroth16 = require('../node_modules/websnark/src/groth16.js');
+const stringifyBigInts = require("../node_modules/websnark/tools/stringifybigint.js").stringifyBigInts;
+const unstringifyBigInts = require("../node_modules/websnark/tools/stringifybigint.js").unstringifyBigInts;
+
+const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes));
+
+function pedersenHash(data) {
+  return babyjub.unpackPoint(pedersen.hash(data))[0];
+}
+
+
+function mimcHash(left, right) {
+  return mimcsponge.multiHash([bigInt(left), bigInt(right)]).toString();
+}
+
+function p256(o) {
+  if ((typeof(o) == "bigint") || (o instanceof bigInt))  {
+    let nstr = o.toString(16);
+    while (nstr.length < 64) nstr = "0"+nstr;
+    nstr = "0x"+nstr;
+    return nstr;
+  } else if (Array.isArray(o)) {
+    return o.map(p256);
+  } else if (typeof o == "object") {
+    const res = {};
+    for (let k in o) {
+      if (k === "value") {
+        return p256(o[k]);
+      }
+      res[k] = p256(o[k]);
+    }
+    return res;
+  } else {
+    return o;
+  }
+}
+
+function convertWitness(witness) {
+  const buffLen = witness.length * 32;
+  const buff = new ArrayBuffer(buffLen);
+  const h = {
+    dataView: new DataView(buff),
+    offset: 0
+  };
+  for (let i=0; i<witness.length; i++) {
+    for (let i=0; i<8; i++) {
+      //const v = witness[i].shiftRight(i*32).and(0xFFFFFFFF).toJSNumber();
+      const v = Number(witness[i].shr(i * 32).and(BigInt(0xFFFFFFFF)));
+      h.dataView.setUint32(h.offset, v, true);
+      h.offset += 4;
+    }
+  }
+  return buff;
+}
+
+function toArrayBuffer(b) {
+  return b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
+}
+
+async function snarkProof(input) {
+  const circuit = new snarkjs.Circuit(unstringifyBigInts(require("../build/circuits/withdraw.json")));
+  const witnessArray = circuit.calculateWitness(input);
+  const witness = convertWitness(witnessArray);
+  const publicSignals = witnessArray.slice(1, circuit.nPubInputs + circuit.nOutputs + 1);
+  const key = toArrayBuffer(fs.readFileSync("../build/circuits/withdraw_proving_key.bin"));
+  const groth16 = await buildGroth16();
+  let proof = await groth16.proof(witness, key);
+  proof = unstringifyBigInts(proof);
+  return p256({
+    pi_a: [proof.pi_a[0], proof.pi_a[1]],
+    pi_b: [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]],
+    pi_c: [proof.pi_c[0], proof.pi_c[1]],
+    publicSignals: publicSignals,
+  });
+}
+
+module.exports = {rbigint, pedersenHash, snarkProof, mimcHash};