From 9d4e1824e9479d519a5b7b24d57ce04135dfdd32 Mon Sep 17 00:00:00 2001 From: Nasi Jofce Date: Fri, 18 Feb 2022 11:31:27 +0100 Subject: [PATCH 1/2] feat(zk-protocol): structure the public signals array obtained while generating a proof from snarkjs The generated proof using snarkjs will return an object of type PublicSignals for the public signals instead of the array BREAKING CHANGE: The generated proof using snarkjs will return an object of type PublicSignals for the public signals instead of the array Former-commit-id: 8b7412ca795df29bd7af7433272531faa870c4af [formerly 58af4273f1b6d6ac5bcbe5d6d82a3e36979f4244] [formerly f123e573aa89b1816821b8e9ecfba42b1ab1a90e [formerly 287a011c52b3b7d41ee47fa363d5415fa1e760c7]] [formerly e3869996b2ef2f3dee93647fa0a3d94457420e39 [formerly 76bebf0a120a1ca2deb0d6f164d62066db0c9e8c] [formerly 4f41dbb19468aa6a1c5c13f7c22aa1efc6f9cf9b [formerly 05130661fbdda396000f125e4d1333048c04a397]]] Former-commit-id: f66b2baeaba8cc91152a0c001a89f612b31c5b21 [formerly ee798d87071a58504b7a3f0da68e49f180040338] [formerly 5c03176115fc3e3ff9541aa74421a82f349cba85 [formerly 15c6814568a2052ae15acb71c62e4245aecead7a]] Former-commit-id: 2e950784fa3c7c2f5aa2adf6b0be84eb7fbc3672 [formerly 0c0ddf7a4d32dab369eead76eabfec114ca80710] Former-commit-id: e5337c77275962c73ed89d09d12786d5739d1ebf --- packages/protocols/src/types/index.ts | 11 ++++++++++- packages/protocols/src/zk-protocol.ts | 25 ++++++++++++++++++++++--- packages/protocols/tests/rln.test.ts | 12 +++++++++++- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/protocols/src/types/index.ts b/packages/protocols/src/types/index.ts index 2d75845..0f9812f 100644 --- a/packages/protocols/src/types/index.ts +++ b/packages/protocols/src/types/index.ts @@ -10,7 +10,16 @@ export type Proof = { export type FullProof = { proof: Proof - publicSignals: StrBigInt[] + publicSignals: PublicSignals +} + +export type PublicSignals = { + yShare: StrBigInt + merkleRoot: StrBigInt + internalNullifier: StrBigInt + signalHash: StrBigInt + epoch: StrBigInt + rlnIdentifier: StrBigInt } export type SolidityProof = StrBigInt[] diff --git a/packages/protocols/src/zk-protocol.ts b/packages/protocols/src/zk-protocol.ts index ec8d36f..40ecea1 100644 --- a/packages/protocols/src/zk-protocol.ts +++ b/packages/protocols/src/zk-protocol.ts @@ -1,8 +1,13 @@ /* istanbul ignore file */ import { groth16 } from "snarkjs" -import { FullProof, SolidityProof } from "./types" +import { FullProof, PublicSignals, SolidityProof, StrBigInt } from "./types" export default class ZkProtocol { + /** + * The number of public signals that should be returned by snarkjs when generating a proof. + */ + private static PUBLIC_SIGNALS_COUNT: number = 6 + /** * Generates a SnarkJS full proof with Groth16. * @param witness The parameters for creating the proof. @@ -11,7 +16,19 @@ export default class ZkProtocol { * @returns The full SnarkJS proof. */ public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise { - const { proof, publicSignals } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null) + const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null) + + if (publicSignalsArray.length !== ZkProtocol.PUBLIC_SIGNALS_COUNT) throw new Error("Error while generating proof") + + const publicSignals: PublicSignals = { + yShare: publicSignalsArray[0], + merkleRoot: publicSignalsArray[1], + internalNullifier: publicSignalsArray[2], + signalHash: publicSignalsArray[3], + epoch: publicSignalsArray[4], + rlnIdentifier: publicSignalsArray[5] + } + return { proof, publicSignals } } @@ -24,7 +41,9 @@ export default class ZkProtocol { public static verifyProof(verificationKey: string, fullProof: FullProof): Promise { const { proof, publicSignals } = fullProof - return groth16.verify(verificationKey, publicSignals, proof) + const publicSignalsArray: StrBigInt[] = Object.values(publicSignals) + + return groth16.verify(verificationKey, publicSignalsArray, proof) } /** diff --git a/packages/protocols/tests/rln.test.ts b/packages/protocols/tests/rln.test.ts index 637df14..172a26a 100644 --- a/packages/protocols/tests/rln.test.ts +++ b/packages/protocols/tests/rln.test.ts @@ -4,6 +4,7 @@ import * as fs from "fs" import * as path from "path" import { RLN } from "../src" import { generateMerkleProof, genExternalNullifier, genSignalHash } from "../src/utils" +import { PublicSignals } from "../src/types" describe("RLN", () => { const zkeyFiles = "./packages/protocols/zkeyFiles" @@ -65,7 +66,15 @@ describe("RLN", () => { const witness = RLN.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier) const [y, nullifier] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash) - const publicSignals = [y, merkleProof.root, nullifier, signalHash, epoch, rlnIdentifier] + + const publicSignals: PublicSignals = { + yShare: y, + merkleRoot: merkleProof.root, + internalNullifier: nullifier, + signalHash, + epoch, + rlnIdentifier + } const vkeyPath = path.join(zkeyFiles, "rln", "verification_key.json") const vKey = JSON.parse(fs.readFileSync(vkeyPath, "utf-8")) @@ -77,6 +86,7 @@ describe("RLN", () => { const response = await RLN.verifyProof(vKey, { proof: fullProof.proof, publicSignals }) expect(response).toBe(true) + expect(fullProof.publicSignals).toEqual(publicSignals) }, 30000) it("Should retrieve user secret after spaming", () => { From d32bbbd7a6db281698f95d56a6d4a7fdf7a8df4f Mon Sep 17 00:00:00 2001 From: Nasi Jofce Date: Fri, 18 Feb 2022 20:29:26 +0100 Subject: [PATCH 2/2] feat(zk-protocol): structure the public signals array obtained while generating a proof from snarkjs The generated proof using snarkjs will return an object of type PublicSignals for the public signals Former-commit-id: deadf7159137f98343e42dbb2cfef813a3adffc3 [formerly 1f7dd580bbd27f39e0975b8bc2967dca191f2e75] [formerly d0641f8a46fd888b44d45020d8393871672759f8 [formerly 422bcbb07e3675b3c124313ce265dd661856075c]] [formerly c93bbaee62c8969bb91c9b75efe642781d740b49 [formerly d5a4c2359230f114287796b6ad502d881ec554c4] [formerly 5f155f5f9927762e8418d9930dda1d0af414af57 [formerly 462a4bb74522d9418ffa11475b5f4ae780562788]]] Former-commit-id: 1f3f2470e637188e7ef3311a15c40d6f9368b1ee [formerly 884540bf7bd1e87311c7e72557e983c6bc769ee5] [formerly d060f4aacfe7f2c5bec5da817b4e785ced9de9d8 [formerly 471a8074b86fb074707ab4981f2831bcf7242a57]] Former-commit-id: 9d93b210d84bf65134e25a970ec42af8afb50253 [formerly c1c1a33c3edd09bf908482185f3ec11914c52131] Former-commit-id: fed1b20295686aef5fdf5597f9bcece2933d984d --- packages/protocols/src/rln.ts | 32 +++++++++++++++++++++- packages/protocols/src/semaphore.ts | 30 +++++++++++++++++++- packages/protocols/src/types/index.ts | 11 ++++++-- packages/protocols/src/zk-protocol.ts | 31 +-------------------- packages/protocols/tests/rln.test.ts | 4 +-- packages/protocols/tests/semaphore.test.ts | 10 ++++++- 6 files changed, 81 insertions(+), 37 deletions(-) diff --git a/packages/protocols/src/rln.ts b/packages/protocols/src/rln.ts index 544ee77..1639660 100644 --- a/packages/protocols/src/rln.ts +++ b/packages/protocols/src/rln.ts @@ -1,10 +1,40 @@ import { MerkleProof } from "@zk-kit/incremental-merkle-tree" import { poseidon } from "circomlibjs" -import { StrBigInt } from "./types" +import { groth16 } from "snarkjs" +import { FullProof, RLNPublicSignals, StrBigInt } from "./types" import { Fq, genSignalHash } from "./utils" import ZkProtocol from "./zk-protocol" export default class RLN extends ZkProtocol { + /** + * The number of public signals that should be returned by snarkjs when generating a proof. + */ + private static PUBLIC_SIGNALS_COUNT: number = 6 + + /** + * Generates a SnarkJS full proof with Groth16. + * @param witness The parameters for creating the proof. + * @param wasmFilePath The WASM file path. + * @param finalZkeyPath The ZKey file path. + * @returns The full SnarkJS proof. + */ + public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise { + const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null) + + if (publicSignalsArray.length !== RLN.PUBLIC_SIGNALS_COUNT) throw new Error("Error while generating proof") + + const publicSignals: RLNPublicSignals = { + yShare: publicSignalsArray[0], + merkleRoot: publicSignalsArray[1], + internalNullifier: publicSignalsArray[2], + signalHash: publicSignalsArray[3], + epoch: publicSignalsArray[4], + rlnIdentifier: publicSignalsArray[5] + } + + return { proof, publicSignals } + } + /** * Creates witness for rln proof * @param identitySecret identity secret diff --git a/packages/protocols/src/semaphore.ts b/packages/protocols/src/semaphore.ts index 03e6bd7..5e9738d 100644 --- a/packages/protocols/src/semaphore.ts +++ b/packages/protocols/src/semaphore.ts @@ -1,10 +1,38 @@ import { MerkleProof } from "@zk-kit/incremental-merkle-tree" import { poseidon } from "circomlibjs" -import { SemaphoreWitness, StrBigInt } from "./types" +import { groth16 } from "snarkjs" +import { FullProof, StrBigInt, SemaphoreWitness, SemaphorePublicSignals } from "./types" import { genSignalHash } from "./utils" import ZkProtocol from "./zk-protocol" export default class Semaphore extends ZkProtocol { + /** + * The number of public signals that should be returned by snarkjs when generating a proof. + */ + private static PUBLIC_SIGNALS_COUNT: number = 6 + + /** + * Generates a SnarkJS full proof with Groth16. + * @param witness The parameters for creating the proof. + * @param wasmFilePath The WASM file path. + * @param finalZkeyPath The ZKey file path. + * @returns The full SnarkJS proof. + */ + public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise { + const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null) + + if (publicSignalsArray.length !== Semaphore.PUBLIC_SIGNALS_COUNT) throw new Error("Error while generating proof") + + const publicSignals: SemaphorePublicSignals = { + merkleRoot: publicSignalsArray[0], + nullifierHash: publicSignalsArray[1], + signalHash: publicSignalsArray[2], + externalNullifier: publicSignalsArray[3] + } + + return { proof, publicSignals } + } + /** * Creates a Semaphore witness for the Semaphore ZK proof. * @param identityTrapdoor The identity trapdoor. diff --git a/packages/protocols/src/types/index.ts b/packages/protocols/src/types/index.ts index 0f9812f..df8d759 100644 --- a/packages/protocols/src/types/index.ts +++ b/packages/protocols/src/types/index.ts @@ -10,10 +10,10 @@ export type Proof = { export type FullProof = { proof: Proof - publicSignals: PublicSignals + publicSignals: RLNPublicSignals | SemaphorePublicSignals } -export type PublicSignals = { +export type RLNPublicSignals = { yShare: StrBigInt merkleRoot: StrBigInt internalNullifier: StrBigInt @@ -22,6 +22,13 @@ export type PublicSignals = { rlnIdentifier: StrBigInt } +export type SemaphorePublicSignals = { + merkleRoot: StrBigInt + nullifierHash: StrBigInt + signalHash: StrBigInt + externalNullifier: StrBigInt +} + export type SolidityProof = StrBigInt[] export type SemaphoreWitness = { diff --git a/packages/protocols/src/zk-protocol.ts b/packages/protocols/src/zk-protocol.ts index 40ecea1..89db91b 100644 --- a/packages/protocols/src/zk-protocol.ts +++ b/packages/protocols/src/zk-protocol.ts @@ -1,37 +1,8 @@ /* istanbul ignore file */ import { groth16 } from "snarkjs" -import { FullProof, PublicSignals, SolidityProof, StrBigInt } from "./types" +import { FullProof, SolidityProof, StrBigInt } from "./types" export default class ZkProtocol { - /** - * The number of public signals that should be returned by snarkjs when generating a proof. - */ - private static PUBLIC_SIGNALS_COUNT: number = 6 - - /** - * Generates a SnarkJS full proof with Groth16. - * @param witness The parameters for creating the proof. - * @param wasmFilePath The WASM file path. - * @param finalZkeyPath The ZKey file path. - * @returns The full SnarkJS proof. - */ - public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise { - const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null) - - if (publicSignalsArray.length !== ZkProtocol.PUBLIC_SIGNALS_COUNT) throw new Error("Error while generating proof") - - const publicSignals: PublicSignals = { - yShare: publicSignalsArray[0], - merkleRoot: publicSignalsArray[1], - internalNullifier: publicSignalsArray[2], - signalHash: publicSignalsArray[3], - epoch: publicSignalsArray[4], - rlnIdentifier: publicSignalsArray[5] - } - - return { proof, publicSignals } - } - /** * Verifies a zero-knowledge SnarkJS proof. * @param verificationKey The zero-knowledge verification key. diff --git a/packages/protocols/tests/rln.test.ts b/packages/protocols/tests/rln.test.ts index 172a26a..de5d6d6 100644 --- a/packages/protocols/tests/rln.test.ts +++ b/packages/protocols/tests/rln.test.ts @@ -4,7 +4,7 @@ import * as fs from "fs" import * as path from "path" import { RLN } from "../src" import { generateMerkleProof, genExternalNullifier, genSignalHash } from "../src/utils" -import { PublicSignals } from "../src/types" +import { RLNPublicSignals } from "../src/types" describe("RLN", () => { const zkeyFiles = "./packages/protocols/zkeyFiles" @@ -67,7 +67,7 @@ describe("RLN", () => { const [y, nullifier] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash) - const publicSignals: PublicSignals = { + const publicSignals: RLNPublicSignals = { yShare: y, merkleRoot: merkleProof.root, internalNullifier: nullifier, diff --git a/packages/protocols/tests/semaphore.test.ts b/packages/protocols/tests/semaphore.test.ts index 29f27ea..9718254 100644 --- a/packages/protocols/tests/semaphore.test.ts +++ b/packages/protocols/tests/semaphore.test.ts @@ -3,6 +3,7 @@ import { getCurveFromName } from "ffjavascript" import fs from "fs" import path from "path" import { Semaphore } from "../src" +import { SemaphorePublicSignals } from "../src/types" import { generateMerkleProof, genExternalNullifier, genSignalHash } from "../src/utils" describe("Semaphore", () => { @@ -64,11 +65,18 @@ describe("Semaphore", () => { const vkeyPath = path.join("./packages/protocols/zkeyFiles", "semaphore", "verification_key.json") const vKey = JSON.parse(fs.readFileSync(vkeyPath, "utf-8")) const nullifierHash = Semaphore.genNullifierHash(externalNullifier, identity.getNullifier()) - const publicSignals = [merkleProof.root.toString(), nullifierHash, genSignalHash(signal), externalNullifier] + + const publicSignals: SemaphorePublicSignals = { + merkleRoot: merkleProof.root.toString(), + nullifierHash, + signalHash: genSignalHash(signal), + externalNullifier + } const response = await Semaphore.verifyProof(vKey, { proof: fullProof.proof, publicSignals }) expect(response).toBe(true) + expect(fullProof.publicSignals).toEqual(publicSignals) }, 30000) }) })