arnaucube
arnaucube/miksi-appPublic Self-hosted repository
miksi-app/index.js
index.jsRaw
  1var circuit = {};
  2var provingKey = {};
  3var witnessCalc = {};
  4const 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"}]`);
  5const miksiAddress = "0x4cc45573481A2977fcC0b9DD9f8c710201B5a5cd";
  6let metamask = false;
  7
  8document.getElementById("contractAddr").innerHTML=`<a href="https://goerli.etherscan.io/address/`+miksiAddress+`" target="_blank" title="Miksi Smart Contract Address">`+miksiAddress+`</a>`;
  9
 10function println(...s) {
 11  let r = "";
 12  for (let i=0; i<s.length; i++) {
 13    r = r + " " + s[i];
 14  }
 15  console.log(r);
 16  document.getElementById("logs").innerHTML += r + "<br>";
 17  document.getElementById("logs").scrollTop = document.getElementById("logs").scrollHeight;
 18}
 19function printerr(...s) {
 20  // println(s);
 21  let r = "";
 22  for (let i=0; i<s.length; i++) {
 23    r = r + " " + s[i];
 24  }
 25  console.log(r);
 26  document.getElementById("logs").innerHTML += `<span style="color:red;">Error: ` + r + `</span><br>`;
 27  document.getElementById("logs").scrollTop = document.getElementById("logs").scrollHeight;
 28}
 29
 30function loadCircuit(circuitname) {
 31  fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => {
 32    return response.arrayBuffer();
 33  }).then( (b) => {
 34    provingKey[circuitname] = b;
 35    println("proving_key loaded for", circuitname);
 36  });
 37
 38  fetch("circuits-files/"+circuitname+".wasm").then( (response) => {
 39    return response.arrayBuffer();
 40  }).then( (b) => {
 41    witnessCalc[circuitname] = b;
 42    console.log("w", b);
 43    println("witnessCalc loaded for", circuitname);
 44  });
 45}
 46
 47async function deposit(circuitname) {
 48  if (!metamask) {
 49    toastr.error("Please connect Metamask by clicking on the button at the top of the page");
 50    return;
 51  }
 52  if (window.ethereum.networkVersion!='5') {
 53    toastr.warning("Please switch to Göerli");
 54    alert("Please switch to Göerli");
 55    return;
 56  }
 57  document.getElementById("depositRes").innerHTML = `
 58    Please wait. Generating the zk-proof and making the deposit...
 59  `;
 60  console.log("circuit:", circuitname);
 61
 62  // TODO
 63  println("generate random secret & nullifier");
 64  const secret = miksi.randBigInt().toString();
 65  const nullifier = miksi.randBigInt().toString();
 66  console.log("S N", secret, nullifier);
 67
 68  println("get commitments from the miksi Smart Contract");
 69  let res = await miksiContract.methods.getCommitments().call();
 70  console.log("res", res);
 71  const commitments = res[0];
 72  const key = res[2];
 73  console.log("commitments", commitments);
 74  console.log("key", key);
 75  // getCommitments from the tree
 76
 77  // calculate witness
 78  println("rebuild the Merkle Tree & calculate witness for deposit");
 79  console.log(witnessCalc[circuitname]);
 80  const cw = await miksi.calcDepositWitness(witnessCalc[circuitname], secret, nullifier, commitments, key).catch((e) => {
 81    toastr.error(e);
 82    printerr(e);
 83  });
 84
 85  const witness = cw.witness;
 86  const publicInputs = cw.publicInputs;
 87  console.log("w", witness);
 88  console.log("publicInputs", publicInputs);
 89
 90  // generate proof
 91  console.log(provingKey[circuitname]);
 92  println("generate zkSNARK Groth16 proof for deposit");
 93  const start = new Date().getTime();
 94  const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
 95  const end = new Date().getTime();
 96  const time = end - start;
 97  println("circuit " + circuitname + " took " + time + "ms to compute");
 98  console.log(proof);
 99  console.log("proof", JSON.stringify(proof));
100
101
102  // send tx
103  const accounts = await web3.eth.getAccounts();
104  const sender = accounts[0];
105  console.log("SENDER", sender);
106
107  console.log("sc call data",
108    publicInputs.commitment,
109    publicInputs.root.toString(),
110    [proof.pi_a[0], proof.pi_a[1]],
111    [
112      [proof.pi_b[0][1], proof.pi_b[0][0]],
113      [proof.pi_b[1][1], proof.pi_b[1][0]]
114    ],
115    [proof.pi_c[0], proof.pi_c[1]],
116  );
117  println("send publicInputs & zkProof to the miksi Smart Contract for the deposit");
118  miksiContract.methods.deposit(
119    publicInputs.commitment,
120    publicInputs.root.toString(),
121    [proof.pi_a[0], proof.pi_a[1]],
122    [
123      [proof.pi_b[0][1], proof.pi_b[0][0]],
124      [proof.pi_b[1][1], proof.pi_b[1][0]]
125    ],
126    [proof.pi_c[0], proof.pi_c[1]],
127  ).send(
128    {from: sender, value: 1000000000000000000},
129    function(error, transactionHash){
130      if (error!=undefined) {
131        console.log(error);
132        toastr.error(error);
133        printerr(JSON.stringify(error));
134      } else {
135        let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
136              https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
137        println(link);
138      }
139    });
140
141  // print secret & nullifier
142  let jw = {
143    secret: secret,
144    nullifier: nullifier,
145    key: key
146  };
147  console.log("jw", JSON.stringify(jw));
148  document.getElementById("depositRes").innerHTML = `
149  </br>
150    <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'>
151    </input>
152</br>
153<b>Please store the data above in a safe place (anyone who has it will be able to withdraw your deposited ETH from the contract).</b>
154  `;
155}
156
157async function withdraw(circuitname) {
158  if (!metamask) {
159    toastr.error("Please connect Metamask by clicking on the button at the top of the page");
160    return;
161  }
162  if (window.ethereum.networkVersion!='5') {
163    toastr.warning("Please switch to Göerli");
164    alert("Please switch to Göerli");
165    return;
166  }
167  document.getElementById("withdrawRes").innerHTML = `
168    Please wait. Generating the zk-proof and making the withdrawal...
169  `;
170  console.log("circuit:", circuitname);
171  let jw;
172  try {
173     jw = JSON.parse(document.getElementById("jsonWithdraw").value); 
174  } catch(e) {
175    toastr.error("Error reading secret & nullifier: " + e);
176  }
177  const secret = jw.secret;
178  const nullifier = jw.nullifier;
179  const key = jw.key;
180  console.log(secret, nullifier);
181  println("calculate commitment for the secret & nullifier");
182  const commitment = miksi.calcCommitment(secret, nullifier);
183
184  // getCommitments from the tree
185  println("get commitments from the miksi Smart Contract");
186  let res = await miksiContract.methods.getCommitments().call();
187  console.log("res", res);
188  const commitments = res[0];
189  console.log("commitments", commitments);
190
191  // calculate witness
192  console.log(witnessCalc[circuitname]);
193  const addr = document.getElementById("withdrawAddress").value;
194  if (addr==undefined) {
195    toastr.error("No withdraw address defined");
196    return;
197  }
198  if (!web3.utils.isAddress(addr)) {
199    toastr.error("Error with withdraw address");
200    return;
201  }
202  println("rebuild the Merkle Tree & calculate witness for withdraw");
203  const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr, key).catch((e) => {
204    toastr.error(e);
205    printerr(e);
206  });
207
208  const witness = cw.witness;
209  const publicInputs = cw.publicInputs;
210  console.log("w", witness);
211  console.log("publicInputs", publicInputs);
212
213  // generate proof
214  console.log(provingKey[circuitname]);
215  println("generate zkSNARK Groth16 proof for withdraw");
216  const start = new Date().getTime();
217  const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
218  const end = new Date().getTime();
219  const time = end - start;
220  println("circuit " + circuitname + " took " + time + "ms to compute");
221  console.log(proof);
222
223
224  // send tx
225  const accounts = await web3.eth.getAccounts();
226  const sender = accounts[0];
227  console.log("SENDER", sender);
228
229  console.log("sc call data",
230    publicInputs.address,
231    publicInputs.nullifier,
232    [proof.pi_a[0], proof.pi_a[1]],
233    [
234      [proof.pi_b[0][1], proof.pi_b[0][0]],
235      [proof.pi_b[1][1], proof.pi_b[1][0]]
236    ],
237    [proof.pi_c[0], proof.pi_c[1]],
238  );
239  println("send publicInputs & zkProof to the miksi Smart Contract for the withdraw");
240  miksiContract.methods.withdraw(
241    publicInputs.address,
242    publicInputs.nullifier,
243    [proof.pi_a[0], proof.pi_a[1]],
244    [
245      [proof.pi_b[0][1], proof.pi_b[0][0]],
246      [proof.pi_b[1][1], proof.pi_b[1][0]]
247    ],
248    [proof.pi_c[0], proof.pi_c[1]],
249  ).send(
250    {from: sender},
251    function(error, transactionHash){
252      if (error!=undefined) {
253        console.log(error);
254        toastr.error(error);
255        printerr(JSON.stringify(error));
256      } else {
257        let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
258              https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
259         println(link);
260	 document.getElementById("withdrawRes").innerHTML = 'Success :)'
261      }
262    });
263}
264
265loadCircuit("deposit");
266loadCircuit("withdraw");
267
268
269
270
271let miksiContract;
272
273async function connectMetamask() {
274  const ethEnabled = () => {
275    if (window.web3) {
276      window.web3 = new Web3(window.web3.currentProvider);
277      window.ethereum.enable();
278      return true;
279    }
280    return false;
281  }
282
283  if (!ethEnabled()) {
284    toastr.warning("Please install Metamask to use miksi");
285    alert("Please install MetaMask to use miksi");
286    return;
287  } else if (window.ethereum.networkVersion!='5') {
288    toastr.warning("Please switch to Göerli");
289    alert("Please switch to Göerli");
290    return;
291  } else {
292    metamask = true;
293  }
294
295  console.log("abi", abi);
296  miksiContract = new web3.eth.Contract(abi, miksiAddress);
297  console.log("miksiContract", miksiContract);
298  toastr.info("Metamask connected. Miksi contract: " + miksiAddress);
299  println("Metamask connected. Miksi contract: ", miksiAddress);
300
301  const acc = await web3.eth.getAccounts();
302  const addr = acc[0];
303  web3.eth.getBalance(addr, function(err, res){
304    console.log("current address balance:", JSON.stringify(res));
305  });
306  const miksiBalance = await web3.eth.getBalance(miksiAddress);
307
308  let html = "<b>miksi</b> Smart Contract current balance: " + miksiBalance/1000000000000000000 + " ETH<br>";
309  let res = await miksiContract.methods.getCommitments().call();
310  const commitments = res[0];
311  const key = res[2];
312  html += "number of commitments: " + commitments.length + "<br>";
313  html += "current key: " + key + "<br>";
314  document.getElementById("stats").innerHTML = html;
315
316}