From c1f057a5816a166f690b0d78835c729d1bc0b65c Mon Sep 17 00:00:00 2001 From: Oskar Thoren Date: Mon, 4 Nov 2019 14:30:27 +0800 Subject: [PATCH] Integrate shamir in antiSpam example --- zksnarks/semaphore/src/hello.js | 227 +++++++++++++++++++++++++++++++- 1 file changed, 225 insertions(+), 2 deletions(-) diff --git a/zksnarks/semaphore/src/hello.js b/zksnarks/semaphore/src/hello.js index 12955e4..07a5390 100644 --- a/zksnarks/semaphore/src/hello.js +++ b/zksnarks/semaphore/src/hello.js @@ -6,6 +6,7 @@ const circomlib = require('circomlib'); const web3Utils = require('web3-utils'); const ethers = require('ethers'); const SMT = require('semaphore-merkle-tree'); +const secrets = require("secrets.js"); const {stringifyBigInts, unstringifyBigInts} = require("snarkjs/src/stringifybigint.js"); const asciiToHex = web3Utils.asciiToHex; @@ -368,7 +369,6 @@ function badRun() { // Need to think about this more - // If this is a voting signal, how do we ensure voting only once? Fixed external nullifier // In voting, we restrict external nullifier to a constant @@ -436,6 +436,7 @@ async function voteTesting() { assert(await votingExample(BigInt(12312), "I vote for A") == true); assert(await votingExample(BigInt(12319), "I vote for A") == false); // TODO: voteState - keep track of nullifier hashes seen between runs + // This ensures you can't signal twice with same external identifier //assert(await votingExample(BigInt(12312), "I vote for B") == false); // TODO: Bad identity, this requires constructing 'untrusted' tree and verifying @@ -450,7 +451,7 @@ async function voteTesting() { } }; -voteTesting(); +//voteTesting(); // // test merkle tree, untrusted pov // let identity = loadIdentity("17939861921584559533262186509737425990469800861754459917147159747570381958900"); @@ -460,3 +461,225 @@ voteTesting(); // root = merkle_root // is_valid(proof) // enough to show that identity is part of merkle tree at some specific position + + +// Rate limiting! Now we want some form of secret shamir sharing here. + +// > Firstly we have a smart contract that allows anyone to deposit some +// > currency and join our group. At any point a users can be removed from the +// > group if someone calls a function passing their private key as an input. +// > Anyone who does this will receive 33% of the slashed stake the remainder is +// > burned. + +// TODO: Clean up above and badRun + +// 1. Based on nullifier hash (hash of external nullifier / leaf private key), we +// generate a private key +// 2. Encrypt leaf private key based on this nullifier private key +// 3. Use Shamir Secret Sharing to encode nullifier private key + +// ...etc, lets break + +// Code up shamir secret sharing? +// What changes to ZKP does this require + +// If previously nullifier hash was hash(external_nullifier, leaf_private_key), wheres private key? + +// nullifiers_hash is uniquely derived from external_nullifier, identity_nullifier and identity_path_index. This ensures a user cannot broadcast a signal with the same external_nullifier more than once. +// sow hat you mean? its hash of identity nullifier +// commitment is just identity nullifier and trapdoor + +// Same external nullifier and different signal: +// Generate same secret shares etc, but reveal different portion +// 2/3 likely it'll pick another one. Then we can slash. +// Lets mock +// Doing in snarks is a bit too involved for now + +// TODO: Add to snark (GHI) fun experiment too, standalone ish + +// Assumption: same complexity ish + +function shamirSeed(pk, en) { + // XXX: probably more safe way to hash pk and en + const seed = sha1(pk + en); + const key = secrets.str2hex(seed); + console.log("Shamir key", key); + return key; +} + +// Calculates a secret key that requires 2/3 shares to compute +// Uses pk and en as seed and returns a random share. +// If pk and en are the same, there's a 2/3 chance of reveal. +// XXX: Share generation not deterministic, need to re-use +// function shamirShare(key) { +// const shares = secrets.share(key, 3, 2); +// //const randIndex = Math.floor(Math.random()*Math.floor(3)); +// //return {secretKey: key, randomShare: shares[randIndex]}; +// return shares; +// } + +// XXX This would be dealt with in Snarks +// XXX: Ugly global +let trustedKeysStore = new Set(); +let shamirSharesStore = new Map(); + +// XXX: Ugly global, should be in local state +// XXX: This seems like an issue actually, how are we supposed to do this efficently? +// Having a hash for same external nullifier, different signals and same id would make detection easier +let sharesStore = []; + +function foundShamirKey() { + log("foundShamirKey"); + + //console.log("sharesStore", sharesStore); + // TODO: This should be all combinations of selection + if (sharesStore.length < 2) { + return false; + } + let candidates = []; + for (let i = 0; i < sharesStore.length - 1; i++) { + for (let j = i+1; j { + //console.log("identity_path", identity_path); + + // Input, what we want to signal + let signal_hash = signal(signal_str); + //console.log("signal hash", signal_hash); + + // Let's say external nullifier has to be this, if it isn't it should fail validation + let msg = message(external_nullifier, signal_hash); + //console.log("msg", msg); + let signature = sign(identity, msg); + //console.log("signature", signature); + checkSignature(msg, signature, getPublicKey(identity)); + let inputs = makeInputs(signature, signal_hash, external_nullifier, identity, identity_path); + let witness = circuit.calculateWitness(inputs); + checkWitness(circuit, witness); + let {proof, publicSignals} = loadOrGenerateProofWithKey(witness); + return untrustedVerifySpam(proof, publicSignals, randomShare); + }) + .catch((err) => { + console.log("error !", err); + return false; + }); + return res; +} + +async function spamTesting() { + try { + // XXX: Just using voting signals to avoid recomputing proofs + // XXX: Bad logic, should go through all and get probabilsitic falseCount + assert(await spamExample(BigInt(12312), "I vote for A") == true); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + assert(await spamExample(BigInt(12312), "I vote for A") == (true || false)); + //assert(await spamExample(BigInt(12312), "I vote for B") == true); + } catch(err) { + console.log("Oops, no good", err); + } +}; + + +// TODO: Clean up this code comments + +spamTesting();