@waku/rln 0.0.2-8a6571f.0 → 0.0.2-9094860.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.
package/bundle/index.js CHANGED
@@ -4,9 +4,11 @@ export { RLNContract } from './packages/rln/dist/contract/rln_contract.js';
4
4
  export { SEPOLIA_CONTRACT } from './packages/rln/dist/contract/constants.js';
5
5
  export { createRLN } from './packages/rln/dist/create.js';
6
6
  export { IdentityCredential } from './packages/rln/dist/identity.js';
7
+ export { IdentityGenerator } from './packages/rln/dist/identity_generator.js';
7
8
  export { Keystore } from './packages/rln/dist/keystore/keystore.js';
8
9
  export { Proof } from './packages/rln/dist/proof.js';
9
10
  export { RLNInstance } from './packages/rln/dist/rln.js';
11
+ export { RLNLight } from './packages/rln/dist/rln_light.js';
10
12
  export { MerkleRootTracker } from './packages/rln/dist/root_tracker.js';
11
13
  export { extractMetaMaskSigner } from './packages/rln/dist/utils/metamask.js';
12
14
  import './packages/rln/dist/utils/epoch.js';
@@ -25,7 +25,8 @@ class RLNContract {
25
25
  rateLimit;
26
26
  _members = new Map();
27
27
  _membersFilter;
28
- _membersRemovedFilter;
28
+ _membershipErasedFilter;
29
+ _membersExpiredFilter;
29
30
  /**
30
31
  * Asynchronous initializer for RLNContract.
31
32
  * Allows injecting a mocked contract for testing purposes.
@@ -47,9 +48,10 @@ class RLNContract {
47
48
  // Use the injected contract if provided; otherwise, instantiate a new one.
48
49
  this.contract = contract || new Contract(address, RLN_ABI, signer);
49
50
  this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
50
- // Initialize event filters for MembershipRegistered and MembershipErased
51
+ // Initialize event filters
51
52
  this._membersFilter = this.contract.filters.MembershipRegistered();
52
- this._membersRemovedFilter = this.contract.filters.MembershipErased();
53
+ this._membershipErasedFilter = this.contract.filters.MembershipErased();
54
+ this._membersExpiredFilter = this.contract.filters.MembershipExpired();
53
55
  }
54
56
  /**
55
57
  * Gets the current rate limit for this contract instance
@@ -110,7 +112,7 @@ class RLNContract {
110
112
  this.contract.maxTotalRateLimit(),
111
113
  this.contract.currentTotalRateLimit()
112
114
  ]);
113
- return maxTotal.sub(currentTotal).toNumber();
115
+ return Number(maxTotal) - Number(currentTotal);
114
116
  }
115
117
  /**
116
118
  * Updates the rate limit for future registrations
@@ -129,11 +131,17 @@ class RLNContract {
129
131
  }
130
132
  return this._membersFilter;
131
133
  }
132
- get membersRemovedFilter() {
133
- if (!this._membersRemovedFilter) {
134
- throw Error("MembersErased filter was not initialized.");
134
+ get membershipErasedFilter() {
135
+ if (!this._membershipErasedFilter) {
136
+ throw Error("MembershipErased filter was not initialized.");
135
137
  }
136
- return this._membersRemovedFilter;
138
+ return this._membershipErasedFilter;
139
+ }
140
+ get membersExpiredFilter() {
141
+ if (!this._membersExpiredFilter) {
142
+ throw Error("MembersExpired filter was not initialized.");
143
+ }
144
+ return this._membersExpiredFilter;
137
145
  }
138
146
  async fetchMembers(rlnInstance, options = {}) {
139
147
  const registeredMemberEvents = await queryFilter(this.contract, {
@@ -144,9 +152,18 @@ class RLNContract {
144
152
  const removedMemberEvents = await queryFilter(this.contract, {
145
153
  fromBlock: this.deployBlock,
146
154
  ...options,
147
- membersFilter: this.membersRemovedFilter
155
+ membersFilter: this.membershipErasedFilter
148
156
  });
149
- const events = [...registeredMemberEvents, ...removedMemberEvents];
157
+ const expiredMemberEvents = await queryFilter(this.contract, {
158
+ fromBlock: this.deployBlock,
159
+ ...options,
160
+ membersFilter: this.membersExpiredFilter
161
+ });
162
+ const events = [
163
+ ...registeredMemberEvents,
164
+ ...removedMemberEvents,
165
+ ...expiredMemberEvents
166
+ ];
150
167
  this.processEvents(rlnInstance, events);
151
168
  }
152
169
  processEvents(rlnInstance, events) {
@@ -156,8 +173,15 @@ class RLNContract {
156
173
  if (!evt.args) {
157
174
  return;
158
175
  }
159
- if (evt.event === "MembershipErased") {
160
- const index = evt.args.index;
176
+ if (evt.event === "MembershipErased" ||
177
+ evt.event === "MembershipExpired") {
178
+ let index = evt.args.index;
179
+ if (!index) {
180
+ return;
181
+ }
182
+ if (typeof index === "number" || typeof index === "string") {
183
+ index = BigNumber.from(index);
184
+ }
161
185
  const toRemoveVal = toRemoveTable.get(evt.blockNumber);
162
186
  if (toRemoveVal != undefined) {
163
187
  toRemoveVal.push(index.toNumber());
@@ -185,13 +209,17 @@ class RLNContract {
185
209
  if (!evt.args)
186
210
  return;
187
211
  const _idCommitment = evt.args.idCommitment;
188
- const index = evt.args.index;
212
+ let index = evt.args.index;
189
213
  if (!_idCommitment || !index) {
190
214
  return;
191
215
  }
216
+ if (typeof index === "number" || typeof index === "string") {
217
+ index = BigNumber.from(index);
218
+ }
192
219
  const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
193
220
  rlnInstance.zerokit.insertMember(idCommitment);
194
- this._members.set(index.toNumber(), {
221
+ const numericIndex = index.toNumber();
222
+ this._members.set(numericIndex, {
195
223
  index,
196
224
  idCommitment: _idCommitment
197
225
  });
@@ -213,18 +241,36 @@ class RLNContract {
213
241
  });
214
242
  }
215
243
  subscribeToMembers(rlnInstance) {
216
- this.contract.on(this.membersFilter, (_idCommitment, _rateLimit, _index, event) => {
244
+ this.contract.on(this.membersFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
245
+ this.processEvents(rlnInstance, [event]);
246
+ });
247
+ this.contract.on(this.membershipErasedFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
217
248
  this.processEvents(rlnInstance, [event]);
218
249
  });
219
- this.contract.on(this.membersRemovedFilter, (_idCommitment, _rateLimit, _index, event) => {
250
+ this.contract.on(this.membersExpiredFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
220
251
  this.processEvents(rlnInstance, [event]);
221
252
  });
222
253
  }
223
254
  async registerWithIdentity(identity) {
224
255
  try {
225
256
  log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
226
- const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit: 300000 });
257
+ // Check if the ID commitment is already registered
258
+ const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt.toString());
259
+ if (existingIndex) {
260
+ throw new Error(`ID commitment is already registered with index ${existingIndex}`);
261
+ }
262
+ // Check if there's enough remaining rate limit
263
+ const remainingRateLimit = await this.getRemainingTotalRateLimit();
264
+ if (remainingRateLimit < this.rateLimit) {
265
+ throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
266
+ }
267
+ const estimatedGas = await this.contract.estimateGas.register(identity.IDCommitmentBigInt, this.rateLimit, []);
268
+ const gasLimit = estimatedGas.add(10000);
269
+ const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit });
227
270
  const txRegisterReceipt = await txRegisterResponse.wait();
271
+ if (txRegisterReceipt.status === 0) {
272
+ throw new Error("Transaction failed on-chain");
273
+ }
228
274
  const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
229
275
  if (!memberRegistered || !memberRegistered.args) {
230
276
  log.error("Failed to register membership: No MembershipRegistered event found");
@@ -232,14 +278,14 @@ class RLNContract {
232
278
  }
233
279
  const decodedData = {
234
280
  idCommitment: memberRegistered.args.idCommitment,
235
- rateLimit: memberRegistered.args.rateLimit,
281
+ membershipRateLimit: memberRegistered.args.membershipRateLimit,
236
282
  index: memberRegistered.args.index
237
283
  };
238
284
  log.info(`Successfully registered membership with index ${decodedData.index} ` +
239
- `and rate limit ${decodedData.rateLimit}`);
285
+ `and rate limit ${decodedData.membershipRateLimit}`);
240
286
  const network = await this.contract.provider.getNetwork();
241
287
  const address = this.contract.address;
242
- const membershipId = decodedData.index.toNumber();
288
+ const membershipId = Number(decodedData.index);
243
289
  return {
244
290
  identity,
245
291
  membership: {
@@ -250,8 +296,32 @@ class RLNContract {
250
296
  };
251
297
  }
252
298
  catch (error) {
253
- log.error(`Error in registerWithIdentity: ${error.message}`);
254
- return undefined;
299
+ if (error instanceof Error) {
300
+ const errorMessage = error.message;
301
+ log.error("registerWithIdentity - error message:", errorMessage);
302
+ log.error("registerWithIdentity - error stack:", error.stack);
303
+ // Try to extract more specific error information
304
+ if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
305
+ throw new Error("Registration failed: Cannot exceed maximum total rate limit");
306
+ }
307
+ else if (errorMessage.includes("InvalidIdCommitment")) {
308
+ throw new Error("Registration failed: Invalid ID commitment");
309
+ }
310
+ else if (errorMessage.includes("InvalidMembershipRateLimit")) {
311
+ throw new Error("Registration failed: Invalid membership rate limit");
312
+ }
313
+ else if (errorMessage.includes("execution reverted")) {
314
+ throw new Error("Contract execution reverted. Check contract requirements.");
315
+ }
316
+ else {
317
+ throw new Error(`Error in registerWithIdentity: ${errorMessage}`);
318
+ }
319
+ }
320
+ else {
321
+ throw new Error("Unknown error in registerWithIdentity", {
322
+ cause: error
323
+ });
324
+ }
255
325
  }
256
326
  }
257
327
  /**
@@ -287,14 +357,14 @@ class RLNContract {
287
357
  }
288
358
  const decodedData = {
289
359
  idCommitment: memberRegistered.args.idCommitment,
290
- rateLimit: memberRegistered.args.rateLimit,
360
+ membershipRateLimit: memberRegistered.args.membershipRateLimit,
291
361
  index: memberRegistered.args.index
292
362
  };
293
363
  log.info(`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
294
- `Rate limit: ${decodedData.rateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
364
+ `Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
295
365
  const network = await this.contract.provider.getNetwork();
296
366
  const address = this.contract.address;
297
- const membershipId = decodedData.index.toNumber();
367
+ const membershipId = Number(decodedData.index);
298
368
  return {
299
369
  identity,
300
370
  membership: {
@@ -0,0 +1,75 @@
1
+ import { keccak256 } from '../../../node_modules/@ethersproject/keccak256/lib.esm/index.js';
2
+ import { toUtf8Bytes } from '../../../node_modules/@ethersproject/strings/lib.esm/utf8.js';
3
+ import { IdentityCredential } from './identity.js';
4
+ import { buildBigIntFromUint8Array } from './utils/bytes.js';
5
+ import './utils/epoch.js';
6
+
7
+ /**
8
+ * A standalone implementation of identity generation for RLN memberships
9
+ * without Zerokit dependency. Only supports identity creation and membership
10
+ * registration.
11
+ */
12
+ class IdentityGenerator {
13
+ /**
14
+ * Generate a new random identity credential
15
+ * @returns An IdentityCredential object
16
+ */
17
+ generateIdentityCredentials() {
18
+ // Generate random values for IDTrapdoor and IDNullifier
19
+ const idTrapdoor = crypto.getRandomValues(new Uint8Array(32));
20
+ const idNullifier = crypto.getRandomValues(new Uint8Array(32));
21
+ // Generate IDSecretHash by concatenating and hashing
22
+ const combined = new Uint8Array(64);
23
+ combined.set(idTrapdoor, 0);
24
+ combined.set(idNullifier, 32);
25
+ const idSecretHash = new Uint8Array(keccak256(combined)
26
+ .substring(2)
27
+ .match(/.{1,2}/g)
28
+ .map((byte) => parseInt(byte, 16)));
29
+ // Generate IDCommitment by hashing IDSecretHash
30
+ const idCommitment = new Uint8Array(keccak256(idSecretHash)
31
+ .substring(2)
32
+ .match(/.{1,2}/g)
33
+ .map((byte) => parseInt(byte, 16)));
34
+ // Generate BigInt from IDCommitment
35
+ const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
36
+ return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
37
+ }
38
+ /**
39
+ * Generate an identity credential from a seed
40
+ * @param seed A string seed to generate a deterministic identity
41
+ * @returns An IdentityCredential object
42
+ */
43
+ generateSeededIdentityCredential(seed) {
44
+ // Convert seed to bytes
45
+ const seedBytes = toUtf8Bytes(seed);
46
+ // Use the seed to derive IDTrapdoor and IDNullifier deterministically
47
+ const seedHash = keccak256(seedBytes);
48
+ const seedHashBytes = new Uint8Array(seedHash
49
+ .substring(2)
50
+ .match(/.{1,2}/g)
51
+ .map((byte) => parseInt(byte, 16)));
52
+ // Use first half for IDTrapdoor, second half for IDNullifier
53
+ const idTrapdoor = seedHashBytes.slice(0, 32);
54
+ const idNullifier = keccak256(seedHashBytes).substring(2);
55
+ const idNullifierBytes = new Uint8Array(idNullifier.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))).slice(0, 32);
56
+ // Generate IDSecretHash by concatenating and hashing
57
+ const combined = new Uint8Array(64);
58
+ combined.set(idTrapdoor, 0);
59
+ combined.set(idNullifierBytes, 32);
60
+ const idSecretHash = new Uint8Array(keccak256(combined)
61
+ .substring(2)
62
+ .match(/.{1,2}/g)
63
+ .map((byte) => parseInt(byte, 16)));
64
+ // Generate IDCommitment by hashing IDSecretHash
65
+ const idCommitment = new Uint8Array(keccak256(idSecretHash)
66
+ .substring(2)
67
+ .match(/.{1,2}/g)
68
+ .map((byte) => parseInt(byte, 16)));
69
+ // Generate BigInt from IDCommitment
70
+ const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
71
+ return new IdentityCredential(idTrapdoor, idNullifierBytes, idSecretHash, idCommitment, idCommitmentBigInt);
72
+ }
73
+ }
74
+
75
+ export { IdentityGenerator };
@@ -100,8 +100,6 @@ class RLNInstance {
100
100
  return this._signer;
101
101
  }
