@waku/rln 0.1.6-b4748fd.0 → 0.1.6-b58de3a.0

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.
Files changed (44) hide show
  1. package/bundle/_virtual/utils.js +2 -2
  2. package/bundle/_virtual/utils2.js +2 -2
  3. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/_sha2.js +1 -1
  4. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/hmac.js +1 -1
  5. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/pbkdf2.js +1 -1
  6. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/scrypt.js +1 -1
  7. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/sha256.js +1 -1
  8. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/sha512.js +1 -1
  9. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/utils.js +1 -1
  10. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
  11. package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
  12. package/bundle/packages/rln/dist/contract/rln_base_contract.js +4 -24
  13. package/bundle/packages/rln/dist/credentials_manager.js +16 -13
  14. package/bundle/packages/rln/dist/identity.js +8 -5
  15. package/bundle/packages/rln/dist/keystore/keystore.js +10 -8
  16. package/bundle/packages/rln/dist/utils/bytes.js +10 -14
  17. package/dist/.tsbuildinfo +1 -1
  18. package/dist/contract/rln_base_contract.d.ts +0 -1
  19. package/dist/contract/rln_base_contract.js +4 -24
  20. package/dist/contract/rln_base_contract.js.map +1 -1
  21. package/dist/contract/test-utils.d.ts +39 -0
  22. package/dist/contract/test-utils.js +118 -0
  23. package/dist/contract/test-utils.js.map +1 -0
  24. package/dist/credentials_manager.js +16 -13
  25. package/dist/credentials_manager.js.map +1 -1
  26. package/dist/identity.d.ts +2 -4
  27. package/dist/identity.js +6 -5
  28. package/dist/identity.js.map +1 -1
  29. package/dist/keystore/keystore.js +10 -8
  30. package/dist/keystore/keystore.js.map +1 -1
  31. package/dist/utils/bytes.d.ts +1 -2
  32. package/dist/utils/bytes.js +9 -13
  33. package/dist/utils/bytes.js.map +1 -1
  34. package/dist/utils/index.d.ts +1 -1
  35. package/dist/utils/index.js +1 -1
  36. package/dist/utils/index.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/contract/rln_base_contract.ts +11 -37
  39. package/src/contract/test-utils.ts +179 -0
  40. package/src/credentials_manager.ts +27 -20
  41. package/src/identity.ts +7 -5
  42. package/src/keystore/keystore.ts +33 -17
  43. package/src/utils/bytes.ts +19 -21
  44. package/src/utils/index.ts +1 -1
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@waku/rln","version":"0.1.6-b4748fd.0","description":"RLN (Rate Limiting Nullifier) implementation for Waku","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"type":"module","homepage":"https://github.com/waku-org/js-waku/tree/master/packages/rln#readme","repository":{"type":"git","url":"https://github.com/waku-org/js-waku.git"},"bugs":{"url":"https://github.com/waku-org/js-waku/issues"},"license":"MIT OR Apache-2.0","keywords":["waku","rln","rate-limiting","privacy","web3"],"scripts":{"build":"run-s build:**","build:copy":"mkdir -p dist/resources && cp -r src/resources/* dist/resources/","build:esm":"tsc","build:bundle":"rollup --config rollup.config.js","fix":"run-s fix:*","fix:lint":"eslint src *.js --fix","check":"run-s check:*","check:tsc":"tsc -p tsconfig.dev.json","check:lint":"eslint \"src/!(resources)/**/*.{ts,js}\" *.js","check:spelling":"cspell \"{README.md,src/**/*.ts}\"","test":"NODE_ENV=test run-s test:*","test:browser":"karma start karma.conf.cjs","watch:build":"tsc -p tsconfig.json -w","watch:test":"mocha --watch","prepublish":"npm run build","reset-hard":"git clean -dfx -e .idea && git reset --hard && npm i && npm run build"},"engines":{"node":">=20"},"devDependencies":{"@rollup/plugin-commonjs":"^25.0.7","@rollup/plugin-json":"^6.0.0","@rollup/plugin-node-resolve":"^15.2.3","@types/chai":"^5.0.1","@types/chai-spies":"^1.0.6","@waku/interfaces":"0.0.31-b4748fd.0","@types/deep-equal-in-any-order":"^1.0.4","@types/lodash":"^4.17.15","@types/sinon":"^17.0.3","@waku/build-utils":"^1.0.0","@waku/message-encryption":"0.0.34-b4748fd.0","deep-equal-in-any-order":"^2.0.6","fast-check":"^3.23.2","rollup-plugin-copy":"^3.5.0"},"files":["dist","bundle","src/**/*.ts","!**/*.spec.*","!**/*.json","CHANGELOG.md","LICENSE","README.md"],"dependencies":{"@chainsafe/bls-keystore":"3.0.0","@waku/core":"0.0.36-b4748fd.0","@waku/utils":"0.0.24-b4748fd.0","@noble/hashes":"^1.2.0","@waku/zerokit-rln-wasm":"^0.0.13","ethereum-cryptography":"^3.1.0","ethers":"^5.7.2","lodash":"^4.17.21","uuid":"^11.0.5","chai":"^5.1.2","chai-as-promised":"^8.0.1","chai-spies":"^1.1.0","chai-subset":"^1.6.0","sinon":"^19.0.2"}}
1
+ {"name":"@waku/rln","version":"0.1.6-b58de3a.0","description":"RLN (Rate Limiting Nullifier) implementation for Waku","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"type":"module","homepage":"https://github.com/waku-org/js-waku/tree/master/packages/rln#readme","repository":{"type":"git","url":"https://github.com/waku-org/js-waku.git"},"bugs":{"url":"https://github.com/waku-org/js-waku/issues"},"license":"MIT OR Apache-2.0","keywords":["waku","rln","rate-limiting","privacy","web3"],"scripts":{"build":"run-s build:**","build:copy":"mkdir -p dist/resources && cp -r src/resources/* dist/resources/","build:esm":"tsc","build:bundle":"rollup --config rollup.config.js","fix":"run-s fix:*","fix:lint":"eslint src *.js --fix","check":"run-s check:*","check:tsc":"tsc -p tsconfig.dev.json","check:lint":"eslint \"src/!(resources)/**/*.{ts,js}\" *.js","check:spelling":"cspell \"{README.md,src/**/*.ts}\"","test":"NODE_ENV=test run-s test:*","test:browser":"karma start karma.conf.cjs","watch:build":"tsc -p tsconfig.json -w","watch:test":"mocha --watch","prepublish":"npm run build","reset-hard":"git clean -dfx -e .idea && git reset --hard && npm i && npm run build"},"engines":{"node":">=20"},"devDependencies":{"@rollup/plugin-commonjs":"^25.0.7","@rollup/plugin-json":"^6.0.0","@rollup/plugin-node-resolve":"^15.2.3","@types/chai":"^5.0.1","@types/chai-spies":"^1.0.6","@waku/interfaces":"0.0.31-b58de3a.0","@types/deep-equal-in-any-order":"^1.0.4","@types/lodash":"^4.17.15","@types/sinon":"^17.0.3","@waku/build-utils":"^1.0.0","@waku/message-encryption":"0.0.34-b58de3a.0","deep-equal-in-any-order":"^2.0.6","fast-check":"^3.23.2","rollup-plugin-copy":"^3.5.0"},"files":["dist","bundle","src/**/*.ts","!**/*.spec.*","!**/*.json","CHANGELOG.md","LICENSE","README.md"],"dependencies":{"@chainsafe/bls-keystore":"3.0.0","@waku/core":"0.0.36-b58de3a.0","@waku/utils":"0.0.24-b58de3a.0","@noble/hashes":"^1.2.0","@waku/zerokit-rln-wasm":"^0.0.13","ethereum-cryptography":"^3.1.0","ethers":"^5.7.2","lodash":"^4.17.21","uuid":"^11.0.5","chai":"^5.1.2","chai-as-promised":"^8.0.1","chai-spies":"^1.1.0","chai-subset":"^1.6.0","sinon":"^19.0.2"}}
@@ -3,7 +3,6 @@ import { ethers } from "ethers";
3
3
 
4
4
  import { IdentityCredential } from "../identity.js";
5
5
  import { DecryptedCredentials } from "../keystore/types.js";
6
- import { buildBigIntFromUint8ArrayBE } from "../utils/bytes.js";
7
6
 
8
7
  import { RLN_ABI } from "./abi.js";
9
8
  import {
@@ -506,32 +505,6 @@ export class RLNBaseContract {
506
505
  }
507
506
  }
508
507
 
509
- private getIdCommitmentBigInt(bytes: Uint8Array): bigint {
510
- let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(bytes);
511
- log.info("1");
512
-
513
- if (!this.contract) {
514
- throw Error("RLN contract is not initialized");
515
- }
516
-
517
- const idCommitmentBigIntLimit = this.contract.idCommitmentBigIntLimit;
518
-
519
- log.info("idCommitmentBigIntBE: ", idCommitmentBigIntBE);
520
- log.info("idCommitmentBigIntLimit: ", idCommitmentBigIntLimit);
521
- log.info(
522
- "idCommitmentBigIntBE >= idCommitmentBigIntLimit: ",
523
- idCommitmentBigIntBE >= idCommitmentBigIntLimit
524
- );
525
-
526
- if (idCommitmentBigIntBE >= idCommitmentBigIntLimit) {
527
- log.warn(
528
- `ID commitment is greater than Q, reducing it by Q(idCommitmentBigIntLimit): ${idCommitmentBigIntBE} % ${idCommitmentBigIntLimit}`
529
- );
530
- idCommitmentBigIntBE = idCommitmentBigIntBE % idCommitmentBigIntLimit;
531
- }
532
- return idCommitmentBigIntBE;
533
- }
534
-
535
508
  public async registerWithIdentity(
536
509
  identity: IdentityCredential
537
510
  ): Promise<DecryptedCredentials | undefined> {
@@ -540,12 +513,10 @@ export class RLNBaseContract {
540
513
  `Registering identity with rate limit: ${this.rateLimit} messages/epoch`
541
514
  );
542
515
 
543
- const idCommitmentBigInt = this.getIdCommitmentBigInt(
544
- identity.IDCommitment
545
- );
546
-
547
516
  // Check if the ID commitment is already registered
548
- const existingIndex = await this.getMemberIndex(idCommitmentBigInt);
517
+ const existingIndex = await this.getMemberIndex(
518
+ identity.IDCommitmentBigInt
519
+ );
549
520
  if (existingIndex) {
550
521
  throw new Error(
551
522
  `ID commitment is already registered with index ${existingIndex}`
@@ -561,16 +532,19 @@ export class RLNBaseContract {
561
532
  }
562
533
 
563
534
  const estimatedGas = await this.contract.estimateGas.register(
564
- idCommitmentBigInt,
535
+ identity.IDCommitmentBigInt,
565
536
  this.rateLimit,
566
537
  []
567
538
  );
568
539
  const gasLimit = estimatedGas.add(10000);
569
540
 
570
541
  const txRegisterResponse: ethers.ContractTransaction =
571
- await this.contract.register(idCommitmentBigInt, this.rateLimit, [], {
572
- gasLimit
573
- });
542
+ await this.contract.register(
543
+ identity.IDCommitmentBigInt,
544
+ this.rateLimit,
545
+ [],
546
+ { gasLimit }
547
+ );
574
548
 
575
549
  const txRegisterReceipt = await txRegisterResponse.wait();
576
550
 
@@ -666,7 +640,7 @@ export class RLNBaseContract {
666
640
  permit.v,
667
641
  permit.r,
668
642
  permit.s,
669
- this.getIdCommitmentBigInt(identity.IDCommitment),
643
+ identity.IDCommitmentBigInt,
670
644
  this.rateLimit,
671
645
  idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
672
646
  );
@@ -0,0 +1,179 @@
1
+ import { hexToBytes } from "@waku/utils/bytes";
2
+ import { expect } from "chai";
3
+ import * as ethers from "ethers";
4
+ import sinon from "sinon";
5
+
6
+ import type { IdentityCredential } from "../identity.js";
7
+
8
+ import { DEFAULT_RATE_LIMIT, LINEA_CONTRACT } from "./constants.js";
9
+
10
+ export const mockRateLimits = {
11
+ minRate: 20,
12
+ maxRate: 600,
13
+ maxTotalRate: 1200,
14
+ currentTotalRate: 500
15
+ };
16
+
17
+ type MockProvider = {
18
+ getLogs: () => never[];
19
+ getBlockNumber: () => Promise<number>;
20
+ getNetwork: () => Promise<{ chainId: number }>;
21
+ };
22
+
23
+ type MockFilters = {
24
+ MembershipRegistered: () => { address: string };
25
+ MembershipErased: () => { address: string };
26
+ MembershipExpired: () => { address: string };
27
+ };
28
+
29
+ export function createMockProvider(): MockProvider {
30
+ return {
31
+ getLogs: () => [],
32
+ getBlockNumber: () => Promise.resolve(1000),
33
+ getNetwork: () => Promise.resolve({ chainId: 11155111 })
34
+ };
35
+ }
36
+
37
+ export function createMockFilters(): MockFilters {
38
+ return {
39
+ MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
40
+ MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
41
+ MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
42
+ };
43
+ }
44
+
45
+ type ContractOverrides = Partial<{
46
+ filters: Record<string, unknown>;
47
+ [key: string]: unknown;
48
+ }>;
49
+
50
+ export function createMockRegistryContract(
51
+ overrides: ContractOverrides = {}
52
+ ): ethers.Contract {
53
+ const filters = {
54
+ MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
55
+ MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
56
+ MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
57
+ };
58
+
59
+ const baseContract = {
60
+ minMembershipRateLimit: () =>
61
+ Promise.resolve(ethers.BigNumber.from(mockRateLimits.minRate)),
62
+ maxMembershipRateLimit: () =>
63
+ Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxRate)),
64
+ maxTotalRateLimit: () =>
65
+ Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxTotalRate)),
66
+ currentTotalRateLimit: () =>
67
+ Promise.resolve(ethers.BigNumber.from(mockRateLimits.currentTotalRate)),
68
+ queryFilter: () => [],
69
+ provider: createMockProvider(),
70
+ filters,
71
+ on: () => ({}),
72
+ removeAllListeners: () => ({}),
73
+ register: () => ({
74
+ wait: () =>
75
+ Promise.resolve({
76
+ events: [mockRLNRegisteredEvent()]
77
+ })
78
+ }),
79
+ estimateGas: {
80
+ register: () => Promise.resolve(ethers.BigNumber.from(100000))
81
+ },
82
+ functions: {
83
+ register: () => Promise.resolve()
84
+ },
85
+ getMemberIndex: () => Promise.resolve(null),
86
+ interface: {
87
+ getEvent: (eventName: string) => ({
88
+ name: eventName,
89
+ format: () => {}
90
+ })
91
+ },
92
+ address: LINEA_CONTRACT.address
93
+ };
94
+
95
+ // Merge overrides while preserving filters
96
+ const merged = {
97
+ ...baseContract,
98
+ ...overrides,
99
+ filters: { ...filters, ...(overrides.filters || {}) }
100
+ };
101
+
102
+ return merged as unknown as ethers.Contract;
103
+ }
104
+
105
+ export function mockRLNRegisteredEvent(idCommitment?: string): ethers.Event {
106
+ return {
107
+ args: {
108
+ idCommitment:
109
+ idCommitment ||
110
+ "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
111
+ membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
112
+ index: ethers.BigNumber.from(1)
113
+ },
114
+ event: "MembershipRegistered"
115
+ } as unknown as ethers.Event;
116
+ }
117
+
118
+ export function formatIdCommitment(idCommitmentBigInt: bigint): string {
119
+ return "0x" + idCommitmentBigInt.toString(16).padStart(64, "0");
120
+ }
121
+
122
+ export function createRegisterStub(
123
+ identity: IdentityCredential
124
+ ): sinon.SinonStub {
125
+ return sinon.stub().callsFake(() => ({
126
+ wait: () =>
127
+ Promise.resolve({
128
+ events: [
129
+ {
130
+ event: "MembershipRegistered",
131
+ args: {
132
+ idCommitment: formatIdCommitment(identity.IDCommitmentBigInt),
133
+ membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
134
+ index: ethers.BigNumber.from(1)
135
+ }
136
+ }
137
+ ]
138
+ })
139
+ }));
140
+ }
141
+
142
+ export function verifyRegistration(
143
+ decryptedCredentials: any,
144
+ identity: IdentityCredential,
145
+ registerStub: sinon.SinonStub,
146
+ insertMemberSpy: sinon.SinonStub
147
+ ): void {
148
+ if (!decryptedCredentials) {
149
+ throw new Error("Decrypted credentials should not be undefined");
150
+ }
151
+
152
+ // Verify registration call
153
+ expect(
154
+ registerStub.calledWith(
155
+ sinon.match.same(identity.IDCommitmentBigInt),
156
+ sinon.match.same(DEFAULT_RATE_LIMIT),
157
+ sinon.match.array,
158
+ sinon.match.object
159
+ )
160
+ ).to.be.true;
161
+
162
+ // Verify credential properties
163
+ expect(decryptedCredentials).to.have.property("identity");
164
+ expect(decryptedCredentials).to.have.property("membership");
165
+ expect(decryptedCredentials.membership).to.include({
166
+ address: LINEA_CONTRACT.address,
167
+ treeIndex: 1
168
+ });
169
+
170
+ // Verify member insertion
171
+ const expectedIdCommitment = ethers.utils.zeroPad(
172
+ hexToBytes(formatIdCommitment(identity.IDCommitmentBigInt)),
173
+ 32
174
+ );
175
+ expect(insertMemberSpy.callCount).to.equal(1);
176
+ expect(insertMemberSpy.getCall(0).args[0]).to.deep.equal(
177
+ expectedIdCommitment
178
+ );
179
+ }
@@ -13,7 +13,10 @@ import type {
13
13
  } from "./keystore/index.js";
