Browse Source

Contract Withdraw call successfully called with zkproof

pull/1/head
arnaucube 4 years ago
parent
commit
ae07f70444
4 changed files with 177 additions and 25 deletions
  1. +5
    -4
      css/dark-theme.css
  2. +3
    -3
      index.html
  3. +93
    -11
      index.js
  4. +76
    -7
      lib/miksi-browser.js

+ 5
- 4
css/dark-theme.css

@ -6,15 +6,16 @@
.card, .card,
.list-group-item, .list-group-item,
input, input,
textarea,
.nav-link { .nav-link {
background: none!important; background: none!important;
border-color: #3b4145; 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; box-shadow: 0px 0px 5px #FF00B9!important;
border: none; border: none;
} }

+ 3
- 3
index.html

@ -67,7 +67,7 @@
Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit. Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
<br><br> <br><br>
<button onclick="deposit('deposit')" class="btn color_primary float-right">Deposit 1 ETH</button> <button onclick="deposit('deposit')" class="btn color_primary float-right">Deposit 1 ETH</button>
<br>
<br><br>
<div id="depositRes"></div> <div id="depositRes"></div>
<br><br> <br><br>
</div> </div>
@ -76,9 +76,9 @@
<hr> <hr>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.
<br><br> <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> <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> <br>
<button onclick="withdraw('withdraw')" class="btn color_primary float-right">Withdraw 1 ETH</button> <button onclick="withdraw('withdraw')" class="btn color_primary float-right">Withdraw 1 ETH</button>
<br> <br>

+ 93
- 11
index.js

@ -2,7 +2,8 @@ var circuit = {};
var provingKey = {}; var provingKey = {};
var witnessCalc = {}; 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 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) { function loadCircuit(circuitname) {
fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => { fetch("circuits-files/"+circuitname+"-proving_key.bin").then( (response) => {
@ -28,18 +29,20 @@ async function deposit(circuitname) {
console.log("circuit:", circuitname); console.log("circuit:", circuitname);
// TODO // 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 = []; const commitments = [];
// getCommitments from the tree // getCommitments from the tree
// calculate witness // calculate witness
console.log(witnessCalc[circuitname]); 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 witness = cw.witness;
const publicInputs = cw.publicInputs; const publicInputs = cw.publicInputs;
console.log("w", witness); console.log("w", witness);
console.log("pi", publicInputs);
console.log("publicInputs", publicInputs);
// generate proof // generate proof
const start = new Date().getTime(); const start = new Date().getTime();
@ -82,10 +85,89 @@ async function deposit(circuitname) {
console.log(error); console.log(error);
}); });
// print secret & nullifier
let jw = {
secret: secret,
nullifier: nullifier
};
console.log("jw", JSON.stringify(jw));
document.getElementById("depositRes").innerHTML = `
<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 // print secret & nullifier
document.getElementById("depositRes").innerHTML = ` document.getElementById("depositRes").innerHTML = `
Secret: <b>`+secret+`</b><br>
Nullifier: <b>`+nullifier+`</b><br>
`; `;
} }
@ -97,7 +179,7 @@ loadCircuit("withdraw");
let miksiContract; let miksiContract;
function connectMetamask() {
async function connectMetamask() {
const ethEnabled = () => { const ethEnabled = () => {
if (window.web3) { if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider); window.web3 = new Web3(window.web3.currentProvider);
@ -115,8 +197,8 @@ function connectMetamask() {
miksiContract = new web3.eth.Contract(abi, miksiAddress); miksiContract = new web3.eth.Contract(abi, miksiAddress);
console.log("miksiContract", miksiContract); 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);
} }

+ 76
- 7
lib/miksi-browser.js

@ -76276,12 +76276,12 @@ function extend() {
const fs = require("fs"); const fs = require("fs");
const bigInt = require("big-integer"); const bigInt = require("big-integer");
const { groth } = require('snarkjs'); const { groth } = require('snarkjs');
const { Fr } = require('ffjavascript').bn128;
const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils; const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils;
const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder; const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
const circomlib = require("circomlib"); const circomlib = require("circomlib");
const smt = require("circomlib").smt; const smt = require("circomlib").smt;
const Web3 = require("web3"); const Web3 = require("web3");
// const buildBn128 = require("wasmsnark").buildBn128;
const nLevels = 5; const nLevels = 5;
@ -76289,18 +76289,26 @@ const coinCode = "0"; // refearing to ETH
const ethAmount = '1'; const ethAmount = '1';
const amount = Web3.utils.toWei(ethAmount, 'ether'); 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 poseidon = circomlib.poseidon.createHash(6, 8, 57);
const commitment = poseidon([coinCode, amount, secret, nullifier]).toString(); 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(); let tree = await smt.newMemEmptyTrie();
await tree.insert(1, 0); await tree.insert(1, 0);
for (let i=0; i<commitments.length; i++) {
await tree.insert(commitments[i], 0);
}
// old root // old root
const rootOld = tree.root; const rootOld = tree.root;
@ -76367,6 +76375,67 @@ exports.calcWitness = async (wasm, secret, nullifier, commitments) => {
}; };
} }
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) }).call(this,require("buffer").Buffer)

Loading…
Cancel
Save