commit
2bb7059c4c
@ -0,0 +1,141 @@ |
|||||||
|
import { ethers, BigNumber } from 'ethers' |
||||||
|
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" |
||||||
|
import { poseidon } from "circomlibjs" // v0.0.8
|
||||||
|
|
||||||
|
import { ZqField } from 'ffjavascript' |
||||||
|
const SNARK_FIELD_SIZE = BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617') |
||||||
|
|
||||||
|
// Creates the finite field
|
||||||
|
const Fq = new ZqField(SNARK_FIELD_SIZE) |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args), |
||||||
|
error: (...args) => console.error(...args), |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Recovers secret from two shares |
||||||
|
* @param x1 signal hash of first message |
||||||
|
* @param x2 signal hash of second message |
||||||
|
* @param y1 yshare of first message |
||||||
|
* @param y2 yshare of second message |
||||||
|
* @returns identity secret |
||||||
|
*/ |
||||||
|
function shamirRecovery(x1: bigint, x2: bigint, y1: bigint, y2: bigint): bigint { |
||||||
|
const slope = Fq.div(Fq.sub(y2, y1), Fq.sub(x2, x1)) |
||||||
|
const privateKey = Fq.sub(y1, Fq.mul(slope, x1)) |
||||||
|
|
||||||
|
return Fq.normalize(privateKey) |
||||||
|
} |
||||||
|
|
||||||
|
function hash(message: any): bigint { |
||||||
|
message = BigNumber.from(message).toTwos(256).toHexString() |
||||||
|
message = ethers.utils.zeroPad(message, 32) |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
function hashNullifier(message: any): bigint { |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { |
||||||
|
console.log('calculate') |
||||||
|
await snarkjs.wtns.calculate(signals, wasm, wtns); |
||||||
|
|
||||||
|
console.log('check') |
||||||
|
await snarkjs.wtns.check(r1cs, wtns, logger);
|
||||||
|
|
||||||
|
console.log('prove') |
||||||
|
const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns); |
||||||
|
|
||||||
|
const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger); |
||||||
|
console.log('zk proof validity', verified); |
||||||
|
return { |
||||||
|
proof, |
||||||
|
x: publicSignals[3], |
||||||
|
y: publicSignals[0] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', true); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
// @ts-ignore
|
||||||
|
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.wasm', true); |
||||||
|
// @ts-ignore
|
||||||
|
const wasm = new Uint8Array(wasmBuffer);
|
||||||
|
|
||||||
|
const zkey_final = { |
||||||
|
type: "mem", |
||||||
|
data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/zk_setup.txt'))) |
||||||
|
} |
||||||
|
const wtns = { type: "mem" };
|
||||||
|
|
||||||
|
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/verification_key.json')) |
||||||
|
|
||||||
|
// build list of identity commitments
|
||||||
|
const secrets = [] |
||||||
|
const identityCommitments = [] |
||||||
|
const rateCommitments = [] |
||||||
|
const userMessageLimit = 0x2 |
||||||
|
for (let k = 0; k < 2; k++) {
|
||||||
|
const identitySecret = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
secrets.push(identitySecret) |
||||||
|
|
||||||
|
const identityCommitment = poseidon([identitySecret]) |
||||||
|
const rateCommitment = poseidon([identityCommitment, userMessageLimit]) |
||||||
|
identityCommitments.push(identityCommitment) |
||||||
|
rateCommitments.push(rateCommitment) |
||||||
|
} |
||||||
|
|
||||||
|
let tree |
||||||
|
|
||||||
|
try { |
||||||
|
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, rateCommitments) // Binary tree.
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const merkleproof1 = tree.createProof(0) |
||||||
|
|
||||||
|
const appId = 0xa |
||||||
|
const epoch = 0x1 |
||||||
|
|
||||||
|
const signals1 = { |
||||||
|
identitySecret: secrets[0], |
||||||
|
userMessageLimit, |
||||||
|
messageId: 0x0, |
||||||
|
pathElements: merkleproof1.siblings, |
||||||
|
identityPathIndex: merkleproof1.pathIndices, |
||||||
|
x: 0xabcd, // hash(message)
|
||||||
|
externalNullifier: 0xa // hash(epoch, appId)
|
||||||
|
} |
||||||
|
const proof1 = await prove(signals1, wasm, wtns, r1cs, zkey_final, vKey) |
||||||
|
|
||||||
|
const signals2 = { |
||||||
|
identitySecret: secrets[0], |
||||||
|
userMessageLimit, |
||||||
|
messageId: 0x0, |
||||||
|
pathElements: merkleproof1.siblings, |
||||||
|
identityPathIndex: merkleproof1.pathIndices, |
||||||
|
x: 0xabce, // hash(message)
|
||||||
|
externalNullifier: 0xa // hash(epoch, appId)
|
||||||
|
} |
||||||
|
const proof2 = await prove(signals2, wasm, wtns, r1cs, zkey_final, vKey) |
||||||
|
|
||||||
|
const secret = shamirRecovery(BigInt(proof1.x), BigInt(proof2.x), BigInt(proof1.y), BigInt(proof2.y)) |
||||||
|
|
||||||
|
console.log(secret.toString(10)) |
||||||
|
console.log(Fq.normalize(secrets[0])) |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
Loading…
Reference in new issue