14
14
  import { KeystoreEntity, Password } from "./keystore/types.js";
15
15
  import { RegisterMembershipOptions, StartRLNOptions } from "./types.js";
16
- import { extractMetaMaskSigner, switchEndianness } from "./utils/index.js";
16
+ import {
17
+ buildBigIntFromUint8ArrayLE,
18
+ extractMetaMaskSigner
19
+ } from "./utils/index.js";
17
20
  import { Zerokit } from "./zerokit.js";
18
21
 
19
22
  const log = new Logger("waku:credentials");
@@ -258,31 +261,35 @@ export class RLNCredentialsManager {
258
261
 
259
262
  // Generate deterministic values using HMAC-SHA256
260
263
  // We use different context strings for each component to ensure they're different
261
- const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
262
- const idNullifierBE = hmac(
263
- sha256,
264
- seedBytes,
265
- encoder.encode("IDNullifier")
266
- );
264
+ const idTrapdoor = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
265
+ const idNullifier = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
266
+
267
+ const combinedBytes = new Uint8Array([...idTrapdoor, ...idNullifier]);
268
+ const idSecretHash = sha256(combinedBytes);
267
269
 
268
- const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
269
- const idSecretHashBE = sha256(combinedBytes);
270
+ const idCommitment = sha256(idSecretHash);
270
271
 
271
- const idCommitmentBE = sha256(idSecretHashBE);
272
+ let idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
273
+ if (!this.contract) {
274
+ throw Error("RLN contract is not initialized");
275
+ }
272
276
 
273
- // All hashing functions return big-endian bytes
274
- // We need to switch to little-endian for the identity credential
275
- const idTrapdoorLE = switchEndianness(idTrapdoorBE);
276
- const idNullifierLE = switchEndianness(idNullifierBE);
277
- const idSecretHashLE = switchEndianness(idSecretHashBE);
278
- const idCommitmentLE = switchEndianness(idCommitmentBE);
277
+ const idCommitmentBigIntLimit = this.contract.idCommitmentBigIntLimit;
278
+
279
+ if (idCommitmentBigInt >= idCommitmentBigIntLimit) {
280
+ log.warn(
281
+ `ID commitment is greater than Q, reducing it by Q(idCommitmentBigIntLimit): ${idCommitmentBigInt} % ${idCommitmentBigIntLimit}`
282
+ );
283
+ idCommitmentBigInt = idCommitmentBigInt % idCommitmentBigIntLimit;
284
+ }
279
285
 
280
286
  log.info("Successfully generated identity credential");
281
287
  return new IdentityCredential(
282
- idTrapdoorLE,
283
- idNullifierLE,
284
- idSecretHashLE,
285
- idCommitmentLE
288
+ idTrapdoor,
289
+ idNullifier,
290
+ idSecretHash,
291
+ idCommitment,
292
+ idCommitmentBigInt
286
293
  );
287
294
  }
