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