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,
.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;
}

+ 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.
<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 @@
<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>

+ 93
- 11
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 @@ async function deposit(circuitname) {
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();
@ -82,10 +85,89 @@ async function deposit(circuitname) {
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
document.getElementById("depositRes").innerHTML = `
Secret: <b>`+secret+`</b><br>
Nullifier: <b>`+nullifier+`</b><br>
`;
}
@ -97,7 +179,7 @@ loadCircuit("withdraw");
let miksiContract;
function connectMetamask() {
async function connectMetamask() {
const ethEnabled = () => {
if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider);
@ -115,8 +197,8 @@ function connectMetamask() {
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);
}

+ 76
- 7
lib/miksi-browser.js

@ -76276,12 +76276,12 @@ function extend() {
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.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)

Loading…
Cancel
Save