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}