@waku/rln 0.1.6-27c1236.0 → 0.1.6-2ce706d.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.
@@ -13,10 +13,7 @@ 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 {
17
- buildBigIntFromUint8ArrayLE,
18
- extractMetaMaskSigner
19
- } from "./utils/index.js";
16
+ import { extractMetaMaskSigner, switchEndianness } from "./utils/index.js";
20
17
  import { Zerokit } from "./zerokit.js";
21
18
 
22
19
  const log = new Logger("waku:credentials");
@@ -261,35 +258,31 @@ export class RLNCredentialsManager {
261
258
 
262
259
  // Generate deterministic values using HMAC-SHA256
263
260
  // We use different context strings for each component to ensure they're different
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);
261
+ const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
262
+ const idNullifierBE = hmac(
263
+ sha256,
264
+ seedBytes,
265
+ encoder.encode("IDNullifier")
266
+ );
269
267
 
270
- const idCommitment = sha256(idSecretHash);
268
+ const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
269
+ const idSecretHashBE = sha256(combinedBytes);
271
270
 
272
- let idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
273
- if (!this.contract) {
274
- throw Error("RLN contract is not initialized");
275
- }
271
+ const idCommitmentBE = sha256(idSecretHashBE);
276
272
 
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
- }
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);
285
279
 
286
280
  log.info("Successfully generated identity credential");
287
281
  return new IdentityCredential(
288
- idTrapdoor,
289
- idNullifier,
290
- idSecretHash,
291
- idCommitment,
292
- idCommitmentBigInt
282
+ idTrapdoorLE,
283
+ idNullifierLE,
284
+ idSecretHashLE,
285
+ idCommitmentLE
293
286
  );
294
287
  }
295
288
  }
package/src/identity.ts CHANGED
@@ -1,12 +1,19 @@
1
- import { buildBigIntFromUint8ArrayLE } from "./utils/index.js";
1
+ import { Logger } from "@waku/utils";
2
+
3
+ import { DEFAULT_Q } from "./contract/constants.js";
4
+ import { buildBigIntFromUint8ArrayBE } from "./utils/bytes.js";
5
+
6
+ const log = new Logger("waku:rln:identity");
2
7
 
