|
|
var circuit = {}; var provingKey = {}; var witnessCalc = {}; const 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"}]`); const miksiAddress = "0x4cc45573481A2977fcC0b9DD9f8c710201B5a5cd"; let metamask = false;
document.getElementById("contractAddr").innerHTML=`<a href="https://goerli.etherscan.io/address/`+miksiAddress+`" target="_blank" title="Miksi Smart Contract Address">`+miksiAddress+`</a>`;
function println(...s) { let r = ""; for (let i=0; i<s.length; i++) { r = r + " " + s[i]; } console.log(r); document.getElementById("logs").innerHTML += r + "<br>"; document.getElementById("logs").scrollTop = document.getElementById("logs").scrollHeight; } function printerr(...s) { // println(s);
let r = ""; for (let i=0; i<s.length; i++) { r = r + " " + s[i]; } console.log(r); document.getElementById("logs").innerHTML += `<span style="color:red;">Error: ` + r + `</span><br>`; document.getElementById("logs").scrollTop = document.getElementById("logs").scrollHeight; }
function loadCircuit(circuitname) { fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => { return response.arrayBuffer(); }).then( (b) => { provingKey[circuitname] = b; println("proving_key loaded for", circuitname); });
fetch("circuits-files/"+circuitname+".wasm").then( (response) => { return response.arrayBuffer(); }).then( (b) => { witnessCalc[circuitname] = b; console.log("w", b); println("witnessCalc loaded for", circuitname); }); }
async function deposit(circuitname) { if (!metamask) { toastr.error("Please connect Metamask by clicking on the button at the top of the page"); return; } if (window.ethereum.networkVersion!='5') { toastr.warning("Please switch to Göerli"); alert("Please switch to Göerli"); return; } document.getElementById("depositRes").innerHTML = `
Please wait. Generating the zk-proof and making the deposit... `;
console.log("circuit:", circuitname);
// TODO
println("generate random secret & nullifier"); const secret = miksi.randBigInt().toString(); const nullifier = miksi.randBigInt().toString(); console.log("S N", secret, nullifier);
println("get commitments from the miksi Smart Contract"); let res = await miksiContract.methods.getCommitments().call(); console.log("res", res); const commitments = res[0]; const key = res[2]; console.log("commitments", commitments); console.log("key", key); // getCommitments from the tree
// calculate witness
println("rebuild the Merkle Tree & calculate witness for deposit"); console.log(witnessCalc[circuitname]); const cw = await miksi.calcDepositWitness(witnessCalc[circuitname], secret, nullifier, commitments, key).catch((e) => { toastr.error(e); printerr(e); });
const witness = cw.witness; const publicInputs = cw.publicInputs; console.log("w", witness); console.log("publicInputs", publicInputs);
// generate proof
console.log(provingKey[circuitname]); println("generate zkSNARK Groth16 proof for deposit"); const start = new Date().getTime(); const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]); const end = new Date().getTime(); const time = end - start; println("circuit " + circuitname + " took " + time + "ms to compute"); console.log(proof); console.log("proof", JSON.stringify(proof));
// send tx
const accounts = await web3.eth.getAccounts(); const sender = accounts[0]; console.log("SENDER", sender);
console.log("sc call data", publicInputs.commitment, publicInputs.root.toString(), [proof.pi_a[0], proof.pi_a[1]], [ [proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]] ], [proof.pi_c[0], proof.pi_c[1]], ); println("send publicInputs & zkProof to the miksi Smart Contract for the deposit"); miksiContract.methods.deposit( publicInputs.commitment, publicInputs.root.toString(), [proof.pi_a[0], proof.pi_a[1]], [ [proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]] ], [proof.pi_c[0], proof.pi_c[1]], ).send( {from: sender, value: 1000000000000000000}, function(error, transactionHash){ if (error!=undefined) { console.log(error); toastr.error(error); printerr(JSON.stringify(error)); } else { let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
println(link); } });
// print secret & nullifier
let jw = { secret: secret, nullifier: nullifier, key: key }; console.log("jw", JSON.stringify(jw)); document.getElementById("depositRes").innerHTML = `
</br> <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'> </input> </br> <b>Please store the data above in a safe place (anyone who has it will be able to withdraw your deposited ETH from the contract).</b> `;
}
async function withdraw(circuitname) { if (!metamask) { toastr.error("Please connect Metamask by clicking on the button at the top of the page"); return; } if (window.ethereum.networkVersion!='5') { toastr.warning("Please switch to Göerli"); alert("Please switch to Göerli"); return; } document.getElementById("withdrawRes").innerHTML = `
Please wait. Generating the zk-proof and making the withdrawal... `;
console.log("circuit:", circuitname); let jw; try { jw = JSON.parse(document.getElementById("jsonWithdraw").value); } catch(e) { toastr.error("Error reading secret & nullifier: " + e); } const secret = jw.secret; const nullifier = jw.nullifier; const key = jw.key; console.log(secret, nullifier); println("calculate commitment for the secret & nullifier"); const commitment = miksi.calcCommitment(secret, nullifier);
// getCommitments from the tree
println("get commitments from the miksi Smart Contract"); let res = await miksiContract.methods.getCommitments().call(); console.log("res", res); const commitments = res[0]; console.log("commitments", commitments);
// calculate witness
console.log(witnessCalc[circuitname]); const addr = document.getElementById("withdrawAddress").value; if (addr==undefined) { toastr.error("No withdraw address defined"); return; } if (!web3.utils.isAddress(addr)) { toastr.error("Error with withdraw address"); return; } println("rebuild the Merkle Tree & calculate witness for withdraw"); const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr, key).catch((e) => { toastr.error(e); printerr(e); });
const witness = cw.witness; const publicInputs = cw.publicInputs; console.log("w", witness); console.log("publicInputs", publicInputs);
// generate proof
console.log(provingKey[circuitname]); println("generate zkSNARK Groth16 proof for withdraw"); const start = new Date().getTime(); const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]); const end = new Date().getTime(); const time = end - start; println("circuit " + circuitname + " took " + time + "ms to compute"); console.log(proof);
// send tx
const accounts = await web3.eth.getAccounts(); const sender = accounts[0]; console.log("SENDER", sender);
console.log("sc call data", publicInputs.address, publicInputs.nullifier, [proof.pi_a[0], proof.pi_a[1]], [ [proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]] ], [proof.pi_c[0], proof.pi_c[1]], ); println("send publicInputs & zkProof to the miksi Smart Contract for the withdraw"); miksiContract.methods.withdraw( publicInputs.address, publicInputs.nullifier, [proof.pi_a[0], proof.pi_a[1]], [ [proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]] ], [proof.pi_c[0], proof.pi_c[1]], ).send( {from: sender}, function(error, transactionHash){ if (error!=undefined) { console.log(error); toastr.error(error); printerr(JSON.stringify(error)); } else { let link = `<a href="https://goerli.etherscan.io/tx/`+transactionHash+`" target="_blank">
https://goerli.etherscan.io/tx/`+transactionHash+`</a>`;
println(link); document.getElementById("withdrawRes").innerHTML = 'Success :)' } }); }
loadCircuit("deposit"); loadCircuit("withdraw");
let miksiContract;
async function connectMetamask() { const ethEnabled = () => { if (window.web3) { window.web3 = new Web3(window.web3.currentProvider); window.ethereum.enable(); return true; } return false; }
if (!ethEnabled()) { toastr.warning("Please install Metamask to use miksi"); alert("Please install MetaMask to use miksi"); return; } else if (window.ethereum.networkVersion!='5') { toastr.warning("Please switch to Göerli"); alert("Please switch to Göerli"); return; } else { metamask = true; }
console.log("abi", abi); miksiContract = new web3.eth.Contract(abi, miksiAddress); console.log("miksiContract", miksiContract); toastr.info("Metamask connected. Miksi contract: " + miksiAddress); println("Metamask connected. Miksi contract: ", miksiAddress);
const acc = await web3.eth.getAccounts(); const addr = acc[0]; web3.eth.getBalance(addr, function(err, res){ console.log("current address balance:", JSON.stringify(res)); }); const miksiBalance = await web3.eth.getBalance(miksiAddress);
let html = "<b>miksi</b> Smart Contract current balance: " + miksiBalance/1000000000000000000 + " ETH<br>"; let res = await miksiContract.methods.getCommitments().call(); const commitments = res[0]; const key = res[2]; html += "number of commitments: " + commitments.length + "<br>"; html += "current key: " + key + "<br>"; document.getElementById("stats").innerHTML = html;
}
|