@waku/rln 0.0.2-3670e82.0 → 0.0.2-6cb9c9c.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 (37) hide show
  1. package/bundle/_virtual/utils.js +2 -2
  2. package/bundle/_virtual/utils2.js +2 -2
  3. package/bundle/index.js +2 -0
  4. package/bundle/packages/rln/dist/contract/rln_contract.js +58 -59
  5. package/bundle/packages/rln/dist/identity_generator.js +75 -0
  6. package/bundle/packages/rln/dist/rln.js +2 -4
  7. package/bundle/packages/rln/dist/rln_light.js +223 -0
  8. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
  9. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
  10. package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +1 -1
  11. package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +1 -1
  12. package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +1 -1
  13. package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +1 -1
  14. package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +1 -1
  15. package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +1 -1
  16. package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +1 -1
  17. package/dist/.tsbuildinfo +1 -1
  18. package/dist/contract/rln_contract.d.ts +4 -4
  19. package/dist/contract/rln_contract.js +58 -59
  20. package/dist/contract/rln_contract.js.map +1 -1
  21. package/dist/identity_generator.d.ts +19 -0
  22. package/dist/identity_generator.js +72 -0
  23. package/dist/identity_generator.js.map +1 -0
  24. package/dist/index.d.ts +4 -1
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/rln.js +2 -4
  28. package/dist/rln.js.map +1 -1
  29. package/dist/rln_light.d.ts +95 -0
  30. package/dist/rln_light.js +206 -0
  31. package/dist/rln_light.js.map +1 -0
  32. package/package.json +1 -1
  33. package/src/contract/rln_contract.ts +73 -107
  34. package/src/identity_generator.ts +108 -0
  35. package/src/index.ts +15 -0
  36. package/src/rln.ts +2 -5
  37. package/src/rln_light.ts +298 -0
@@ -1,3 +1,3 @@
1
- var utils = {exports: {}};
1
+ var utils = {};
2
2
 
3
- export { utils as __module };
3
+ export { utils as __exports };
@@ -1,3 +1,3 @@
1
- var utils = {};
1
+ var utils = {exports: {}};
2
2
 
3
- export { utils as __exports };
3
+ export { utils as __module };
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';
@@ -10,7 +10,6 @@ import { RATE_LIMIT_PARAMS, DEFAULT_RATE_LIMIT } from './constants.js';
10
10
  import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
11
11
  import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
12
12
 
13
- /* eslint-disable no-console */
14
13
  const log = new Logger("waku:rln:contract");
15
14
  var MembershipState;
