@waku/rln 0.1.5-053bb95.0 → 0.1.5-1d384f2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/_virtual/utils.js +2 -2
- package/bundle/_virtual/utils2.js +2 -2
- package/bundle/packages/rln/dist/contract/constants.js +5 -2
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +244 -114
- package/bundle/packages/rln/dist/contract/rln_contract.js +74 -89
- package/bundle/packages/rln/dist/credentials_manager.js +1 -1
- package/bundle/packages/rln/dist/identity.js +0 -8
- package/bundle/packages/rln/dist/keystore/keystore.js +28 -15
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/constants.d.ts +1 -1
- package/dist/contract/constants.js +1 -1
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/rln_base_contract.d.ts +30 -22
- package/dist/contract/rln_base_contract.js +244 -114
- package/dist/contract/rln_base_contract.js.map +1 -1
- package/dist/contract/rln_contract.d.ts +4 -24
- package/dist/contract/rln_contract.js +74 -89
- package/dist/contract/rln_contract.js.map +1 -1
- package/dist/credentials_manager.js +1 -1
- package/dist/credentials_manager.js.map +1 -1
- package/dist/identity.d.ts +0 -1
- package/dist/identity.js +0 -8
- package/dist/identity.js.map +1 -1
- package/dist/keystore/keystore.d.ts +1 -0
- package/dist/keystore/keystore.js +28 -15
- package/dist/keystore/keystore.js.map +1 -1
- package/package.json +1 -1
- package/src/contract/constants.ts +1 -1
- package/src/contract/rln_base_contract.ts +390 -183
- package/src/contract/rln_contract.ts +95 -120
- package/src/credentials_manager.ts +1 -1
- package/src/identity.ts +0 -9
- package/src/keystore/keystore.ts +50 -27
- package/bundle/packages/rln/dist/contract/errors.js +0 -62
- package/dist/contract/errors.d.ts +0 -30
- package/dist/contract/errors.js +0 -61
- package/dist/contract/errors.js.map +0 -1
- package/src/contract/errors.ts +0 -75
package/bundle/_virtual/utils.js
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
var utils = {
|
1
|
+
var utils = {};
|
2
2
|
|
3
|
-
export { utils as
|
3
|
+
export { utils as __exports };
|
@@ -1,3 +1,3 @@
|
|
1
|
-
var utils = {};
|
1
|
+
var utils = {exports: {}};
|
2
2
|
|
3
|
-
export { utils as
|
3
|
+
export { utils as __module };
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { RLN_ABI } from './abi.js';
|
2
2
|
|
3
3
|
const LINEA_CONTRACT = {
|
4
|
-
chainId:
|
4
|
+
chainId: 59141,
|
5
5
|
address: "0xb9cd878c90e49f797b4431fbf4fb333108cb90e6",
|
6
6
|
abi: RLN_ABI
|
7
7
|
};
|
@@ -17,7 +17,10 @@ const RATE_LIMIT_TIERS = {
|
|
17
17
|
// Global rate limit parameters
|
18
18
|
const RATE_LIMIT_PARAMS = {
|
19
19
|
MIN_RATE: RATE_LIMIT_TIERS.LOW,
|
20
|
-
MAX_RATE: RATE_LIMIT_TIERS.HIGH
|
20
|
+
MAX_RATE: RATE_LIMIT_TIERS.HIGH,
|
21
|
+
MAX_TOTAL_RATE: 160_000, // Maximum total rate limit across all memberships
|
22
|
+
EPOCH_LENGTH: 600 // Epoch length in seconds (10 minutes)
|
23
|
+
};
|
21
24
|
const DEFAULT_RATE_LIMIT = RATE_LIMIT_PARAMS.MAX_RATE;
|
22
25
|
|
23
26
|
export { DEFAULT_RATE_LIMIT, LINEA_CONTRACT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS };
|
@@ -15,7 +15,6 @@ import '../../../../node_modules/multiformats/dist/src/codecs/json.js';
|
|
15
15
|
import { Logger } from '../../../utils/dist/logger/index.js';
|
16
16
|
import { RLN_ABI } from './abi.js';
|
17
17
|
import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
|
18
|
-
import { RLNContractError, InvalidMembershipError, MembershipNotFoundError, MembershipExistsError, RateLimitExceededError, InvalidRateLimitError, TransactionError } from './errors.js';
|
19
18
|
import { MembershipState } from './types.js';
|
20
19
|
import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
|
21
20
|
import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
|
@@ -23,12 +22,33 @@ import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib
|
|
23
22
|
const log = new Logger("waku:rln:contract:base");
|
24
23
|
class RLNBaseContract {
|
25
24
|
contract;
|
25
|
+
deployBlock;
|
26
26
|
rateLimit;
|
27
|
+
_members = new Map();
|
28
|
+
_membersFilter;
|
29
|
+
_membershipErasedFilter;
|
30
|
+
_membersExpiredFilter;
|
31
|
+
/**
|
32
|
+
* Constructor for RLNBaseContract.
|
33
|
+
* Allows injecting a mocked contract for testing purposes.
|
34
|
+
*/
|
27
35
|
constructor(options) {
|
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
|
+
});
|
28
44
|
const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
|
29
45
|
this.validateRateLimit(rateLimit);
|
30
46
|
this.contract = contract || new Contract(address, RLN_ABI, signer);
|
31
47
|
this.rateLimit = rateLimit;
|
48
|
+
// Initialize event filters
|
49
|
+
this._membersFilter = this.contract.filters.MembershipRegistered();
|
50
|
+
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
51
|
+
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
32
52
|
}
|
33
53
|
/**
|
34
54
|
* Gets the current rate limit for this contract instance
|
@@ -99,60 +119,155 @@ class RLNBaseContract {
|
|
99
119
|
this.validateRateLimit(newRateLimit);
|
100
120
|
this.rateLimit = newRateLimit;
|
101
121
|
}
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
+
async fetchMembers(options = {}) {
|
127
|
+
const registeredMemberEvents = await RLNBaseContract.queryFilter(this.contract, {
|
128
|
+
fromBlock: this.deployBlock,
|
129
|
+
...options,
|
130
|
+
membersFilter: this.membersFilter
|
131
|
+
});
|
132
|
+
const removedMemberEvents = await RLNBaseContract.queryFilter(this.contract, {
|
133
|
+
fromBlock: this.deployBlock,
|
134
|
+
...options,
|
135
|
+
membersFilter: this.membershipErasedFilter
|
136
|
+
});
|
137
|
+
const expiredMemberEvents = await RLNBaseContract.queryFilter(this.contract, {
|
138
|
+
fromBlock: this.deployBlock,
|
139
|
+
...options,
|
140
|
+
membersFilter: this.membersExpiredFilter
|
141
|
+
});
|
142
|
+
const events = [
|
143
|
+
...registeredMemberEvents,
|
144
|
+
...removedMemberEvents,
|
145
|
+
...expiredMemberEvents
|
146
|
+
];
|
147
|
+
this.processEvents(events);
|
148
|
+
}
|
149
|
+
static async queryFilter(contract, options) {
|
150
|
+
const FETCH_CHUNK = 5;
|
151
|
+
const BLOCK_RANGE = 3000;
|
152
|
+
const { fromBlock, membersFilter, fetchRange = BLOCK_RANGE, fetchChunks = FETCH_CHUNK } = options;
|
153
|
+
if (fromBlock === undefined) {
|
154
|
+
return contract.queryFilter(membersFilter);
|
155
|
+
}
|
156
|
+
if (!contract.provider) {
|
157
|
+
throw Error("No provider found on the contract.");
|
158
|
+
}
|
159
|
+
const toBlock = await contract.provider.getBlockNumber();
|
160
|
+
if (toBlock - fromBlock < fetchRange) {
|
161
|
+
return contract.queryFilter(membersFilter, fromBlock, toBlock);
|
162
|
+
}
|
163
|
+
const events = [];
|
164
|
+
const chunks = RLNBaseContract.splitToChunks(fromBlock, toBlock, fetchRange);
|
165
|
+
for (const portion of RLNBaseContract.takeN(chunks, fetchChunks)) {
|
166
|
+
const promises = portion.map(([left, right]) => RLNBaseContract.ignoreErrors(contract.queryFilter(membersFilter, left, right), []));
|
167
|
+
const fetchedEvents = await Promise.all(promises);
|
168
|
+
events.push(fetchedEvents.flatMap((v) => v));
|
169
|
+
}
|
170
|
+
return events.flatMap((v) => v);
|
171
|
+
}
|
172
|
+
processEvents(events) {
|
173
|
+
const toRemoveTable = new Map();
|
174
|
+
const toInsertTable = new Map();
|
175
|
+
events.forEach((evt) => {
|
176
|
+
if (!evt.args) {
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
if (evt.event === "MembershipErased" ||
|
180
|
+
evt.event === "MembershipExpired") {
|
181
|
+
let index = evt.args.index;
|
182
|
+
if (!index) {
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
if (typeof index === "number" || typeof index === "string") {
|
186
|
+
index = BigNumber.from(index);
|
187
|
+
}
|
188
|
+
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
189
|
+
if (toRemoveVal != undefined) {
|
190
|
+
toRemoveVal.push(index.toNumber());
|
191
|
+
toRemoveTable.set(evt.blockNumber, toRemoveVal);
|
192
|
+
}
|
193
|
+
else {
|
194
|
+
toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
|
195
|
+
}
|
196
|
+
}
|
197
|
+
else if (evt.event === "MembershipRegistered") {
|
198
|
+
let eventsPerBlock = toInsertTable.get(evt.blockNumber);
|
199
|
+
if (eventsPerBlock == undefined) {
|
200
|
+
eventsPerBlock = [];
|
201
|
+
}
|
202
|
+
eventsPerBlock.push(evt);
|
203
|
+
toInsertTable.set(evt.blockNumber, eventsPerBlock);
|
204
|
+
}
|
205
|
+
});
|
206
|
+
}
|
207
|
+
static splitToChunks(from, to, step) {
|
208
|
+
const chunks = [];
|
209
|
+
let left = from;
|
210
|
+
while (left < to) {
|
211
|
+
const right = left + step < to ? left + step : to;
|
212
|
+
chunks.push([left, right]);
|
213
|
+
left = right;
|
214
|
+
}
|
215
|
+
return chunks;
|
216
|
+
}
|
217
|
+
static *takeN(array, size) {
|
218
|
+
let start = 0;
|
219
|
+
while (start < array.length) {
|
220
|
+
const portion = array.slice(start, start + size);
|
221
|
+
yield portion;
|
222
|
+
start += size;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
static async ignoreErrors(promise, defaultValue) {
|
108
226
|
try {
|
109
|
-
|
110
|
-
const idCommitments = await this.contract.getRateCommitmentsInRangeBoundsInclusive(startIndex, endIndex - 1 // -1 because getRateCommitmentsInRangeBoundsInclusive is inclusive
|
111
|
-
);
|
112
|
-
// Get membership info for each commitment in a single batch
|
113
|
-
const membershipPromises = idCommitments.map((idCommitment) => this.contract
|
114
|
-
.memberships(idCommitment)
|
115
|
-
.then((info) => ({
|
116
|
-
idCommitment: idCommitment.toString(),
|
117
|
-
index: BigNumber.from(info.index)
|
118
|
-
}))
|
119
|
-
.catch(() => null) // Skip invalid members
|
120
|
-
);
|
121
|
-
// Wait for all promises to resolve
|
122
|
-
const members = (await Promise.all(membershipPromises)).filter((m) => m !== null);
|
123
|
-
return members;
|
227
|
+
return await promise;
|
124
228
|
}
|
125
|
-
catch (
|
126
|
-
if (
|
127
|
-
error.message
|
128
|
-
|
229
|
+
catch (err) {
|
230
|
+
if (err instanceof Error) {
|
231
|
+
log.info(`Ignoring an error during query: ${err.message}`);
|
232
|
+
}
|
233
|
+
else {
|
234
|
+
log.info(`Ignoring an unknown error during query`);
|
129
235
|
}
|
130
|
-
|
236
|
+
return defaultValue;
|
131
237
|
}
|
132
238
|
}
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
239
|
+
subscribeToMembers() {
|
240
|
+
this.contract.on(this.membersFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
241
|
+
this.processEvents([event]);
|
242
|
+
});
|
243
|
+
this.contract.on(this.membershipErasedFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
244
|
+
this.processEvents([event]);
|
245
|
+
});
|
246
|
+
this.contract.on(this.membersExpiredFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
247
|
+
this.processEvents([event]);
|
248
|
+
});
|
139
249
|
}
|
140
250
|
/**
|
141
|
-
*
|
142
|
-
*
|
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
|
143
254
|
*/
|
144
|
-
async
|
255
|
+
async getRemainingMessages(membershipId) {
|
145
256
|
try {
|
146
|
-
const
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
const
|
151
|
-
|
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());
|
152
267
|
}
|
153
268
|
catch (error) {
|
154
|
-
log.error(`Error getting
|
155
|
-
|
269
|
+
log.error(`Error getting remaining messages: ${error.message}`);
|
270
|
+
return 0; // Fail safe: assume no messages remaining on error
|
156
271
|
}
|
157
272
|
}
|
158
273
|
async getMembershipInfo(idCommitment) {
|
@@ -170,9 +285,8 @@ class RLNBaseContract {
|
|
170
285
|
state = MembershipState.Expired;
|
171
286
|
}
|
172
287
|
const index = await this.getMemberIndex(idCommitment);
|
173
|
-
if (index
|
174
|
-
|
175
|
-
}
|
288
|
+
if (!index)
|
289
|
+
return undefined;
|
176
290
|
return {
|
177
291
|
index,
|
178
292
|
idCommitment,
|
@@ -183,51 +297,29 @@ class RLNBaseContract {
|
|
183
297
|
};
|
184
298
|
}
|
185
299
|
catch (error) {
|
186
|
-
|
187
|
-
throw error;
|
188
|
-
}
|
189
|
-
log.error(`Error getting membership info: ${error.message}`);
|
190
|
-
throw new InvalidMembershipError(idCommitment);
|
300
|
+
return undefined;
|
191
301
|
}
|
192
302
|
}
|
193
303
|
async extendMembership(idCommitment) {
|
194
|
-
|
195
|
-
await this.confirmTransaction(tx, "MembershipExtended", (event) => ({
|
196
|
-
idCommitment: event.args.idCommitment,
|
197
|
-
endBlock: event.args.endBlock
|
198
|
-
}));
|
304
|
+
return this.contract.extendMemberships([idCommitment]);
|
199
305
|
}
|
200
306
|
async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
|
201
|
-
|
202
|
-
await this.confirmTransaction(tx, "MembershipErased", (event) => ({
|
203
|
-
idCommitment: event.args.idCommitment,
|
204
|
-
index: event.args.index
|
205
|
-
}));
|
307
|
+
return this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
|
206
308
|
}
|
207
309
|
async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
|
208
310
|
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
209
311
|
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
210
312
|
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
|
211
313
|
}
|
212
|
-
|
213
|
-
await this.confirmTransaction(tx, "MembershipRegistered", (event) => ({
|
214
|
-
idCommitment: event.args.idCommitment,
|
215
|
-
membershipRateLimit: event.args.membershipRateLimit,
|
216
|
-
index: event.args.index
|
217
|
-
}));
|
314
|
+
return this.contract.register(idCommitment, rateLimit, []);
|
218
315
|
}
|
219
316
|
async withdraw(token, holder) {
|
220
317
|
try {
|
221
318
|
const tx = await this.contract.withdraw(token, { from: holder });
|
222
|
-
await
|
223
|
-
token: event.args.token,
|
224
|
-
holder: event.args.holder,
|
225
|
-
amount: event.args.amount
|
226
|
-
}));
|
319
|
+
await tx.wait();
|
227
320
|
}
|
228
321
|
catch (error) {
|
229
322
|
log.error(`Error in withdraw: ${error.message}`);
|
230
|
-
throw error;
|
231
323
|
}
|
232
324
|
}
|
233
325
|
async registerWithIdentity(identity) {
|
@@ -235,82 +327,100 @@ class RLNBaseContract {
|
|
235
327
|
log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
|
236
328
|
// Check if the ID commitment is already registered
|
237
329
|
const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt.toString());
|
238
|
-
if (existingIndex
|
239
|
-
throw new
|
330
|
+
if (existingIndex) {
|
331
|
+
throw new Error(`ID commitment is already registered with index ${existingIndex}`);
|
240
332
|
}
|
241
333
|
// Check if there's enough remaining rate limit
|
242
334
|
const remainingRateLimit = await this.getRemainingTotalRateLimit();
|
243
335
|
if (remainingRateLimit < this.rateLimit) {
|
244
|
-
throw new
|
336
|
+
throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
|
245
337
|
}
|
246
338
|
const estimatedGas = await this.contract.estimateGas.register(identity.IDCommitmentBigInt, this.rateLimit, []);
|
247
339
|
const gasLimit = estimatedGas.add(10000);
|
248
|
-
const
|
249
|
-
const
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
340
|
+
const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit });
|
341
|
+
const txRegisterReceipt = await txRegisterResponse.wait();
|
342
|
+
if (txRegisterReceipt.status === 0) {
|
343
|
+
throw new Error("Transaction failed on-chain");
|
344
|
+
}
|
345
|
+
const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
|
346
|
+
if (!memberRegistered || !memberRegistered.args) {
|
347
|
+
log.error("Failed to register membership: No MembershipRegistered event found");
|
348
|
+
return undefined;
|
349
|
+
}
|
350
|
+
const decodedData = {
|
351
|
+
idCommitment: memberRegistered.args.idCommitment,
|
352
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
353
|
+
index: memberRegistered.args.index
|
354
|
+
};
|
254
355
|
log.info(`Successfully registered membership with index ${decodedData.index} ` +
|
255
356
|
`and rate limit ${decodedData.membershipRateLimit}`);
|
256
357
|
const network = await this.contract.provider.getNetwork();
|
257
358
|
const address = this.contract.address;
|
258
|
-
const membershipId = decodedData.index
|
359
|
+
const membershipId = Number(decodedData.index);
|
259
360
|
return {
|
260
361
|
identity,
|
261
362
|
membership: {
|
262
363
|
address,
|
263
|
-
treeIndex:
|
364
|
+
treeIndex: membershipId,
|
264
365
|
chainId: network.chainId.toString(),
|
265
366
|
rateLimit: decodedData.membershipRateLimit.toNumber()
|
266
367
|
}
|
267
368
|
};
|
268
369
|
}
|
269
370
|
catch (error) {
|
270
|
-
if (error instanceof RLNContractError) {
|
271
|
-
throw error;
|
272
|
-
}
|
273
371
|
if (error instanceof Error) {
|
274
372
|
const errorMessage = error.message;
|
275
373
|
log.error("registerWithIdentity - error message:", errorMessage);
|
276
374
|
log.error("registerWithIdentity - error stack:", error.stack);
|
277
|
-
//
|
375
|
+
// Try to extract more specific error information
|
278
376
|
if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
|
279
|
-
throw new
|
377
|
+
throw new Error("Registration failed: Cannot exceed maximum total rate limit");
|
280
378
|
}
|
281
379
|
else if (errorMessage.includes("InvalidIdCommitment")) {
|
282
|
-
throw new
|
380
|
+
throw new Error("Registration failed: Invalid ID commitment");
|
283
381
|
}
|
284
382
|
else if (errorMessage.includes("InvalidMembershipRateLimit")) {
|
285
|
-
throw new
|
383
|
+
throw new Error("Registration failed: Invalid membership rate limit");
|
286
384
|
}
|
287
385
|
else if (errorMessage.includes("execution reverted")) {
|
288
|
-
throw new
|
386
|
+
throw new Error("Contract execution reverted. Check contract requirements.");
|
387
|
+
}
|
388
|
+
else {
|
389
|
+
throw new Error(`Error in registerWithIdentity: ${errorMessage}`);
|
289
390
|
}
|
290
|
-
throw new RLNContractError(`Error in registerWithIdentity: ${errorMessage}`);
|
291
391
|
}
|
292
|
-
|
392
|
+
else {
|
393
|
+
throw new Error("Unknown error in registerWithIdentity", {
|
394
|
+
cause: error
|
395
|
+
});
|
396
|
+
}
|
293
397
|
}
|
294
398
|
}
|
295
399
|
async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
|
296
400
|
try {
|
297
401
|
log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
|
298
|
-
const
|
299
|
-
const
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
402
|
+
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)));
|
403
|
+
const txRegisterReceipt = await txRegisterResponse.wait();
|
404
|
+
const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
|
405
|
+
if (!memberRegistered || !memberRegistered.args) {
|
406
|
+
log.error("Failed to register membership with permit: No MembershipRegistered event found");
|
407
|
+
return undefined;
|
408
|
+
}
|
409
|
+
const decodedData = {
|
410
|
+
idCommitment: memberRegistered.args.idCommitment,
|
411
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
412
|
+
index: memberRegistered.args.index
|
413
|
+
};
|
304
414
|
log.info(`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
305
415
|
`Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
|
306
416
|
const network = await this.contract.provider.getNetwork();
|
307
417
|
const address = this.contract.address;
|
308
|
-
const membershipId = decodedData.index
|
418
|
+
const membershipId = Number(decodedData.index);
|
309
419
|
return {
|
310
420
|
identity,
|
311
421
|
membership: {
|
312
422
|
address,
|
313
|
-
treeIndex:
|
423
|
+
treeIndex: membershipId,
|
314
424
|
chainId: network.chainId.toString(),
|
315
425
|
rateLimit: decodedData.membershipRateLimit.toNumber()
|
316
426
|
}
|
@@ -318,7 +428,7 @@ class RLNBaseContract {
|
|
318
428
|
}
|
319
429
|
catch (error) {
|
320
430
|
log.error(`Error in registerWithPermitAndErase: ${error.message}`);
|
321
|
-
|
431
|
+
return undefined;
|
322
432
|
}
|
323
433
|
}
|
324
434
|
/**
|
@@ -328,19 +438,39 @@ class RLNBaseContract {
|
|
328
438
|
validateRateLimit(rateLimit) {
|
329
439
|
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
330
440
|
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
331
|
-
throw new
|
441
|
+
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`);
|
332
442
|
}
|
333
443
|
}
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
444
|
+
get membersFilter() {
|
445
|
+
if (!this._membersFilter) {
|
446
|
+
throw Error("Members filter was not initialized.");
|
447
|
+
}
|
448
|
+
return this._membersFilter;
|
449
|
+
}
|
450
|
+
get membershipErasedFilter() {
|
451
|
+
if (!this._membershipErasedFilter) {
|
452
|
+
throw Error("MembershipErased filter was not initialized.");
|
453
|
+
}
|
454
|
+
return this._membershipErasedFilter;
|
455
|
+
}
|
456
|
+
get membersExpiredFilter() {
|
457
|
+
if (!this._membersExpiredFilter) {
|
458
|
+
throw Error("MembersExpired filter was not initialized.");
|
459
|
+
}
|
460
|
+
return this._membersExpiredFilter;
|
461
|
+
}
|
462
|
+
async getMemberIndex(idCommitment) {
|
463
|
+
try {
|
464
|
+
const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(idCommitment));
|
465
|
+
if (events.length === 0)
|
466
|
+
return undefined;
|
467
|
+
// Get the most recent registration event
|
468
|
+
const event = events[events.length - 1];
|
469
|
+
return event.args?.index;
|
470
|
+
}
|
471
|
+
catch (error) {
|
472
|
+
return undefined;
|
342
473
|
}
|
343
|
-
return transform(event);
|
344
474
|
}
|
345
475
|
}
|
346
476
|
|