3
8
  export class IdentityCredential {
9
+ /**
10
+ * All variables are in little-endian format
11
+ */
4
12
  public constructor(
5
13
  public readonly IDTrapdoor: Uint8Array,
6
14
  public readonly IDNullifier: Uint8Array,
7
15
  public readonly IDSecretHash: Uint8Array,
8
- public readonly IDCommitment: Uint8Array,
9
- public readonly IDCommitmentBigInt: bigint
16
+ public readonly IDCommitment: Uint8Array
10
17
  ) {}
11
18
 
12
19
  public static fromBytes(memKeys: Uint8Array): IdentityCredential {
@@ -18,14 +25,33 @@ export class IdentityCredential {
18
25
  const idNullifier = memKeys.subarray(32, 64);
19
26
  const idSecretHash = memKeys.subarray(64, 96);
20
27
  const idCommitment = memKeys.subarray(96, 128);
21
- const idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
22
28
 
23
29
  return new IdentityCredential(
24
30
  idTrapdoor,
25
31
  idNullifier,
26
32
  idSecretHash,
27
- idCommitment,
28
- idCommitmentBigInt
33
+ idCommitment
29
34
  );
30
35
  }
36
+
37
+ /**
38
+ * Converts an ID commitment from bytes to a BigInt, normalizing it against a limit if needed
39
+ * @param bytes The ID commitment bytes to convert
40
+ * @param limit Optional limit to normalize against (Q value)
41
+ * @returns The ID commitment as a BigInt
42
+ */
43
+ public static getIdCommitmentBigInt(
44
+ bytes: Uint8Array,
45
+ limit: bigint = DEFAULT_Q
46
+ ): bigint {
47
+ let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(bytes);
48
+
49
+ if (limit && idCommitmentBigIntBE >= limit) {
50
+ log.warn(
51
+ `ID commitment is greater than Q, reducing it by Q: ${idCommitmentBigIntBE} % ${limit}`
52
+ );
53
+ idCommitmentBigIntBE = idCommitmentBigIntBE % limit;
54
+ }
55
+ return idCommitmentBigIntBE;
56
+ }
31
57
  }
@@ -14,8 +14,6 @@ import {
14
14
  import _ from "lodash";
15
15
  import { v4 as uuidV4 } from "uuid";
16
16
 
17
- import { buildBigIntFromUint8ArrayLE } from "../utils/bytes.js";
18
-
19
17
  import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
20
18
  import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
21
19
  import type {
@@ -246,38 +244,29 @@ export class Keystore {
246
244
  private static fromBytesToIdentity(
247
245
  bytes: Uint8Array
248
246
  ): undefined | KeystoreEntity {
249
- function fromLittleEndian(bytes: Uint8Array): Uint8Array {
250
- return new Uint8Array(bytes).reverse();
251
- }
252
247
  try {
253
248
  const str = bytesToUtf8(bytes);
254
249
  const obj = JSON.parse(str);
255
250
 
256
- // Use little-endian bytes directly for BigInt conversion (matches storage and contract expectation)
257
251
  const idCommitmentLE = Keystore.fromArraylikeToBytes(
258
252
  _.get(obj, "identityCredential.idCommitment", [])
259
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
+ );
260
263
 
261
264
  return {
262
265
  identity: {
263
- IDCommitment: fromLittleEndian(idCommitmentLE),
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
- // Do NOT reverse for BigInt conversion; use little-endian as stored
275
- IDCommitmentBigInt: buildBigIntFromUint8ArrayLE(idCommitmentLE),
276
- IDSecretHash: fromLittleEndian(
277
- Keystore.fromArraylikeToBytes(
278
- _.get(obj, "identityCredential.idSecretHash", [])
279
- )
280
- )
266
+ IDCommitment: idCommitmentLE,
267
+ IDTrapdoor: idTrapdoorLE,
268
+ IDNullifier: idNullifierLE,
269
+ IDSecretHash: idSecretHashLE
281
270
  },
282
271
  membership: {
283
272
  treeIndex: _.get(obj, "treeIndex"),
@@ -17,18 +17,13 @@ export function concatenate(...input: Uint8Array[]): Uint8Array {
17
17
  return result;
18
18
  }
19
19
 
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");
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);
32
27
  }
33
28
 
34
29
  export function writeUIntLE(
@@ -56,13 +51,6 @@ export function writeUIntLE(
56
51
  return buf;
57
52
  }
58
53
 
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
-
66
54
  /**
67
55
  * Fills with zeros to set length
68
56
  * @param array little endian Uint8Array
@@ -76,3 +64,17 @@ export function zeroPadLE(array: Uint8Array, length: number): Uint8Array {
76
64
  }
77
65
  return result;
78
66
  }
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
- buildBigIntFromUint8ArrayLE,
5
+ switchEndianness,
6
6
  zeroPadLE
7
7
  } from "./bytes.js";
8
8
  export { sha256, poseidonHash } from "./hash.js";
@@ -1,39 +0,0 @@
1
- import * as ethers from "ethers";
2
- import sinon from "sinon";
3
- import type { IdentityCredential } from "../identity.js";
4
- export declare const mockRateLimits: {
5
- minRate: number;
6
- maxRate: number;
7
- maxTotalRate: number;
8
- currentTotalRate: number;
9
- };
10
- type MockProvider = {
11
- getLogs: () => never[];
12
- getBlockNumber: () => Promise<number>;
13
- getNetwork: () => Promise<{
14
- chainId: number;
15
- }>;
16
- };
17
- type MockFilters = {
18
- MembershipRegistered: () => {
19
- address: string;
20
- };
21
- MembershipErased: () => {
22
- address: string;
23
- };
24
- MembershipExpired: () => {
25
- address: string;
26
- };
27
- };
28
- export declare function createMockProvider(): MockProvider;
29
- export declare function createMockFilters(): MockFilters;
30
- type ContractOverrides = Partial<{
31
- filters: Record<string, unknown>;
32
- [key: string]: unknown;
33
- }>;
34
- export declare function createMockRegistryContract(overrides?: ContractOverrides): ethers.Contract;
35
- export declare function mockRLNRegisteredEvent(idCommitment?: string): ethers.Event;
36
- export declare function formatIdCommitment(idCommitmentBigInt: bigint): string;
37
- export declare function createRegisterStub(identity: IdentityCredential): sinon.SinonStub;
38
- export declare function verifyRegistration(decryptedCredentials: any, identity: IdentityCredential, registerStub: sinon.SinonStub, insertMemberSpy: sinon.SinonStub): void;
39
- export {};
@@ -1,118 +0,0 @@
1
- import { hexToBytes } from "@waku/utils/bytes";
2
- import { expect } from "chai";
3
- import * as ethers from "ethers";
4
- import sinon from "sinon";
5
- import { DEFAULT_RATE_LIMIT, LINEA_CONTRACT } from "./constants.js";
6
- export const mockRateLimits = {
7
- minRate: 20,
8
- maxRate: 600,
9
- maxTotalRate: 1200,
10
- currentTotalRate: 500
11
- };
12
- export function createMockProvider() {
13
- return {
14
- getLogs: () => [],
15
- getBlockNumber: () => Promise.resolve(1000),
16
- getNetwork: () => Promise.resolve({ chainId: 11155111 })
17
- };
18
- }
19
- export function createMockFilters() {
20
- return {
21
- MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
22
- MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
23
- MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
24
- };
25
- }
26
- export function createMockRegistryContract(overrides = {}) {
27
- const filters = {
28
- MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
29
- MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
30
- MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
31
- };
32
- const baseContract = {
33
- minMembershipRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.minRate)),
34
- maxMembershipRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxRate)),
35
- maxTotalRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxTotalRate)),
36
- currentTotalRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.currentTotalRate)),
37
- queryFilter: () => [],
38
- provider: createMockProvider(),
39
- filters,
40
- on: () => ({}),
41
- removeAllListeners: () => ({}),
42
- register: () => ({
43
- wait: () => Promise.resolve({
44
- events: [mockRLNRegisteredEvent()]
45
- })
46
- }),
47
- estimateGas: {
48
- register: () => Promise.resolve(ethers.BigNumber.from(100000))
49
- },
50
- functions: {
51
- register: () => Promise.resolve()
52
- },
53
- getMemberIndex: () => Promise.resolve(null),
54
- interface: {
55
- getEvent: (eventName) => ({
56
- name: eventName,
57
- format: () => { }
58
- })
59
- },
60
- address: LINEA_CONTRACT.address
61
- };
62
- // Merge overrides while preserving filters
63
- const merged = {
64
- ...baseContract,
65
- ...overrides,
66
- filters: { ...filters, ...(overrides.filters || {}) }
67
- };
68
- return merged;
69
- }
70
- export function mockRLNRegisteredEvent(idCommitment) {
71
- return {
72
- args: {
73
- idCommitment: idCommitment ||
74
- "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
75
- membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
76
- index: ethers.BigNumber.from(1)
77
- },
78
- event: "MembershipRegistered"
79
- };
80
- }
81
- export function formatIdCommitment(idCommitmentBigInt) {
82
- return "0x" + idCommitmentBigInt.toString(16).padStart(64, "0");
83
- }
84
- export function createRegisterStub(identity) {
85
- return sinon.stub().callsFake(() => ({
86
- wait: () => Promise.resolve({
87
- events: [
88
- {
89
- event: "MembershipRegistered",
90
- args: {
91
- idCommitment: formatIdCommitment(identity.IDCommitmentBigInt),
92
- membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
93
- index: ethers.BigNumber.from(1)
94
- }
95
- }
96
- ]
97
- })
98
- }));
99
- }
100
- export function verifyRegistration(decryptedCredentials, identity, registerStub, insertMemberSpy) {
101
- if (!decryptedCredentials) {
102
- throw new Error("Decrypted credentials should not be undefined");
103
- }
104
- // Verify registration call
105
- expect(registerStub.calledWith(sinon.match.same(identity.IDCommitmentBigInt), sinon.match.same(DEFAULT_RATE_LIMIT), sinon.match.array, sinon.match.object)).to.be.true;
106
- // Verify credential properties
107
- expect(decryptedCredentials).to.have.property("identity");
108
- expect(decryptedCredentials).to.have.property("membership");
109
- expect(decryptedCredentials.membership).to.include({
110
- address: LINEA_CONTRACT.address,
111
- treeIndex: 1
112
- });
113
- // Verify member insertion
114
- const expectedIdCommitment = ethers.utils.zeroPad(hexToBytes(formatIdCommitment(identity.IDCommitmentBigInt)), 32);
115
- expect(insertMemberSpy.callCount).to.equal(1);
116
- expect(insertMemberSpy.getCall(0).args[0]).to.deep.equal(expectedIdCommitment);
117
- }
118
- //# sourceMappingURL=test-utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/contract/test-utils.ts"],"names":[],"mappings":"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"}
@@ -1,179 +0,0 @@
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
- }