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}