@waku/rln 0.1.6-006cd41.0 → 0.1.6-0877e51.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/index2.js +1 -1
- package/bundle/index.js +2 -1
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/checksum.js +3 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/cipher.js +4 -4
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/class.js +7 -7
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/functional.js +7 -7
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/index.js +6 -6
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/kdf.js +5 -5
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/password.js +1 -1
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/schema-validation-generated.js +1 -1
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/schema-validation.js +2 -2
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/types.js +1 -1
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_assert.js +1 -1
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_sha2.js +3 -3
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_u64.js +1 -1
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/cryptoBrowser.js +1 -1
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/hmac.js +3 -3
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/pbkdf2.js +4 -4
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/scrypt.js +5 -5
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/sha256.js +3 -3
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/sha512.js +4 -4
- package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/utils.js +2 -2
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/aes.js +3 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/pbkdf2.js +7 -7
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +3 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/scrypt.js +3 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/sha256.js +3 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +7 -7
- package/bundle/node_modules/@ethersproject/keccak256/lib.esm/index.js +1 -1
- package/bundle/node_modules/@noble/hashes/esm/_assert.js +6 -32
- package/bundle/node_modules/@noble/hashes/esm/_md.js +22 -11
- package/bundle/node_modules/@noble/hashes/esm/_u64.js +4 -3
- package/bundle/{packages/rln/node_modules → node_modules}/@noble/hashes/esm/hmac.js +19 -10
- package/bundle/{packages/rln/node_modules/@noble/hashes/esm/sha256.js → node_modules/@noble/hashes/esm/sha2.js} +36 -50
- package/bundle/node_modules/@noble/hashes/esm/sha256.js +5 -102
- package/bundle/node_modules/@noble/hashes/esm/sha3.js +30 -24
- package/bundle/node_modules/@noble/hashes/esm/utils.js +69 -18
- package/bundle/node_modules/bn.js/lib/bn.js +1 -0
- package/bundle/node_modules/ethereum-cryptography/esm/sha256.js +1 -1
- package/bundle/node_modules/{@ethersproject/keccak256/node_modules/js-sha3 → js-sha3}/src/sha3.js +2 -2
- package/bundle/packages/core/dist/lib/message/version_0.js +1 -4
- package/bundle/packages/rln/dist/contract/constants.js +8 -1
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +135 -74
- package/bundle/packages/rln/dist/credentials_manager.js +18 -16
- package/bundle/packages/rln/dist/identity.js +37 -7
- package/bundle/packages/rln/dist/keystore/cipher.js +3 -3
- package/bundle/packages/rln/dist/keystore/keystore.js +16 -12
- package/bundle/packages/rln/dist/message.js +11 -0
- package/bundle/packages/rln/dist/utils/bytes.js +14 -16
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/constants.d.ts +6 -0
- package/dist/contract/constants.js +6 -0
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +1 -0
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/rln_base_contract.d.ts +32 -22
- package/dist/contract/rln_base_contract.js +135 -74
- package/dist/contract/rln_base_contract.js.map +1 -1
- package/dist/contract/types.d.ts +5 -0
- package/dist/contract/types.js.map +1 -1
- package/dist/credentials_manager.js +17 -15
- package/dist/credentials_manager.js.map +1 -1
- package/dist/identity.d.ts +11 -2
- package/dist/identity.js +23 -6
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keystore/keystore.js +15 -11
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/keystore/types.d.ts +2 -2
- package/dist/message.d.ts +5 -4
- package/dist/message.js +2 -0
- package/dist/message.js.map +1 -1
- package/dist/utils/bytes.d.ts +2 -6
- package/dist/utils/bytes.js +13 -15
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contract/constants.ts +9 -0
- package/src/contract/index.ts +1 -0
- package/src/contract/rln_base_contract.ts +176 -113
- package/src/contract/types.ts +5 -0
- package/src/credentials_manager.ts +28 -22
- package/src/identity.ts +32 -6
- package/src/index.ts +3 -1
- package/src/keystore/keystore.ts +32 -26
- package/src/keystore/types.ts +2 -2
- package/src/message.ts +7 -4
- package/src/utils/bytes.ts +21 -25
- package/src/utils/index.ts +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/_assert.js +0 -43
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/_sha2.js +0 -116
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/utils.js +0 -43
- package/dist/contract/test-utils.d.ts +0 -39
- package/dist/contract/test-utils.js +0 -118
- package/dist/contract/test-utils.js.map +0 -1
- package/src/contract/test-utils.ts +0 -179
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/index.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/md5.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/nil.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/parse.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/regex.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/rng.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/sha1.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/stringify.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v1.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v3.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v35.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v4.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v5.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/validate.js +0 -0
- /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/version.js +0 -0
@@ -13,6 +13,7 @@ import '../../../../node_modules/multiformats/dist/src/bases/base8.js';
|
|
13
13
|
import '../../../../node_modules/multiformats/dist/src/bases/identity.js';
|
14
14
|
import '../../../../node_modules/multiformats/dist/src/codecs/json.js';
|
15
15
|
import { Logger } from '../../../utils/dist/logger/index.js';
|
16
|
+
import { IdentityCredential } from '../identity.js';
|
16
17
|
import { RLN_ABI } from './abi.js';
|
17
18
|
import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
|
18
19
|
import { MembershipState } from './types.js';
|
@@ -24,15 +25,32 @@ class RLNBaseContract {
|
|
24
25
|
contract;
|
25
26
|
deployBlock;
|
26
27
|
rateLimit;
|
28
|
+
minRateLimit;
|
29
|
+
maxRateLimit;
|
27
30
|
_members = new Map();
|
28
31
|
_membersFilter;
|
29
32
|
_membershipErasedFilter;
|
30
33
|
_membersExpiredFilter;
|
31
34
|
/**
|
32
|
-
*
|
33
|
-
* Allows injecting a mocked contract for testing purposes.
|
35
|
+
* Private constructor for RLNBaseContract. Use static create() instead.
|
34
36
|
*/
|
35
37
|
constructor(options) {
|
38
|
+
const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
|
39
|
+
log.info("Initializing RLNBaseContract", { address, rateLimit });
|
40
|
+
this.contract = contract || new Contract(address, RLN_ABI, signer);
|
41
|
+
this.rateLimit = rateLimit;
|
42
|
+
try {
|
43
|
+
log.info("Setting up event filters");
|
44
|
+
// Initialize event filters
|
45
|
+
this._membersFilter = this.contract.filters.MembershipRegistered();
|
46
|
+
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
47
|
+
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
48
|
+
log.info("Event filters initialized successfully");
|
49
|
+
}
|
50
|
+
catch (error) {
|
51
|
+
log.error("Failed to initialize event filters", { error });
|
52
|
+
throw new Error("Failed to initialize event filters: " + error.message);
|
53
|
+
}
|
36
54
|
// Initialize members and subscriptions
|
37
55
|
this.fetchMembers()
|
38
56
|
.then(() => {
|
@@ -41,14 +59,21 @@ class RLNBaseContract {
|
|
41
59
|
.catch((error) => {
|
42
60
|
log.error("Failed to initialize members", { error });
|
43
61
|
});
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
62
|
+
}
|
63
|
+
/**
|
64
|
+
* Static async factory to create and initialize RLNBaseContract
|
65
|
+
*/
|
66
|
+
static async create(options) {
|
67
|
+
const instance = new RLNBaseContract(options);
|
68
|
+
const [min, max] = await Promise.all([
|
69
|
+
instance.contract.minMembershipRateLimit(),
|
70
|
+
instance.contract.maxMembershipRateLimit(),
|
71
|
+
instance.contract.Q()
|
72
|
+
]);
|
73
|
+
instance.minRateLimit = BigNumber.from(min).toNumber();
|
74
|
+
instance.maxRateLimit = BigNumber.from(max).toNumber();
|
75
|
+
instance.validateRateLimit(instance.rateLimit);
|
76
|
+
return instance;
|
52
77
|
}
|
53
78
|
/**
|
54
79
|
* Gets the current rate limit for this contract instance
|
@@ -69,20 +94,20 @@ class RLNBaseContract {
|
|
69
94
|
return this.contract.provider;
|
70
95
|
}
|
71
96
|
/**
|
72
|
-
* Gets the minimum allowed rate limit
|
73
|
-
* @returns Promise<number> The minimum rate limit in messages per epoch
|
97
|
+
* Gets the minimum allowed rate limit (cached)
|
74
98
|
*/
|
75
|
-
|
76
|
-
|
77
|
-
|
99
|
+
getMinRateLimit() {
|
100
|
+
if (this.minRateLimit === undefined)
|
101
|
+
throw new Error("minRateLimit not initialized");
|
102
|
+
return this.minRateLimit;
|
78
103
|
}
|
79
104
|
/**
|
80
|
-
* Gets the maximum allowed rate limit
|
81
|
-
* @returns Promise<number> The maximum rate limit in messages per epoch
|
105
|
+
* Gets the maximum allowed rate limit (cached)
|
82
106
|
*/
|
83
|
-
|
84
|
-
|
85
|
-
|
107
|
+
getMaxRateLimit() {
|
108
|
+
if (this.maxRateLimit === undefined)
|
109
|
+
throw new Error("maxRateLimit not initialized");
|
110
|
+
return this.maxRateLimit;
|
86
111
|
}
|
87
112
|
/**
|
88
113
|
* Gets the maximum total rate limit across all memberships
|
@@ -115,7 +140,7 @@ class RLNBaseContract {
|
|
115
140
|
* Updates the rate limit for future registrations
|
116
141
|
* @param newRateLimit The new rate limit to use
|
117
142
|
*/
|
118
|
-
|
143
|
+
setRateLimit(newRateLimit) {
|
119
144
|
this.validateRateLimit(newRateLimit);
|
120
145
|
this.rateLimit = newRateLimit;
|
121
146
|
}
|
@@ -247,75 +272,67 @@ class RLNBaseContract {
|
|
247
272
|
this.processEvents([event]);
|
248
273
|
});
|
249
274
|
}
|
250
|
-
|
251
|
-
* Helper method to get remaining messages in current epoch
|
252
|
-
* @param membershipId The ID of the membership to check
|
253
|
-
* @returns number of remaining messages allowed in current epoch
|
254
|
-
*/
|
255
|
-
async getRemainingMessages(membershipId) {
|
256
|
-
try {
|
257
|
-
const [startTime, , rateLimit] = await this.contract.getMembershipInfo(membershipId);
|
258
|
-
// Calculate current epoch
|
259
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
260
|
-
const epochsPassed = Math.floor((currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH);
|
261
|
-
const currentEpochStart = startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
|
262
|
-
// Get message count in current epoch using contract's function
|
263
|
-
const messageCount = await this.contract.getMessageCount(membershipId, currentEpochStart);
|
264
|
-
return Math.max(0, BigNumber.from(rateLimit)
|
265
|
-
.sub(BigNumber.from(messageCount))
|
266
|
-
.toNumber());
|
267
|
-
}
|
268
|
-
catch (error) {
|
269
|
-
log.error(`Error getting remaining messages: ${error.message}`);
|
270
|
-
return 0; // Fail safe: assume no messages remaining on error
|
271
|
-
}
|
272
|
-
}
|
273
|
-
async getMembershipInfo(idCommitment) {
|
275
|
+
async getMembershipInfo(idCommitmentBigInt) {
|
274
276
|
try {
|
275
|
-
const
|
277
|
+
const membershipData = await this.contract.memberships(idCommitmentBigInt);
|
276
278
|
const currentBlock = await this.contract.provider.getBlockNumber();
|
279
|
+
const [depositAmount, activeDuration, gracePeriodStartTimestamp, gracePeriodDuration, rateLimit, index, holder, token] = membershipData;
|
280
|
+
const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
|
277
281
|
let state;
|
278
|
-
if (currentBlock <
|
282
|
+
if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
|
279
283
|
state = MembershipState.Active;
|
280
284
|
}
|
281
|
-
else if (currentBlock <
|
285
|
+
else if (currentBlock < gracePeriodEnd.toNumber()) {
|
282
286
|
state = MembershipState.GracePeriod;
|
283
287
|
}
|
284
288
|
else {
|
285
289
|
state = MembershipState.Expired;
|
286
290
|
}
|
287
|
-
const index = await this.getMemberIndex(idCommitment);
|
288
|
-
if (!index)
|
289
|
-
return undefined;
|
290
291
|
return {
|
291
292
|
index,
|
292
|
-
idCommitment,
|
293
|
-
rateLimit: rateLimit
|
294
|
-
startBlock:
|
295
|
-
endBlock:
|
296
|
-
state
|
293
|
+
idCommitment: idCommitmentBigInt.toString(),
|
294
|
+
rateLimit: Number(rateLimit),
|
295
|
+
startBlock: gracePeriodStartTimestamp.toNumber(),
|
296
|
+
endBlock: gracePeriodEnd.toNumber(),
|
297
|
+
state,
|
298
|
+
depositAmount,
|
299
|
+
activeDuration,
|
300
|
+
gracePeriodDuration,
|
301
|
+
holder,
|
302
|
+
token
|
297
303
|
};
|
298
304
|
}
|
299
305
|
catch (error) {
|
306
|
+
log.error("Error in getMembershipInfo:", error);
|
300
307
|
return undefined;
|
301
308
|
}
|
302
309
|
}
|
303
|
-
async extendMembership(
|
304
|
-
|
310
|
+
async extendMembership(idCommitmentBigInt) {
|
311
|
+
const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
|
312
|
+
await tx.wait();
|
313
|
+
return tx;
|
305
314
|
}
|
306
|
-
async eraseMembership(
|
307
|
-
|
315
|
+
async eraseMembership(idCommitmentBigInt, eraseFromMembershipSet = true) {
|
316
|
+
if (!(await this.isExpired(idCommitmentBigInt)) ||
|
317
|
+
!(await this.isInGracePeriod(idCommitmentBigInt))) {
|
318
|
+
throw new Error("Membership is not expired or in grace period");
|
319
|
+
}
|
320
|
+
const estimatedGas = await this.contract.estimateGas["eraseMemberships(uint256[],bool)"]([idCommitmentBigInt], eraseFromMembershipSet);
|
321
|
+
const gasLimit = estimatedGas.add(10000);
|
322
|
+
const tx = await this.contract["eraseMemberships(uint256[],bool)"]([idCommitmentBigInt], eraseFromMembershipSet, { gasLimit });
|
323
|
+
await tx.wait();
|
324
|
+
return tx;
|
308
325
|
}
|
309
|
-
async registerMembership(
|
326
|
+
async registerMembership(idCommitmentBigInt, rateLimit = DEFAULT_RATE_LIMIT) {
|
310
327
|
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
311
328
|
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
312
329
|
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
|
313
330
|
}
|
314
|
-
return this.contract.register(
|
331
|
+
return this.contract.register(idCommitmentBigInt, rateLimit, []);
|
315
332
|
}
|
316
|
-
async withdraw(token,
|
333
|
+
async withdraw(token, walletAddress) {
|
317
334
|
try {
|
318
|
-
const tx = await this.contract.withdraw(token,
|
335
|
+
const tx = await this.contract.withdraw(token, walletAddress);
|
319
336
|
await tx.wait();
|
320
337
|
}
|
321
338
|
catch (error) {
|
@@ -325,8 +342,9 @@ class RLNBaseContract {
|
|
325
342
|
async registerWithIdentity(identity) {
|
326
343
|
try {
|
327
344
|
log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
|
345
|
+
const idCommitmentBigInt = IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment);
|
328
346
|
// Check if the ID commitment is already registered
|
329
|
-
const existingIndex = await this.getMemberIndex(
|
347
|
+
const existingIndex = await this.getMemberIndex(idCommitmentBigInt);
|
330
348
|
if (existingIndex) {
|
331
349
|
throw new Error(`ID commitment is already registered with index ${existingIndex}`);
|
332
350
|
}
|
@@ -335,9 +353,11 @@ class RLNBaseContract {
|
|
335
353
|
if (remainingRateLimit < this.rateLimit) {
|
336
354
|
throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
|
337
355
|
}
|
338
|
-
const estimatedGas = await this.contract.estimateGas.register(
|
356
|
+
const estimatedGas = await this.contract.estimateGas.register(idCommitmentBigInt, this.rateLimit, []);
|
339
357
|
const gasLimit = estimatedGas.add(10000);
|
340
|
-
const txRegisterResponse = await this.contract.register(
|
358
|
+
const txRegisterResponse = await this.contract.register(idCommitmentBigInt, this.rateLimit, [], {
|
359
|
+
gasLimit
|
360
|
+
});
|
341
361
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
342
362
|
if (txRegisterReceipt.status === 0) {
|
343
363
|
throw new Error("Transaction failed on-chain");
|
@@ -399,7 +419,7 @@ class RLNBaseContract {
|
|
399
419
|
async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
|
400
420
|
try {
|
401
421
|
log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
|
402
|
-
const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, identity.
|
422
|
+
const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment), this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
|
403
423
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
404
424
|
const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
|
405
425
|
if (!memberRegistered || !memberRegistered.args) {
|
@@ -432,13 +452,15 @@ class RLNBaseContract {
|
|
432
452
|
}
|
433
453
|
}
|
434
454
|
/**
|
435
|
-
* Validates that the rate limit is within the allowed range
|
455
|
+
* Validates that the rate limit is within the allowed range (sync)
|
436
456
|
* @throws Error if the rate limit is outside the allowed range
|
437
457
|
*/
|
438
458
|
validateRateLimit(rateLimit) {
|
439
|
-
if (
|
440
|
-
|
441
|
-
|
459
|
+
if (this.minRateLimit === undefined || this.maxRateLimit === undefined) {
|
460
|
+
throw new Error("Rate limits not initialized");
|
461
|
+
}
|
462
|
+
if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) {
|
463
|
+
throw new Error(`Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch`);
|
442
464
|
}
|
443
465
|
}
|
444
466
|
get membersFilter() {
|
@@ -459,9 +481,9 @@ class RLNBaseContract {
|
|
459
481
|
}
|
460
482
|
return this._membersExpiredFilter;
|
461
483
|
}
|
462
|
-
async getMemberIndex(
|
484
|
+
async getMemberIndex(idCommitmentBigInt) {
|
463
485
|
try {
|
464
|
-
const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(
|
486
|
+
const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(idCommitmentBigInt));
|
465
487
|
if (events.length === 0)
|
466
488
|
return undefined;
|
467
489
|
// Get the most recent registration event
|
@@ -472,6 +494,45 @@ class RLNBaseContract {
|
|
472
494
|
return undefined;
|
473
495
|
}
|
474
496
|
}
|
497
|
+
async getMembershipStatus(idCommitment) {
|
498
|
+
const [isExpired, isInGrace] = await Promise.all([
|
499
|
+
this.contract.isExpired(idCommitment),
|
500
|
+
this.contract.isInGracePeriod(idCommitment)
|
501
|
+
]);
|
502
|
+
if (isExpired)
|
503
|
+
return "expired";
|
504
|
+
if (isInGrace)
|
505
|
+
return "grace";
|
506
|
+
return "active";
|
507
|
+
}
|
508
|
+
/**
|
509
|
+
* Checks if a membership is expired for the given idCommitment
|
510
|
+
* @param idCommitmentBigInt The idCommitment as bigint
|
511
|
+
* @returns Promise<boolean> True if expired, false otherwise
|
512
|
+
*/
|
513
|
+
async isExpired(idCommitmentBigInt) {
|
514
|
+
try {
|
515
|
+
return await this.contract.isExpired(idCommitmentBigInt);
|
516
|
+
}
|
517
|
+
catch (error) {
|
518
|
+
log.error("Error in isExpired:", error);
|
519
|
+
return false;
|
520
|
+
}
|
521
|
+
}
|
522
|
+
/**
|
523
|
+
* Checks if a membership is in grace period for the given idCommitment
|
524
|
+
* @param idCommitmentBigInt The idCommitment as bigint
|
525
|
+
* @returns Promise<boolean> True if in grace period, false otherwise
|
526
|
+
*/
|
527
|
+
async isInGracePeriod(idCommitmentBigInt) {
|
528
|
+
try {
|
529
|
+
return await this.contract.isInGracePeriod(idCommitmentBigInt);
|
530
|
+
}
|
531
|
+
catch (error) {
|
532
|
+
log.error("Error in isInGracePeriod:", error);
|
533
|
+
return false;
|
534
|
+
}
|
535
|
+
}
|
475
536
|
}
|
476
537
|
|
477
538
|
export { RLNBaseContract };
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { hmac } from '
|
2
|
-
import { sha256 } from '
|
1
|
+
import { hmac } from '../../../node_modules/@noble/hashes/esm/hmac.js';
|
2
|
+
import { sha256 } from '../../../node_modules/@noble/hashes/esm/sha2.js';
|
3
3
|
import '../../interfaces/dist/protocols.js';
|
4
4
|
import '../../interfaces/dist/connection_manager.js';
|
5
5
|
import '../../interfaces/dist/health_indicator.js';
|
@@ -20,7 +20,7 @@ import { RLNBaseContract } from './contract/rln_base_contract.js';
|
|
20
20
|
import { IdentityCredential } from './identity.js';
|
21
21
|
import { Keystore } from './keystore/keystore.js';
|
22
22
|
import { extractMetaMaskSigner } from './utils/metamask.js';
|
23
|
-
import {
|
23
|
+
import { switchEndianness } from './utils/bytes.js';
|
24
24
|
import './utils/epoch.js';
|
25
25
|
|
26
26
|
const log = new Logger("waku:credentials");
|
@@ -64,7 +64,7 @@ class RLNCredentialsManager {
|
|
64
64
|
}
|
65
65
|
this.credentials = credentials;
|
66
66
|
this.signer = signer;
|
67
|
-
this.contract =
|
67
|
+
this.contract = await RLNBaseContract.create({
|
68
68
|
address: address,
|
69
69
|
signer: signer,
|
70
70
|
rateLimit: rateLimit ?? this.zerokit?.rateLimit
|
@@ -95,7 +95,7 @@ class RLNCredentialsManager {
|
|
95
95
|
}
|
96
96
|
else {
|
97
97
|
log.info("Using local implementation to generate identity");
|
98
|
-
identity = this.generateSeededIdentityCredential(options.signature);
|
98
|
+
identity = await this.generateSeededIdentityCredential(options.signature);
|
99
99
|
}
|
100
100
|
}
|
101
101
|
if (!identity) {
|
@@ -191,24 +191,26 @@ class RLNCredentialsManager {
|
|
191
191
|
* @param seed A string seed to generate the identity from
|
192
192
|
* @returns IdentityCredential
|
193
193
|
*/
|
194
|
-
generateSeededIdentityCredential(seed) {
|
194
|
+
async generateSeededIdentityCredential(seed) {
|
195
195
|
log.info("Generating seeded identity credential");
|
196
196
|
// Convert the seed to bytes
|
197
197
|
const encoder = new TextEncoder();
|
198
198
|
const seedBytes = encoder.encode(seed);
|
199
199
|
// Generate deterministic values using HMAC-SHA256
|
200
200
|
// We use different context strings for each component to ensure they're different
|
201
|
-
const
|
202
|
-
const
|
203
|
-
|
204
|
-
const
|
205
|
-
const
|
206
|
-
//
|
207
|
-
|
208
|
-
|
209
|
-
const
|
201
|
+
const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
|
202
|
+
const idNullifierBE = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
|
203
|
+
const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
|
204
|
+
const idSecretHashBE = sha256(combinedBytes);
|
205
|
+
const idCommitmentBE = sha256(idSecretHashBE);
|
206
|
+
// All hashing functions return big-endian bytes
|
207
|
+
// We need to switch to little-endian for the identity credential
|
208
|
+
const idTrapdoorLE = switchEndianness(idTrapdoorBE);
|
209
|
+
const idNullifierLE = switchEndianness(idNullifierBE);
|
210
|
+
const idSecretHashLE = switchEndianness(idSecretHashBE);
|
211
|
+
const idCommitmentLE = switchEndianness(idCommitmentBE);
|
210
212
|
log.info("Successfully generated identity credential");
|
211
|
-
return new IdentityCredential(
|
213
|
+
return new IdentityCredential(idTrapdoorLE, idNullifierLE, idSecretHashLE, idCommitmentLE);
|
212
214
|
}
|
213
215
|
}
|
214
216
|
|
@@ -1,18 +1,35 @@
|
|
1
|
-
import
|
2
|
-
import '
|
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 { DEFAULT_Q } from './contract/constants.js';
|
17
|
+
import { buildBigIntFromUint8ArrayBE } from './utils/bytes.js';
|
3
18
|
|
19
|
+
const log = new Logger("waku:rln:identity");
|
4
20
|
class IdentityCredential {
|
5
21
|
IDTrapdoor;
|
6
22
|
IDNullifier;
|
7
23
|
IDSecretHash;
|
8
24
|
IDCommitment;
|
9
|
-
|
10
|
-
|
25
|
+
/**
|
26
|
+
* All variables are in little-endian format
|
27
|
+
*/
|
28
|
+
constructor(IDTrapdoor, IDNullifier, IDSecretHash, IDCommitment) {
|
11
29
|
this.IDTrapdoor = IDTrapdoor;
|
12
30
|
this.IDNullifier = IDNullifier;
|
13
31
|
this.IDSecretHash = IDSecretHash;
|
14
32
|
this.IDCommitment = IDCommitment;
|
15
|
-
this.IDCommitmentBigInt = IDCommitmentBigInt;
|
16
33
|
}
|
17
34
|
static fromBytes(memKeys) {
|
18
35
|
if (memKeys.length < 128) {
|
@@ -22,8 +39,21 @@ class IdentityCredential {
|
|
22
39
|
const idNullifier = memKeys.subarray(32, 64);
|
23
40
|
const idSecretHash = memKeys.subarray(64, 96);
|
24
41
|
const idCommitment = memKeys.subarray(96, 128);
|
25
|
-
|
26
|
-
|
42
|
+
return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment);
|
43
|
+
}
|
44
|
+
/**
|
45
|
+
* Converts an ID commitment from bytes to a BigInt, normalizing it against a limit if needed
|
46
|
+
* @param bytes The ID commitment bytes to convert
|
47
|
+
* @param limit Optional limit to normalize against (Q value)
|
48
|
+
* @returns The ID commitment as a BigInt
|
49
|
+
*/
|
50
|
+
static getIdCommitmentBigInt(idCommitment, limit = DEFAULT_Q) {
|
51
|
+
let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(idCommitment);
|
52
|
+
if (limit && idCommitmentBigIntBE >= limit) {
|
53
|
+
log.warn(`ID commitment is greater than Q, reducing it by Q: ${idCommitmentBigIntBE} % ${limit}`);
|
54
|
+
idCommitmentBigIntBE = idCommitmentBigIntBE % limit;
|
55
|
+
}
|
56
|
+
return idCommitmentBigIntBE;
|
27
57
|
}
|
28
58
|
}
|
29
59
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import { cipherDecrypt as cipherDecrypt_1 } from '
|
2
|
-
import { kdf as kdf_2 } from '
|
3
|
-
import { normalizePassword as normalizePassword_1 } from '
|
1
|
+
import { cipherDecrypt as cipherDecrypt_1 } from '../../../../node_modules/@chainsafe/bls-keystore/lib/cipher.js';
|
2
|
+
import { kdf as kdf_2 } from '../../../../node_modules/@chainsafe/bls-keystore/lib/kdf.js';
|
3
|
+
import { normalizePassword as normalizePassword_1 } from '../../../../node_modules/@chainsafe/bls-keystore/lib/password.js';
|
4
4
|
import { keccak256 } from '../../../../node_modules/ethereum-cryptography/esm/keccak.js';
|
5
5
|
import { hexToBytes } from '../../../../node_modules/ethereum-cryptography/esm/utils.js';
|
6
6
|
import { concatBytes, bytesToHex } from '../../../../node_modules/@noble/hashes/esm/utils.js';
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import '
|
1
|
+
import '../../../../node_modules/@chainsafe/bls-keystore/lib/index.js';
|
2
2
|
import '../../../interfaces/dist/protocols.js';
|
3
3
|
import '../../../interfaces/dist/connection_manager.js';
|
4
4
|
import '../../../interfaces/dist/health_indicator.js';
|
@@ -17,7 +17,6 @@ import { Logger } from '../../../utils/dist/logger/index.js';
|
|
17
17
|
import { sha256 } from '../../../../node_modules/ethereum-cryptography/esm/sha256.js';
|
18
18
|
import { bytesToUtf8 } from '../../../../node_modules/ethereum-cryptography/esm/utils.js';
|
19
19
|
import _ from '../../../../node_modules/lodash/lodash.js';
|
20
|
-
import { buildBigIntFromUint8Array } from '../utils/bytes.js';
|
21
20
|
import { keccak256Checksum, decryptEipKeystore } from './cipher.js';
|
22
21
|
import { isKeystoreValid, isCredentialValid } from './schema_validator.js';
|
23
22
|
import { __exports as lib } from '../../../../_virtual/index.js';
|
@@ -164,14 +163,16 @@ class Keystore {
|
|
164
163
|
try {
|
165
164
|
const str = bytesToUtf8(bytes);
|
166
165
|
const obj = JSON.parse(str);
|
167
|
-
|
166
|
+
const idCommitmentLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idCommitment", []));
|
167
|
+
const idTrapdoorLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idTrapdoor", []));
|
168
|
+
const idNullifierLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idNullifier", []));
|
169
|
+
const idSecretHashLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idSecretHash", []));
|
168
170
|
return {
|
169
171
|
identity: {
|
170
|
-
IDCommitment:
|
171
|
-
IDTrapdoor:
|
172
|
-
IDNullifier:
|
173
|
-
|
174
|
-
IDSecretHash: Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idSecretHash", []))
|
172
|
+
IDCommitment: idCommitmentLE,
|
173
|
+
IDTrapdoor: idTrapdoorLE,
|
174
|
+
IDNullifier: idNullifierLE,
|
175
|
+
IDSecretHash: idSecretHashLE
|
175
176
|
},
|
176
177
|
membership: {
|
177
178
|
treeIndex: _.get(obj, "treeIndex"),
|
@@ -208,13 +209,16 @@ class Keystore {
|
|
208
209
|
// follows nwaku implementation
|
209
210
|
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L98
|
210
211
|
static fromIdentityToBytes(options) {
|
212
|
+
function toLittleEndian(bytes) {
|
213
|
+
return new Uint8Array(bytes).reverse();
|
214
|
+
}
|
211
215
|
return utf8ToBytes(JSON.stringify({
|
212
216
|
treeIndex: options.membership.treeIndex,
|
213
217
|
identityCredential: {
|
214
|
-
idCommitment: Array.from(options.identity.IDCommitment),
|
215
|
-
idNullifier: Array.from(options.identity.IDNullifier),
|
216
|
-
idSecretHash: Array.from(options.identity.IDSecretHash),
|
217
|
-
idTrapdoor: Array.from(options.identity.IDTrapdoor)
|
218
|
+
idCommitment: Array.from(toLittleEndian(options.identity.IDCommitment)),
|
219
|
+
idNullifier: Array.from(toLittleEndian(options.identity.IDNullifier)),
|
220
|
+
idSecretHash: Array.from(toLittleEndian(options.identity.IDSecretHash)),
|
221
|
+
idTrapdoor: Array.from(toLittleEndian(options.identity.IDTrapdoor))
|
218
222
|
},
|
219
223
|
membershipContract: {
|
220
224
|
chainId: options.membership.chainId,
|
@@ -1,4 +1,14 @@
|
|
1
|
+
import { Version } from '../../core/dist/lib/message/version_0.js';
|
2
|
+
import '../../core/dist/lib/filter/filter.js';
|
3
|
+
import '../../core/dist/lib/light_push/light_push.js';
|
4
|
+
import '../../core/dist/lib/store/store.js';
|
5
|
+
import '../../core/dist/lib/connection_manager/connection_manager.js';
|
6
|
+
import '../../interfaces/dist/protocols.js';
|
7
|
+
import '../../interfaces/dist/connection_manager.js';
|
8
|
+
import '../../interfaces/dist/health_indicator.js';
|
1
9
|
import { utf8ToBytes } from '../../utils/dist/bytes/index.js';
|
10
|
+
import '../../../node_modules/debug/src/browser.js';
|
11
|
+
import '../../core/dist/lib/metadata/metadata.js';
|
2
12
|
import { epochBytesToInt } from './utils/epoch.js';
|
3
13
|
|
4
14
|
function toRLNSignal(contentTopic, msg) {
|
@@ -10,6 +20,7 @@ class RlnMessage {
|
|
10
20
|
msg;
|
11
21
|
rateLimitProof;
|
12
22
|
pubsubTopic = "";
|
23
|
+
version = Version;
|
13
24
|
constructor(rlnInstance, msg, rateLimitProof) {
|
14
25
|
this.rlnInstance = rlnInstance;
|
15
26
|
this.msg = msg;
|
@@ -16,12 +16,12 @@ function concatenate(...input) {
|
|
16
16
|
}
|
17
17
|
return result;
|
18
18
|
}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
function switchEndianness(bytes) {
|
20
|
+
return new Uint8Array(bytes.reverse());
|
21
|
+
}
|
22
|
+
function buildBigIntFromUint8ArrayBE(bytes) {
|
23
|
+
// Interpret bytes as big-endian
|
24
|
+
return bytes.reduce((acc, byte) => (acc << 8n) + BigInt(byte), 0n);
|
25
25
|
}
|
26
26
|
function writeUIntLE(buf, value, offset, byteLength, noAssert) {
|
27
27
|
value = +value;
|
@@ -39,15 +39,6 @@ function writeUIntLE(buf, value, offset, byteLength, noAssert) {
|
|
39
39
|
}
|
40
40
|
return buf;
|
41
41
|
}
|
42
|
-
/**
|
43
|
-
* Transforms Uint8Array into BigInt
|
44
|
-
* @param array: Uint8Array
|
45
|
-
* @returns BigInt
|
46
|
-
*/
|
47
|
-
function buildBigIntFromUint8Array(array, byteOffset = 0) {
|
48
|
-
const dataView = new DataView(array.buffer);
|
49
|
-
return dataView.getBigUint64(byteOffset, true);
|
50
|
-
}
|
51
42
|
/**
|
52
43
|
* Fills with zeros to set length
|
53
44
|
* @param array little endian Uint8Array
|
@@ -61,5 +52,12 @@ function zeroPadLE(array, length) {
|
|
61
52
|
}
|
62
53
|
return result;
|
63
54
|
}
|
55
|
+
// Adapted from https://github.com/feross/buffer
|
56
|
+
function checkInt(buf, value, offset, ext, max, min) {
|
57
|
+
if (value > max || value < min)
|
58
|
+
throw new RangeError('"value" argument is out of bounds');
|
59
|
+
if (offset + ext > buf.length)
|
60
|
+
throw new RangeError("Index out of range");
|
61
|
+
}
|
64
62
|
|
65
|
-
export {
|
63
|
+
export { buildBigIntFromUint8ArrayBE, concatenate, switchEndianness, writeUIntLE, zeroPadLE };
|