@waku/rln 0.1.4-d27db21.0 → 0.1.5-053bb95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +5 -0
  3. package/bundle/_virtual/utils.js +2 -2
  4. package/bundle/_virtual/utils2.js +2 -2
  5. package/bundle/index.js +3 -1
  6. package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/abstract-coder.js +1 -1
  7. package/bundle/node_modules/@ethersproject/abi/lib.esm/interface.js +1 -1
  8. package/bundle/node_modules/@ethersproject/abstract-provider/lib.esm/index.js +1 -1
  9. package/bundle/node_modules/@ethersproject/abstract-signer/lib.esm/index.js +11 -11
  10. package/bundle/node_modules/@ethersproject/bytes/lib.esm/_version.js +1 -1
  11. package/bundle/node_modules/@ethersproject/contracts/lib.esm/index.js +7 -7
  12. package/bundle/node_modules/@ethersproject/hash/lib.esm/ens-normalize/lib.js +2 -2
  13. package/bundle/node_modules/@ethersproject/hash/lib.esm/typed-data.js +2 -2
  14. package/bundle/node_modules/@ethersproject/keccak256/node_modules/js-sha3/src/sha3.js +1 -1
  15. package/bundle/node_modules/@ethersproject/logger/lib.esm/_version.js +1 -1
  16. package/bundle/node_modules/@ethersproject/properties/lib.esm/index.js +1 -1
  17. package/bundle/node_modules/@ethersproject/providers/lib.esm/base-provider.js +44 -44
  18. package/bundle/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js +8 -8
  19. package/bundle/node_modules/@ethersproject/rlp/lib.esm/_version.js +1 -1
  20. package/bundle/node_modules/@ethersproject/transactions/lib.esm/index.js +1 -1
  21. package/bundle/node_modules/@ethersproject/web/lib.esm/geturl.js +1 -1
  22. package/bundle/node_modules/@ethersproject/web/lib.esm/index.js +2 -2
  23. package/bundle/node_modules/@multiformats/multiaddr/dist/src/multiaddr.js +1 -0
  24. package/bundle/node_modules/@noble/hashes/esm/sha3.js +1 -1
  25. package/bundle/node_modules/bn.js/lib/bn.js +1 -1
  26. package/bundle/packages/core/dist/lib/connection_manager/connection_manager.js +6 -6
  27. package/bundle/packages/rln/dist/contract/abi.js +502 -248
  28. package/bundle/packages/rln/dist/contract/constants.js +5 -9
  29. package/bundle/packages/rln/dist/contract/errors.js +62 -0
  30. package/bundle/packages/rln/dist/contract/rln_base_contract.js +347 -0
  31. package/bundle/packages/rln/dist/contract/rln_contract.js +81 -392
  32. package/bundle/packages/rln/dist/contract/types.js +9 -0
  33. package/bundle/packages/rln/dist/create.js +1 -1
  34. package/bundle/packages/rln/dist/credentials_manager.js +215 -0
  35. package/bundle/packages/rln/dist/identity.js +8 -0
  36. package/bundle/packages/rln/dist/keystore/keystore.js +20 -28
  37. package/bundle/packages/rln/dist/rln.js +56 -166
  38. package/bundle/packages/rln/dist/zerokit.js +5 -5
  39. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/checksum.js +2 -2
  40. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/cipher.js +3 -3
  41. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/class.js +4 -4
  42. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/functional.js +4 -4
  43. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/kdf.js +4 -4
  44. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/password.js +1 -1
  45. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/schema-validation-generated.js +1 -1
  46. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/schema-validation.js +1 -1
  47. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/aes.js +1 -1
  48. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/pbkdf2.js +1 -1
  49. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +2 -2
  50. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/scrypt.js +1 -1
  51. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/sha256.js +1 -1
  52. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +3 -3
  53. package/bundle/packages/rln/node_modules/@noble/hashes/_assert.js +1 -1
  54. package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +2 -2
  55. package/bundle/packages/rln/node_modules/@noble/hashes/_u64.js +1 -1
  56. package/bundle/packages/rln/node_modules/@noble/hashes/cryptoBrowser.js +1 -1
  57. package/bundle/packages/rln/node_modules/@noble/hashes/esm/_assert.js +43 -0
  58. package/bundle/packages/rln/node_modules/@noble/hashes/esm/_sha2.js +116 -0
  59. package/bundle/packages/rln/node_modules/@noble/hashes/esm/hmac.js +79 -0
  60. package/bundle/packages/rln/node_modules/@noble/hashes/esm/sha256.js +126 -0
  61. package/bundle/packages/rln/node_modules/@noble/hashes/esm/utils.js +43 -0
  62. package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +2 -2
  63. package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +2 -2
  64. package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +3 -3
  65. package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +2 -2
  66. package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +3 -3
  67. package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +2 -2
  68. package/dist/.tsbuildinfo +1 -1
  69. package/dist/codec.test-utils.d.ts +36 -0
  70. package/dist/codec.test-utils.js +56 -0
  71. package/dist/codec.test-utils.js.map +1 -0
  72. package/dist/contract/abi.d.ts +21 -17
  73. package/dist/contract/abi.js +502 -248
  74. package/dist/contract/abi.js.map +1 -1
  75. package/dist/contract/constants.d.ts +23 -19
  76. package/dist/contract/constants.js +3 -3
  77. package/dist/contract/constants.js.map +1 -1
  78. package/dist/contract/errors.d.ts +30 -0
  79. package/dist/contract/errors.js +61 -0
  80. package/dist/contract/errors.js.map +1 -0
  81. package/dist/contract/rln_base_contract.d.ts +88 -0
  82. package/dist/contract/rln_base_contract.js +330 -0
  83. package/dist/contract/rln_base_contract.js.map +1 -0
  84. package/dist/contract/rln_contract.d.ts +19 -109
  85. package/dist/contract/rln_contract.js +80 -390
  86. package/dist/contract/rln_contract.js.map +1 -1
  87. package/dist/contract/test-setup.d.ts +26 -0
  88. package/dist/contract/test-setup.js +56 -0
  89. package/dist/contract/test-setup.js.map +1 -0
  90. package/dist/contract/test-utils.d.ts +39 -0
  91. package/dist/contract/test-utils.js +118 -0
  92. package/dist/contract/test-utils.js.map +1 -0
  93. package/dist/contract/types.d.ts +40 -0
  94. package/dist/contract/types.js +8 -0
  95. package/dist/contract/types.js.map +1 -0
  96. package/dist/create.js +1 -1
  97. package/dist/create.js.map +1 -1
  98. package/dist/credentials_manager.d.ts +44 -0
  99. package/dist/credentials_manager.js +197 -0
  100. package/dist/credentials_manager.js.map +1 -0
  101. package/dist/identity.d.ts +1 -0
  102. package/dist/identity.js +8 -0
  103. package/dist/identity.js.map +1 -1
  104. package/dist/index.d.ts +5 -2
  105. package/dist/index.js +4 -2
  106. package/dist/index.js.map +1 -1
  107. package/dist/keystore/keystore.d.ts +0 -1
  108. package/dist/keystore/keystore.js +20 -28
  109. package/dist/keystore/keystore.js.map +1 -1
  110. package/dist/keystore/types.d.ts +2 -1
  111. package/dist/rln.d.ts +9 -52
  112. package/dist/rln.js +54 -163
  113. package/dist/rln.js.map +1 -1
  114. package/dist/types.d.ts +27 -0
  115. package/dist/types.js +2 -0
  116. package/dist/types.js.map +1 -0
  117. package/dist/zerokit.d.ts +3 -3
  118. package/dist/zerokit.js +5 -5
  119. package/dist/zerokit.js.map +1 -1
  120. package/package.json +1 -1
  121. package/src/codec.test-utils.ts +80 -0
  122. package/src/contract/abi.ts +502 -248
  123. package/src/contract/constants.ts +3 -3
  124. package/src/contract/errors.ts +75 -0
  125. package/src/contract/rln_base_contract.ts +500 -0
  126. package/src/contract/rln_contract.ts +102 -619
  127. package/src/contract/test-setup.ts +86 -0
  128. package/src/contract/test-utils.ts +179 -0
  129. package/src/contract/types.ts +48 -0
  130. package/src/create.ts +1 -1
  131. package/src/credentials_manager.ts +282 -0
  132. package/src/identity.ts +9 -0
  133. package/src/index.ts +17 -2
  134. package/src/keystore/keystore.ts +32 -46
  135. package/src/keystore/types.ts +2 -1
  136. package/src/rln.ts +67 -258
  137. package/src/types.ts +31 -0
  138. package/src/zerokit.ts +3 -3
