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