@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
@@ -22,6 +22,8 @@ export class RLNBaseContract {
|
|
22
22
|
public contract: ethers.Contract;
|
23
23
|
private deployBlock: undefined | number;
|
24
24
|
private rateLimit: number;
|
25
|
+
private minRateLimit?: number;
|
26
|
+
private maxRateLimit?: number;
|
25
27
|
|
26
28
|
protected _members: Map<number, Member> = new Map();
|
27
29
|
private _membersFilter: ethers.EventFilter;
|
@@ -29,19 +31,9 @@ export class RLNBaseContract {
|
|
29
31
|
private _membersExpiredFilter: ethers.EventFilter;
|
30
32
|
|
31
33
|
/**
|
32
|
-
*
|
33
|
-
* Allows injecting a mocked contract for testing purposes.
|
34
|
+
* Private constructor for RLNBaseContract. Use static create() instead.
|
34
35
|
*/
|
35
|
-
|
36
|
-
// Initialize members and subscriptions
|
37
|
-
this.fetchMembers()
|
38
|
-
.then(() => {
|
39
|
-
this.subscribeToMembers();
|
40
|
-
})
|
41
|
-
.catch((error) => {
|
42
|
-
log.error("Failed to initialize members", { error });
|
43
|
-
});
|
44
|
-
|
36
|
+
protected constructor(options: RLNContractInitOptions) {
|
45
37
|
const {
|
46
38
|
address,
|
47
39
|
signer,
|
@@ -49,15 +41,52 @@ export class RLNBaseContract {
|
|
49
41
|
contract
|
50
42
|
} = options;
|
51
43
|
|
52
|
-
|
44
|
+
log.info("Initializing RLNBaseContract", { address, rateLimit });
|
53
45
|
|
54
46
|
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
55
47
|
this.rateLimit = rateLimit;
|
56
48
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
try {
|
50
|
+
log.info("Setting up event filters");
|
51
|
+
// Initialize event filters
|
52
|
+
this._membersFilter = this.contract.filters.MembershipRegistered();
|
53
|
+
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
54
|
+
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
55
|
+
log.info("Event filters initialized successfully");
|
56
|
+
} catch (error) {
|
57
|
+
log.error("Failed to initialize event filters", { error });
|
58
|
+
throw new Error(
|
59
|
+
"Failed to initialize event filters: " + (error as Error).message
|
60
|
+
);
|
61
|
+
}
|
62
|
+
|
63
|
+
// Initialize members and subscriptions
|
64
|
+
this.fetchMembers()
|
65
|
+
.then(() => {
|
66
|
+
this.subscribeToMembers();
|
67
|
+
})
|
68
|
+
.catch((error) => {
|
69
|
+
log.error("Failed to initialize members", { error });
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Static async factory to create and initialize RLNBaseContract
|
75
|
+
*/
|
76
|
+
public static async create(
|
77
|
+
options: RLNContractInitOptions
|
78
|
+
): Promise<RLNBaseContract> {
|
79
|
+
const instance = new RLNBaseContract(options);
|
80
|
+
const [min, max] = await Promise.all([
|
81
|
+
instance.contract.minMembershipRateLimit(),
|
82
|
+
instance.contract.maxMembershipRateLimit(),
|
83
|
+
instance.contract.Q()
|
84
|
+
]);
|
85
|
+
instance.minRateLimit = ethers.BigNumber.from(min).toNumber();
|
86
|
+
instance.maxRateLimit = ethers.BigNumber.from(max).toNumber();
|
87
|
+
|
88
|
+
instance.validateRateLimit(instance.rateLimit);
|
89
|
+
return instance;
|
61
90
|
}
|
62
91
|
|
63
92
|
/**
|
@@ -82,21 +111,21 @@ export class RLNBaseContract {
|
|
82
111
|
}
|
83
112
|
|
84
113
|
/**
|
85
|
-
* Gets the minimum allowed rate limit
|
86
|
-
* @returns Promise<number> The minimum rate limit in messages per epoch
|
114
|
+
* Gets the minimum allowed rate limit (cached)
|
87
115
|
*/
|
88
|
-
public
|
89
|
-
|
90
|
-
|
116
|
+
public getMinRateLimit(): number {
|
117
|
+
if (this.minRateLimit === undefined)
|
118
|
+
throw new Error("minRateLimit not initialized");
|
119
|
+
return this.minRateLimit;
|
91
120
|
}
|
92
121
|
|
93
122
|
/**
|
94
|
-
* Gets the maximum allowed rate limit
|
95
|
-
* @returns Promise<number> The maximum rate limit in messages per epoch
|
123
|
+
* Gets the maximum allowed rate limit (cached)
|
96
124
|
*/
|
97
|
-
public
|
98
|
-
|
99
|
-
|
125
|
+
public getMaxRateLimit(): number {
|
126
|
+
if (this.maxRateLimit === undefined)
|
127
|
+
throw new Error("maxRateLimit not initialized");
|
128
|
+
return this.maxRateLimit;
|
100
129
|
}
|
101
130
|
|
102
131
|
/**
|
@@ -133,7 +162,7 @@ export class RLNBaseContract {
|
|
133
162
|
* Updates the rate limit for future registrations
|
134
163
|
* @param newRateLimit The new rate limit to use
|
135
164
|
*/
|
136
|
-
public
|
165
|
+
public setRateLimit(newRateLimit: number): void {
|
137
166
|
this.validateRateLimit(newRateLimit);
|
138
167
|
this.rateLimit = newRateLimit;
|
139
168
|
}
|
@@ -324,7 +353,7 @@ export class RLNBaseContract {
|
|
324
353
|
this.contract.on(
|
325
354
|
this.membersFilter,
|
326
355
|
(
|
327
|
-
_idCommitment:
|
356
|
+
_idCommitment: bigint,
|
328
357
|
_membershipRateLimit: ethers.BigNumber,
|
329
358
|
_index: ethers.BigNumber,
|
330
359
|
event: ethers.Event
|
@@ -336,7 +365,7 @@ export class RLNBaseContract {
|
|
336
365
|
this.contract.on(
|
337
366
|
this.membershipErasedFilter,
|
338
367
|
(
|
339
|
-
_idCommitment:
|
368
|
+
_idCommitment: bigint,
|
340
369
|
_membershipRateLimit: ethers.BigNumber,
|
341
370
|
_index: ethers.BigNumber,
|
342
371
|
event: ethers.Event
|
@@ -348,7 +377,7 @@ export class RLNBaseContract {
|
|
348
377
|
this.contract.on(
|
349
378
|
this.membersExpiredFilter,
|
350
379
|
(
|
351
|
-
_idCommitment:
|
380
|
+
_idCommitment: bigint,
|
352
381
|
_membershipRateLimit: ethers.BigNumber,
|
353
382
|
_index: ethers.BigNumber,
|
354
383
|
event: ethers.Event
|
@@ -358,94 +387,89 @@ export class RLNBaseContract {
|
|
358
387
|
);
|
359
388
|
}
|
360
389
|
|
361
|
-
/**
|
362
|
-
* Helper method to get remaining messages in current epoch
|
363
|
-
* @param membershipId The ID of the membership to check
|
364
|
-
* @returns number of remaining messages allowed in current epoch
|
365
|
-
*/
|
366
|
-
public async getRemainingMessages(membershipId: number): Promise<number> {
|
367
|
-
try {
|
368
|
-
const [startTime, , rateLimit] =
|
369
|
-
await this.contract.getMembershipInfo(membershipId);
|
370
|
-
|
371
|
-
// Calculate current epoch
|
372
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
373
|
-
const epochsPassed = Math.floor(
|
374
|
-
(currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH
|
375
|
-
);
|
376
|
-
const currentEpochStart =
|
377
|
-
startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
|
378
|
-
|
379
|
-
// Get message count in current epoch using contract's function
|
380
|
-
const messageCount = await this.contract.getMessageCount(
|
381
|
-
membershipId,
|
382
|
-
currentEpochStart
|
383
|
-
);
|
384
|
-
return Math.max(
|
385
|
-
0,
|
386
|
-
ethers.BigNumber.from(rateLimit)
|
387
|
-
.sub(ethers.BigNumber.from(messageCount))
|
388
|
-
.toNumber()
|
389
|
-
);
|
390
|
-
} catch (error) {
|
391
|
-
log.error(
|
392
|
-
`Error getting remaining messages: ${(error as Error).message}`
|
393
|
-
);
|
394
|
-
return 0; // Fail safe: assume no messages remaining on error
|
395
|
-
}
|
396
|
-
}
|
397
|
-
|
398
390
|
public async getMembershipInfo(
|
399
|
-
|
391
|
+
idCommitmentBigInt: bigint
|
400
392
|
): Promise<MembershipInfo | undefined> {
|
401
393
|
try {
|
402
|
-
const
|
403
|
-
await this.contract.
|
394
|
+
const membershipData =
|
395
|
+
await this.contract.memberships(idCommitmentBigInt);
|
404
396
|
const currentBlock = await this.contract.provider.getBlockNumber();
|
397
|
+
const [
|
398
|
+
depositAmount,
|
399
|
+
activeDuration,
|
400
|
+
gracePeriodStartTimestamp,
|
401
|
+
gracePeriodDuration,
|
402
|
+
rateLimit,
|
403
|
+
index,
|
404
|
+
holder,
|
405
|
+
token
|
406
|
+
] = membershipData;
|
407
|
+
|
408
|
+
const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
|
405
409
|
|
406
410
|
let state: MembershipState;
|
407
|
-
if (currentBlock <
|
411
|
+
if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
|
408
412
|
state = MembershipState.Active;
|
409
|
-
} else if (currentBlock <
|
413
|
+
} else if (currentBlock < gracePeriodEnd.toNumber()) {
|
410
414
|
state = MembershipState.GracePeriod;
|
411
415
|
} else {
|
412
416
|
state = MembershipState.Expired;
|
413
417
|
}
|
414
418
|
|
415
|
-
const index = await this.getMemberIndex(idCommitment);
|
416
|
-
if (!index) return undefined;
|
417
|
-
|
418
419
|
return {
|
419
420
|
index,
|
420
|
-
idCommitment,
|
421
|
-
rateLimit: rateLimit
|
422
|
-
startBlock:
|
423
|
-
endBlock:
|
424
|
-
state
|
421
|
+
idCommitment: idCommitmentBigInt.toString(),
|
422
|
+
rateLimit: Number(rateLimit),
|
423
|
+
startBlock: gracePeriodStartTimestamp.toNumber(),
|
424
|
+
endBlock: gracePeriodEnd.toNumber(),
|
425
|
+
state,
|
426
|
+
depositAmount,
|
427
|
+
activeDuration,
|
428
|
+
gracePeriodDuration,
|
429
|
+
holder,
|
430
|
+
token
|
425
431
|
};
|
426
432
|
} catch (error) {
|
433
|
+
log.error("Error in getMembershipInfo:", error);
|
427
434
|
return undefined;
|
428
435
|
}
|
429
436
|
}
|
430
437
|
|
431
438
|
public async extendMembership(
|
432
|
-
|
439
|
+
idCommitmentBigInt: bigint
|
433
440
|
): Promise<ethers.ContractTransaction> {
|
434
|
-
|
441
|
+
const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
|
442
|
+
await tx.wait();
|
443
|
+
return tx;
|
435
444
|
}
|
436
445
|
|
437
446
|
public async eraseMembership(
|
438
|
-
|
447
|
+
idCommitmentBigInt: bigint,
|
439
448
|
eraseFromMembershipSet: boolean = true
|
440
449
|
): Promise<ethers.ContractTransaction> {
|
441
|
-
|
442
|
-
|
443
|
-
|
450
|
+
if (
|
451
|
+
!(await this.isExpired(idCommitmentBigInt)) ||
|
452
|
+
!(await this.isInGracePeriod(idCommitmentBigInt))
|
453
|
+
) {
|
454
|
+
throw new Error("Membership is not expired or in grace period");
|
455
|
+
}
|
456
|
+
|
457
|
+
const estimatedGas = await this.contract.estimateGas[
|
458
|
+
"eraseMemberships(uint256[],bool)"
|
459
|
+
]([idCommitmentBigInt], eraseFromMembershipSet);
|
460
|
+
const gasLimit = estimatedGas.add(10000);
|
461
|
+
|
462
|
+
const tx = await this.contract["eraseMemberships(uint256[],bool)"](
|
463
|
+
[idCommitmentBigInt],
|
464
|
+
eraseFromMembershipSet,
|
465
|
+
{ gasLimit }
|
444
466
|
);
|
467
|
+
await tx.wait();
|
468
|
+
return tx;
|
445
469
|
}
|
446
470
|
|
447
471
|
public async registerMembership(
|
448
|
-
|
472
|
+
idCommitmentBigInt: bigint,
|
449
473
|
rateLimit: number = DEFAULT_RATE_LIMIT
|
450
474
|
): Promise<ethers.ContractTransaction> {
|
451
475
|
if (
|
@@ -456,18 +480,17 @@ export class RLNBaseContract {
|
|
456
480
|
`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
|
457
481
|
);
|
458
482
|
}
|
459
|
-
return this.contract.register(
|
483
|
+
return this.contract.register(idCommitmentBigInt, rateLimit, []);
|
460
484
|
}
|
461
485
|
|
462
|
-
public async withdraw(token: string,
|
486
|
+
public async withdraw(token: string, walletAddress: string): Promise<void> {
|
463
487
|
try {
|
464
|
-
const tx = await this.contract.withdraw(token,
|
488
|
+
const tx = await this.contract.withdraw(token, walletAddress);
|
465
489
|
await tx.wait();
|
466
490
|
} catch (error) {
|
467
491
|
log.error(`Error in withdraw: ${(error as Error).message}`);
|
468
492
|
}
|
469
493
|
}
|
470
|
-
|
471
494
|
public async registerWithIdentity(
|
472
495
|
identity: IdentityCredential
|
473
496
|
): Promise<DecryptedCredentials | undefined> {
|
@@ -476,10 +499,12 @@ export class RLNBaseContract {
|
|
476
499
|
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
|
477
500
|
);
|
478
501
|
|
479
|
-
|
480
|
-
|
481
|
-
identity.IDCommitmentBigInt.toString()
|
502
|
+
const idCommitmentBigInt = IdentityCredential.getIdCommitmentBigInt(
|
503
|
+
identity.IDCommitment
|
482
504
|
);
|
505
|
+
|
506
|
+
// Check if the ID commitment is already registered
|
507
|
+
const existingIndex = await this.getMemberIndex(idCommitmentBigInt);
|
483
508
|
if (existingIndex) {
|
484
509
|
throw new Error(
|
485
510
|
`ID commitment is already registered with index ${existingIndex}`
|
@@ -495,19 +520,16 @@ export class RLNBaseContract {
|
|
495
520
|
}
|
496
521
|
|
497
522
|
const estimatedGas = await this.contract.estimateGas.register(
|
498
|
-
|
523
|
+
idCommitmentBigInt,
|
499
524
|
this.rateLimit,
|
500
525
|
[]
|
501
526
|
);
|
502
527
|
const gasLimit = estimatedGas.add(10000);
|
503
528
|
|
504
529
|
const txRegisterResponse: ethers.ContractTransaction =
|
505
|
-
await this.contract.register(
|
506
|
-
|
507
|
-
|
508
|
-
[],
|
509
|
-
{ gasLimit }
|
510
|
-
);
|
530
|
+
await this.contract.register(idCommitmentBigInt, this.rateLimit, [], {
|
531
|
+
gasLimit
|
532
|
+
});
|
511
533
|
|
512
534
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
513
535
|
|
@@ -516,7 +538,7 @@ export class RLNBaseContract {
|
|
516
538
|
}
|
517
539
|
|
518
540
|
const memberRegistered = txRegisterReceipt.events?.find(
|
519
|
-
(event) => event.event === "MembershipRegistered"
|
541
|
+
(event: ethers.Event) => event.event === "MembershipRegistered"
|
520
542
|
);
|
521
543
|
|
522
544
|
if (!memberRegistered || !memberRegistered.args) {
|
@@ -603,14 +625,14 @@ export class RLNBaseContract {
|
|
603
625
|
permit.v,
|
604
626
|
permit.r,
|
605
627
|
permit.s,
|
606
|
-
identity.
|
628
|
+
IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment),
|
607
629
|
this.rateLimit,
|
608
630
|
idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
|
609
631
|
);
|
610
632
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
611
633
|
|
612
634
|
const memberRegistered = txRegisterReceipt.events?.find(
|
613
|
-
(event) => event.event === "MembershipRegistered"
|
635
|
+
(event: ethers.Event) => event.event === "MembershipRegistered"
|
614
636
|
);
|
615
637
|
|
616
638
|
if (!memberRegistered || !memberRegistered.args) {
|
@@ -653,16 +675,16 @@ export class RLNBaseContract {
|
|
653
675
|
}
|
654
676
|
|
655
677
|
/**
|
656
|
-
* Validates that the rate limit is within the allowed range
|
678
|
+
* Validates that the rate limit is within the allowed range (sync)
|
657
679
|
* @throws Error if the rate limit is outside the allowed range
|
658
680
|
*/
|
659
681
|
private validateRateLimit(rateLimit: number): void {
|
660
|
-
if (
|
661
|
-
|
662
|
-
|
663
|
-
) {
|
682
|
+
if (this.minRateLimit === undefined || this.maxRateLimit === undefined) {
|
683
|
+
throw new Error("Rate limits not initialized");
|
684
|
+
}
|
685
|
+
if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) {
|
664
686
|
throw new Error(
|
665
|
-
`Rate limit must be between ${
|
687
|
+
`Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch`
|
666
688
|
);
|
667
689
|
}
|
668
690
|
}
|
@@ -689,11 +711,11 @@ export class RLNBaseContract {
|
|
689
711
|
}
|
690
712
|
|
691
713
|
private async getMemberIndex(
|
692
|
-
|
714
|
+
idCommitmentBigInt: bigint
|
693
715
|
): Promise<ethers.BigNumber | undefined> {
|
694
716
|
try {
|
695
717
|
const events = await this.contract.queryFilter(
|
696
|
-
this.contract.filters.MembershipRegistered(
|
718
|
+
this.contract.filters.MembershipRegistered(idCommitmentBigInt)
|
697
719
|
);
|
698
720
|
if (events.length === 0) return undefined;
|
699
721
|
|
@@ -704,4 +726,45 @@ export class RLNBaseContract {
|
|
704
726
|
return undefined;
|
705
727
|
}
|
706
728
|
}
|
729
|
+
|
730
|
+
public async getMembershipStatus(
|
731
|
+
idCommitment: bigint
|
732
|
+
): Promise<"expired" | "grace" | "active"> {
|
733
|
+
const [isExpired, isInGrace] = await Promise.all([
|
734
|
+
this.contract.isExpired(idCommitment),
|
735
|
+
this.contract.isInGracePeriod(idCommitment)
|
736
|
+
]);
|
737
|
+
|
738
|
+
if (isExpired) return "expired";
|
739
|
+
if (isInGrace) return "grace";
|
740
|
+
return "active";
|
741
|
+
}
|
742
|
+
|
743
|
+
/**
|
744
|
+
* Checks if a membership is expired for the given idCommitment
|
745
|
+
* @param idCommitmentBigInt The idCommitment as bigint
|
746
|
+
* @returns Promise<boolean> True if expired, false otherwise
|
747
|
+
*/
|
748
|
+
public async isExpired(idCommitmentBigInt: bigint): Promise<boolean> {
|
749
|
+
try {
|
750
|
+
return await this.contract.isExpired(idCommitmentBigInt);
|
751
|
+
} catch (error) {
|
752
|
+
log.error("Error in isExpired:", error);
|
753
|
+
return false;
|
754
|
+
}
|
755
|
+
}
|
756
|
+
|
757
|
+
/**
|
758
|
+
* Checks if a membership is in grace period for the given idCommitment
|
759
|
+
* @param idCommitmentBigInt The idCommitment as bigint
|
760
|
+
* @returns Promise<boolean> True if in grace period, false otherwise
|
761
|
+
*/
|
762
|
+
public async isInGracePeriod(idCommitmentBigInt: bigint): Promise<boolean> {
|
763
|
+
try {
|
764
|
+
return await this.contract.isInGracePeriod(idCommitmentBigInt);
|
765
|
+
} catch (error) {
|
766
|
+
log.error("Error in isInGracePeriod:", error);
|
767
|
+
return false;
|
768
|
+
}
|
769
|
+
}
|
707
770
|
}
|
package/src/contract/types.ts
CHANGED
@@ -38,6 +38,11 @@ export interface MembershipInfo {
|
|
38
38
|
startBlock: number;
|
39
39
|
endBlock: number;
|
40
40
|
state: MembershipState;
|
41
|
+
depositAmount: ethers.BigNumber;
|
42
|
+
activeDuration: number;
|
43
|
+
gracePeriodDuration: number;
|
44
|
+
holder: string;
|
45
|
+
token: string;
|
41
46
|
}
|
42
47
|
|
43
48
|
export enum MembershipState {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { hmac } from "@noble/hashes/hmac";
|
2
|
-
import { sha256 } from "@noble/hashes/
|
2
|
+
import { sha256 } from "@noble/hashes/sha2";
|
3
3
|
import { Logger } from "@waku/utils";
|
4
4
|
import { ethers } from "ethers";
|
5
5
|
|
@@ -13,10 +13,7 @@ import type {
|
|
13
13
|
} from "./keystore/index.js";
|
14
14
|
import { KeystoreEntity, Password } from "./keystore/types.js";
|
15
15
|
import { RegisterMembershipOptions, StartRLNOptions } from "./types.js";
|
16
|
-
import {
|
17
|
-
buildBigIntFromUint8Array,
|
18
|
-
extractMetaMaskSigner
|
19
|
-
} from "./utils/index.js";
|
16
|
+
import { extractMetaMaskSigner, switchEndianness } from "./utils/index.js";
|
20
17
|
import { Zerokit } from "./zerokit.js";
|
21
18
|
|
22
19
|
const log = new Logger("waku:credentials");
|
@@ -80,7 +77,7 @@ export class RLNCredentialsManager {
|
|
80
77
|
|
81
78
|
this.credentials = credentials;
|
82
79
|
this.signer = signer!;
|
83
|
-
this.contract =
|
80
|
+
this.contract = await RLNBaseContract.create({
|
84
81
|
address: address!,
|
85
82
|
signer: signer!,
|
86
83
|
rateLimit: rateLimit ?? this.zerokit?.rateLimit
|
@@ -116,7 +113,9 @@ export class RLNCredentialsManager {
|
|
116
113
|
);
|
117
114
|
} else {
|
118
115
|
log.info("Using local implementation to generate identity");
|
119
|
-
identity = this.generateSeededIdentityCredential(
|
116
|
+
identity = await this.generateSeededIdentityCredential(
|
117
|
+
options.signature
|
118
|
+
);
|
120
119
|
}
|
121
120
|
}
|
122
121
|
|
@@ -249,7 +248,9 @@ export class RLNCredentialsManager {
|
|
249
248
|
* @param seed A string seed to generate the identity from
|
250
249
|
* @returns IdentityCredential
|
251
250
|
*/
|
252
|
-
private generateSeededIdentityCredential(
|
251
|
+
private async generateSeededIdentityCredential(
|
252
|
+
seed: string
|
253
|
+
): Promise<IdentityCredential> {
|
253
254
|
log.info("Generating seeded identity credential");
|
254
255
|
// Convert the seed to bytes
|
255
256
|
const encoder = new TextEncoder();
|
@@ -257,26 +258,31 @@ export class RLNCredentialsManager {
|
|
257
258
|
|
258
259
|
// Generate deterministic values using HMAC-SHA256
|
259
260
|
// We use different context strings for each component to ensure they're different
|
260
|
-
const
|
261
|
-
const
|
261
|
+
const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
|
262
|
+
const idNullifierBE = hmac(
|
263
|
+
sha256,
|
264
|
+
seedBytes,
|
265
|
+
encoder.encode("IDNullifier")
|
266
|
+
);
|
262
267
|
|
263
|
-
|
264
|
-
const
|
265
|
-
const idSecretHash = sha256(combinedBytes);
|
268
|
+
const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
|
269
|
+
const idSecretHashBE = sha256(combinedBytes);
|
266
270
|
|
267
|
-
|
268
|
-
const idCommitment = sha256(idSecretHash);
|
271
|
+
const idCommitmentBE = sha256(idSecretHashBE);
|
269
272
|
|
270
|
-
//
|
271
|
-
|
273
|
+
// All hashing functions return big-endian bytes
|
274
|
+
// We need to switch to little-endian for the identity credential
|
275
|
+
const idTrapdoorLE = switchEndianness(idTrapdoorBE);
|
276
|
+
const idNullifierLE = switchEndianness(idNullifierBE);
|
277
|
+
const idSecretHashLE = switchEndianness(idSecretHashBE);
|
278
|
+
const idCommitmentLE = switchEndianness(idCommitmentBE);
|
272
279
|
|
273
280
|
log.info("Successfully generated identity credential");
|
274
281
|
return new IdentityCredential(
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
idCommitmentBigInt
|
282
|
+
idTrapdoorLE,
|
283
|
+
idNullifierLE,
|
284
|
+
idSecretHashLE,
|
285
|
+
idCommitmentLE
|
280
286
|
);
|
281
287
|
}
|
282
288
|
}
|
package/src/identity.ts
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
-
import {
|
1
|
+
import { Logger } from "@waku/utils";
|
2
|
+
|
3
|
+
import { DEFAULT_Q } from "./contract/constants.js";
|
4
|
+
import { buildBigIntFromUint8ArrayBE } from "./utils/bytes.js";
|
5
|
+
|
6
|
+
const log = new Logger("waku:rln:identity");
|
2
7
|
|
3
8
|
export class IdentityCredential {
|
9
|
+
/**
|
10
|
+
* All variables are in little-endian format
|
11
|
+
*/
|
4
12
|
public constructor(
|
5
13
|
public readonly IDTrapdoor: Uint8Array,
|
6
14
|
public readonly IDNullifier: Uint8Array,
|
7
15
|
public readonly IDSecretHash: Uint8Array,
|
8
|
-
public readonly IDCommitment: Uint8Array
|
9
|
-
public readonly IDCommitmentBigInt: bigint
|
16
|
+
public readonly IDCommitment: Uint8Array
|
10
17
|
) {}
|
11
18
|
|
12
19
|
public static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
@@ -18,14 +25,33 @@ export class IdentityCredential {
|
|
18
25
|
const idNullifier = memKeys.subarray(32, 64);
|
19
26
|
const idSecretHash = memKeys.subarray(64, 96);
|
20
27
|
const idCommitment = memKeys.subarray(96, 128);
|
21
|
-
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
|
22
28
|
|
23
29
|
return new IdentityCredential(
|
24
30
|
idTrapdoor,
|
25
31
|
idNullifier,
|
26
32
|
idSecretHash,
|
27
|
-
idCommitment
|
28
|
-
idCommitmentBigInt
|
33
|
+
idCommitment
|
29
34
|
);
|
30
35
|
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Converts an ID commitment from bytes to a BigInt, normalizing it against a limit if needed
|
39
|
+
* @param bytes The ID commitment bytes to convert
|
40
|
+
* @param limit Optional limit to normalize against (Q value)
|
41
|
+
* @returns The ID commitment as a BigInt
|
42
|
+
*/
|
43
|
+
public static getIdCommitmentBigInt(
|
44
|
+
idCommitment: Uint8Array,
|
45
|
+
limit: bigint = DEFAULT_Q
|
46
|
+
): bigint {
|
47
|
+
let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(idCommitment);
|
48
|
+
|
49
|
+
if (limit && idCommitmentBigIntBE >= limit) {
|
50
|
+
log.warn(
|
51
|
+
`ID commitment is greater than Q, reducing it by Q: ${idCommitmentBigIntBE} % ${limit}`
|
52
|
+
);
|
53
|
+
idCommitmentBigIntBE = idCommitmentBigIntBE % limit;
|
54
|
+
}
|
55
|
+
return idCommitmentBigIntBE;
|
56
|
+
}
|
31
57
|
}
|