mirror of
https://github.com/vacp2p/research.git
synced 2025-02-23 11:48:22 +00:00
Integrate shamir in antiSpam example
This commit is contained in:
parent
1d3f5c5439
commit
c1f057a581
@ -6,6 +6,7 @@ const circomlib = require('circomlib');
|
|||||||
const web3Utils = require('web3-utils');
|
const web3Utils = require('web3-utils');
|
||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
const SMT = require('semaphore-merkle-tree');
|
const SMT = require('semaphore-merkle-tree');
|
||||||
|
const secrets = require("secrets.js");
|
||||||
|
|
||||||
const {stringifyBigInts, unstringifyBigInts} = require("snarkjs/src/stringifybigint.js");
|
const {stringifyBigInts, unstringifyBigInts} = require("snarkjs/src/stringifybigint.js");
|
||||||
const asciiToHex = web3Utils.asciiToHex;
|
const asciiToHex = web3Utils.asciiToHex;
|
||||||
@ -368,7 +369,6 @@ function badRun() {
|
|||||||
|
|
||||||
// Need to think about this more
|
// Need to think about this more
|
||||||
|
|
||||||
|
|
||||||
// If this is a voting signal, how do we ensure voting only once? Fixed external nullifier
|
// 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
|
// 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(12312), "I vote for A") == true);
|
||||||
assert(await votingExample(BigInt(12319), "I vote for A") == false);
|
assert(await votingExample(BigInt(12319), "I vote for A") == false);
|
||||||
// TODO: voteState - keep track of nullifier hashes seen between runs
|
// 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);
|
//assert(await votingExample(BigInt(12312), "I vote for B") == false);
|
||||||
|
|
||||||
// TODO: Bad identity, this requires constructing 'untrusted' tree and verifying
|
// TODO: Bad identity, this requires constructing 'untrusted' tree and verifying
|
||||||
@ -450,7 +451,7 @@ async function voteTesting() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
voteTesting();
|
//voteTesting();
|
||||||
|
|
||||||
// // test merkle tree, untrusted pov
|
// // test merkle tree, untrusted pov
|
||||||
// let identity = loadIdentity("17939861921584559533262186509737425990469800861754459917147159747570381958900");
|
// let identity = loadIdentity("17939861921584559533262186509737425990469800861754459917147159747570381958900");
|
||||||
@ -460,3 +461,225 @@ voteTesting();
|
|||||||
// root = merkle_root
|
// root = merkle_root
|
||||||
// is_valid(proof)
|
// is_valid(proof)
|
||||||
// enough to show that identity is part of merkle tree at some specific position
|
// 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<sharesStore.length - 1; j++) {
|
||||||
|
candidates.push([sharesStore[i], sharesStore[j]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("CANDIDATES:", candidates);
|
||||||
|
|
||||||
|
//console.log("trustedKeysStore", trustedKeysStore);
|
||||||
|
for (let k = 0; k < candidates.length; k++) {
|
||||||
|
let key_candidate = secrets.combine(candidates[k]);
|
||||||
|
//console.log("key candidate", key_candidate);
|
||||||
|
if (trustedKeysStore.has(key_candidate)) {
|
||||||
|
console.log("Found the private key, can slash!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//let sel = [sharesStore[0], sharesStore[1]];
|
||||||
|
//let key_candidate = secrets.combine(sel);
|
||||||
|
// console.log("key candidate", key_candidate);
|
||||||
|
// console.log("trustedKeysStore", trustedKeysStore);
|
||||||
|
// if (trustedKeysStore.has(key_candidate)) {
|
||||||
|
// console.log("Found the private key, can slash!");
|
||||||
|
// return true;
|
||||||
|
// } else {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
function untrustedVerifySpam(proof, publicSignals, randomShare) {
|
||||||
|
let merkle_root = publicSignals[0];
|
||||||
|
let nullifier_hash = publicSignals[1];
|
||||||
|
let signal_hash = publicSignals[2];
|
||||||
|
let external_nullifier = publicSignals[3];
|
||||||
|
|
||||||
|
// "With same external nullifier more than once"
|
||||||
|
// TODO: Restrict allow_en to be timestamp +- 20s, say
|
||||||
|
let allowed_en = external_nullifier;
|
||||||
|
|
||||||
|
// Add to seen shares;
|
||||||
|
sharesStore = sharesStore.concat(randomShare);
|
||||||
|
let slashable = foundShamirKey();
|
||||||
|
|
||||||
|
try {
|
||||||
|
//assert(external_nullifier == allowed_en, "Wrong token!");
|
||||||
|
assert(verifyProofWithKey(proof, publicSignals));
|
||||||
|
assert(slashable == false, "Slashable!");
|
||||||
|
console.log("All checks out!");
|
||||||
|
return true;
|
||||||
|
} catch(err) {
|
||||||
|
console.log("Assertions failed:", err.message);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function spamExample(external_nullifier, signal_str) {
|
||||||
|
let identity = loadIdentity("17939861921584559533262186509737425990469800861754459917147159747570381958900");
|
||||||
|
let tree = MakeMerkleTree();
|
||||||
|
let circuit = loadCircuit();
|
||||||
|
|
||||||
|
// Lets try Shamir secret etc here
|
||||||
|
// For every same external nullifier, generate shamir secret share
|
||||||
|
// Can we use as seed?
|
||||||
|
|
||||||
|
// QQQ
|
||||||
|
// XXX: This logic should be in snarks
|
||||||
|
let key = shamirSeed(identity.private_key, external_nullifier);
|
||||||
|
let shares;
|
||||||
|
if (shamirSharesStore.get(key)) {
|
||||||
|
console.log("Already used key, picking random share to reveal");
|
||||||
|
} else {
|
||||||
|
console.log("New key, getting secretKey and shares");
|
||||||
|
shares = secrets.share(key, 3, 2);
|
||||||
|
//console.log("Shares", shares);
|
||||||
|
// XXX: This doesn't deal with multiple keys, assumes one store
|
||||||
|
shamirSharesStore.set(key, shares);
|
||||||
|
//console.log("foo", shamirSharesStore);
|
||||||
|
}
|
||||||
|
// Picks a random share, assumes same key
|
||||||
|
const randIndex = Math.floor(Math.random()*Math.floor(3));
|
||||||
|
//console.log("shamirSharesStore", shamirSharesStore);
|
||||||
|
let allShares = shamirSharesStore.get(key);
|
||||||
|
//console.log("allShares", allShares);
|
||||||
|
let randomShare = allShares[randIndex];
|
||||||
|
|
||||||
|
console.log("spamExample", external_nullifier, signal_str);
|
||||||
|
//console.log("secretKey", key);
|
||||||
|
//console.log("shamirShares", shares);
|
||||||
|
|
||||||
|
// XXX: Adding secret key and shares store, this would be in snark
|
||||||
|
trustedKeysStore.add(key);
|
||||||
|
|
||||||
|
/// QQQ: I bet this logic is wrong
|
||||||
|
|
||||||
|
// Add identity to tree
|
||||||
|
let res = updateTreeAndGetPath(tree, 1, identity.identity_commitment)
|
||||||
|
.then((identity_path) => {
|
||||||
|
//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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user