@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.
- package/bundle/_virtual/utils.js +2 -2
- package/bundle/_virtual/utils2.js +2 -2
- package/bundle/index.js +2 -0
- package/bundle/packages/rln/dist/contract/rln_contract.js +58 -59
- package/bundle/packages/rln/dist/identity_generator.js +75 -0
- package/bundle/packages/rln/dist/rln.js +2 -4
- package/bundle/packages/rln/dist/rln_light.js +223 -0
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/rln_contract.d.ts +4 -4
- package/dist/contract/rln_contract.js +58 -59
- package/dist/contract/rln_contract.js.map +1 -1
- package/dist/identity_generator.d.ts +19 -0
- package/dist/identity_generator.js +72 -0
- package/dist/identity_generator.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/rln.js +2 -4
- package/dist/rln.js.map +1 -1
- package/dist/rln_light.d.ts +95 -0
- package/dist/rln_light.js +206 -0
- package/dist/rln_light.js.map +1 -0
- package/package.json +1 -1
- package/src/contract/rln_contract.ts +73 -107
- package/src/identity_generator.ts +108 -0
- package/src/index.ts +15 -0
- package/src/rln.ts +2 -5
- package/src/rln_light.ts +298 -0
package/bundle/_virtual/utils.js
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
var utils = {
|
1
|
+
var utils = {};
|
2
2
|
|
3
|
-
export { utils as
|
3
|
+
export { utils as __exports };
|
@@ -1,3 +1,3 @@
|
|
1
|
-
var utils = {};
|
1
|
+
var utils = {exports: {}};
|
2
2
|
|
3
|
-
export { utils as
|
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
|
-
|
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.
|
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
|
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
|
136
|
-
if (!this.
|
137
|
-
throw Error("
|
134
|
+
get membershipErasedFilter() {
|
135
|
+
if (!this._membershipErasedFilter) {
|
136
|
+
throw Error("MembershipErased filter was not initialized.");
|
138
137
|
}
|
139
|
-
return this.
|
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.
|
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,
|
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.
|
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
|
-
|
268
|
-
const
|
269
|
-
|
270
|
-
|
271
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
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
|
-
|
302
|
-
|
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
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
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
|
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
|
-
|
422
|
-
return await tx.wait();
|
425
|
+
return this.contract.extendMemberships([idCommitment]);
|
423
426
|
}
|
424
427
|
async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
|
425
|
-
|
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
|
-
|
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:
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
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
|
|