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.

242 lines
8.4 KiB

  1. var circuit = {};
  2. var provingKey = {};
  3. var witnessCalc = {};
  4. const abi = JSON.parse(`[{"inputs":[{"internalType":"address","name":"_depositVerifierContractAddr","type":"address"},{"internalType":"address","name":"_withdrawVerifierContractAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"_commitment","type":"uint256"},{"internalType":"uint256","name":"_root","type":"uint256"},{"internalType":"uint256[2]","name":"a","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"b","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"c","type":"uint256[2]"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getCommitments","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_address","type":"address"},{"internalType":"uint256","name":"nullifier","type":"uint256"},{"internalType":"uint256[2]","name":"a","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"b","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"c","type":"uint256[2]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]`);
  5. const miksiAddress = "0x4cc45573481A2977fcC0b9DD9f8c710201B5a5cd";
  6. let metamask = false;
  7. document.getElementById("contractAddr").innerHTML=`<a href="https://goerli.etherscan.io/address/`+miksiAddress+`" target="_blank">`+miksiAddress+`</a>`;
  8. function loadCircuit(circuitname) {
  9. fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => {
  10. return response.arrayBuffer();
  11. }).then( (b) => {
  12. provingKey[circuitname] = b;
  13. console.log("proving_key loaded for", circuitname);
  14. });
  15. fetch("circuits-files/"+circuitname+".wasm").then( (response) => {
  16. return response.arrayBuffer();
  17. }).then( (b) => {
  18. witnessCalc[circuitname] = b;
  19. console.log("w", b);
  20. console.log("witnessCalc loaded for", circuitname);
  21. });
  22. }
  23. async function deposit(circuitname) {
  24. if (!metamask) {
  25. toastr.error("Please install/connect Metamask");
  26. return;
  27. }
  28. document.getElementById("depositRes").innerHTML = `
  29. Generating zkProof & making the deposit
  30. `;
  31. console.log("circuit:", circuitname);
  32. // TODO
  33. const secret = miksi.randBigInt().toString();
  34. const nullifier = miksi.randBigInt().toString();
  35. let res = await miksiContract.methods.getCommitments().call();
  36. console.log("res", res);
  37. const commitments = res[0];
  38. const key = res[2];
  39. console.log("commitments", commitments);
  40. console.log("key", key);
  41. // getCommitments from the tree
  42. // calculate witness
  43. console.log(witnessCalc[circuitname]);
  44. const cw = await miksi.calcDepositWitness(witnessCalc[circuitname], secret, nullifier, commitments, key);
  45. const witness = cw.witness;
  46. const publicInputs = cw.publicInputs;
  47. console.log("w", witness);
  48. console.log("publicInputs", publicInputs);
  49. // generate proof
  50. const start = new Date().getTime();
  51. console.log(provingKey[circuitname]);
  52. const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
  53. const end = new Date().getTime();
  54. const time = end - start;
  55. console.log("circuit " + circuitname + " took " + time + "ms to compute");
  56. console.log(proof);
  57. // send tx
  58. const accounts = await web3.eth.getAccounts();
  59. const sender = accounts[0];
  60. console.log("SENDER", sender);
  61. console.log("sc call data",
  62. publicInputs.commitment,
  63. publicInputs.root.toString(),
  64. [proof.pi_a[0], proof.pi_a[1]],
  65. [
  66. [proof.pi_b[0][1], proof.pi_b[0][0]],
  67. [proof.pi_b[1][1], proof.pi_b[1][0]]
  68. ],
  69. [proof.pi_c[0], proof.pi_c[1]],
  70. );
  71. miksiContract.methods.deposit(
  72. publicInputs.commitment,
  73. publicInputs.root.toString(),
  74. [proof.pi_a[0], proof.pi_a[1]],
  75. [
  76. [proof.pi_b[0][1], proof.pi_b[0][0]],
  77. [proof.pi_b[1][1], proof.pi_b[1][0]]
  78. ],
  79. [proof.pi_c[0], proof.pi_c[1]],
  80. ).send(
  81. {from: sender, value: 1000000000000000000},
  82. function(error, transactionHash){
  83. console.log("https://goerli.etherscan.io/tx/"+transactionHash);
  84. console.log(error);
  85. });
  86. // print secret & nullifier
  87. let jw = {
  88. secret: secret,
  89. nullifier: nullifier,
  90. key: key
  91. };
  92. console.log("jw", JSON.stringify(jw));
  93. document.getElementById("depositRes").innerHTML = `
  94. <b>Please store the secret data in a safe place:</b><br>
  95. <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'>
  96. </input>
  97. `;
  98. }
  99. async function withdraw(circuitname) {
  100. if (!metamask) {
  101. toastr.error("Please install/connect Metamask");
  102. return;
  103. }
  104. document.getElementById("withdrawRes").innerHTML = `
  105. Generating zkProof & making the withdraw
  106. `;
  107. console.log("circuit:", circuitname);
  108. const jw = JSON.parse(document.getElementById("jsonWithdraw").value);
  109. const secret = jw.secret;
  110. const nullifier = jw.nullifier;
  111. const key = jw.key;
  112. console.log(secret, nullifier);
  113. const commitment = miksi.calcCommitment(secret, nullifier);
  114. // getCommitments from the tree
  115. let res = await miksiContract.methods.getCommitments().call();
  116. console.log("res", res);
  117. const commitments = res[0];
  118. console.log("commitments", commitments);
  119. // calculate witness
  120. console.log(witnessCalc[circuitname]);
  121. const addr = document.getElementById("withdrawAddress").value;
  122. if (addr==undefined) {
  123. toastr.error("No withdraw address defined");
  124. return;
  125. }
  126. if (!web3.utils.isAddress(addr)) {
  127. toastr.error("Error with withdraw address");
  128. return;
  129. }
  130. const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr, key);
  131. const witness = cw.witness;
  132. const publicInputs = cw.publicInputs;
  133. console.log("w", witness);
  134. console.log("publicInputs", publicInputs);
  135. // generate proof
  136. const start = new Date().getTime();
  137. console.log(provingKey[circuitname]);
  138. const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
  139. const end = new Date().getTime();
  140. const time = end - start;
  141. console.log("circuit " + circuitname + " took " + time + "ms to compute");
  142. console.log(proof);
  143. // send tx
  144. const accounts = await web3.eth.getAccounts();
  145. const sender = accounts[0];
  146. console.log("SENDER", sender);
  147. console.log("sc call data",
  148. publicInputs.address,
  149. publicInputs.nullifier,
  150. [proof.pi_a[0], proof.pi_a[1]],
  151. [
  152. [proof.pi_b[0][1], proof.pi_b[0][0]],
  153. [proof.pi_b[1][1], proof.pi_b[1][0]]
  154. ],
  155. [proof.pi_c[0], proof.pi_c[1]],
  156. );
  157. miksiContract.methods.withdraw(
  158. publicInputs.address,
  159. publicInputs.nullifier,
  160. [proof.pi_a[0], proof.pi_a[1]],
  161. [
  162. [proof.pi_b[0][1], proof.pi_b[0][0]],
  163. [proof.pi_b[1][1], proof.pi_b[1][0]]
  164. ],
  165. [proof.pi_c[0], proof.pi_c[1]],
  166. ).send(
  167. {from: sender},
  168. function(error, transactionHash){
  169. console.log("https://goerli.etherscan.io/tx/"+transactionHash);
  170. console.log(error);
  171. });
  172. // print secret & nullifier
  173. document.getElementById("depositRes").innerHTML = `
  174. `;
  175. }
  176. loadCircuit("deposit");
  177. loadCircuit("withdraw");
  178. let miksiContract;
  179. async function connectMetamask() {
  180. const ethEnabled = () => {
  181. if (window.web3) {
  182. window.web3 = new Web3(window.web3.currentProvider);
  183. window.ethereum.enable();
  184. return true;
  185. }
  186. return false;
  187. }
  188. if (!ethEnabled()) {
  189. toastr.warning("Please install Metamask to use miksi");
  190. alert("Please install MetaMask to use miksi");
  191. } else {
  192. metamask = true;
  193. }
  194. console.log("abi", abi);
  195. miksiContract = new web3.eth.Contract(abi, miksiAddress);
  196. console.log("miksiContract", miksiContract);
  197. toastr.info("Metamask connected. Miksi contract: ", miksiAddress);
  198. const acc = await web3.eth.getAccounts();
  199. const addr = acc[0];
  200. web3.eth.getBalance(addr, function(err, res){
  201. console.log("current address balance:", JSON.stringify(res));
  202. });
  203. const miksiBalance = await web3.eth.getBalance(miksiAddress);
  204. let html = "<b>miksi</b> Smart Contract current balance: " + miksiBalance/1000000000000000000 + " ETH<br>";
  205. let res = await miksiContract.methods.getCommitments().call();
  206. const commitments = res[0];
  207. const key = res[2];
  208. html += "number of commitments: " + commitments.length + "<br>";
  209. html += "current key: " + key + "<br>";
  210. document.getElementById("stats").innerHTML = html;
  211. }