Skip to content

Commit 76da41a

Browse files
artwymanrrrliu
authored andcommitted
Remove circomlibjs dependency in favor of zk-kit (#1911)
Minimalist approach to switching away from circomlibjs in order to eliminate its massive poseidon_constants from our bundle size. This drops the bundle size in my measurements from 9.4MB to 6.4MB. Functionality remains unchanged (validated by pre-existing backward compatibility tests), with one known exception. The EdDSA PCD now only supports "messages" (bigint arrays) of sizes in the set {1, 2, 3, 12, 13}. Other sizes will throw an error at proving time. This is because poseidon-lite also has a set of constants (albeit much smaller than circomlibjs) needed for each size. There are comments in the code explaining why each size was selected. Two things explicitly not done, given the minimalist approach: - This PR doesn't attempt to unify/centralize uses of zk-kit/poseidon-lite into a single package. Instead the old EdDSA PCDs use the appropriate zk-kit/poseidon-lite libraries directly. - This PR also doesn't change the format (in code, or in serialization) of EdDSA keys to match the new form used by PODs. All externally-visible formats are kept unchanged.
1 parent ab654be commit 76da41a

17 files changed

Lines changed: 114 additions & 122 deletions

File tree

.vscode/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@
1919
"port": 4321,
2020
"restart": true,
2121
"cwd": "${workspaceRoot}"
22+
},
23+
{
24+
"args": [
25+
"-r",
26+
"ts-node/register",
27+
"--config",
28+
"${workspaceFolder}/.mocharc.js",
29+
"--no-timeouts",
30+
"--exit",
31+
"${file}"
32+
],
33+
"cwd": "${fileDirname}",
34+
"internalConsoleOptions": "openOnSessionStart",
35+
"name": "TS Mocha Test (Current File)",
36+
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
37+
"request": "launch",
38+
"type": "node"
2239
}
2340
]
2441
}

apps/consumer-client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"@semaphore-protocol/identity": "^3.15.2",
3838
"@simplewebauthn/browser": "^7.2.0",
3939
"@simplewebauthn/server": "^7.2.0",
40-
"@zk-kit/eddsa-poseidon": "1.0.2",
40+
"@zk-kit/eddsa-poseidon": "1.0.3",
4141
"dotenv": "^16.0.3",
4242
"ethers": "^5.7.2",
4343
"json-bigint": "^1.0.0",

apps/passport-server/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"@sendgrid/mail": "^7.7.0",
5353
"@types/lodash": "^4.14.191",
5454
"@types/pg": "^8.6.6",
55-
"@zk-kit/eddsa-poseidon": "1.0.2",
5655
"async-lock": "^1.4.0",
5756
"cookie-parser": "^1.4.6",
5857
"cors": "^2.8.5",

apps/zupoll-client/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
6767
"@pcd/eslint-config-custom": "0.11.4",
6868
"@pcd/tsconfig": "0.11.4",
69-
"@types/circomlibjs": "^0.1.5",
7069
"@types/expect": "^24.3.0",
7170
"@types/fuzzy-search": "^2.1.5",
7271
"@types/json-stable-stringify": "^1.0.34",

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
"postinstall": "patch-package"
4848
},
4949
"devDependencies": {
50-
"@types/circomlibjs": "0.1.6",
5150
"@types/node": "^20.11.28",
5251
"plop": "^4.0.1",
5352
"prettier": "^3.0.0",

packages/lib/gpc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"eslint": "^8.57.0",
5050
"fix-esm-import-path": "^1.10.0",
5151
"mocha": "^10.2.0",
52-
"poseidon-lite": "^0.2.1",
52+
"poseidon-lite": "^0.3.0",
5353
"ts-mocha": "^10.0.0",
5454
"typescript": "^5.3.3"
5555
},

packages/lib/gpcircuits/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"fix-esm-import-path": "^1.10.0",
5757
"lodash": "^4.17.21",
5858
"mocha": "^10.2.0",
59-
"poseidon-lite": "^0.2.1",
59+
"poseidon-lite": "^0.3.0",
6060
"ts-mocha": "^10.0.0",
6161
"typescript": "^5.3.3"
6262
},