16
15
  (function (MembershipState) {
@@ -26,7 +25,7 @@ class RLNContract {
26
25
  rateLimit;
27
26
  _members = new Map();
28
27
  _membersFilter;
29
- _membersRemovedFilter;
28
+ _membershipErasedFilter;
30
29
  _membersExpiredFilter;
31
30
  /**
32
31
  * Asynchronous initializer for RLNContract.
@@ -51,7 +50,7 @@ class RLNContract {
51
50
  this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
52
51
  // Initialize event filters
53
52
  this._membersFilter = this.contract.filters.MembershipRegistered();
54
- this._membersRemovedFilter = this.contract.filters.MembershipErased();
53
+ this._membershipErasedFilter = this.contract.filters.MembershipErased();
55
54
  this._membersExpiredFilter = this.contract.filters.MembershipExpired();
56
55
  }
57
56
  /**
@@ -113,7 +112,7 @@ class RLNContract {
113
112
  this.contract.maxTotalRateLimit(),
114
113
  this.contract.currentTotalRateLimit()
115
114
  ]);
116
- return maxTotal.sub(currentTotal).toNumber();
115
+ return Number(maxTotal) - Number(currentTotal);
117
116
  }
118
117
  /**
119
118
  * Updates the rate limit for future registrations
@@ -132,11 +131,11 @@ class RLNContract {
132
131
  }
133
132
  return this._membersFilter;
134
133
  }
135
- get membersRemovedFilter() {
136
- if (!this._membersRemovedFilter) {
137
- throw Error("MembersErased filter was not initialized.");
134
+ get membershipErasedFilter() {
135
+ if (!this._membershipErasedFilter) {
136
+ throw Error("MembershipErased filter was not initialized.");
138
137
  }
139
- return this._membersRemovedFilter;
138
+ return this._membershipErasedFilter;
140
139
  }
141
140
  get membersExpiredFilter() {
142
141
  if (!this._membersExpiredFilter) {
@@ -153,7 +152,7 @@ class RLNContract {
153
152
  const removedMemberEvents = await queryFilter(this.contract, {
154
153
  fromBlock: this.deployBlock,
155
154
  ...options,
156
- membersFilter: this.membersRemovedFilter
155
+ membersFilter: this.membershipErasedFilter
157
156
  });
158
157
  const expiredMemberEvents = await queryFilter(this.contract, {
159
158
  fromBlock: this.deployBlock,
@@ -176,12 +175,10 @@ class RLNContract {
176
175
  }
177
176
  if (evt.event === "MembershipErased" ||
178
177
  evt.event === "MembershipExpired") {
179
- // Both MembershipErased and MembershipExpired events should remove members
180
178
  let index = evt.args.index;
181
179
  if (!index) {
182
180
  return;
183
181
  }
184
- // Convert index to ethers.BigNumber if it's not already
185
182
  if (typeof index === "number" || typeof index === "string") {
186
183
  index = BigNumber.from(index);
187
184
  }
@@ -213,20 +210,17 @@ class RLNContract {
213
210
  return;
214
211
  const _idCommitment = evt.args.idCommitment;
215
212
  let index = evt.args.index;
216
- // Ensure index is an ethers.BigNumber
217
213
  if (!_idCommitment || !index) {
218
214
  return;
219
215
  }
220
- // Convert index to ethers.BigNumber if it's not already
221
216
  if (typeof index === "number" || typeof index === "string") {
222
217
  index = BigNumber.from(index);
223
218
  }
224
219
  const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
225
220
  rlnInstance.zerokit.insertMember(idCommitment);
226
- // Always store the numeric index as the key, but the BigNumber as the value
227
221
  const numericIndex = index.toNumber();
228
222
  this._members.set(numericIndex, {
229
- index, // This is always a BigNumber
223
+ index,
230
224
  idCommitment: _idCommitment
231
225
  });
232
226
  });
@@ -250,7 +244,7 @@ class RLNContract {
250
244
  this.contract.on(this.membersFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
251
245
  this.processEvents(rlnInstance, [event]);
252
246
  });
253
- this.contract.on(this.membersRemovedFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
247
+ this.contract.on(this.membershipErasedFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
254
248
  this.processEvents(rlnInstance, [event]);
255
249
  });
256
250
  this.contract.on(this.membersExpiredFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
@@ -259,49 +253,40 @@ class RLNContract {
259
253
  }
260
254
  async registerWithIdentity(identity) {
261
255
  try {
262
- console.log("registerWithIdentity - starting registration process");
263
- console.log("registerWithIdentity - identity:", identity);
264
- console.log("registerWithIdentity - IDCommitmentBigInt:", identity.IDCommitmentBigInt.toString());
265
- console.log("registerWithIdentity - rate limit:", this.rateLimit);
266
256
  log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
267
- console.log("registerWithIdentity - calling contract.register");
268
- const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit: 300000 });
269
- console.log("registerWithIdentity - txRegisterResponse:", txRegisterResponse);
270
- console.log("registerWithIdentity - hash:", txRegisterResponse.hash);
271
- console.log("registerWithIdentity - waiting for transaction confirmation...");
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 });
272
270
  const txRegisterReceipt = await txRegisterResponse.wait();
273
- console.log("registerWithIdentity - txRegisterReceipt:", txRegisterReceipt);
274
- console.log("registerWithIdentity - transaction status:", txRegisterReceipt.status);
275
- console.log("registerWithIdentity - block number:", txRegisterReceipt.blockNumber);
276
- console.log("registerWithIdentity - gas used:", txRegisterReceipt.gasUsed.toString());
271
+ if (txRegisterReceipt.status === 0) {
272
+ throw new Error("Transaction failed on-chain");
273
+ }
277
274
  const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
278
- console.log("registerWithIdentity - memberRegistered event:", memberRegistered);
279
275
  if (!memberRegistered || !memberRegistered.args) {
280
- console.log("registerWithIdentity - ERROR: no memberRegistered event found");
281
- console.log("registerWithIdentity - all events:", txRegisterReceipt.events);
282
276
  log.error("Failed to register membership: No MembershipRegistered event found");
283
277
  return undefined;
284
278
  }
285
- console.log("registerWithIdentity - memberRegistered args:", memberRegistered.args);
286
279
  const decodedData = {
287
280
  idCommitment: memberRegistered.args.idCommitment,
288
281
  membershipRateLimit: memberRegistered.args.membershipRateLimit,
289
282
  index: memberRegistered.args.index
290
283
  };
291
- console.log("registerWithIdentity - decodedData:", decodedData);
292
- console.log("registerWithIdentity - index:", decodedData.index.toString());
293
- console.log("registerWithIdentity - membershipRateLimit:", decodedData.membershipRateLimit.toString());
294
284
  log.info(`Successfully registered membership with index ${decodedData.index} ` +
295
285
  `and rate limit ${decodedData.membershipRateLimit}`);
296
- console.log("registerWithIdentity - getting network information");
297
286
  const network = await this.contract.provider.getNetwork();
298
- console.log("registerWithIdentity - network:", network);
299
- console.log("registerWithIdentity - chainId:", network.chainId);
300
287
  const address = this.contract.address;
301
- console.log("registerWithIdentity - contract address:", address);
302
- const membershipId = decodedData.index.toNumber();
303
- console.log("registerWithIdentity - membershipId:", membershipId);
304
- const result = {
288
+ const membershipId = Number(decodedData.index);
289
+ return {
305
290
  identity,
306
291
  membership: {
307
292
  address,
@@ -309,15 +294,34 @@ class RLNContract {
309
294
  chainId: network.chainId
310
295
  }
311
296
  };
312
- console.log("registerWithIdentity - returning result:", result);
313
- return result;
314
297
  }
315
298
  catch (error) {
316
- console.log("registerWithIdentity - ERROR:", error);
317
- console.log("registerWithIdentity - error message:", error.message);
318
- console.log("registerWithIdentity - error stack:", error.stack);
319
- log.error(`Error in registerWithIdentity: ${error.message}`);
320
- 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
+ }
321
325
  }
322
326
  }
323
327
  /**
@@ -360,7 +364,7 @@ class RLNContract {
360
364
  `Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
361
365
  const network = await this.contract.provider.getNetwork();
362
366
  const address = this.contract.address;
363
- const membershipId = decodedData.index.toNumber();
367
+ const membershipId = Number(decodedData.index);
364
368
  return {
365
369
  identity,
366
370
  membership: {
@@ -418,22 +422,17 @@ class RLNContract {
418
422
  }
419
423
  }
420
424
  async extendMembership(idCommitment) {
421
- const tx = await this.contract.extendMemberships([idCommitment]);
422
- return await tx.wait();
425
+ return this.contract.extendMemberships([idCommitment]);
423
426
  }
424
427
  async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
425
- const tx = await this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
426
- return await tx.wait();
428
+ return this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
427
429
  }
428
430
  async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
429
431
  if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
430
432
  rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
431
433
  throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
432
434
  }
433
- console.log("registering membership", idCommitment, rateLimit);
434
- const txn = this.contract.register(idCommitment, rateLimit, []);
435
- console.log("txn", txn);
436
- return txn;
435
+ return this.contract.register(idCommitment, rateLimit, []);
437
436
  }
438
437
  async getMemberIndex(idCommitment) {
439
438
  try {
@@ -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 };
@@ -106,7 +106,7 @@ class RLNInstance {
106
106
  this.starting = true;
107
107
  try {
108
108
  const { credentials, keystore } = await RLNInstance.decryptCredentialsIfNeeded(options.credentials);
109
- const { signer, address } = await this.determineStartOptions(options, credentials);
109
+ const { signer, address, rateLimit } = await this.determineStartOptions(options, credentials);
110
110
  if (keystore) {
111
111
  this.keystore = keystore;
112
112
  }
@@ -115,7 +115,7 @@ class RLNInstance {
115
115
  this._contract = await RLNContract.init(this, {
116
116
  address: address,
117
117
  signer: signer,
118
- rateLimit: options.rateLimit ?? this.zerokit.getRateLimit
118
+ rateLimit: rateLimit ?? this.zerokit.getRateLimit
119
119
  });
120
120
  this.started = true;
121
121
  }
@@ -166,8 +166,6 @@ class RLNInstance {
166
166
  if ("signature" in options) {
167
167
  identity = this.zerokit.generateSeededIdentityCredential(options.signature);
168
168
  }
169
- // eslint-disable-next-line no-console
170
- console.log("registering membership", identity);
171
169
  if (!identity) {
172
170
  throw Error("Missing signature or identity to register membership.");
173
171
  }
@@ -0,0 +1,223 @@
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 { IdentityGenerator } from './identity_generator.js';
18
+ import { Keystore } from './keystore/keystore.js';
19
+ import { extractMetaMaskSigner } from './utils/metamask.js';
20
+ import './utils/epoch.js';
21
+
22
+ const log = new Logger("waku:rln-light");
23
+ /**
24
+ * Lightweight RLN implementation without Zerokit
25
+ * Only supports Keystore and membership registration
26
+ */
27
+ class RLNLight {
28
+ started = false;
29
+ starting = false;
30
+ _contract;
31
+ _signer;
32
+ keystore = Keystore.create();
33
+ _credentials;
34
+ identityGenerator = new IdentityGenerator();
35
+ get contract() {
36
+ return this._contract;
37
+ }
38
+ get signer() {
39
+ return this._signer;
40
+ }
41
+ /**
42
+ * Get the current credentials
43
+ */
44
+ get credentials() {
45
+ return this._credentials;
46
+ }
47
+ /**
48
+ * Start the RLNLight instance
49
+ * @param options Configuration options
50
+ */
51
+ async start(options = {}) {
52
+ if (this.started || this.starting) {
53
+ log.warn("RLNLight already started or starting");
54
+ return;
55
+ }
56
+ this.starting = true;
57
+ try {
58
+ // Determine correct options based on credentials, if any
59
+ const keystoreCredentials = options.address
60
+ ? await this.findCredentialsByAddress(options.address)
61
+ : undefined;
62
+ const { address, signer, rateLimit } = await this.determineStartOptions(options, keystoreCredentials);
63
+ // Set the signer
64
+ if (signer) {
65
+ this._signer = signer;
66
+ }
67
+ else {
68
+ this._signer = await extractMetaMaskSigner();
69
+ }
70
+ const contractOptions = {
71
+ signer: this._signer,
72
+ address: address,
73
+ rateLimit: rateLimit
74
+ };
75
+ // We need to create a minimal compatible object with RLNInstance interface
76
+ // but we only need it for contract initialization
77
+ const compatibleRLN = {
78
+ zerokit: {
79
+ getMerkleRoot: () => new Uint8Array(32),
80
+ insertMember: (_idCommitment) => { },
81
+ insertMembers: (_index, ..._idCommitments) => { },
82
+ deleteMember: (_index) => { }
83
+ }
84
+ };
85
+ this._contract = await RLNContract.init(
86
+ // Type assertion needed for compatibility with RLNInstance interface
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ compatibleRLN, contractOptions);
89
+ this.started = true;
90
+ }
91
+ catch (error) {
92
+ log.error("Failed to start RLNLight:", error);
93
+ throw error;
94
+ }
95
+ finally {
96
+ this.starting = false;
97
+ }
98
+ }
99
+ /**
100
+ * Find credentials in the keystore by contract address
101
+ */
102
+ async findCredentialsByAddress(address) {
103
+ // Since Keystore doesn't have a direct method to find by address,
104
+ // we need to iterate through all credentials
105
+ const hashes = this.keystore.keys();
106
+ for (const hash of hashes) {
107
+ try {
108
+ // Try with an empty password - this won't work but lets us check schema
109
+ const credential = await this.keystore.readCredential(hash, new Uint8Array(0));
110
+ if (credential?.membership.address === address) {
111
+ return credential;
112
+ }
113
+ }
114
+ catch (e) {
115
+ // Ignore errors, just means we couldn't read this credential
116
+ }
117
+ }
118
+ return undefined;
119
+ }
120
+ /**
121
+ * Determine the correct options for starting RLNLight
122
+ */
123
+ async determineStartOptions(options, credentials) {
124
+ if (options.credentials) {
125
+ const { credentials: decrypted } = await RLNLight.decryptCredentialsIfNeeded(options.credentials);
126
+ if (decrypted?.membership) {
127
+ this._credentials = decrypted;
128
+ return {
129
+ ...options,
130
+ address: decrypted.membership.address
131
+ };
132
+ }
133
+ }
134
+ else if (credentials) {
135
+ return {
136
+ ...options,
137
+ address: credentials.membership.address
138
+ };
139
+ }
140
+ return options;
141
+ }
142
+ /**
143
+ * Decrypt credentials if they are encrypted
144
+ */
145
+ static async decryptCredentialsIfNeeded(credentials) {
146
+ if (!credentials) {
147
+ return {};
148
+ }
149
+ if ("identity" in credentials) {
150
+ return { credentials };
151
+ }
152
+ else {
153
+ const keystore = Keystore.create();
154
+ try {
155
+ const decrypted = await keystore.readCredential(credentials.id, credentials.password);
156
+ if (!decrypted) {
157
+ return {};
158
+ }
159
+ return {
160
+ credentials: {
161
+ identity: decrypted.identity,
162
+ membership: decrypted.membership
163
+ },
164
+ keystore
165
+ };
166
+ }
167
+ catch (e) {
168
+ log.error("Failed to decrypt credentials", e);
169
+ return {};
170
+ }
171
+ }
172
+ }
173
+ /**
174
+ * Register a membership using an identity or signature
175
+ * @param options Options including identity or signature
176
+ * @returns Decrypted credentials if successful
177
+ */
178
+ async registerMembership(options) {
179
+ if (!this.contract) {
180
+ throw Error("RLN Contract is not initialized.");
181
+ }
182
+ let identity = "identity" in options && options.identity;
183
+ if ("signature" in options) {
184
+ identity = this.identityGenerator.generateSeededIdentityCredential(options.signature);
185
+ }
186
+ if (!identity) {
187
+ throw Error("Missing signature or identity to register membership.");
188
+ }
189
+ return this.contract.registerWithIdentity(identity);
190
+ }
191
+ /**
192
+ * Changes credentials in use by relying on provided Keystore
193
+ * @param id Hash of credentials to select from Keystore
194
+ * @param password Password to decrypt credentials from Keystore
195
+ */
196
+ async useCredentials(id, password) {
197
+ const decrypted = await this.keystore.readCredential(id, password);
198
+ if (!decrypted) {
199
+ throw new Error("Credentials not found or incorrect password");
200
+ }
201
+ this._credentials = {
202
+ identity: decrypted.identity,
203
+ membership: decrypted.membership
204
+ };
205
+ }
206
+ /**
207
+ * Generate a new identity credential
208
+ * @returns New identity credential
209
+ */
210
+ generateIdentityCredential() {
211
+ return this.identityGenerator.generateIdentityCredentials();
212
+ }
213
+ /**
214
+ * Generate a seeded identity credential
215
+ * @param seed Seed string to generate deterministic identity
216
+ * @returns Seeded identity credential
217
+ */
218
+ generateSeededIdentityCredential(seed) {
219
+ return this.identityGenerator.generateSeededIdentityCredential(seed);
220
+ }
221
+ }
222
+
223
+ export { RLNLight };
@@ -1,6 +1,6 @@
1
1
  import { __exports as random } from '../../../../../../../_virtual/random.js';
2
2
  import '../../../../@noble/hashes/utils.js';
3
- import { __exports as utils } from '../../../../../../../_virtual/utils2.js';
3
+ import { __exports as utils } from '../../../../../../../_virtual/utils.js';
4
4
 
5
5
  Object.defineProperty(random, "__esModule", { value: true });
6
6
  random.getRandomBytes = random.getRandomBytesSync = undefined;
@@ -1,10 +1,10 @@
1
1
  import { commonjsGlobal } from '../../../../../../../_virtual/_commonjsHelpers.js';
2
2
  import { commonjsRequire } from '../../../../../../../_virtual/_commonjs-dynamic-modules.js';
3
- import { __module as utils } from '../../../../../../../_virtual/utils.js';
3
+ import { __module as utils } from '../../../../../../../_virtual/utils2.js';
4
4
  import '../../../../@noble/hashes/_assert.js';
5
5
  import '../../../../@noble/hashes/utils.js';
6
6
  import { __exports as _assert } from '../../../../../../../_virtual/_assert.js';
7
- import { __exports as utils$1 } from '../../../../../../../_virtual/utils2.js';
7
+ import { __exports as utils$1 } from '../../../../../../../_virtual/utils.js';
8
8
 
9
9
  utils.exports;
10
10
 
@@ -2,7 +2,7 @@ import { __exports as _sha2 } from '../../../../../_virtual/_sha2.js';
2
2
  import './_assert.js';
3
3
  import './utils.js';
4
4
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
5
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
5
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
6
6
 
7
7
  Object.defineProperty(_sha2, "__esModule", { value: true });
8
8
  _sha2.SHA2 = undefined;
@@ -2,7 +2,7 @@ import { __exports as hmac } from '../../../../../_virtual/hmac.js';
2
2
  import './_assert.js';
3
3
  import './utils.js';
4
4
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
5
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
5
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
6
6
 
7
7
  (function (exports) {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,7 +2,7 @@ import { __exports as pbkdf2$1 } from '../../../../../_virtual/pbkdf22.js';
2
2
  import './_assert.js';
3
3
  import './hmac.js';
4
4
  import './utils.js';
5
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
5
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
6
6
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
7
7
  import { __exports as hmac } from '../../../../../_virtual/hmac.js';
8
8