Merge pull request #18 from appliedzkp/fix/public-signals

fix: remove public signal checks
Former-commit-id: ba8a2d812ae7560cc963e230b22ee575605c00e2 [formerly dd1adcb485dc385d29185d730524b65eb391d0a1] [formerly 1b80a557aae032f80e02469703b416a3090f713f [formerly a7dadc5de656de409ec47939dad88180d378ba66]] [formerly a74f36b04df47f92ec22874392a50e2f671e89dd [formerly 32f594062b22d8be2b43a5c62f1d3531f1c40f8c] [formerly f4eec7da9407d04e134be024f0dbe8a0cd9d1712 [formerly f7a0b8c079]]]
Former-commit-id: 039cc5d5f9bb098d97da9f2ff74afa88d404956d [formerly 22b72ebee4a6716e565917d489c594cb47be3b1b] [formerly 06294e11a63cd5524a550295d253cb5638dba0ea [formerly 3c6fef20a3a61251bd5f1f0b626066aef7ea26a1]]
Former-commit-id: 0c3c48b49b2bab6a05fa9deba12c1a21b8a0214c [formerly f04c7bcb3579a9544d53c5dd3143cce7f9918d91]
Former-commit-id: 4894867e3476f9efb233746803f764f7bee04292
This commit is contained in:
Omar Desogus
2022-02-21 18:38:26 +01:00
committed by GitHub
7 changed files with 128 additions and 114 deletions

View File

@@ -1,17 +1,7 @@
import { MerkleProof } from "@zk-kit/incremental-merkle-tree"
import RLN from "./rln"
import Semaphore from "./semaphore"
import { FullProof, SolidityProof } from "./types"
import { generateMerkleProof, generateMerkleTree, genExternalNullifier, genSignalHash } from "./utils"
export {
Semaphore,
RLN,
generateMerkleProof,
generateMerkleTree,
genExternalNullifier,
genSignalHash,
MerkleProof,
FullProof,
SolidityProof
}
export { Semaphore, RLN, generateMerkleProof, generateMerkleTree, genExternalNullifier, genSignalHash, MerkleProof }
export * from "./types"

View File

@@ -1,16 +1,10 @@
import { MerkleProof } from "@zk-kit/incremental-merkle-tree"
import { poseidon } from "circomlibjs"
import { groth16 } from "snarkjs"
import { FullProof, RLNPublicSignals, StrBigInt } from "./types"
import { RLNFullProof, 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
export default class RLN {
/**
* Generates a SnarkJS full proof with Groth16.
* @param witness The parameters for creating the proof.
@@ -18,21 +12,43 @@ export default class RLN extends ZkProtocol {
* @param finalZkeyPath The ZKey file path.
* @returns The full SnarkJS proof.
*/
public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise<FullProof> {
const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null)
/* istanbul ignore next */
public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise<RLNFullProof> {
const { proof, publicSignals } = 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: {
yShare: publicSignals[0],
merkleRoot: publicSignals[1],
internalNullifier: publicSignals[2],
signalHash: publicSignals[3],
epoch: publicSignals[4],
rlnIdentifier: publicSignals[5]
}
}
}
return { proof, publicSignals }
/**
* Verifies a zero-knowledge SnarkJS proof.
* @param verificationKey The zero-knowledge verification key.
* @param fullProof The SnarkJS full proof.
* @returns True if the proof is valid, false otherwise.
*/
/* istanbul ignore next */
public static verifyProof(verificationKey: string, { proof, publicSignals }: RLNFullProof): Promise<boolean> {
return groth16.verify(
verificationKey,
[
publicSignals.yShare,
publicSignals.merkleRoot,
publicSignals.internalNullifier,
publicSignals.signalHash,
publicSignals.epoch,
publicSignals.rlnIdentifier
],
proof
)
}
/**

View File

@@ -1,16 +1,10 @@
import { MerkleProof } from "@zk-kit/incremental-merkle-tree"
import { poseidon } from "circomlibjs"
import { groth16 } from "snarkjs"
import { FullProof, StrBigInt, SemaphoreWitness, SemaphorePublicSignals } from "./types"
import { SemaphoreFullProof, SemaphoreSolidityProof, SemaphoreWitness, StrBigInt } 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
export default class Semaphore {
/**
* Generates a SnarkJS full proof with Groth16.
* @param witness The parameters for creating the proof.
@@ -18,19 +12,39 @@ export default class Semaphore extends ZkProtocol {
* @param finalZkeyPath The ZKey file path.
* @returns The full SnarkJS proof.
*/
public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise<FullProof> {
const { proof, publicSignalsArray } = await groth16.fullProve(witness, wasmFilePath, finalZkeyPath, null)
/* istanbul ignore next */
public static async genProof(witness: any, wasmFilePath: string, finalZkeyPath: string): Promise<SemaphoreFullProof> {
const { proof, publicSignals } = 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: {
merkleRoot: publicSignals[0],
nullifierHash: publicSignals[1],
signalHash: publicSignals[2],
externalNullifier: publicSignals[3]
}
}
}
return { proof, publicSignals }
/**
* Verifies a zero-knowledge SnarkJS proof.
* @param verificationKey The zero-knowledge verification key.
* @param fullProof The SnarkJS full proof.
* @returns True if the proof is valid, false otherwise.
*/
/* istanbul ignore next */
public static verifyProof(verificationKey: string, { proof, publicSignals }: SemaphoreFullProof): Promise<boolean> {
return groth16.verify(
verificationKey,
[
publicSignals.merkleRoot,
publicSignals.nullifierHash,
publicSignals.signalHash,
publicSignals.externalNullifier
],
proof
)
}
/**
@@ -70,4 +84,25 @@ export default class Semaphore extends ZkProtocol {
public static genNullifierHash(externalNullifier: StrBigInt, identityNullifier: StrBigInt): bigint {
return poseidon([BigInt(externalNullifier), BigInt(identityNullifier)])
}
/**
* Converts a full proof in a proof compatible with the Verifier.sol method inputs.
* @param fullProof The proof generated with SnarkJS.
* @returns The Solidity compatible proof.
*/
/* istanbul ignore next */
public static packToSolidityProof(fullProof: SemaphoreFullProof): SemaphoreSolidityProof {
const { proof } = fullProof
return [
proof.pi_a[0],
proof.pi_a[1],
proof.pi_b[0][1],
proof.pi_b[0][0],
proof.pi_b[1][1],
proof.pi_b[1][0],
proof.pi_c[0],
proof.pi_c[1]
]
}
}

