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 install/connect Metamask");
 50    return;
 51  }
 52  document.getElementById("depositRes").innerHTML = `
 53    Generating zkProof & making the deposit
 54  `;
 55  console.log("circuit:", circuitname);
 56
 57  // TODO
 58  println("generate random secret & nullifier");
 59  const secret = miksi.randBigInt().toString();
 60  const nullifier = miksi.randBigInt().toString();
 61  console.log("S N", secret, nullifier);
 62
 63  println("get commitments from the miksi Smart Contract");
 64  let res = await miksiContract.methods.getCommitments().call();
 65  console.log("res", res);
 66  const commitments = res[0];
 67  const key = res[2];
 68  console.log("commitments", commitments);
 69  console.log("key", key);
 70  // getCommitments from the tree
 71
 72  // calculate witness
 73  println("rebuild the Merkle Tree & calculate witness for deposit");
 74  console.log(witnessCalc[circuitname]);
 75  const cw = await miksi.calcDepositWitness(witnessCalc[circuitname], secret, nullifier, commitments, key).catch((e) => {
 76    toastr.error(e);
 77    printerr(e);
 78  });
 79
 80  const witness = cw.witness;
 81  const publicInputs = cw.publicInputs;
 82  console.log("w", witness);
 83  console.log("publicInputs", publicInputs);
 84
 85  // generate proof
 86  console.log(provingKey[circuitname]);
 87  println("generate zkSNARK Groth16 proof for deposit");
 88  const start = new Date().getTime();
 89  const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
 90  const end = new Date().getTime();
 91  const time = end - start;
 92  println("circuit " + circuitname + " took " + time + "ms to compute");
 93  console.log(proof);
 94
 95
 96  // send tx
 97  const accounts = await web3.eth.getAccounts();
 98  const sender = accounts[0];
 99  console.log("SENDER", sender);
