@semaphore-protocol/proof 2.6.0 → 3.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
+ import { BytesLike, Hexable } from "@ethersproject/bytes";
1
2
  import { Group } from "@semaphore-protocol/group";
2
3
  import type { Identity } from "@semaphore-protocol/identity";
3
4
  import { MerkleProof } from "@zk-kit/incremental-merkle-tree";
4
- import { BigNumberish, FullProof, SnarkArtifacts } from "./types";
5
- export default function generateProof({ trapdoor, nullifier, commitment }: Identity, groupOrMerkleProof: Group | MerkleProof, externalNullifier: BigNumberish, signal: string, snarkArtifacts?: SnarkArtifacts): Promise<FullProof>;
5
+ import { FullProof, SnarkArtifacts } from "./types";
6
+ export default function generateProof({ trapdoor, nullifier, commitment }: Identity, groupOrMerkleProof: Group | MerkleProof, externalNullifier: BytesLike | Hexable | number | bigint, signal: BytesLike | Hexable | number | bigint, snarkArtifacts?: SnarkArtifacts): Promise<FullProof>;
@@ -0,0 +1,7 @@
1
+ import { BytesLike, Hexable } from "@ethersproject/bytes";
2
+ /**
3
+ * Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
4
+ * @param message The message to be hashed.
5
+ * @returns The message digest.
6
+ */
7
+ export default function hash(message: BytesLike | Hexable | number | bigint): bigint;
@@ -1,7 +1,5 @@
1
- import generateNullifierHash from "./generateNullifierHash";
2
1
  import generateProof from "./generateProof";
3
2
  import verifyProof from "./verifyProof";
4
- import generateSignalHash from "./generateSignalHash";
5
3
  import packToSolidityProof from "./packToSolidityProof";
6
- export { generateNullifierHash, generateProof, verifyProof, generateSignalHash, packToSolidityProof };
4
+ export { generateProof, verifyProof, packToSolidityProof };
7
5
  export * from "./types";