@@ -5,434 +5,123 @@ import { hexToBytes } from '../../../utils/dist/bytes/index.js';
5
5
  import { Logger } from '../../../utils/dist/logger/index.js';
6
6
  import { MerkleRootTracker } from '../root_tracker.js';
7
7
  import { zeroPadLE } from '../utils/bytes.js';
8
- import { RLN_ABI } from './abi.js';
9
- import { RATE_LIMIT_PARAMS, DEFAULT_RATE_LIMIT } from './constants.js';
10
- import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
11
- import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
8
+ import { ContractStateError } from './errors.js';
9
+ import { RLNBaseContract } from './rln_base_contract.js';
12
10
 
13
11
  const log = new Logger("waku:rln:contract");
14
- var MembershipState;
15
- (function (MembershipState) {
16
- MembershipState["Active"] = "Active";
17
- MembershipState["GracePeriod"] = "GracePeriod";
18
- MembershipState["Expired"] = "Expired";
19
- MembershipState["ErasedAwaitsWithdrawal"] = "ErasedAwaitsWithdrawal";
20
- })(MembershipState || (MembershipState = {}));
21
- class RLNContract {
22
- contract;
12
+ class RLNContract extends RLNBaseContract {
13
+ instance;
23
14
  merkleRootTracker;
24
- deployBlock;
25
- rateLimit;
26
- _members = new Map();
27
- _membersFilter;
28
- _membersRemovedFilter;
15
+ lastSyncedBlock = 0;
29
16
  /**
30
17
  * Asynchronous initializer for RLNContract.
31
18
  * Allows injecting a mocked contract for testing purposes.
32
19
  */
33
20
  static async init(rlnInstance, options) {
34
21
  const rlnContract = new RLNContract(rlnInstance, options);
35
- await rlnContract.fetchMembers(rlnInstance);
36
- rlnContract.subscribeToMembers(rlnInstance);
22
+ await rlnContract.syncState();
37
23
  return rlnContract;
38
24
  }
39
- constructor(rlnInstance, options) {
40
- const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
41
- if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
42
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
43
- throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`);
44
- }
45
- this.rateLimit = rateLimit;
46
- const initialRoot = rlnInstance.zerokit.getMerkleRoot();
47
- // Use the injected contract if provided; otherwise, instantiate a new one.
48
- this.contract = contract || new Contract(address, RLN_ABI, signer);
49
- this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
50
- // Initialize event filters for MembershipRegistered and MembershipRemoved
51
- this._membersFilter = this.contract.filters.MembershipRegistered();
52
- this._membersRemovedFilter = this.contract.filters.MembershipRemoved();
53
- }
54
- /**
55
- * Gets the current rate limit for this contract instance
56
- */
57
- getRateLimit() {
58
- return this.rateLimit;
59
- }
60
- /**
61
- * Gets the contract address
62
- */
63
- get address() {
64
- return this.contract.address;
65
- }
66
25
  /**
67
- * Gets the contract provider
26
+ * Override base contract method to keep Merkle tree in sync
27
+ * Registers a new membership with the given commitment and rate limit
68
28
  */
69
- get provider() {
70
- return this.contract.provider;
29
+ async registerMembership(idCommitment, rateLimit = this.getRateLimit()) {
30
+ await super.registerMembership(idCommitment, rateLimit);
31
+ await this.syncState();
71
32
  }
72
33
  /**
73
- * Gets the minimum allowed rate limit from the contract
74
- * @returns Promise<number> The minimum rate limit in messages per epoch
34
+ * Override base contract method to keep Merkle tree in sync
35
+ * Erases an existing membership from the contract
75
36
  */
76
- async getMinRateLimit() {
77
- const minRate = await this.contract.minMembershipRateLimit();
78
- return minRate.toNumber();
79
- }
80
- /**
81
- * Gets the maximum allowed rate limit from the contract
82
- * @returns Promise<number> The maximum rate limit in messages per epoch
83
- */
84
- async getMaxRateLimit() {
85
- const maxRate = await this.contract.maxMembershipRateLimit();
86
- return maxRate.toNumber();
87
- }
88
- /**
89
- * Gets the maximum total rate limit across all memberships
90
- * @returns Promise<number> The maximum total rate limit in messages per epoch
91
- */
92
- async getMaxTotalRateLimit() {
93
- const maxTotalRate = await this.contract.maxTotalRateLimit();
94
- return maxTotalRate.toNumber();
37
+ async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
38
+ await super.eraseMembership(idCommitment, eraseFromMembershipSet);
39
+ await this.syncState();
95
40
  }
96
41
  /**
97
- * Gets the current total rate limit usage across all memberships
98
- * @returns Promise<number> The current total rate limit usage in messages per epoch
42
+ * Gets the current Merkle root
43
+ * Returns the latest valid root or empty array if no roots exist
99
44
  */
100
- async getCurrentTotalRateLimit() {
101
- const currentTotal = await this.contract.currentTotalRateLimit();
102
- return currentTotal.toNumber();
45
+ async getMerkleRoot() {
46
+ await this.syncState();
47
+ const roots = this.merkleRootTracker.roots();
48
+ return roots.length > 0 ? roots[0] : new Uint8Array();
103
49
  }
104
- /**
105
- * Gets the remaining available total rate limit that can be allocated
106
- * @returns Promise<number> The remaining rate limit that can be allocated
107
- */
108
- async getRemainingTotalRateLimit() {
109
- const [maxTotal, currentTotal] = await Promise.all([
110
- this.contract.maxTotalRateLimit(),
111
- this.contract.currentTotalRateLimit()
112
- ]);
113
- return maxTotal.sub(currentTotal).toNumber();
50
+ constructor(rlnInstance, options) {
51
+ super(options);
52
+ this.instance = rlnInstance;
53
+ const initialRoot = rlnInstance.zerokit.getMerkleRoot();
54
+ this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
114
55
  }
115
56
  /**
116
- * Updates the rate limit for future registrations
117
- * @param newRateLimit The new rate limit to use
57
+ * Syncs the local Merkle tree with the current contract state
118
58
  */
119
- async setRateLimit(newRateLimit) {
120
- this.rateLimit = newRateLimit;
121
- }
122
- get members() {
123
- const sortedMembers = Array.from(this._members.values()).sort((left, right) => left.index.toNumber() - right.index.toNumber());
124
- return sortedMembers;
125
- }
126
- get membersFilter() {
127
- if (!this._membersFilter) {
128
- throw Error("Members filter was not initialized.");
129
- }
130
- return this._membersFilter;
131
- }
132
- get membersRemovedFilter() {
133
- if (!this._membersRemovedFilter) {
134
- throw Error("MembersRemoved filter was not initialized.");
135
- }
136
- return this._membersRemovedFilter;
137
- }
138
- async fetchMembers(rlnInstance, options = {}) {
139
- const registeredMemberEvents = await queryFilter(this.contract, {
140
- fromBlock: this.deployBlock,
141
- ...options,
142
- membersFilter: this.membersFilter
143
- });
144
- const removedMemberEvents = await queryFilter(this.contract, {
145
- fromBlock: this.deployBlock,
146
- ...options,
147
- membersFilter: this.membersRemovedFilter
148
- });
149
- const events = [...registeredMemberEvents, ...removedMemberEvents];
150
- this.processEvents(rlnInstance, events);
151
- }
152
- processEvents(rlnInstance, events) {
153
- const toRemoveTable = new Map();
154
- const toInsertTable = new Map();
155
- events.forEach((evt) => {
156
- if (!evt.args) {
59
+ async syncState() {
60
+ try {
61
+ const currentBlock = await this.provider.getBlockNumber();
62
+ // If we're already synced, just get new members
63
+ if (this.lastSyncedBlock > 0) {
64
+ await this.syncNewMembers(this.lastSyncedBlock, currentBlock);
65
+ this.lastSyncedBlock = currentBlock;
157
66
  return;
158
67
  }
159
- if (evt.event === "MembershipRemoved") {
160
- const index = evt.args.index;
161
- const toRemoveVal = toRemoveTable.get(evt.blockNumber);
162
- if (toRemoveVal != undefined) {
163
- toRemoveVal.push(index.toNumber());
164
- toRemoveTable.set(evt.blockNumber, toRemoveVal);
68
+ // First time sync - get all members
69
+ const nextIndex = await this.contract.nextFreeIndex();
70
+ const members = await this.getMembersInRange(0, nextIndex.toNumber());
71
+ // Clear existing members by deleting them one by one
72
+ // This effectively resets the tree without needing resetTree()
73
+ for (let i = 0; i < nextIndex.toNumber(); i++) {
74
+ try {
75
+ this.instance.zerokit.deleteMember(i);
165
76
  }
166
- else {
167
- toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
77
+ catch (error) {
78
+ // Ignore errors for non-existent members
79
+ continue;
168
80
  }
169
81
  }
170
- else if (evt.event === "MembershipRegistered") {
171
- let eventsPerBlock = toInsertTable.get(evt.blockNumber);
172
- if (eventsPerBlock == undefined) {
173
- eventsPerBlock = [];
174
- }
175
- eventsPerBlock.push(evt);
176
- toInsertTable.set(evt.blockNumber, eventsPerBlock);
177
- }
178
- });
179
- this.removeMembers(rlnInstance, toRemoveTable);
180
- this.insertMembers(rlnInstance, toInsertTable);
181
- }
182
- insertMembers(rlnInstance, toInsert) {
183
- toInsert.forEach((events, blockNumber) => {
184
- events.forEach((evt) => {
185
- if (!evt.args)
186
- return;
187
- const _idCommitment = evt.args.idCommitment;
188
- const index = evt.args.index;
189
- if (!_idCommitment || !index) {
190
- return;
191
- }
192
- const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
193
- rlnInstance.zerokit.insertMember(idCommitment);
194
- this._members.set(index.toNumber(), {
195
- index,
196
- idCommitment: _idCommitment
197
- });
198
- });
199
- const currentRoot = rlnInstance.zerokit.getMerkleRoot();
200
- this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
201
- });
202
- }
203
- removeMembers(rlnInstance, toRemove) {
204
- const removeDescending = new Map([...toRemove].reverse());
205
- removeDescending.forEach((indexes, blockNumber) => {
206
- indexes.forEach((index) => {
207
- if (this._members.has(index)) {
208
- this._members.delete(index);
209
- rlnInstance.zerokit.deleteMember(index);
210
- }
211
- });
212
- this.merkleRootTracker.backFill(blockNumber);
213
- });
214
- }
215
- subscribeToMembers(rlnInstance) {
216
- this.contract.on(this.membersFilter, (_idCommitment, _rateLimit, _index, event) => {
217
- this.processEvents(rlnInstance, [event]);
218
- });
219
- this.contract.on(this.membersRemovedFilter, (_idCommitment, _index, event) => {
220
- this.processEvents(rlnInstance, [event]);
221
- });
222
- }
223
- async registerWithIdentity(identity) {
224
- try {
225
- log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
226
- const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit: 300000 });
227
- const txRegisterReceipt = await txRegisterResponse.wait();
228
- const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
229
- if (!memberRegistered || !memberRegistered.args) {
230
- log.error("Failed to register membership: No MembershipRegistered event found");
231
- return undefined;
82
+ // Insert all members
83
+ for (const member of members) {
84
+ const idCommitment = zeroPadLE(hexToBytes(member.idCommitment), 32);
85
+ this.instance.zerokit.insertMember(idCommitment);
232
86
  }
233
- const decodedData = {
234
- idCommitment: memberRegistered.args.idCommitment,
235
- rateLimit: memberRegistered.args.rateLimit,
236
- index: memberRegistered.args.index
237
- };
238
- log.info(`Successfully registered membership with index ${decodedData.index} ` +
239
- `and rate limit ${decodedData.rateLimit}`);
240
- const network = await this.contract.provider.getNetwork();
241
- const address = this.contract.address;
242
- const membershipId = decodedData.index.toNumber();
243
- return {
244
- identity,
245
- membership: {
246
- address,
247
- treeIndex: membershipId,
248
- chainId: network.chainId
249
- }
250
- };
87
+ // Update root tracker
88
+ const currentRoot = this.instance.zerokit.getMerkleRoot();
89
+ this.merkleRootTracker.pushRoot(currentBlock, currentRoot);
90
+ this.lastSyncedBlock = currentBlock;
91
+ log.info(`Synced ${members.length} members to current block ${currentBlock}`);
251
92
  }
252
93
  catch (error) {
253
- log.error(`Error in registerWithIdentity: ${error.message}`);
254
- return undefined;
94
+ log.error("Failed to sync state", error);
95
+ throw new ContractStateError("Failed to sync contract state");
255
96
  }
256
97
  }
257
98
  /**
258
- * Helper method to get remaining messages in current epoch
259
- * @param membershipId The ID of the membership to check
260
- * @returns number of remaining messages allowed in current epoch
99
+ * Syncs new members added between fromBlock and toBlock
261
100
  */
262
- async getRemainingMessages(membershipId) {
263
- try {
264
- const [startTime, , rateLimit] = await this.contract.getMembershipInfo(membershipId);
265
- // Calculate current epoch
266
- const currentTime = Math.floor(Date.now() / 1000);
267
- const epochsPassed = Math.floor((currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH);
268
- const currentEpochStart = startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
269
- // Get message count in current epoch using contract's function
270
- const messageCount = await this.contract.getMessageCount(membershipId, currentEpochStart);
271
- return Math.max(0, rateLimit.sub(messageCount).toNumber());
272
- }
273
- catch (error) {
274
- log.error(`Error getting remaining messages: ${error.message}`);
275
- return 0; // Fail safe: assume no messages remaining on error
276
- }
277
- }
278
- async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
279
- try {
280
- log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
281
- const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, identity.IDCommitmentBigInt, this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
282
- const txRegisterReceipt = await txRegisterResponse.wait();
283
- const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
284
- if (!memberRegistered || !memberRegistered.args) {
285
- log.error("Failed to register membership with permit: No MembershipRegistered event found");
286
- return undefined;
287
- }
288
- const decodedData = {
289
- idCommitment: memberRegistered.args.idCommitment,
290
- rateLimit: memberRegistered.args.rateLimit,
291
- index: memberRegistered.args.index
292
- };
293
- log.info(`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
294
- `Rate limit: ${decodedData.rateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
295
- const network = await this.contract.provider.getNetwork();
296
- const address = this.contract.address;
297
- const membershipId = decodedData.index.toNumber();
298
- return {
299
- identity,
300
- membership: {
301
- address,
302
- treeIndex: membershipId,
303
- chainId: network.chainId
304
- }
305
- };
306
- }
307
- catch (error) {
308
- log.error(`Error in registerWithPermitAndErase: ${error.message}`);
309
- return undefined;
310
- }
311
- }
312
- roots() {
313
- return this.merkleRootTracker.roots();
314
- }
315
- async withdraw(token, holder) {
316
- try {
317
- const tx = await this.contract.withdraw(token, { from: holder });
318
- await tx.wait();
319
- }
320
- catch (error) {
321
- log.error(`Error in withdraw: ${error.message}`);
322
- }
323
- }
324
- async getMembershipInfo(idCommitment) {
325
- try {
326
- const [startBlock, endBlock, rateLimit] = await this.contract.getMembershipInfo(idCommitment);
327
- const currentBlock = await this.contract.provider.getBlockNumber();
328
- let state;
329
- if (currentBlock < startBlock) {
330
- state = MembershipState.Active;
331
- }
332
- else if (currentBlock < endBlock) {
333
- state = MembershipState.GracePeriod;
334
- }
335
- else {
336
- state = MembershipState.Expired;
337
- }
338
- const index = await this.getMemberIndex(idCommitment);
339
- if (!index)
340
- return undefined;
341
- return {
342
- index,
343
- idCommitment,
344
- rateLimit: rateLimit.toNumber(),
345
- startBlock: startBlock.toNumber(),
346
- endBlock: endBlock.toNumber(),
347
- state
348
- };
349
- }
350
- catch (error) {
351
- return undefined;
352
- }
353
- }
354
- async extendMembership(idCommitment) {
355
- return this.contract.extendMemberships([idCommitment]);
356
- }
357
- async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
358
- return this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
359
- }
360
- async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
361
- if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
362
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
363
- throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
364
- }
365
- return this.contract.register(idCommitment, rateLimit, []);
366
- }
367
- async getMemberIndex(idCommitment) {
368
- try {
369
- const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(idCommitment));
370
- if (events.length === 0)
371
- return undefined;
372
- // Get the most recent registration event
373
- const event = events[events.length - 1];
374
- return event.args?.index;
375
- }
376
- catch (error) {
377
- return undefined;
378
- }
379
- }
380
- }
381
- // These values should be tested on other networks
382
- const FETCH_CHUNK = 5;
383
- const BLOCK_RANGE = 3000;
384
- async function queryFilter(contract, options) {
385
- const { fromBlock, membersFilter, fetchRange = BLOCK_RANGE, fetchChunks = FETCH_CHUNK } = options;
386
- if (fromBlock === undefined) {
387
- return contract.queryFilter(membersFilter);
388
- }
389
- if (!contract.provider) {
390
- throw Error("No provider found on the contract.");
391
- }
392
- const toBlock = await contract.provider.getBlockNumber();
393
- if (toBlock - fromBlock < fetchRange) {
394
- return contract.queryFilter(membersFilter, fromBlock, toBlock);
395
- }
396
- const events = [];
397
- const chunks = splitToChunks(fromBlock, toBlock, fetchRange);
398
- for (const portion of takeN(chunks, fetchChunks)) {
399
- const promises = portion.map(([left, right]) => ignoreErrors(contract.queryFilter(membersFilter, left, right), []));
400
- const fetchedEvents = await Promise.all(promises);
401
- events.push(fetchedEvents.flatMap((v) => v));
402
- }
403
- return events.flatMap((v) => v);
404
- }
405
- function splitToChunks(from, to, step) {
406
- const chunks = [];
407
- let left = from;
408
- while (left < to) {
409
- const right = left + step < to ? left + step : to;
410
- chunks.push([left, right]);
411
- left = right;
412
- }
413
- return chunks;
414
- }
415
- function* takeN(array, size) {
416
- let start = 0;
417
- while (start < array.length) {
418
- const portion = array.slice(start, start + size);
419
- yield portion;
420
- start += size;
421
- }
422
- }
423
- async function ignoreErrors(promise, defaultValue) {
424
- try {
425
- return await promise;
426
- }
427
- catch (err) {
428
- if (err instanceof Error) {
429
- log.info(`Ignoring an error during query: ${err.message}`);
430
- }
431
- else {
432
- log.info(`Ignoring an unknown error during query`);
101
+ async syncNewMembers(fromBlock, toBlock) {
102
+ // Get members that were added
103
+ const filter = this.contract.filters.MembershipRegistered();
104
+ const addEvents = await this.contract.queryFilter(filter, fromBlock, toBlock);
105
+ // Get members that were removed
106
+ const removeFilter = this.contract.filters.MembershipErased();
107
+ const removeEvents = await this.contract.queryFilter(removeFilter, fromBlock, toBlock);
108
+ // Process removals first (in reverse block order)
109
+ for (const evt of removeEvents.sort((a, b) => b.blockNumber - a.blockNumber)) {
110
+ if (!evt.args)
111
+ continue;
112
+ const index = evt.args.index.toNumber();
113
+ this.instance.zerokit.deleteMember(index);
114
+ this.merkleRootTracker.backFill(evt.blockNumber);
115
+ }
116
+ // Then process additions
117
+ for (const evt of addEvents) {
118
+ if (!evt.args)
119
+ continue;
120
+ const idCommitment = zeroPadLE(hexToBytes(evt.args.idCommitment), 32);
121
+ this.instance.zerokit.insertMember(idCommitment);
122
+ this.merkleRootTracker.pushRoot(evt.blockNumber, this.instance.zerokit.getMerkleRoot());
433
123
  }
434
- return defaultValue;
435
124
  }
436
125
  }
437
126
 
438
- export { MembershipState, RLNContract };
127
+ export { RLNContract };
@@ -0,0 +1,9 @@
1
+ var MembershipState;
2
+ (function (MembershipState) {
3
+ MembershipState["Active"] = "Active";
4
+ MembershipState["GracePeriod"] = "GracePeriod";
5
+ MembershipState["Expired"] = "Expired";
6
+ MembershipState["ErasedAwaitsWithdrawal"] = "ErasedAwaitsWithdrawal";
7
+ })(MembershipState || (MembershipState = {}));
8
+
9
+ export { MembershipState };
@@ -3,7 +3,7 @@ async function createRLN() {
3
3
  // asynchronously. This file does the single async import, so
4
4
  // that no one else needs to worry about it again.
5
5
  const rlnModule = await import('./rln.js');
6
- return rlnModule.create();
6
+ return rlnModule.RLNInstance.create();
7
7
  }
8
8
 
9
9
  export { createRLN };