102
102
  async start(options = {}) {
103
- // eslint-disable-next-line no-console
104
- console.log("starting", options);
105
103
  if (this.started || this.starting) {
106
104
  return;
107
105
  }
@@ -133,12 +131,6 @@ class RLNInstance {
133
131
  if (address === SEPOLIA_CONTRACT.address) {
134
132
  chainId = SEPOLIA_CONTRACT.chainId;
135
133
  }
136
- // eslint-disable-next-line no-console
137
- console.log({
138
- chainId,
139
- address,
140
- SEPOLIA_CONTRACT
141
- });
142
134
  const signer = options.signer || (await extractMetaMaskSigner());
143
135
  const currentChainId = await signer.getChainId();
144
136
  if (chainId && chainId !== currentChainId) {
@@ -0,0 +1,228 @@
1
+ import '../../interfaces/dist/protocols.js';
2
+ import '../../interfaces/dist/connection_manager.js';
3
+ import '../../interfaces/dist/health_indicator.js';
4
+ import '../../../node_modules/multiformats/dist/src/bases/base10.js';
5
+ import '../../../node_modules/multiformats/dist/src/bases/base16.js';
6
+ import '../../../node_modules/multiformats/dist/src/bases/base2.js';
7
+ import '../../../node_modules/multiformats/dist/src/bases/base256emoji.js';
8
+ import '../../../node_modules/multiformats/dist/src/bases/base32.js';
9
+ import '../../../node_modules/multiformats/dist/src/bases/base36.js';
10
+ import '../../../node_modules/multiformats/dist/src/bases/base58.js';
11
+ import '../../../node_modules/multiformats/dist/src/bases/base64.js';
12
+ import '../../../node_modules/multiformats/dist/src/bases/base8.js';
13
+ import '../../../node_modules/multiformats/dist/src/bases/identity.js';
14
+ import '../../../node_modules/multiformats/dist/src/codecs/json.js';
15
+ import { Logger } from '../../utils/dist/logger/index.js';
16
+ import { RLNContract } from './contract/rln_contract.js';
17
+ import { SEPOLIA_CONTRACT } from './contract/constants.js';
18
+ import { IdentityGenerator } from './identity_generator.js';
19
+ import { Keystore } from './keystore/keystore.js';
20
+ import { extractMetaMaskSigner } from './utils/metamask.js';
21
+ import './utils/epoch.js';
22
+
23
+ const log = new Logger("waku:rln-light");
24
+ /**
25
+ * Lightweight RLN implementation without Zerokit
26
+ * Only supports Keystore and membership registration
27
+ */
28
+ class RLNLight {
29
+ started = false;
30
+ starting = false;
31
+ _contract;
32
+ _signer;
33
+ keystore = Keystore.create();
34
+ _credentials;
35
+ identityGenerator = new IdentityGenerator();
36
+ get contract() {
37
+ return this._contract;
38
+ }
39
+ get signer() {
40
+ return this._signer;
41
+ }
42
+ /**
43
+ * Get the current credentials
44
+ */
45
+ get credentials() {
46
+ return this._credentials;
47
+ }
48
+ /**
49
+ * Start the RLNLight instance
50
+ * @param options Configuration options
51
+ */
52
+ async start(options = {}) {
53
+ if (this.started || this.starting) {
54
+ log.warn("RLNLight already started or starting");
55
+ return;
56
+ }
57
+ this.starting = true;
58
+ try {
59
+ // Determine correct options based on credentials, if any
60
+ const keystoreCredentials = options.address
61
+ ? await this.findCredentialsByAddress(options.address)
62
+ : undefined;
63
+ const startOptions = await this.determineStartOptions(options, keystoreCredentials);
64
+ // Set the signer
65
+ if (startOptions.signer) {
66
+ this._signer = startOptions.signer;
67
+ }
68
+ else {
69
+ this._signer = await extractMetaMaskSigner();
70
+ }
71
+ // Initialize the contract with the correct address format
72
+ const finalAddress = typeof startOptions.address === "string"
73
+ ? startOptions.address
74
+ : SEPOLIA_CONTRACT.address;
75
+ const contractOptions = {
76
+ signer: this._signer,
77
+ address: finalAddress,
78
+ rateLimit: startOptions.rateLimit
79
+ };
80
+ // We need to create a minimal compatible object with RLNInstance interface
81
+ // but we only need it for contract initialization
82
+ const compatibleRLN = {
83
+ zerokit: {
84
+ getMerkleRoot: () => new Uint8Array(32),
85
+ insertMember: (_idCommitment) => { },
86
+ insertMembers: (_index, ..._idCommitments) => { },
87
+ deleteMember: (_index) => { }
88
+ }
89
+ };
90
+ this._contract = await RLNContract.init(
91
+ // Type assertion needed for compatibility with RLNInstance interface
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ compatibleRLN, contractOptions);
94
+ this.started = true;
95
+ }
96
+ catch (error) {
97
+ log.error("Failed to start RLNLight:", error);
98
+ throw error;
99
+ }
100
+ finally {
101
+ this.starting = false;
102
+ }
103
+ }
104
+ /**
105
+ * Find credentials in the keystore by contract address
106
+ */
107
+ async findCredentialsByAddress(address) {
108
+ // Since Keystore doesn't have a direct method to find by address,
109
+ // we need to iterate through all credentials
110
+ const hashes = this.keystore.keys();
111
+ for (const hash of hashes) {
112
+ try {
113
+ // Try with an empty password - this won't work but lets us check schema
114
+ const credential = await this.keystore.readCredential(hash, new Uint8Array(0));
115
+ if (credential?.membership.address === address) {
116
+ return credential;
117
+ }
118
+ }
119
+ catch (e) {
120
+ // Ignore errors, just means we couldn't read this credential
121
+ }
122
+ }
123
+ return undefined;
124
+ }
125
+ /**
126
+ * Determine the correct options for starting RLNLight
127
+ */
128
+ async determineStartOptions(options, credentials) {
129
+ if (options.credentials) {
130
+ const { credentials: decrypted } = await RLNLight.decryptCredentialsIfNeeded(options.credentials);
131
+ if (decrypted?.membership) {
132
+ this._credentials = decrypted;
133
+ return {
134
+ ...options,
135
+ address: decrypted.membership.address
136
+ };
137
+ }
138
+ }
139
+ else if (credentials) {
140
+ return {
141
+ ...options,
142
+ address: credentials.membership.address
143
+ };
144
+ }
145
+ return options;
146
+ }
147
+ /**
148
+ * Decrypt credentials if they are encrypted
149
+ */
150
+ static async decryptCredentialsIfNeeded(credentials) {
151
+ if (!credentials) {
152
+ return {};
153
+ }
154
+ if ("identity" in credentials) {
155
+ return { credentials };
156
+ }
157
+ else {
158
+ const keystore = Keystore.create();
159
+ try {
160
+ const decrypted = await keystore.readCredential(credentials.id, credentials.password);
161
+ if (!decrypted) {
162
+ return {};
163
+ }
164
+ return {
165
+ credentials: {
166
+ identity: decrypted.identity,
167
+ membership: decrypted.membership
168
+ },
169
+ keystore
170
+ };
171
+ }
172
+ catch (e) {
173
+ log.error("Failed to decrypt credentials", e);
174
+ return {};
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * Register a membership using an identity or signature
180
+ * @param options Options including identity or signature
181
+ * @returns Decrypted credentials if successful
182
+ */
183
+ async registerMembership(options) {
184
+ if (!this.contract) {
185
+ throw Error("RLN Contract is not initialized.");
186
+ }
187
+ let identity = "identity" in options && options.identity;
188
+ if ("signature" in options) {
189
+ identity = this.identityGenerator.generateSeededIdentityCredential(options.signature);
190
+ }
191
+ if (!identity) {
192
+ throw Error("Missing signature or identity to register membership.");
193
+ }
194
+ return this.contract.registerWithIdentity(identity);
195
+ }
196
+ /**
197
+ * Changes credentials in use by relying on provided Keystore
198
+ * @param id Hash of credentials to select from Keystore
199
+ * @param password Password to decrypt credentials from Keystore
200
+ */
201
+ async useCredentials(id, password) {
202
+ const decrypted = await this.keystore.readCredential(id, password);
203
+ if (!decrypted) {
204
+ throw new Error("Credentials not found or incorrect password");
205
+ }
206
+ this._credentials = {
207
+ identity: decrypted.identity,
208
+ membership: decrypted.membership
209
+ };
210
+ }
211
+ /**
212
+ * Generate a new identity credential
213
+ * @returns New identity credential
214
+ */
215
+ generateIdentityCredential() {
216
+ return this.identityGenerator.generateIdentityCredentials();
217
+ }
218
+ /**
219
+ * Generate a seeded identity credential
220
+ * @param seed Seed string to generate deterministic identity
221
+ * @returns Seeded identity credential
222
+ */
223
+ generateSeededIdentityCredential(seed) {
224
+ return this.identityGenerator.generateSeededIdentityCredential(seed);
225
+ }
226
+ }
227
+
228
+ export { RLNLight };