Skip to content

Commit

Permalink
Merge pull request #180 from privacy-scaling-explorations/refactor/nu…
Browse files Browse the repository at this point in the history
…llifier

Remove `nullifier` from `poseidon-proof` circuit
  • Loading branch information
cedoor committed Feb 22, 2024
2 parents 718a5c2 + ab34a5b commit 0b4ccbe
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 262 deletions.
10 changes: 4 additions & 6 deletions packages/circuits/circom/poseidon-proof.circom
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ include "poseidon.circom";
// for zero-knowledge proof contexts. A parameter is defined to specify
// the number of inputs a Poseidon hash function can support
// (i.e. 'NUMBER_OF_INPUTS').
// A scope value can be used to define a nullifier to prevent the same
// proof from being re-used twice.
// A scope value can be used to externally compute a nullifier to prevent
// the same proof from being re-used twice.
template PoseidonProof(NUMBER_OF_INPUTS) {
// The circuit takes two inputs: the pre-images and an additional scope parameter.
signal input preimages[NUMBER_OF_INPUTS];
Expand All @@ -22,8 +22,6 @@ template PoseidonProof(NUMBER_OF_INPUTS) {
signal output digest;
digest <== Poseidon(NUMBER_OF_INPUTS)(preimages);

// A nullifier is also computed using both the scope and the digest, providing a value
// to prevent the same proof from being reused twice.
signal output nullifier;
nullifier <== Poseidon(2)([scope, digest]);
// Dummy constraint to prevent compiler from optimizing it.
signal dummySquare <== scope * scope;
}
8 changes: 3 additions & 5 deletions packages/circuits/tests/poseidon-proof.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { WitnessTester } from "circomkit"
import { poseidon2, poseidon3 } from "poseidon-lite"
import { poseidon3 } from "poseidon-lite"
import { circomkit } from "./common"

describe("poseidon-proof", () => {
let circuit: WitnessTester<["preimages", "scope"], ["digest", "nullifier"]>
let circuit: WitnessTester<["preimages", "scope"], ["digest"]>

const numberOfInputs = 3
const preimages = [1, 2, 3]
const scope = 2
const digest = poseidon3(preimages)
const nullifier = poseidon2([scope, digest])

const INPUT = {
preimages,
scope
}

const OUTPUT = {
digest,
nullifier
digest
}

before(async () => {
Expand Down
3 changes: 1 addition & 2 deletions packages/poseidon-proof/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { PoseidonProof, SnarkArtifacts } from "./types"
/**
* Creates a zero-knowledge proof to prove that you have the preimages of a hash,
* without disclosing the actual preimages themselves.
* The use of a scope parameter along with a nullifier helps ensure the uniqueness
* The use of a scope parameter helps ensure the uniqueness
* and non-reusability of the proofs, enhancing security in applications like
* blockchain transactions or private data verification.
* If, for example, this package were used with Semaphore to demonstrate possession
Expand Down Expand Up @@ -44,7 +44,6 @@ export default async function generate(
return {
scope: BigNumber.from(scope).toString() as NumericString,
digest: publicSignals[0],
nullifier: publicSignals[1],
proof: packProof(proof)
}
}
1 change: 0 additions & 1 deletion packages/poseidon-proof/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export type SnarkArtifacts = {
export type PoseidonProof = {
scope: NumericString
digest: NumericString
nullifier: NumericString
proof: PackedProof
}

Expand Down
402 changes: 161 additions & 241 deletions packages/poseidon-proof/src/verification-keys.json

Large diffs are not rendered by default.

7 changes: 2 additions & 5 deletions packages/poseidon-proof/src/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@ import verificationKeys from "./verification-keys.json"
* @param poseidonProof The Poseidon zero-knowledge proof.
* @returns True if the proof is valid, false otherwise.
*/
export default function verify(
numberOfInputs: number,
{ scope, digest, nullifier, proof }: PoseidonProof
): Promise<boolean> {
export default function verify(numberOfInputs: number, { scope, digest, proof }: PoseidonProof): Promise<boolean> {
const verificationKey = {
...verificationKeys,
vk_delta_2: verificationKeys.vk_delta_2[numberOfInputs - 1],
IC: verificationKeys.IC[numberOfInputs - 1]
}

return groth16.verify(verificationKey, [digest, nullifier, hash(scope)], unpackProof(proof))
return groth16.verify(verificationKey, [digest, hash(scope)], unpackProof(proof))
}
2 changes: 0 additions & 2 deletions packages/poseidon-proof/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,10 @@ describe("PoseidonProof", () => {
fullProof = await generate(currentPreimages, scope)

const digest = computePoseidon(currentPreimages.map((preimage) => hash(preimage)))
const nullifier = poseidon2([hash(scope), digest])

expect(fullProof.proof).toHaveLength(8)
expect(fullProof.scope).toBe(scope.toString())
expect(fullProof.digest).toBe(digest.toString())
expect(fullProof.nullifier).toBe(nullifier.toString())

// Verify.
const response = await verify(currentPreimages.length, fullProof)
Expand Down

0 comments on commit 0b4ccbe

Please sign in to comment.