moar cleanup

This commit is contained in:
Oskar Thoren 2019-11-01 14:22:27 +08:00
parent 57a6b8fab3
commit cadbfb8365
No known key found for this signature in database
GPG Key ID: B2ECCFD3BC2EF77E
1 changed files with 140 additions and 124 deletions

View File

@ -14,6 +14,10 @@ const mimcsponge = circomlib.mimcsponge;
// Utils
function log(s) {
console.log(new Date(), s);
}
function pedersenHash(ints) {
const p = circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(Buffer.concat(
ints.map(x => x.leInt2Buff(32))
@ -30,6 +34,7 @@ beBuff2int = function(buff) {
return res;
};
// TODO: Optionally persist this identity
function generate_identity() {
const private_key = crypto.randomBytes(32).toString('hex');
const prvKey = Buffer.from(private_key, 'hex');
@ -38,32 +43,41 @@ function generate_identity() {
// XXX: Right now just using random bytes, what should this be?
const identity_nullifier = '0x' + crypto.randomBytes(31).toString('hex');
const identity_trapdoor = '0x' + crypto.randomBytes(31).toString('hex');
console.log(`generate identity from (private_key, public_key[0], public_key[1], identity_nullifier): (${private_key}, ${pubKey[0]}, ${pubKey[1]}, ${identity_nullifier}, ${identity_trapdoor})`);
log(`generate identity from (private_key, public_key[0], public_key[1], identity_nullifier): (${private_key}, ${pubKey[0]}, ${pubKey[1]}, ${identity_nullifier}, ${identity_trapdoor})`);
const identity_commitment = pedersenHash([bigInt(circomlib.babyJub.mulPointEscalar(pubKey, 8)[0]), bigInt(identity_nullifier), bigInt(identity_trapdoor)]);
console.log(`identity_commitment : ${identity_commitment}`);
console.log(`identity_commitment: ${identity_commitment}`);
const generated_identity = {
private_key,
identity_nullifier: identity_nullifier.toString(),
identity_trapdoor: identity_trapdoor.toString(),
identity_commitment: identity_commitment.toString(),
public_key: pubKey
};
return generated_identity;
}
// Using Groth
function MakeMerkleTree() {
// 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?
//const depth = 2; // only 60k->35k constraints
// 1. Compile circuit with circom:
// circom snark/circuit.circom -o build/circuit.json
const circuitDef = unstringifyBigInts(JSON.parse(fs.readFileSync("build/circuit.json", "utf-8")));
const circuit = new zkSnark.Circuit(circuitDef);
const tree = new SMT.tree.MerkleTree(
`${prefix}_id_tree`,
storage,
hasher,
depth,
default_value,
);
return tree;
}
// Inspect circuit with e.g.:
// circuits.nConstraints
// 2. Perform setup - only done once
function performSetup(circuit) {
const setup = zkSnark.groth.setup(circuit);
fs.writeFileSync("myCircuit.vk_proof", JSON.stringify(stringifyBigInts(setup.vk_proof)), "utf8");
@ -71,107 +85,57 @@ function performSetup(circuit) {
console.log("Toxic waste:", setup.toxic); // Must be discarded.
}
// TODO: Conditionally run if file exists
//performSetup(circuit);
// TODO: Optionally persist this identity
let loaded_identity = generate_identity();
console.log("loaded_identity", loaded_identity);
let private_key = loaded_identity.private_key;
const prvKey = Buffer.from(private_key, 'hex');
// XXX: external nullifier what should this be set to?
let signal_str = "hello world";
let external_nullifier = bigInt(12312);
// XXX: It'd be good to minimize this to whats actually minimally needed
const signal_to_contract = asciiToHex(signal_str);
const signal_to_contract_bytes = new Buffer(signal_to_contract.slice(2), 'hex');
const signal_hash_raw = ethers.utils.solidityKeccak256(
['bytes'],
[signal_to_contract_bytes],
);
// XXX: Buffer deprecated, replace
const signal_hash_raw_bytes = new Buffer(signal_hash_raw.slice(2), 'hex');
const signal_hash = beBuff2int(signal_hash_raw_bytes.slice(0, 31));
console.log("signal_hash", signal_hash);
// XXX: what does this mean exactly?
// We are signing the external nullifer and signal hash
// https://github.com/iden3/circomlib/pull/13
// https://eprint.iacr.org/2016/492.pdf
// https://en.wikipedia.org/wiki/Sponge_function
// Why multihash? Whats interaction between EDDSA, MiMC and Pedersen?
const msg = mimcsponge.multiHash([external_nullifier, signal_hash]);
console.log("msg", msg);
const signature = eddsa.signMiMCSponge(prvKey, msg);
console.log("signature", signature);
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;
// 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?
const tree = new SMT.tree.MerkleTree(
`${prefix}_id_tree`,
storage,
hasher,
depth,
default_value,
);
const leaf = identity_commitment;
console.log("leaf", leaf);
// Global scope for REPL debugging
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);
// Find path to a index containing identity commitment.
// XXX: What happens if tree changes?
async function updateTreeAndGetPath(tree, i, commitment) {
await tree.update(i, commitment);
let leaf_index = await tree.element_index(commitment);
let identity_path = await tree.path(leaf_index);
return identity_path;
};
// XXX: Very ugly
updateTree()
.then(() => {
console.log("leaf_index", leaf_index);
console.log("tree", tree);
bar();
})
.catch((err) => {
console.log("error", err);
});
// XXX: original vs goth? whats difference?
// XXX: This takes 15m to run
function generateProofWithKey(witness) {
const vk_proof = JSON.parse(fs.readFileSync("build/myCircuit.vk_proof", "utf8"));
log("generateProof");
let proof = zkSnark.groth.genProof(unstringifyBigInts(vk_proof), unstringifyBigInts(witness));
fs.writeFileSync("build/proof1", JSON.stringify(stringifyBigInts(proof)), "utf8");
console.log("proof done and persisted", proof);
return proof;
}
let inputs;
let identity_path;
let witness;
let vk_proof;
let proof;
let publicSignals;
function verifyProofWithKey(proof, publicSignals) {
const vk_verifier = JSON.parse(fs.readFileSync("build/myCircuit.vk_verifier", "utf8"));
log("verifyProof");
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");
}
}
function bar() {
console.log("path", leaf_index);
identity_path = path;
console.log("identity_path", identity_path);
function checkSignature(msg, signature, pubKey) {
console.log("checkSignature");
assert(eddsa.verifyMiMCSponge(msg, signature, pubKey));
}
// XXX: What does this actually do?
function checkWitness(circuit, witness) {
console.log("checkWitness");
assert(circuit.checkWitness(witness));
}
// Inputs used for witness
function makeInputs(signature, signal_hash, external_nullifier, identity, identity_path) {
const identity_nullifier = identity.identity_nullifier;
const identity_trapdoor = identity.identity_trapdoor;
const pubKey = identity.public_key;
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));
inputs = {
const inputs = {
'identity_pk[0]': pubKey[0],
'identity_pk[1]': pubKey[1],
'auth_sig_r[0]': signature.R8[0],
@ -185,24 +149,76 @@ function bar() {
identity_path_index,
fake_zero: bigInt(0),
};
witness = circuit.calculateWitness(inputs);
assert(circuit.checkWitness(witness));
// 3. Verify proof
vk_proof = JSON.parse(fs.readFileSync("build/myCircuit.vk_proof", "utf8"));
// XXX: original vs goth? whats difference?
console.log("gen proof");
// XXX: This takes a long time, benchmark?
let {proof, publicSignals} = zkSnark.groth.genProof(unstringifyBigInts(vk_proof), unstringifyBigInts(witness));
// 4. Verify proof
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(unstringifyBigInts(vk_verifier), unstringifyBigInts(proof), unstringifyBigInts(publicSignals))) {
console.log("The proof is valid");
} else {
console.log("The proof is not valid");
}
return inputs;
}
// XXX: A lot of conversation, it'd be good to minimize this
function signal(str) {
let signal_str = str;
const signal_to_contract = asciiToHex(signal_str);
const signal_to_contract_bytes = new Buffer(signal_to_contract.slice(2), 'hex');
const signal_hash_raw = ethers.utils.solidityKeccak256(
['bytes'],
[signal_to_contract_bytes],
);
// XXX: Buffer deprecated, replace
const signal_hash_raw_bytes = new Buffer(signal_hash_raw.slice(2), 'hex');
const signal_hash = beBuff2int(signal_hash_raw_bytes.slice(0, 31));
return signal_hash;
}
function message(external_nullifier, signal_hash) {
const msg = mimcsponge.multiHash([external_nullifier, signal_hash]);
return msg;
}
function sign(identity, msg) {
let private_key = identity.private_key;
const prvKey = Buffer.from(private_key, 'hex');
const signature = eddsa.signMiMCSponge(prvKey, msg);
return signature;
}
function loadCircuit() {
const circuitDef = unstringifyBigInts(JSON.parse(fs.readFileSync("build/circuit.json", "utf-8")));
const circuit = new zkSnark.Circuit(circuitDef);
return circuit;
}
//////////////////////////////////////////////////////////////////////////////
// NOTE: Using Groth
// 1. Compile circuit with circom:
// circom snark/semaphore.circom -o build/circuit.json (~7m)
// 2. Perform setup - only done once
// TODO: Conditionally run if file exists
// performSetup(circuit);
function run() {
let identity = generate_identity();
let tree = MakeMerkleTree();
let circuit = loadCircuit();
// Perform setup - only done once
// performSetup(circuit);
updateTreeAndGetPath(tree, 1, identity.identity_commitment)
.then((identity_path) => {
// Input, what we want to signal
let signal_hash = signal("hello world");
// In order to prevent double signals
let external_nullifier = bigInt(12312);
let msg = message(external_nullifier, signal_hash);
let signature = sign(identity, msg);
checkSignature(msg, signature, identity.public_key);
let inputs = makeInputs(signature, signal_hash, external_nullifier, identity, identity_path);
let witness = circuit.calculateWitness(inputs);
checkWitness(circuit, witness);
let {proof, publicSignals} = generateProofWithKey(witness);
verifyProofWithKey(proof, publicSignals);
})
.catch((err) => {
console.log("error", err);
});
}