View File

@@ -8,9 +8,14 @@ export type Proof = {
curve: string
}
export type FullProof = {
export type RLNFullProof = {
proof: Proof
publicSignals: RLNPublicSignals | SemaphorePublicSignals
publicSignals: RLNPublicSignals
}
export type SemaphoreFullProof = {
proof: Proof
publicSignals: SemaphorePublicSignals
}
export type RLNPublicSignals = {
@@ -29,7 +34,16 @@ export type SemaphorePublicSignals = {
externalNullifier: StrBigInt
}
export type SolidityProof = StrBigInt[]
export type SemaphoreSolidityProof = [
StrBigInt,
StrBigInt,
StrBigInt,
StrBigInt,
StrBigInt,
StrBigInt,
StrBigInt,
StrBigInt
]
export type SemaphoreWitness = {
identityNullifier: StrBigInt

View File

@@ -1,39 +0,0 @@
/* istanbul ignore file */
import { groth16 } from "snarkjs"
import { FullProof, SolidityProof, StrBigInt } from "./types"
export default class ZkProtocol {
/**
* Verifies a zero-knowledge SnarkJS proof.
* @param verificationKey The zero-knowledge verification key.
* @param fullProof The SnarkJS full proof.
* @returns True if the proof is valid, false otherwise.
*/
public static verifyProof(verificationKey: string, fullProof: FullProof): Promise<boolean> {
const { proof, publicSignals } = fullProof
const publicSignalsArray: StrBigInt[] = Object.values(publicSignals)
return groth16.verify(verificationKey, publicSignalsArray, proof)
}
/**
* Converts a full proof in a proof compatible with the Verifier.sol method inputs.
* @param fullProof The proof generated with SnarkJS.
* @returns The Solidity compatible proof.
*/
public static packToSolidityProof(fullProof: FullProof): SolidityProof {
const { proof } = fullProof
return [
proof.pi_a[0],
proof.pi_a[1],
proof.pi_b[0][1],
proof.pi_b[0][0],
proof.pi_b[1][1],
proof.pi_b[1][0],
proof.pi_c[0],
proof.pi_c[1]
]
}
}

View File

@@ -58,6 +58,26 @@ describe("RLN", () => {
expect(fun).toThrow("Can't generate a proof for a zero leaf")
})
it("Should retrieve user secret after spaming", () => {
const identity = new ZkIdentity()
const secretHash = identity.getSecretHash()
const signal1 = "hey hey"
const signalHash1 = genSignalHash(signal1)
const signal2 = "hey hey again"
const signalHash2 = genSignalHash(signal2)
const epoch = genExternalNullifier("test-epoch")
const rlnIdentifier = RLN.genIdentifier()
const [y1] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash1)
const [y2] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash2)
const retrievedSecret = RLN.retrieveSecret(signalHash1, signalHash2, y1, y2)
expect(retrievedSecret).toEqual(secretHash)
})
// eslint-disable-next-line jest/no-disabled-tests
it.skip("Should generate rln proof and verify it", async () => {
const identity = new ZkIdentity()
@@ -96,27 +116,6 @@ 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", () => {
const identity = new ZkIdentity()
const secretHash = identity.getSecretHash()
const signal1 = "hey hey"
const signalHash1 = genSignalHash(signal1)
const signal2 = "hey hey again"
const signalHash2 = genSignalHash(signal2)
const epoch = genExternalNullifier("test-epoch")
const rlnIdentifier = RLN.genIdentifier()
const [y1] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash1)
const [y2] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash2)
const retrievedSecret = RLN.retrieveSecret(signalHash1, signalHash2, y1, y2)
expect(retrievedSecret).toEqual(secretHash)
})
})
})

View File

@@ -76,7 +76,6 @@ describe("Semaphore", () => {
const response = await Semaphore.verifyProof(vKey, { proof: fullProof.proof, publicSignals })
expect(response).toBe(true)
expect(fullProof.publicSignals).toEqual(publicSignals)
}, 30000)
})
})