@waku/rln 0.0.2-3ab8023.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 +30 -150
  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 -10
  19. package/dist/contract/rln_contract.js +30 -150
  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 +36 -218
  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
@@ -0,0 +1,298 @@
1
+ import { Logger } from "@waku/utils";
2
+ import { ethers } from "ethers";
3
+
4
+ import { RLNContract } from "./contract/index.js";
5
+ import { IdentityCredential } from "./identity.js";
6
+ import { IdentityGenerator } from "./identity_generator.js";
7
+ import { Keystore } from "./keystore/index.js";
8
+ import type {
9
+ DecryptedCredentials,
10
+ EncryptedCredentials
11
+ } from "./keystore/index.js";
12
+ import { KeystoreEntity, Password } from "./keystore/types.js";
13
+ import { extractMetaMaskSigner } from "./utils/index.js";
14
+
15
+ const log = new Logger("waku:rln-light");
16
+
17
+ /**
18
+ * Options for starting the RLNLight instance
19
+ */
20
+ type StartRLNLightOptions = {
21
+ /**
22
+ * If not set - will extract MetaMask account and get signer from it.
23
+ */
24
+ signer?: ethers.Signer;
25
+ /**
26
+ * If not set - will use default SEPOLIA_CONTRACT address.
27
+ */
28
+ address?: string;
29
+ /**
30
+ * Credentials to use for membership registration.
31
+ * If provided used for validating the network chainId and connecting to registry contract.
32
+ */
33
+ credentials?: EncryptedCredentials | DecryptedCredentials;
34
+ /**
35
+ * Rate limit for the member.
36
+ */
37
+ rateLimit?: number;
38
+ };
39
+
40
+ /**
41
+ * Options for registering membership
42
+ */
43
+ type RegisterMembershipOptions =
44
+ | { signature: string }
45
+ | { identity: IdentityCredential };
46
+
47
+ /**
48
+ * Lightweight RLN implementation without Zerokit
49
+ * Only supports Keystore and membership registration
50
+ */
51
+ export class RLNLight {
52
+ private started = false;
53
+ private starting = false;
54
+
55
+ private _contract: undefined | RLNContract;
56
+ private _signer: undefined | ethers.Signer;
57
+
58
+ private keystore = Keystore.create();
59
+ private _credentials: undefined | DecryptedCredentials;
60
+ private identityGenerator = new IdentityGenerator();
61
+
62
+ public get contract(): undefined | RLNContract {
63
+ return this._contract;
64
+ }
65
+
66
+ public get signer(): undefined | ethers.Signer {
67
+ return this._signer;
68
+ }
69
+
70
+ /**
71
+ * Get the current credentials
72
+ */
73
+ public get credentials(): undefined | DecryptedCredentials {
74
+ return this._credentials;
75
+ }
76
+
77
+ /**
78
+ * Start the RLNLight instance
79
+ * @param options Configuration options
80
+ */
81
+ public async start(options: StartRLNLightOptions = {}): Promise<void> {
82
+ if (this.started || this.starting) {
83
+ log.warn("RLNLight already started or starting");
84
+ return;
85
+ }
86
+
87
+ this.starting = true;
88
+
89
+ try {
90
+ // Determine correct options based on credentials, if any
91
+ const keystoreCredentials = options.address
92
+ ? await this.findCredentialsByAddress(options.address)
93
+ : undefined;
94
+
95
+ const { address, signer, rateLimit } = await this.determineStartOptions(
96
+ options,
97
+ keystoreCredentials
98
+ );
99
+
100
+ // Set the signer
101
+ if (signer) {
102
+ this._signer = signer;
103
+ } else {
104
+ this._signer = await extractMetaMaskSigner();
105
+ }
106
+
107
+ const contractOptions = {
108
+ signer: this._signer,
109
+ address: address!,
110
+ rateLimit: rateLimit!
111
+ };
112
+
113
+ // We need to create a minimal compatible object with RLNInstance interface
114
+ // but we only need it for contract initialization
115
+ const compatibleRLN = {
116
+ zerokit: {
117
+ getMerkleRoot: () => new Uint8Array(32),
118
+ insertMember: (_idCommitment: Uint8Array) => {},
119
+ insertMembers: (
120
+ _index: number,
121
+ ..._idCommitments: Array<Uint8Array>
122
+ ) => {},
123
+ deleteMember: (_index: number) => {}
124
+ }
125
+ };
126
+
127
+ this._contract = await RLNContract.init(
128
+ // Type assertion needed for compatibility with RLNInstance interface
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ compatibleRLN as any,
131
+ contractOptions
132
+ );
133
+
134
+ this.started = true;
135
+ } catch (error) {
136
+ log.error("Failed to start RLNLight:", error);
137
+ throw error;
138
+ } finally {
139
+ this.starting = false;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Find credentials in the keystore by contract address
145
+ */
146
+ private async findCredentialsByAddress(
147
+ address: string
148
+ ): Promise<KeystoreEntity | undefined> {
149
+ // Since Keystore doesn't have a direct method to find by address,
150
+ // we need to iterate through all credentials
151
+ const hashes = this.keystore.keys();
152
+
153
+ for (const hash of hashes) {
154
+ try {
155
+ // Try with an empty password - this won't work but lets us check schema
156
+ const credential = await this.keystore.readCredential(
157
+ hash,
158
+ new Uint8Array(0)
159
+ );
160
+ if (credential?.membership.address === address) {
161
+ return credential;
162
+ }
163
+ } catch (e) {
164
+ // Ignore errors, just means we couldn't read this credential
165
+ }
166
+ }
167
+
168
+ return undefined;
169
+ }
170
+
171
+ /**
172
+ * Determine the correct options for starting RLNLight
173
+ */
174
+ private async determineStartOptions(
175
+ options: StartRLNLightOptions,
176
+ credentials: KeystoreEntity | undefined
177
+ ): Promise<StartRLNLightOptions> {
178
+ if (options.credentials) {
179
+ const { credentials: decrypted } =
180
+ await RLNLight.decryptCredentialsIfNeeded(options.credentials);
181
+
182
+ if (decrypted?.membership) {
183
+ this._credentials = decrypted;
184
+ return {
185
+ ...options,
186
+ address: decrypted.membership.address
187
+ };
188
+ }
189
+ } else if (credentials) {
190
+ return {
191
+ ...options,
192
+ address: credentials.membership.address
193
+ };
194
+ }
195
+
196
+ return options;
197
+ }
198
+
199
+ /**
200
+ * Decrypt credentials if they are encrypted
201
+ */
202
+ private static async decryptCredentialsIfNeeded(
203
+ credentials?: EncryptedCredentials | DecryptedCredentials
204
+ ): Promise<{ credentials?: DecryptedCredentials; keystore?: Keystore }> {
205
+ if (!credentials) {
206
+ return {};
207
+ }
208
+
209
+ if ("identity" in credentials) {
210
+ return { credentials };
211
+ } else {
212
+ const keystore = Keystore.create();
213
+ try {
214
+ const decrypted = await keystore.readCredential(
215
+ credentials.id,
216
+ credentials.password
217
+ );
218
+
219
+ if (!decrypted) {
220
+ return {};
221
+ }
222
+
223
+ return {
224
+ credentials: {
225
+ identity: decrypted.identity,
226
+ membership: decrypted.membership
227
+ },
228
+ keystore
229
+ };
230
+ } catch (e) {
231
+ log.error("Failed to decrypt credentials", e);
232
+ return {};
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Register a membership using an identity or signature
239
+ * @param options Options including identity or signature
240
+ * @returns Decrypted credentials if successful
241
+ */
242
+ public async registerMembership(
243
+ options: RegisterMembershipOptions
244
+ ): Promise<undefined | DecryptedCredentials> {
245
+ if (!this.contract) {
246
+ throw Error("RLN Contract is not initialized.");
247
+ }
248
+
249
+ let identity = "identity" in options && options.identity;
250
+
251
+ if ("signature" in options) {
252
+ identity = this.identityGenerator.generateSeededIdentityCredential(
253
+ options.signature
254
+ );
255
+ }
256
+
257
+ if (!identity) {
258
+ throw Error("Missing signature or identity to register membership.");
259
+ }
260
+
261
+ return this.contract.registerWithIdentity(identity);
262
+ }
263
+
264
+ /**
265
+ * Changes credentials in use by relying on provided Keystore
266
+ * @param id Hash of credentials to select from Keystore
267
+ * @param password Password to decrypt credentials from Keystore
268
+ */
269
+ public async useCredentials(id: string, password: Password): Promise<void> {
270
+ const decrypted = await this.keystore.readCredential(id, password);
271
+
272
+ if (!decrypted) {
273
+ throw new Error("Credentials not found or incorrect password");
274
+ }
275
+
276
+ this._credentials = {
277
+ identity: decrypted.identity,
278
+ membership: decrypted.membership
279
+ };
280
+ }
281
+
282
+ /**
283
+ * Generate a new identity credential
284
+ * @returns New identity credential
285
+ */
286
+ public generateIdentityCredential(): IdentityCredential {
287
+ return this.identityGenerator.generateIdentityCredentials();
288
+ }
289
+
290
+ /**
291
+ * Generate a seeded identity credential
292
+ * @param seed Seed string to generate deterministic identity
293
+ * @returns Seeded identity credential
294
+ */
295
+ public generateSeededIdentityCredential(seed: string): IdentityCredential {
296
+ return this.identityGenerator.generateSeededIdentityCredential(seed);
297
+ }
298
+ }