mirror of
https://github.com/privacy-scaling-explorations/zk-kit.git
synced 2026-04-22 03:00:15 -04:00
fix: remove public signal checks
Also, the old zk-protocols common class has been removed and the proof methods are now specific for each protocol.
This commit is contained in:
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user