arnaucube
arnaucube/miksi-appPublic Self-hosted repository
Contract Withdraw call successfully called with zkproof
arnaucube root@arnaucube.com
Thu, 14 May 2020 19:43:30 +0200
commit

ae07f704444f19e47a123a8725e8d1c1fdba2450

parent

693d6340649f96537f87711c4edafad07bf9ca62

4 files changed, 177 insertions(+), 25 deletions(-)

jump to
M css/dark-theme.css → css/dark-theme.css

@@ -6,15 +6,16 @@ .dark-theme

.card, .list-group-item, input, + textarea, .nav-link { background: none!important; border-color: #3b4145; } -.dark-theme input { - color: #FF00B9; +.dark-theme input, textarea { + color: #FF00B9!important; } -.dark-theme input:focus { - color: #FF00B9; +.dark-theme input:focus, textarea:focus { + color: #FF00B9!important; box-shadow: 0px 0px 5px #FF00B9!important; border: none; }
M index.html → index.html

@@ -67,7 +67,7 @@ a

Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit. <br><br> <button onclick="deposit('deposit')" class="btn color_primary float-right">Deposit 1 ETH</button> - <br> + <br><br> <div id="depositRes"></div> <br><br> </div>

@@ -76,9 +76,9 @@ <h5 class="card-title">Withdraw</h5>

<hr> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. <br><br> - <input id="secret" type="text" class="form-control" placeholder="secret" value="1234567890"></input> + <textarea id="jsonWithdraw" class="form-control" style="width:100%;" placeholder="paste here your secret & nullifier"></textarea> <br> - <input id="nullifier" type="text" class="form-control" placeholder="nullifier" value="567891234"></input> + <input id="withdrawAddress" class="form-control" placeholder="address where to withdraw"></input> <br> <button onclick="withdraw('withdraw')" class="btn color_primary float-right">Withdraw 1 ETH</button> <br>
M index.js → index.js

@@ -2,7 +2,8 @@ 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"}],"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 = "0x6E77f4bB1356426baD1Bd014d04388eFAc197Fe1"; +// const miksiAddress = "0x6E77f4bB1356426baD1Bd014d04388eFAc197Fe1"; +const miksiAddress = "0xc9009d9Df413883B6064503FDA93A7A8ab9B7afb"; function loadCircuit(circuitname) { fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => {

@@ -28,18 +29,20 @@ `;

console.log("circuit:", circuitname); // TODO - const secret = "1234567890"; - const nullifier = "567891234"; + const secret = miksi.randBigInt().toString(); + const nullifier = miksi.randBigInt().toString(); + // const secret = "1234567890"; + // const nullifier = "5678901234"; const commitments = []; // getCommitments from the tree // calculate witness console.log(witnessCalc[circuitname]); - const cw = await miksi.calcWitness(witnessCalc[circuitname], secret, nullifier, commitments); + const cw = await miksi.calcDepositWitness(witnessCalc[circuitname], secret, nullifier, commitments); const witness = cw.witness; const publicInputs = cw.publicInputs; console.log("w", witness); - console.log("pi", publicInputs); + console.log("publicInputs", publicInputs); // generate proof const start = new Date().getTime();

@@ -83,9 +86,88 @@ console.log(error);

}); // print secret & nullifier + let jw = { + secret: secret, + nullifier: nullifier + }; + console.log("jw", JSON.stringify(jw)); document.getElementById("depositRes").innerHTML = ` - Secret: <b>`+secret+`</b><br> - Nullifier: <b>`+nullifier+`</b><br> + <b>Please store the secret data in a safe place:</b><br> + <input class="form-control" onClick="this.select();" readonly value='`+JSON.stringify(jw)+`'> + </input> + `; +} + +async function withdraw(circuitname) { + document.getElementById("withdrawRes").innerHTML = ` + Generating zkProof & making the withdraw + `; + console.log("circuit:", circuitname); + const jw = JSON.parse(document.getElementById("jsonWithdraw").value); + const secret = jw.secret; + const nullifier = jw.nullifier; + console.log(secret, nullifier); + const commitment = miksi.calcCommitment(secret, nullifier); + + // getCommitments from the tree + 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 proverAccounts = await web3.eth.getAccounts(); + const addr = proverAccounts[0]; + const cw = await miksi.calcWithdrawWitness(witnessCalc[circuitname], secret, nullifier, commitments, addr); + const witness = cw.witness; + const publicInputs = cw.publicInputs; + console.log("w", witness); + console.log("publicInputs", publicInputs); + + // generate proof + const start = new Date().getTime(); + console.log(provingKey[circuitname]); + const proof = await window.groth16GenProof(witness.buffer, provingKey[circuitname]); + const end = new Date().getTime(); + const time = end - start; + console.log("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]], + ); + 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){ + console.log("https://goerli.etherscan.io/tx/"+transactionHash); + console.log(error); + }); + + // print secret & nullifier + document.getElementById("depositRes").innerHTML = ` `; }

@@ -97,7 +179,7 @@

let miksiContract; -function connectMetamask() { +async function connectMetamask() { const ethEnabled = () => { if (window.web3) { window.web3 = new Web3(window.web3.currentProvider);

@@ -115,8 +197,8 @@ console.log("abi", abi);

miksiContract = new web3.eth.Contract(abi, miksiAddress); console.log("miksiContract", miksiContract); - web3.eth.getBalance("0x35d4dCDdB728CeBF80F748be65bf84C776B0Fbaf", function(err, res){console.log("BAL", JSON.stringify(res));}); + const acc = await web3.eth.getAccounts(); + const addr = acc[0]; + web3.eth.getBalance(addr, function(err, res){console.log("BAL", JSON.stringify(res));}); - miksiContract.methods.getCommitments().call() - .then(console.log); }
M lib/miksi-browser.js → lib/miksi-browser.js

@@ -76276,12 +76276,12 @@ (function (Buffer){

const fs = require("fs"); const bigInt = require("big-integer"); const { groth } = require('snarkjs'); +const { Fr } = require('ffjavascript').bn128; const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils; const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder; const circomlib = require("circomlib"); const smt = require("circomlib").smt; const Web3 = require("web3"); -// const buildBn128 = require("wasmsnark").buildBn128; const nLevels = 5;

@@ -76289,18 +76289,26 @@ const coinCode = "0"; // refearing to ETH

const ethAmount = '1'; const amount = Web3.utils.toWei(ethAmount, 'ether'); -// let bn128; -// -// exports.init = async () => { -// bn128 = await buildBn128(); -// } +exports.randBigInt = () => { + return Fr.random(); +}; -exports.calcWitness = async (wasm, secret, nullifier, commitments) => { +exports.calcCommitment = (secret, nullifier) => { const poseidon = circomlib.poseidon.createHash(6, 8, 57); const commitment = poseidon([coinCode, amount, secret, nullifier]).toString(); + return commitment; +}; +exports.calcDepositWitness = async (wasm, secret, nullifier, commitments) => { + const poseidon = circomlib.poseidon.createHash(6, 8, 57); + const commitment = poseidon([coinCode, amount, secret, nullifier]).toString(); + + // rebuild the tree let tree = await smt.newMemEmptyTrie(); await tree.insert(1, 0); + for (let i=0; i<commitments.length; i++) { + await tree.insert(commitments[i], 0); + } // old root const rootOld = tree.root;

@@ -76367,6 +76375,67 @@ }

}; } +exports.calcWithdrawWitness = async (wasm, secret, nullifier, commitments, addr) => { + const poseidon = circomlib.poseidon.createHash(6, 8, 57); + const commitment = poseidon([coinCode, amount, secret, nullifier]).toString(); + + // rebuild the tree + let tree = await smt.newMemEmptyTrie(); + await tree.insert(1, 0); + for (let i=0; i<commitments.length; i++) { + await tree.insert(commitments[i], 0); + } + // await tree.insert(commitment, 0); + + // root + const root = tree.root; + const res = await tree.find(commitment); + if (!res.found) { + console.error("leaf expect to exist but not exists"); + } + let siblings = res.siblings; + while (siblings.length < nLevels) { + siblings.push("0"); + }; + + // calculate witness + const input = unstringifyBigInts({ + "coinCode": coinCode, + "amount": amount, + "secret": secret, + "nullifier": nullifier, + "siblings": siblings, + "root": root, + "address": addr + }); + console.log("input", input); + // const options = {}; + // const wc = await WitnessCalculatorBuilder(wasm, options); + + const wc = await WitnessCalculatorBuilder(wasm); + + const witness = await wc.calculateWitness(input, {sanityCheck: true}); + + const wBuff = Buffer.allocUnsafe(witness.length*32); + + for (let i=0; i<witness.length; i++) { + for (let j=0; j<8; j++) { + const bi = witness[i]; + const v = bigInt(bi).shiftRight(j*32).and(0xFFFFFFFF).toJSNumber(); + wBuff.writeUInt32LE(v, i*32 + j*4, 4) + } + } + + + // const witness = unstringifyBigInts(stringifyBigInts(w)); + return { + witness: wBuff, + publicInputs: { + address:addr, + nullifier:nullifier + } + }; +} }).call(this,require("buffer").Buffer)