100
101  console.log("sc call data",
102    publicInputs.commitment,
103    publicInputs.root.toString(),
104    [proof.pi_a[0], proof.pi_a[1]],
105    [
106      [proof.pi_b[0][1], proof.pi_b[0][0]],
107      [proof.pi_b[1][1], proof.pi_b[1][0]]
108    ],
109    [proof.pi_c[0], proof.pi_c[1]],
110  );
111  println("send publicInputs & zkProof to the miksi Smart Contract for the deposit");
112  miksiContract.methods.deposit(
113    publicInputs.commitment,
114    publicInputs.root.toString(),
115    [proof.pi_a[0], proof.pi_a[1]],
116    [
117      [proof.pi_b[0][1], proof.pi_b[0][0]],
118      [proof.pi_b[1][1], proof.pi_b[1][0]]
119    ],
120    [proof.pi_c[0], proof.pi_c[1]],
121  ).send(
122    {from: sender, value: 1000000000000000000},
123    function(error, transactionHash){
124      if (error!=undefined) {
125        console.log(error);
126        toastr.error(error);
127        printerr(JSON.stringify(error));
128      } else {
129        let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
130              https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
131        println(link);
132      }
133    });
134
135  // print secret & nullifier
136  let jw = {
137    secret: secret,
138    nullifier: nullifier,
139    key: key
140  };
141  console.log("jw", JSON.stringify(jw));
142  document.getElementById("depositRes").innerHTML = `
143  <b>Please store the secret data in a safe place:</b><br>
144    <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'>
145    </input>
146  `;
147}
148
149async function withdraw(circuitname) {
150  if (!metamask) {
151    toastr.error("Please install/connect Metamask");
152    return;
153  }
154  document.getElementById("withdrawRes").innerHTML = `
155    Generating zkProof & making the withdraw
156  `;
157  console.log("circuit:", circuitname);
158  let jw;
159  try {
160  jw = JSON.parse(document.getElementById("jsonWithdraw").value);
161  } catch(e) {
162    toastr.error("Error reading secret & nullifier: " + e);
163  }
164  const secret = jw.secret;
165  const nullifier = jw.nullifier;
166  const key = jw.key;
167  console.log(secret, nullifier);
168  println("calculate commitment for the secret & nullifier");
169  const commitment = miksi.calcCommitment(secret, nullifier);
170
171  // getCommitments from the tree
172  println("get commitments from the miksi Smart Contract");
173  let res = await miksiContract.methods.getCommitments().call();
174  console.log("res", res);
175  const commitments = res[0];
176  console.log("commitments", commitments);
177
178  // calculate witness
179  console.log(witnessCalc[circuitname]);
180  const addr = document.getElementById("withdrawAddress").value;
181  if (addr==undefined) {
182    toastr.error("No withdraw address defined");
183    return;
184  }
185  if (!web3.utils.isAddress(addr)) {
186    toastr.error("Error with withdraw address");
187    return;
188  }
189  println("rebuild the Merkle Tree & calculate witness for withdraw");
190  const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr, key).catch((e) => {
191    toastr.error(e);
192    printerr(e);
193  });
194
195  const witness = cw.witness;
196  const publicInputs = cw.publicInputs;
197  console.log("w", witness);
198  console.log("publicInputs", publicInputs);
199
200  // generate proof
201  console.log(provingKey[circuitname]);
202  println("generate zkSNARK Groth16 proof for withdraw");
203  const start = new Date().getTime();
204  const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
205  const end = new Date().getTime();
206  const time = end - start;
207  println("circuit " + circuitname + " took " + time + "ms to compute");
208  console.log(proof);
209
210
211  // send tx
212  const accounts = await web3.eth.getAccounts();
213  const sender = accounts[0];
214  console.log("SENDER", sender);
215
216  console.log("sc call data",
217    publicInputs.address,
218    publicInputs.nullifier,
219    [proof.pi_a[0], proof.pi_a[1]],
220    [
221      [proof.pi_b[0][1], proof.pi_b[0][0]],
222      [proof.pi_b[1][1], proof.pi_b[1][0]]
223    ],
224    [proof.pi_c[0], proof.pi_c[1]],
225  );
226  println("send publicInputs & zkProof to the miksi Smart Contract for the withdraw");
227  miksiContract.methods.withdraw(
228    publicInputs.address,
229    publicInputs.nullifier,
230    [proof.pi_a[0], proof.pi_a[1]],
231    [
232      [proof.pi_b[0][1], proof.pi_b[0][0]],
233      [proof.pi_b[1][1], proof.pi_b[1][0]]
234    ],
235    [proof.pi_c[0], proof.pi_c[1]],
236  ).send(
237    {from: sender},
238    function(error, transactionHash){
239      if (error!=undefined) {
240        console.log(error);
241        toastr.error(error);
242        printerr(JSON.stringify(error));
243      } else {
244        let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
245              https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
246        println(link);
247      }
248    });
249}
250
251loadCircuit("deposit");
252loadCircuit("withdraw");
253
254
255
256
257let miksiContract;
258
259async function connectMetamask() {
260  const ethEnabled = () => {
261    if (window.web3) {
262      window.web3 = new Web3(window.web3.currentProvider);
263      window.ethereum.enable();
264      return true;
265    }
266    return false;
267  }
268
269  if (!ethEnabled()) {
270    toastr.warning("Please install Metamask to use miksi");
271    alert("Please install MetaMask to use miksi");
272  } else {
273    metamask = true;
274  }
275
276  console.log("abi", abi);
277  miksiContract = new web3.eth.Contract(abi, miksiAddress);
278  console.log("miksiContract", miksiContract);
279  toastr.info("Metamask connected. Miksi contract: " + miksiAddress);
280  println("Metamask connected. Miksi contract: ", miksiAddress);
281
282  const acc = await web3.eth.getAccounts();
283  const addr = acc[0];
284  web3.eth.getBalance(addr, function(err, res){
285    console.log("current address balance:", JSON.stringify(res));
286  });
287  const miksiBalance = await web3.eth.getBalance(miksiAddress);
288
289  let html = "<b>miksi</b> Smart Contract current balance: " + miksiBalance/1000000000000000000 + " ETH<br>";
290  let res = await miksiContract.methods.getCommitments().call();
291  const commitments = res[0];
292  const key = res[2];
293  html += "number of commitments: " + commitments.length + "<br>";
294  html += "current key: " + key + "<br>";
295  document.getElementById("stats").innerHTML = html;
296
297}