@@ -1,26 +1,26 @@
1
- export declare type BigNumberish = string | bigint;
2
- export declare type SnarkArtifacts = {
1
+ export type BigNumberish = string | bigint;
2
+ export type SnarkArtifacts = {
3
3
  wasmFilePath: string;
4
4
  zkeyFilePath: string;
5
5
  };
6
- export declare type Proof = {
6
+ export type Proof = {
7
7
  pi_a: BigNumberish[];
8
8
  pi_b: BigNumberish[][];
9
9
  pi_c: BigNumberish[];
10
10
  protocol: string;
11
11
  curve: string;
12
12
  };
13
- export declare type FullProof = {
13
+ export type FullProof = {
14
14
  proof: Proof;
15
15
  publicSignals: PublicSignals;
16
16
  };
17
- export declare type PublicSignals = {
18
- merkleRoot: BigNumberish;
17
+ export type PublicSignals = {
18
+ merkleTreeRoot: BigNumberish;
19
19
  nullifierHash: BigNumberish;
20
20
  signalHash: BigNumberish;
21
21
  externalNullifier: BigNumberish;
22
22
  };
23
- export declare type SolidityProof = [
23
+ export type SolidityProof = [
24
24
  BigNumberish,
25
25
  BigNumberish,
26
26
  BigNumberish,
@@ -1,8 +1,8 @@
1
1
  import { FullProof } from "./types";
2
2
  /**
3
3
  * Verifies a SnarkJS proof.
4
- * @param verificationKey The zero-knowledge verification key.
5
4
  * @param fullProof The SnarkJS full proof.
5
+ * @param treeDepth The Merkle tree depth.
6
6
  * @returns True if the proof is valid, false otherwise.
7
7
  */
8
- export default function verifyProof(verificationKey: any, { proof, publicSignals }: FullProof): Promise<boolean>;
8
+ export default function verifyProof({ proof, publicSignals }: FullProof, treeDepth: number): Promise<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semaphore-protocol/proof",
3
- "version": "2.6.0",
3
+ "version": "3.0.0-beta.5",
4
4
  "description": "A library to generate and verify Semaphore proofs.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.node.js",
@@ -30,21 +30,22 @@
30
30
  "access": "public"
31
31
  },
32
32
  "devDependencies": {
33
+ "@rollup/plugin-json": "^5.0.1",
33
34
  "ffjavascript": "^0.2.54",
34
35
  "rollup-plugin-cleanup": "^3.2.1",
35
36
  "rollup-plugin-typescript2": "^0.31.2",
36
37
  "typedoc": "^0.22.11"
37
38
  },
38
39
  "peerDependencies": {
39
- "@semaphore-protocol/group": "2.6.0",
40
- "@semaphore-protocol/identity": "2.6.0"
40
+ "@semaphore-protocol/group": "3.0.0-beta.5",
41
+ "@semaphore-protocol/identity": "3.0.0-beta.5"
41
42
  },
42
43
  "dependencies": {
44
+ "@ethersproject/bignumber": "^5.5.0",
43
45
  "@ethersproject/bytes": "^5.7.0",
44
- "@ethersproject/solidity": "^5.5.0",
46
+ "@ethersproject/keccak256": "^5.7.0",
45
47
  "@ethersproject/strings": "^5.5.0",
46
48
  "@zk-kit/incremental-merkle-tree": "0.4.3",
47
- "circomlibjs": "0.0.8",
48
49
  "snarkjs": "^0.4.13"
49
50
  }
50
51
  }
@@ -1,15 +1,16 @@
1
+ import { BytesLike, Hexable } from "@ethersproject/bytes"
1
2
  import { Group } from "@semaphore-protocol/group"
2
3
  import type { Identity } from "@semaphore-protocol/identity"
3
4
  import { MerkleProof } from "@zk-kit/incremental-merkle-tree"
4
5
  import { groth16 } from "snarkjs"
5
- import generateSignalHash from "./generateSignalHash"
6
- import { BigNumberish, FullProof, SnarkArtifacts } from "./types"
6
+ import hash from "./hash"
7
+ import { FullProof, SnarkArtifacts } from "./types"
7
8
 
8
9
  export default async function generateProof(
9
10
  { trapdoor, nullifier, commitment }: Identity,
10
11
  groupOrMerkleProof: Group | MerkleProof,
11
- externalNullifier: BigNumberish,
12
- signal: string,
12
+ externalNullifier: BytesLike | Hexable | number | bigint,
13
+ signal: BytesLike | Hexable | number | bigint,
13
14
  snarkArtifacts?: SnarkArtifacts
14
15
  ): Promise<FullProof> {
15
16
  let merkleProof: MerkleProof
@@ -21,7 +22,7 @@ export default async function generateProof(
21
22
  throw new Error("The identity is not part of the group")
22
23
  }
23
24
 
24
- merkleProof = groupOrMerkleProof.generateProofOfMembership(index)
25
+ merkleProof = groupOrMerkleProof.generateMerkleProof(index)
25
26
  } else {
26
27
  merkleProof = groupOrMerkleProof
27
28
  }
@@ -39,8 +40,8 @@ export default async function generateProof(
39
40
  identityNullifier: nullifier,
40
41
  treePathIndices: merkleProof.pathIndices,
41
42
  treeSiblings: merkleProof.siblings,
42
- externalNullifier,
43
- signalHash: generateSignalHash(signal)
43
+ externalNullifier: hash(externalNullifier),
44
+ signalHash: hash(signal)
44
45
  },
45
46
  snarkArtifacts.wasmFilePath,
46
47
  snarkArtifacts.zkeyFilePath
@@ -49,7 +50,7 @@ export default async function generateProof(
49
50
  return {
50
51
  proof,
51
52
  publicSignals: {
52
- merkleRoot: publicSignals[0],
53
+ merkleTreeRoot: publicSignals[0],
53
54
  nullifierHash: publicSignals[1],
54
55
  signalHash: publicSignals[2],
55
56
  externalNullifier: publicSignals[3]
package/src/hash.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { BigNumber } from "@ethersproject/bignumber"
2
+ import { BytesLike, Hexable, zeroPad } from "@ethersproject/bytes"
3
+ import { keccak256 } from "@ethersproject/keccak256"
4
+
5
+ /**
6
+ * Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
7
+ * @param message The message to be hashed.
8
+ * @returns The message digest.
9
+ */
10
+ export default function hash(message: BytesLike | Hexable | number | bigint): bigint {
11
+ message = BigNumber.from(message).toTwos(256).toHexString()
12
+ message = zeroPad(message, 32)
13
+
14
+ return BigInt(keccak256(message)) >> BigInt(8)
15
+ }
package/src/index.test.ts CHANGED
@@ -1,11 +1,8 @@
1
- import { formatBytes32String } from "@ethersproject/strings"
2
1
  import { Group } from "@semaphore-protocol/group"
3
2
  import { Identity } from "@semaphore-protocol/identity"
4
3
  import { getCurveFromName } from "ffjavascript"
5
- import fs from "fs"
6
- import generateNullifierHash from "./generateNullifierHash"
7
4
  import generateProof from "./generateProof"
8
- import generateSignalHash from "./generateSignalHash"
5
+ import hash from "./hash"
9
6
  import packToSolidityProof from "./packToSolidityProof"
10
7
  import { FullProof } from "./types"
11
8
  import verifyProof from "./verifyProof"
@@ -13,12 +10,11 @@ import verifyProof from "./verifyProof"
13
10
  describe("Proof", () => {
14
11
  const treeDepth = Number(process.env.TREE_DEPTH) || 20
15
12
 
16
- const externalNullifier = "1"
17
- const signal = "0x111"
13
+ const externalNullifier = 1
14
+ const signal = 2
18
15
 
19
- const wasmFilePath = `./snark-artifacts/semaphore.wasm`
20
- const zkeyFilePath = `./snark-artifacts/semaphore.zkey`
21
- const verificationKeyPath = `./snark-artifacts/semaphore.json`
16
+ const wasmFilePath = `./snark-artifacts/${treeDepth}/semaphore.wasm`
17
+ const zkeyFilePath = `./snark-artifacts/${treeDepth}/semaphore.zkey`
22
18
 
23
19
  const identity = new Identity()
24
20
 
@@ -69,45 +65,79 @@ describe("Proof", () => {
69
65
  })
70
66
 
71
67
  expect(typeof fullProof).toBe("object")
72
- expect(fullProof.publicSignals.externalNullifier).toBe(externalNullifier)
73
- expect(fullProof.publicSignals.merkleRoot).toBe(group.root.toString())
68
+ expect(fullProof.publicSignals.merkleTreeRoot).toBe(group.root.toString())
74
69
  }, 20000)
75
70
 
76
- it("Should generate a Semaphore proof passing a Merkle proof as parametr", async () => {
71
+ it("Should generate a Semaphore proof passing a Merkle proof as parameter", async () => {
77
72
  const group = new Group(treeDepth)
78
73
 
79
74
  group.addMembers([BigInt(1), BigInt(2), identity.commitment])
80
75
 
81
- fullProof = await generateProof(identity, group.generateProofOfMembership(2), externalNullifier, signal, {
76
+ fullProof = await generateProof(identity, group.generateMerkleProof(2), externalNullifier, signal, {
82
77
  wasmFilePath,
83
78
  zkeyFilePath
84
79
  })
85
80
 
86
81
  expect(typeof fullProof).toBe("object")
87
- expect(fullProof.publicSignals.externalNullifier).toBe(externalNullifier)
88
- expect(fullProof.publicSignals.merkleRoot).toBe(group.root.toString())
82
+ expect(fullProof.publicSignals.merkleTreeRoot).toBe(group.root.toString())
89
83
  }, 20000)
90
84
  })
91
85
 
92
- describe("# generateSignalHash", () => {
93
- it("Should generate a valid signal hash", async () => {
94
- const signalHash = generateSignalHash(signal)
86
+ describe("# verifyProof", () => {
87
+ it("Should not verify a proof if the tree depth is wrong", () => {
88
+ const fun = () => verifyProof(fullProof, 3)
95
89
 
96
- expect(signalHash.toString()).toBe(fullProof.publicSignals.signalHash)
90
+ expect(fun).toThrow("The tree depth must be a number between 16 and 32")
97
91
  })
98
92
 
99
- it("Should generate a valid signal hash by passing a valid hex string", async () => {
100
- const signalHash = generateSignalHash(formatBytes32String(signal))
93
+ it("Should verify a Semaphore proof", async () => {
94
+ const response = await verifyProof(fullProof, treeDepth)
101
95
 
102
- expect(signalHash.toString()).toBe(fullProof.publicSignals.signalHash)
96
+ expect(response).toBe(true)
103
97
  })
104
98
  })
105
99
 
106
- describe("# generateNullifierHash", () => {
107
- it("Should generate a valid nullifier hash", async () => {
108
- const nullifierHash = generateNullifierHash(externalNullifier, identity.getNullifier())
100
+ describe("# hash", () => {
101
+ it("Should hash the signal value correctly", async () => {
102
+ const signalHash = hash(signal)
109
103
 
110
- expect(nullifierHash.toString()).toBe(fullProof.publicSignals.nullifierHash)
104
+ expect(signalHash.toString()).toBe(fullProof.publicSignals.signalHash)
105
+ })
106
+
107
+ it("Should hash the external nullifier value correctly", async () => {
108
+ const externalNullifierHash = hash(externalNullifier)
109
+
110
+ expect(externalNullifierHash.toString()).toBe(fullProof.publicSignals.externalNullifier)
111
+ })
112
+
113
+ it("Should hash a number", async () => {
114
+ expect(hash(2).toString()).toBe(
115
+ "113682330006535319932160121224458771213356533826860247409332700812532759386"
116
+ )
117
+ })
118
+
119
+ it("Should hash a big number", async () => {
120
+ expect(hash(BigInt(2)).toString()).toBe(
121
+ "113682330006535319932160121224458771213356533826860247409332700812532759386"
122
+ )
123
+ })
124
+
125
+ it("Should hash an hex number", async () => {
126
+ expect(hash("0x2").toString()).toBe(
127
+ "113682330006535319932160121224458771213356533826860247409332700812532759386"
128
+ )
129
+ })
130
+
131
+ it("Should hash an string number", async () => {
132
+ expect(hash("2").toString()).toBe(
133
+ "113682330006535319932160121224458771213356533826860247409332700812532759386"
134
+ )
135
+ })
136
+
137
+ it("Should hash an array", async () => {
138
+ expect(hash([2]).toString()).toBe(
139
+ "113682330006535319932160121224458771213356533826860247409332700812532759386"
140
+ )
111
141
  })
112
142
  })
113
143
 
@@ -118,14 +148,4 @@ describe("Proof", () => {
118
148
  expect(solidityProof).toHaveLength(8)
119
149
  })
120
150
  })
121
-
122
- describe("# verifyProof", () => {
123
- it("Should generate and verify a Semaphore proof", async () => {
124
- const verificationKey = JSON.parse(fs.readFileSync(verificationKeyPath, "utf-8"))
125
-
126
- const response = await verifyProof(verificationKey, fullProof)
127
-
128
- expect(response).toBe(true)
129
- })
130
- })
131
151
  })
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
1
- import generateNullifierHash from "./generateNullifierHash"
2
1
  import generateProof from "./generateProof"
3
2
  import verifyProof from "./verifyProof"
4
- import generateSignalHash from "./generateSignalHash"
5
3
  import packToSolidityProof from "./packToSolidityProof"
6
4
 
7
- export { generateNullifierHash, generateProof, verifyProof, generateSignalHash, packToSolidityProof }
5
+ export { generateProof, verifyProof, packToSolidityProof }
8
6
  export * from "./types"
@@ -19,7 +19,7 @@ export type FullProof = {
19
19
  }
20
20
 
21
21
  export type PublicSignals = {
22
- merkleRoot: BigNumberish
22
+ merkleTreeRoot: BigNumberish
23
23
  nullifierHash: BigNumberish
24
24
  signalHash: BigNumberish
25
25
  externalNullifier: BigNumberish