288
295
  }
package/src/identity.ts CHANGED
@@ -1,12 +1,12 @@
1
+ import { buildBigIntFromUint8ArrayLE } from "./utils/index.js";
2
+
1
3
  export class IdentityCredential {
2
- /**
3
- * All variables are in little-endian format
4
- */
5
4
  public constructor(
6
5
  public readonly IDTrapdoor: Uint8Array,
7
6
  public readonly IDNullifier: Uint8Array,
8
7
  public readonly IDSecretHash: Uint8Array,
9
- public readonly IDCommitment: Uint8Array
8
+ public readonly IDCommitment: Uint8Array,
9
+ public readonly IDCommitmentBigInt: bigint
10
10
  ) {}
11
11
 
12
12
  public static fromBytes(memKeys: Uint8Array): IdentityCredential {
@@ -18,12 +18,14 @@ export class IdentityCredential {
18
18
  const idNullifier = memKeys.subarray(32, 64);
19
19
  const idSecretHash = memKeys.subarray(64, 96);
20
20
  const idCommitment = memKeys.subarray(96, 128);
21
+ const idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
21
22
 
22
23
  return new IdentityCredential(
23
24
  idTrapdoor,
24
25
  idNullifier,
25
26
  idSecretHash,
26
- idCommitment
27
+ idCommitment,
28
+ idCommitmentBigInt
27
29
  );
28
30
  }
29
31
  }
@@ -14,6 +14,8 @@ import {
14
14
  import _ from "lodash";
15
15
  import { v4 as uuidV4 } from "uuid";
16
16
 
17
+ import { buildBigIntFromUint8ArrayLE } from "../utils/bytes.js";
18
+
17
19
  import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
18
20
  import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
19
21
  import type {
@@ -244,29 +246,43 @@ export class Keystore {
244
246
  private static fromBytesToIdentity(
245
247
  bytes: Uint8Array
246
248
  ): undefined | KeystoreEntity {
249
+ function fromLittleEndian(bytes: Uint8Array): Uint8Array {
250
+ return new Uint8Array(bytes).reverse();
251
+ }
247
252
  try {
248
253
  const str = bytesToUtf8(bytes);
249
254
  const obj = JSON.parse(str);
250
255
 
251
- const idCommitmentLE = Keystore.fromArraylikeToBytes(
252
- _.get(obj, "identityCredential.idCommitment", [])
253
- );
254
- const idTrapdoorLE = Keystore.fromArraylikeToBytes(
255
- _.get(obj, "identityCredential.idTrapdoor", [])
256
- );
257
- const idNullifierLE = Keystore.fromArraylikeToBytes(
258
- _.get(obj, "identityCredential.idNullifier", [])
259
- );
260
- const idSecretHashLE = Keystore.fromArraylikeToBytes(
261
- _.get(obj, "identityCredential.idSecretHash", [])
262
- );
263
-
256
+ // TODO: add runtime validation of nwaku credentials
264
257
  return {
265
258
  identity: {
266
- IDCommitment: idCommitmentLE,
267
- IDTrapdoor: idTrapdoorLE,
268
- IDNullifier: idNullifierLE,
269
- IDSecretHash: idSecretHashLE
259
+ IDCommitment: fromLittleEndian(
260
+ Keystore.fromArraylikeToBytes(
261
+ _.get(obj, "identityCredential.idCommitment", [])
262
+ )
263
+ ),
264
+ IDTrapdoor: fromLittleEndian(
265
+ Keystore.fromArraylikeToBytes(
266
+ _.get(obj, "identityCredential.idTrapdoor", [])
267
+ )
268
+ ),
269
+ IDNullifier: fromLittleEndian(
270
+ Keystore.fromArraylikeToBytes(
271
+ _.get(obj, "identityCredential.idNullifier", [])
272
+ )
273
+ ),
274
+ IDCommitmentBigInt: buildBigIntFromUint8ArrayLE(
275
+ fromLittleEndian(
276
+ Keystore.fromArraylikeToBytes(
277
+ _.get(obj, "identityCredential.idCommitment", [])
278
+ )
279
+ )
280
+ ),
281
+ IDSecretHash: fromLittleEndian(
282
+ Keystore.fromArraylikeToBytes(
283
+ _.get(obj, "identityCredential.idSecretHash", [])
284
+ )
285
+ )
270
286
  },
271
287
  membership: {
272
288
  treeIndex: _.get(obj, "treeIndex"),
@@ -17,13 +17,18 @@ export function concatenate(...input: Uint8Array[]): Uint8Array {
17
17
  return result;
18
18
  }
19
19
 
20
- export function switchEndianness(bytes: Uint8Array): Uint8Array {
21
- return new Uint8Array(bytes.reverse());
22
- }
23
-
24
- export function buildBigIntFromUint8ArrayBE(bytes: Uint8Array): bigint {
25
- // Interpret bytes as big-endian
26
- return bytes.reduce((acc, byte) => (acc << 8n) + BigInt(byte), 0n);
20
+ // Adapted from https://github.com/feross/buffer
21
+ function checkInt(
22
+ buf: Uint8Array,
23
+ value: number,
24
+ offset: number,
25
+ ext: number,
26
+ max: number,
27
+ min: number
28
+ ): void {
29
+ if (value > max || value < min)
30
+ throw new RangeError('"value" argument is out of bounds');
31
+ if (offset + ext > buf.length) throw new RangeError("Index out of range");
27
32
  }
28
33
 
29
34
  export function writeUIntLE(
@@ -51,6 +56,13 @@ export function writeUIntLE(
51
56
  return buf;
52
57
  }
53
58
 
59
+ export function buildBigIntFromUint8ArrayLE(bytes: Uint8Array): bigint {
60
+ return bytes.reduce(
61
+ (acc, byte, i) => acc + BigInt(byte) * (1n << (8n * BigInt(i))),
62
+ 0n
63
+ );
64
+ }
65
+
54
66
  /**
55
67
  * Fills with zeros to set length
56
68
  * @param array little endian Uint8Array
@@ -64,17 +76,3 @@ export function zeroPadLE(array: Uint8Array, length: number): Uint8Array {
64
76
  }
65
77
  return result;
66
78
  }
67
-
68
- // Adapted from https://github.com/feross/buffer
69
- function checkInt(
70
- buf: Uint8Array,
71
- value: number,
72
- offset: number,
73
- ext: number,
74
- max: number,
75
- min: number
76
- ): void {
77
- if (value > max || value < min)
78
- throw new RangeError('"value" argument is out of bounds');
79
- if (offset + ext > buf.length) throw new RangeError("Index out of range");
80
- }
@@ -2,7 +2,7 @@ export { extractMetaMaskSigner } from "./metamask.js";
2
2
  export {
3
3
  concatenate,
4
4
  writeUIntLE,
5
- switchEndianness,
5
+ buildBigIntFromUint8ArrayLE,
6
6
  zeroPadLE
7
7
  } from "./bytes.js";
8
8
  export { sha256, poseidonHash } from "./hash.js";