packages/lib/pod/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@
3535
"@zk-kit/utils": "1.2.1",
3636
"js-sha256": "^0.10.1",
3737
"json-bigint": "^1.0.0",
38-
"poseidon-lite": "^0.2.1"
38+
"poseidon-lite": "^0.3.0"
3939
},
4040
"devDependencies": {
4141
"@pcd/eddsa-pcd": "0.6.5",
4242
"@pcd/eslint-config-custom": "0.11.4",
4343
"@pcd/pcd-types": "0.11.4",
4444
"@pcd/tsconfig": "0.11.4",
4545
"@semaphore-protocol/identity": "^3.15.2",
46+
"@types/circomlibjs": "^0.1.6",
4647
"@types/chai": "^4.3.5",
4748
"@types/mocha": "^10.0.1",
4849
"circomlibjs": "^0.1.7",

packages/pcd/eddsa-pcd/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"dependencies": {
3535
"@pcd/pcd-types": "0.11.4",
3636
"@pcd/util": "0.5.4",
37-
"circomlibjs": "^0.1.7",
37+
"@zk-kit/eddsa-poseidon": "1.0.3",
38+
"poseidon-lite": "^0.3.0",
3839
"uuid": "^9.0.0"
3940
},
4041
"devDependencies": {

packages/pcd/eddsa-pcd/src/EDDSAPCDPackage.ts

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import { DisplayOptions, PCDPackage, SerializedPCD } from "@pcd/pcd-types";
22
import { fromHexString, requireDefinedParameter, toHexString } from "@pcd/util";
3-
import { Eddsa, Point, buildEddsa } from "circomlibjs";
3+
import {
4+
derivePublicKey,
5+
packSignature,
6+
signMessage,
7+
unpackSignature,
8+
verifySignature
9+
} from "@zk-kit/eddsa-poseidon";
10+
import { poseidon1 } from "poseidon-lite/poseidon1";
11+
import { poseidon12 } from "poseidon-lite/poseidon12";
12+
import { poseidon13 } from "poseidon-lite/poseidon13";
13+
import { poseidon2 } from "poseidon-lite/poseidon2";
14+
import { poseidon3 } from "poseidon-lite/poseidon3";
415
import { v4 as uuid } from "uuid";
516
import {
617
EdDSAInitArgs,
@@ -12,31 +23,11 @@ import {
1223
EdDSAPublicKey
1324
} from "./EdDSAPCD";
1425

15-
let initializedPromise: Promise<void> | undefined;
16-
let eddsa: Eddsa;
17-
18-
/**
19-
* A promise designed to make sure that the EdDSA algorithm
20-
* of the `circomlibjs` package has been properly initialized.
21-
* It only initializes them once.
22-
*/
23-
async function ensureInitialized(): Promise<void> {
24-
if (!initializedPromise) {
25-
initializedPromise = (async (): Promise<void> => {
26-
eddsa = await buildEddsa();
27-
})();
28-
}
29-
30-
await initializedPromise;
31-
}
32-
3326
/**
3427
* Creates a new {@link EdDSAPCD} by generating an {@link EdDSAPCDProof}
3528
* and deriving an {@link EdDSAPCDClaim} from the given {@link EdDSAPCDArgs}.
3629
*/
3730
export async function prove(args: EdDSAPCDArgs): Promise<EdDSAPCD> {
38-
await ensureInitialized();
39-
4031
let message;
4132

4233
if (!args.privateKey.value) throw new Error("No private key value provided");
@@ -60,16 +51,12 @@ export async function prove(args: EdDSAPCDArgs): Promise<EdDSAPCD> {
6051
const id = typeof args.id.value === "string" ? args.id.value : uuid();
6152
const prvKey = fromHexString(args.privateKey.value);
6253

63-
const hashedMessage = eddsa.poseidon(message);
54+
const hashedMessage = poseidonHashMessage(message);
6455
const publicKey = await getEdDSAPublicKey(prvKey);
6556

6657
// Make the signature on the message.
67-
// Note: packSignature converts the R8 coordinates from Mongtomery form to
68-
// standard form for use outside of circomlibjs.
69-
// This is a reference to Montgomery form of numbers for modular
70-
// multiplication, NOT Montgomery form of eliptic curves. See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#Montgomery_form
7158
const signature = toHexString(
72-
eddsa.packSignature(eddsa.signPoseidon(prvKey, hashedMessage))
59+
packSignature(signMessage(prvKey, hashedMessage))
7360
);
7461

7562
return new EdDSAPCD(id, { message, publicKey }, { signature });
@@ -81,22 +68,20 @@ export async function prove(args: EdDSAPCDArgs): Promise<EdDSAPCD> {
8168
*/
8269
export async function verify(pcd: EdDSAPCD): Promise<boolean> {
8370
try {
84-
await ensureInitialized();
85-
86-
const signature = eddsa.unpackSignature(fromHexString(pcd.proof.signature));
71+
const signature = unpackSignature(fromHexString(pcd.proof.signature));
8772

8873
// Note: `F.fromObject` converts a coordinate from standard format to
8974
// Montgomery form, which is expected by circomlibjs. unpackSignature above
9075
// does the same for its R8 point.
9176
// This is a reference to Montgomery form of numbers for modular
9277
// multiplication, NOT Montgomery form of eliptic curves. See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#Montgomery_form
93-
const pubKey = pcd.claim.publicKey.map((p) =>
94-
eddsa.F.fromObject(p)
95-
) as Point;
78+
const pubKey = pcd.claim.publicKey.map((coordinateString: string) =>
79+
BigInt("0x" + coordinateString)
80+
) as [bigint, bigint];
9681

97-
const hashedMessage = eddsa.poseidon(pcd.claim.message);
82+
const hashedMessage = poseidonHashMessage(pcd.claim.message);
9883

99-
return eddsa.verifyPoseidon(hashedMessage, signature, pubKey);
84+
return verifySignature(hashedMessage, signature, pubKey);
10085
} catch {
10186
return false;
10287
}
@@ -205,17 +190,37 @@ export const EdDSAPCDPackage: PCDPackage<
205190
export async function getEdDSAPublicKey(
206191
privateKey: string | Uint8Array
207192
): Promise<EdDSAPublicKey> {
208-
await ensureInitialized();
209-
210193
if (typeof privateKey === "string") {
211194
privateKey = fromHexString(privateKey);
212195
}
213196

214-
return eddsa.prv2pub(privateKey).map((p) =>
215-
// Note: `F.toObject` converts a point from the Montgomery format used by
216-
// circomlibjs to standard form.
217-
// This is a reference to Montgomery form of numbers for modular
218-
// multiplication, NOT Montgomery form of eliptic curves. See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#Montgomery_form
219-
eddsa.F.toObject(p).toString(16).padStart(64, "0")
197+
return derivePublicKey(privateKey).map((coordinate: bigint) =>
198+
coordinate.toString(16).padStart(64, "0")
220199
) as EdDSAPublicKey;
221200
}
201+
202+
function poseidonHashMessage(message: bigint[]): bigint {
203+
switch (message.length) {
204+
case 1:
205+
// Used by PODs for value hashing, so no extra bundle size impact.
206+
return poseidon1(message);
207+
case 2:
208+
// Used by PODs for Merkle tree hasing, so no extra bundle size impact.
209+
return poseidon2(message);
210+
case 3:
211+
// Used by unit tests, including backward-compatibility with fixed values.
212+
// Used by GPCs for tuple hasing, so no extra bundle size impact.
213+
return poseidon3(message);
214+
case 12:
215+
// Tailored to the size of EdDSATicketPCD.
216+
return poseidon12(message);
217+
case 13:
218+
// Tailored to the size of EdDSAFrogPCD.
219+
return poseidon13(message);
220+
default:
221+
break;
222+
}
223+
throw new Error(
224+
`Unsupported EdDSAMessagePCD message size ${message.length}.`
225+
);
226+
}

0 commit comments

Comments
 (0)