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.

277 lines
9.2 KiB

  1. const DepositVerifier = artifacts.require("../../contracts/DepositVerifier");
  2. const WithdrawVerifier = artifacts.require("../../contracts/WithdrawVerifier");
  3. const Miksi = artifacts.require("../../contracts/Miksi.sol");
  4. const chai = require("chai");
  5. const expect = chai.expect;
  6. const truffleAssert = require('truffle-assertions');
  7. const fs = require("fs");
  8. const { groth } = require('snarkjs');
  9. const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils;
  10. const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
  11. const circomlib = require("circomlib");
  12. const smt = require("circomlib").smt;
  13. let insVerifier;
  14. let insMiksi;
  15. const nLevels = 5;
  16. const secret = ["1234567890", "987654321", "123"];
  17. const coinCode = "0"; // refearing to ETH
  18. const ethAmount = '1';
  19. const amount = web3.utils.toWei(ethAmount, 'ether');
  20. const nullifier = ["567891234", "432198765", "321"];
  21. let commitment = [];
  22. let tree;
  23. let oldKey = [];
  24. let siblingsOld = [];
  25. let siblingsNew = [];
  26. let rootOld = [];
  27. let rootNew = [];
  28. // let commitment = [];
  29. let proof = [];
  30. let publicSignals = [];
  31. let commitmentsArray = [];
  32. let u = 0;
  33. contract("miksi", (accounts) => {
  34. const {
  35. 0: owner,
  36. 1: addr1, // used for the deposit
  37. 2: addr2, // used for the withdraw
  38. 3: addr3,
  39. 4: addr4,
  40. } = accounts;
  41. before(async () => {
  42. insDepositVerifier = await DepositVerifier.new();
  43. insWithdrawVerifier = await WithdrawVerifier.new();
  44. insMiksi = await Miksi.new(insDepositVerifier.address, insWithdrawVerifier.address);
  45. });
  46. before(async() => {
  47. let balance_wei = await web3.eth.getBalance(addr1);
  48. // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
  49. expect(balance_wei).to.be.equal('100000000000000000000');
  50. tree = await smt.newMemEmptyTrie();
  51. await tree.insert(1, 0);
  52. await computeTree(0);
  53. expect(rootOld[0].toString()).to.be.equal('11499909227292257605992378629333104385616480982267969744564817844870636870870');
  54. expect(rootNew[0].toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
  55. });
  56. it("Make first deposit", async () => {
  57. await makeDeposit(0, addr1);
  58. balance_wei = await web3.eth.getBalance(addr1);
  59. // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
  60. // expect(balance_wei).to.be.equal('98993526980000000000');
  61. });
  62. it("Make second deposit", async () => {
  63. await computeTree(1);
  64. await makeDeposit(1, addr3);
  65. });
  66. it("Make 3rd deposit", async () => {
  67. await computeTree(2);
  68. await makeDeposit(2, addr3);
  69. });
  70. it("Get the commitments data", async () => {
  71. // getCommitments data
  72. let res = await insMiksi.getCommitments();
  73. expect(res[0][0].toString()).to.be.equal('189025084074544266465422070282645213792582195466360448472858620722286781863');
  74. // expect(res[1].toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
  75. console.log(res[0]);
  76. commitmentsArray[0] = res[0];
  77. });
  78. it("Rebuild the tree from sc commitments", async () => {
  79. let treeTmp = await smt.newMemEmptyTrie();
  80. await treeTmp.insert(1, 0);
  81. for (let i=0; i<commitmentsArray[0].length; i++) {
  82. await treeTmp.insert(commitmentsArray[0][i], 0);
  83. }
  84. expect(treeTmp.root).to.be.equal(tree.root);
  85. });
  86. it("Calculate witness and generate the zkProof", async () => {
  87. await genZKProof(0, addr2);
  88. await genZKProof(1, addr4);
  89. await genZKProof(2, addr4);
  90. });
  91. it("Try to use the zkProof with another address and get revert", async () => {
  92. // console.log("Try to reuse the zkproof and expect revert");
  93. await truffleAssert.fails(
  94. withdrawSC(0, addr1),
  95. truffleAssert.ErrorType.REVERT,
  96. "zkProof withdraw could not be verified"
  97. );
  98. });
  99. it("Withdraw 1 ETH with the zkProof of the 1st deposit to addr2", async () => {
  100. // withdraw
  101. // console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
  102. let resW = await withdrawSC(0, addr2);
  103. // console.log("resW", resW);
  104. balance_wei = await web3.eth.getBalance(addr2);
  105. // console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
  106. expect(balance_wei).to.be.equal('101000000000000000000');
  107. });
  108. it("Try to reuse the zkProof and get revert", async () => {
  109. // console.log("Try to reuse the zkproof and expect revert");
  110. await truffleAssert.fails(
  111. withdrawSC(0, addr2),
  112. truffleAssert.ErrorType.REVERT,
  113. "nullifier already used"
  114. );
  115. balance_wei = await web3.eth.getBalance(addr2);
  116. expect(balance_wei).to.be.equal('101000000000000000000');
  117. });
  118. it("Withdraw 1 ETH with the zkProof of the 2nd deposit to addr4", async () => {
  119. let resW = await withdrawSC(1, addr4);
  120. balance_wei = await web3.eth.getBalance(addr4);
  121. expect(balance_wei).to.be.equal('101000000000000000000');
  122. });
  123. it("Withdraw 1 ETH with the zkProof of the 3rd deposit to addr4", async () => {
  124. let resW = await withdrawSC(2, addr4);
  125. balance_wei = await web3.eth.getBalance(addr4);
  126. expect(balance_wei).to.be.equal('102000000000000000000');
  127. });
  128. });
  129. async function computeTree(u) {
  130. const poseidon = circomlib.poseidon.createHash(6, 8, 57);
  131. commitment[u] = poseidon([coinCode, amount, secret[u], nullifier[u]]).toString();
  132. // deposit
  133. // add commitment into SMT
  134. rootOld[u] = tree.root;
  135. const resC = await tree.find(commitment[u]);
  136. assert(!resC.found);
  137. oldKey[u] = "1";
  138. if (!resC.found) {
  139. oldKey[u] = resC.notFoundKey.toString();
  140. }
  141. siblingsOld[u] = resC.siblings;
  142. while (siblingsOld[u].length < nLevels) {
  143. siblingsOld[u].push("0");
  144. };
  145. await tree.insert(commitment[u], 0);
  146. rootNew[u] = tree.root;
  147. }
  148. async function makeDeposit(u, addr) {
  149. const resC = await tree.find(commitment[u]);
  150. assert(resC.found);
  151. siblingsNew[u] = resC.siblings;
  152. while (siblingsNew[u].length < nLevels) {
  153. siblingsNew[u].push("0");
  154. };
  155. // calculate witness
  156. const wasm = await fs.promises.readFile("./build/deposit.wasm");
  157. const input = unstringifyBigInts({
  158. "coinCode": coinCode,
  159. "amount": amount,
  160. "secret": secret[u],
  161. "nullifier": nullifier[u],
  162. "oldKey": oldKey[u],
  163. "siblingsOld": siblingsOld[u],
  164. "siblingsNew": siblingsNew[u],
  165. "rootOld": rootOld[u],
  166. "rootNew": rootNew[u],
  167. "commitment": commitment[u]
  168. });
  169. const options = {};
  170. // console.log("Calculate witness");
  171. const wc = await WitnessCalculatorBuilder(wasm, options);
  172. const w = await wc.calculateWitness(input);
  173. const witness = unstringifyBigInts(stringifyBigInts(w));
  174. // generate zkproof of commitment using snarkjs (as is a test)
  175. const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-proving_key.json", "utf8")));
  176. // console.log("Generate zkSNARK proof");
  177. const res = groth.genProof(provingKey, witness);
  178. proof[u] = res.proof;
  179. publicSignals[u] = res.publicSignals;
  180. const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-verification_key.json", "utf8")));
  181. let pubI = unstringifyBigInts([coinCode, amount, rootOld[u].toString(), rootNew[u].toString(), commitment[u]]);
  182. let validCheck = groth.isValid(verificationKey, proof[u], pubI);
  183. assert(validCheck);
  184. await insMiksi.deposit(
  185. commitment[u],
  186. tree.root.toString(),
  187. [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
  188. [
  189. [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
  190. [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
  191. ],
  192. [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()],
  193. {from: addr, value: amount}
  194. );
  195. }
  196. async function genZKProof(u, addr) {
  197. const resC = await tree.find(commitment[u]);
  198. assert(resC.found);
  199. let siblings = resC.siblings;
  200. while (siblings.length < nLevels) {
  201. siblings.push("0");
  202. };
  203. // console.log("siblings", siblings);
  204. // calculate witness
  205. const wasm = await fs.promises.readFile("./build/withdraw.wasm");
  206. const input = unstringifyBigInts({
  207. "coinCode": coinCode,
  208. "amount": amount,
  209. "secret": secret[u],
  210. "nullifier": nullifier[u],
  211. "siblings": siblings,
  212. "root": tree.root,
  213. "address": addr
  214. });
  215. const options = {};
  216. // console.log("Calculate witness");
  217. const wc = await WitnessCalculatorBuilder(wasm, options);
  218. const w = await wc.calculateWitness(input);
  219. const witness = unstringifyBigInts(stringifyBigInts(w));
  220. // generate zkproof of commitment using snarkjs (as is a test)
  221. const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8")));
  222. // console.log("Generate zkSNARK proof");
  223. const res = groth.genProof(provingKey, witness);
  224. proof[u] = res.proof;
  225. publicSignals[u] = res.publicSignals;
  226. }
  227. async function withdrawSC(u, addr) {
  228. return insMiksi.withdraw(
  229. addr,
  230. nullifier[u],
  231. [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
  232. [
  233. [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
  234. [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
  235. ],
  236. [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()]
  237. );
  238. }