messy but 'the proof is valid' for semaphore, finally

This commit is contained in:
Oskar Thoren 2019-10-31 20:38:50 +08:00
parent b9ae969427
commit 2395eab71f
No known key found for this signature in database
GPG Key ID: B2ECCFD3BC2EF77E
1 changed files with 164 additions and 44 deletions

View File

@ -1,21 +1,27 @@
const crypto = require('crypto');
var fs = require('fs');
const fs = require('fs');
const zkSnark = require('snarkjs');
const circomlib = require('circomlib');
const web3Utils = require('web3-utils');
const ethers = require('ethers');
const SMT = require('semaphore-merkle-tree');
const {stringifyBigInts, unstringifyBigInts} = require("snarkjs/src/stringifybigint.js");
//const {stringifyBigInts, unstringifyBigInts} = require("./node_modules/snarkjs/src/stringifybigint.js");
const asciiToHex = web3Utils.asciiToHex;
const bigInt = zkSnark.bigInt;
const eddsa = circomlib.eddsa;
const mimcsponge = circomlib.mimcsponge;
// Utils
function pedersenHash(ints) {
const p = circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(Buffer.concat(
ints.map(x => x.leInt2Buff(32))
)));
return bigInt(p[0]);
}
beBuff2int = function(buff) {
let res = bigInt.zero;
for (let i=0; i<buff.length; i++) {
@ -25,6 +31,8 @@ beBuff2int = function(buff) {
return res;
};
// XXX: stringifyBigInts etc probably more robust
// Dealing with JSON BigInt serialization/deserialization
function stringifyReplacer(key, value) {
if (typeof value === 'bigint') {
@ -85,12 +93,19 @@ const circuit = new zkSnark.Circuit(circuitDef);
// 3. Perform setup - only done once
function performSetup(circuit) {
const setup = zkSnark.groth.setup(circuit);
fs.writeFileSync("build/proving_key.json", serialize(setup.vk_proof), "utf8");
fs.writeFileSync("build/verification_key.json", serialize(setup.vk_verifier), "utf8");
/// XXX run this again - takes 1h so do at home
fs.writeFileSync("myCircuit.vk_proof", JSON.stringify(stringifyBigInts(setup.vk_proof)), "utf8");
fs.writeFileSync("myCircuit.vk_verifier", JSON.stringify(stringifyBigInts(setup.vk_verifier)), "utf8");
// fs.writeFileSync("build/proving_key.json", serialize(setup.vk_proof), "utf8");
// fs.writeFileSync("build/verification_key.json", serialize(setup.vk_verifier), "utf8");
console.log("Toxic waste:", setup.toxic); // Must be discarded.
}
// TODO: Conditionally run if file exists
// performSetup(circuit);
//performSetup(circuit);
//console.log("Done, exiting");
//process.exit(1);
// TODO: run this to get new proving key with stringified etc
////////////////////////////////////////////////////////////
@ -146,58 +161,163 @@ const pubKey = eddsa.prv2pub(prvKey);
// XXX: What are these exactly?
let identity_nullifier = loaded_identity.identity_nullifier;
let identity_trapdoor = loaded_identity.identity_trapdoor;
let identity_commitment = loaded_identity.identity_commitment;
// identity_path we await from server, glurgh.
// XXX: Not an easy place to leave, but that's where I am at
// server.js: path_for_element/:el
// semaphore.tree.element_index(leaf)
// identity tree index, MerkleTree
// HERE AM
const identity_path_elements = identity_path.path_elements;
const identity_path_index = identity_path.path_index;
assert(eddsa.verifyMiMCSponge(msg, signature, pubKey));
// => true
// Construct tree
const storage = new SMT.storage.MemStorage();
const hasher = new SMT.hashers.MimcSpongeHasher();
const prefix = 'semaphore';
const default_value = '0';
const depth = 20; // quite deep?
// What are the different types of signature fields?
//async function foo() {
// loaded_identity.identity_nullifier?
const tree = new SMT.tree.MerkleTree(
`${prefix}_id_tree`,
storage,
hasher,
depth,
default_value,
);
const inputs = {
'identity_pk[0]': pubKey[0],
'identity_pk[1]': pubKey[1],
'auth_sig_r[0]': signature.R8[0],
'auth_sig_r[1]': signature.R8[1],
auth_sig_s: signature.S,
signal_hash,
external_nullifier,
identity_nullifier,
identity_trapdoor,
identity_path_elements,
identity_path_index,
fake_zero: bigInt(0),
// ok, we got the tree, now what?
// Add to the tree! we ge that identity commitment
// get tree index, etc etc
// first have to add to tree
// let loaded_identity = generate_identity();
// semapohore url
// server://path_for_element/:identity_commitment OR if exists
// server://path/identity.index
// 1. How is tree constructed?
const leaf = identity_commitment; // element
// tostring?
console.log("leaf", leaf);
// adding identity commitment to tree
let leaf_index;
let path;
// XXX: Ugly as hell here
async function updateTree() {
await tree.update(1, leaf);
leaf_index = await tree.element_index(leaf);
// find path to a index containing identity commitment;
path = await tree.path(leaf_index);
console.log("***leaf_index", leaf_index);
};
// const w = circuit.calculateWitness(inputs);
// assert(circuit.checkWitness(w));
// returns promise, we can use .then and go
updateTree()
.then(() => {
console.log("***leaf_index outside", leaf_index);
console.log("tree", tree);
bar();
})
.catch((err) => {
console.log("error", err);
});
// XXX
let inputs;
let identity_path;
let witness;
let vk_proof;
let proof;
let publicSignals;
function bar() {
console.log("path", leaf_index);
identity_path = path;
console.log("identity_path", identity_path);
// XXX
const identity_path_elements = identity_path.path_elements;
console.log("identity_path_elements", identity_path_elements);
const identity_path_index = identity_path.path_index;
assert(eddsa.verifyMiMCSponge(msg, signature, pubKey));
// => true
// What are the different types of signature fields?
// loaded_identity.identity_nullifier?
inputs = {
'identity_pk[0]': pubKey[0],
'identity_pk[1]': pubKey[1],
'auth_sig_r[0]': signature.R8[0],
'auth_sig_r[1]': signature.R8[1],
auth_sig_s: signature.S,
// XXX
signal_hash,
external_nullifier,
identity_nullifier,
identity_trapdoor,
identity_path_elements,
identity_path_index,
fake_zero: bigInt(0),
};
// So we can define inputs skeleton before
witness = circuit.calculateWitness(inputs);
assert(circuit.checkWitness(witness));
// ok? I assume key is correct
//vk_proof = deserialize(fs.readFileSync("build/myCircuit.vk_proof", "utf8"));
vk_proof = JSON.parse(fs.readFileSync("build/myCircuit.vk_proof", "utf8"));
// XXX: original vs goth?
// xxx vk_proof is a hing, whats up witness
// unsringifybigints of vk proof loooks...weird tho. like a string.
console.log("gen proof");
// now it looks OK but smt wrong
// compile time
// loading
let {proof, publicSignals} = zkSnark.groth.genProof(unstringifyBigInts(vk_proof), unstringifyBigInts(witness));
// error TypeError: Cannot read property '5' of undefined
// gyuess would be it doesn't work.
// a.affine is not a function
// https://github.com/iden3/snarkjs/issues/9 err I thought I did this already
// zkSnark.groth.genProof(vk_proof, witness); fails
//let {proof, publicSignals} = zkSnark.groth.genProof(vk_proof, witness);
// // 5. Verify proof
// XXX dunno if good
console.log("before vk verifier");
const vk_verifier = JSON.parse(fs.readFileSync("build/myCircuit.vk_verifier", "utf8"));
console.log("after vk verifier");
// if (zkSnark.groth.isValid(vk_verifier, proof, publicSignals)) {
// wow works
if (zkSnark.groth.isValid(unstringifyBigInts(vk_verifier), unstringifyBigInts(proof), unstringifyBigInts(publicSignals))) {
console.log("The proof is valid");
} else {
console.log("The proof is not valid");
}
}
////////////////////////////////////////////////////////////
//// 4. Generate proof
// const input = {
// "a": "3",
// "b": "11"
// }
// const witness = circuit.calculateWitness(input);
// const vk_proof = deserialize(fs.readFileSync("build/proving_key.json", "utf8"));
// nice, progress
// const {proof, publicSignals} = zkSnark.groth.genProof(vk_proof, witness);
//const w = circuit.calculateWitness(inputs);
// // 5. Verify proof
// const vk_verifier = deserialize(fs.readFileSync("build/verification_key.json", "utf8"));
//};
// if (zkSnark.groth.isValid(vk_verifier, proof, publicSignals)) {
// console.log("The proof is valid");
// } else {
// console.log("The proof is not valid");
// }
//foo();