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 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 Generating zkProof & 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 <b>Please store the secret data in a safe place:</b><br>
150 <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'>
151 </input>
152 `;
153}
154
155async function withdraw(circuitname) {
156 if (!metamask) {
157 toastr.error("Please install/connect Metamask");
158 return;
159 }
160 if (window.ethereum.networkVersion!='5') {
161 toastr.warning("Please switch to Göerli");
162 alert("Please switch to Göerli");
163 return;
164 }
165 document.getElementById("withdrawRes").innerHTML = `
166 Generating zkProof & making the withdraw
167 `;
168 console.log("circuit:", circuitname);
169 let jw;
170 try {
171 jw = JSON.parse(document.getElementById("jsonWithdraw").value);
172 } catch(e) {
173 toastr.error("Error reading secret & nullifier: " + e);
174 }
175 const secret = jw.secret;
176 const nullifier = jw.nullifier;
177 const key = jw.key;
178 console.log(secret, nullifier);
179 println("calculate commitment for the secret & nullifier");
180 const commitment = miksi.calcCommitment(secret, nullifier);
181
182 // getCommitments from the tree
183 println("get commitments from the miksi Smart Contract");
184 let res = await miksiContract.methods.getCommitments().call();
185 console.log("res", res);
186 const commitments = res[0];
187 console.log("commitments", commitments);
188
189 // calculate witness
190 console.log(witnessCalc[circuitname]);
191 const addr = document.getElementById("withdrawAddress").value;
192 if (addr==undefined) {
193 toastr.error("No withdraw address defined");
194 return;
195 }
196 if (!web3.utils.isAddress(addr)) {
197 toastr.error("Error with withdraw address");
198 return;
199 }
200 println("rebuild the Merkle Tree & calculate witness for withdraw");
201 const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr, key).catch((e) => {
202 toastr.error(e);
203 printerr(e);
204 });
205
206 const witness = cw.witness;
207 const publicInputs = cw.publicInputs;
208 console.log("w", witness);
209 console.log("publicInputs", publicInputs);
210
211 // generate proof
212 console.log(provingKey[circuitname]);
213 println("generate zkSNARK Groth16 proof for withdraw");
214 const start = new Date().getTime();
215 const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]);
216 const end = new Date().getTime();
217 const time = end - start;
218 println("circuit " + circuitname + " took " + time + "ms to compute");
219 console.log(proof);
220
221
222 // send tx
223 const accounts = await web3.eth.getAccounts();
224 const sender = accounts[0];
225 console.log("SENDER", sender);
226
227 console.log("sc call data",
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 );
237 println("send publicInputs & zkProof to the miksi Smart Contract for the withdraw");
238 miksiContract.methods.withdraw(
239 publicInputs.address,
240 publicInputs.nullifier,
241 [proof.pi_a[0], proof.pi_a[1]],
242 [
243 [proof.pi_b[0][1], proof.pi_b[0][0]],
244 [proof.pi_b[1][1], proof.pi_b[1][0]]
245 ],
246 [proof.pi_c[0], proof.pi_c[1]],
247 ).send(
248 {from: sender},
249 function(error, transactionHash){
250 if (error!=undefined) {
251 console.log(error);
252 toastr.error(error);
253 printerr(JSON.stringify(error));
254 } else {
255 let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
256 https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
257 println(link);
258 }
259 });
260}
261
262loadCircuit("deposit");
263loadCircuit("withdraw");
264
265
266
267
268let miksiContract;
269
270async function connectMetamask() {
271 const ethEnabled = () => {
272 if (window.web3) {
273 window.web3 = new Web3(window.web3.currentProvider);
274 window.ethereum.enable();
275 return true;
276 }
277 return false;
278 }
279
280 if (!ethEnabled()) {
281 toastr.warning("Please install Metamask to use miksi");
282 alert("Please install MetaMask to use miksi");
283 return;
284 } else if (window.ethereum.networkVersion!='5') {
285 toastr.warning("Please switch to Göerli");
286 alert("Please switch to Göerli");
287 return;
288 } else {
289 metamask = true;
290 }
291
292 console.log("abi", abi);
293 miksiContract = new web3.eth.Contract(abi, miksiAddress);
294 console.log("miksiContract", miksiContract);
295 toastr.info("Metamask connected. Miksi contract: " + miksiAddress);
296 println("Metamask connected. Miksi contract: ", miksiAddress);
297
298 const acc = await web3.eth.getAccounts();
299 const addr = acc[0];
300 web3.eth.getBalance(addr, function(err, res){
301 console.log("current address balance:", JSON.stringify(res));
302 });
303 const miksiBalance = await web3.eth.getBalance(miksiAddress);
304
305 let html = "<b>miksi</b> Smart Contract current balance: " + miksiBalance/1000000000000000000 + " ETH<br>";
306 let res = await miksiContract.methods.getCommitments().call();
307 const commitments = res[0];
308 const key = res[2];
309 html += "number of commitments: " + commitments.length + "<br>";
310 html += "current key: " + key + "<br>";
311 document.getElementById("stats").innerHTML = html;
312
313}