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