@waku/rln 0.0.2-c41b319.0 → 0.0.2-c8128d1.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 +1 -1
- package/bundle/packages/interfaces/dist/protocols.js +40 -45
- package/bundle/packages/rln/dist/contract/abi.js +648 -0
- package/bundle/packages/rln/dist/contract/constants.js +8 -13
- package/bundle/packages/rln/dist/contract/rln_contract.js +148 -25
- package/bundle/packages/rln/dist/identity.js +0 -24
- package/bundle/packages/rln/dist/rln.js +33 -15
- package/bundle/packages/rln/dist/zerokit.js +22 -16
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/{abi/rlnv2.d.ts → abi.d.ts} +22 -18
- package/dist/contract/abi.js +647 -0
- package/dist/contract/abi.js.map +1 -0
- package/dist/contract/constants.d.ts +22 -23
- package/dist/contract/constants.js +7 -12
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/rln_contract.d.ts +13 -3
- package/dist/contract/rln_contract.js +148 -25
- package/dist/contract/rln_contract.js.map +1 -1
- package/dist/identity.d.ts +0 -1
- package/dist/identity.js +0 -24
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/rln.js +33 -14
- package/dist/rln.js.map +1 -1
- package/dist/zerokit.d.ts +5 -1
- package/dist/zerokit.js +22 -16
- package/dist/zerokit.js.map +1 -1
- package/package.json +1 -1
- package/src/contract/abi.ts +646 -0
- package/src/contract/constants.ts +8 -14
- package/src/contract/rln_contract.ts +227 -27
- package/src/identity.ts +0 -42
- package/src/index.ts +2 -2
- package/src/rln.ts +48 -14
- package/src/zerokit.ts +45 -16
- package/bundle/node_modules/@iden3/js-crypto/dist/browser/esm/index.js +0 -7
- package/bundle/node_modules/@stablelib/binary/lib/binary.js +0 -22
- package/bundle/node_modules/@stablelib/chacha/lib/chacha.js +0 -245
- package/bundle/node_modules/@stablelib/wipe/lib/wipe.js +0 -26
- package/bundle/packages/rln/dist/contract/abi/rlnv2.js +0 -394
- package/dist/contract/abi/rlnv2.js +0 -393
- package/dist/contract/abi/rlnv2.js.map +0 -1
- package/src/contract/abi/rlnv2.ts +0 -392
@@ -5,11 +5,12 @@ 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 {
|
8
|
+
import { RLN_ABI } from './abi.js';
|
9
9
|
import { RATE_LIMIT_PARAMS, DEFAULT_RATE_LIMIT } from './constants.js';
|
10
10
|
import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
|
11
11
|
import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
|
12
12
|
|
13
|
+
/* eslint-disable no-console */
|
13
14
|
const log = new Logger("waku:rln:contract");
|
14
15
|
var MembershipState;
|
15
16
|
(function (MembershipState) {
|
@@ -26,6 +27,7 @@ class RLNContract {
|
|
26
27
|
_members = new Map();
|
27
28
|
_membersFilter;
|
28
29
|
_membersRemovedFilter;
|
30
|
+
_membersExpiredFilter;
|
29
31
|
/**
|
30
32
|
* Asynchronous initializer for RLNContract.
|
31
33
|
* Allows injecting a mocked contract for testing purposes.
|
@@ -45,12 +47,12 @@ class RLNContract {
|
|
45
47
|
this.rateLimit = rateLimit;
|
46
48
|
const initialRoot = rlnInstance.zerokit.getMerkleRoot();
|
47
49
|
// Use the injected contract if provided; otherwise, instantiate a new one.
|
48
|
-
this.contract =
|
49
|
-
contract || new Contract(address, RLN_V2_ABI, signer);
|
50
|
+
this.contract = contract || new Contract(address, RLN_ABI, signer);
|
50
51
|
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
|
51
|
-
// Initialize event filters
|
52
|
+
// Initialize event filters
|
52
53
|
this._membersFilter = this.contract.filters.MembershipRegistered();
|
53
|
-
this._membersRemovedFilter = this.contract.filters.
|
54
|
+
this._membersRemovedFilter = this.contract.filters.MembershipErased();
|
55
|
+
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
54
56
|
}
|
55
57
|
/**
|
56
58
|
* Gets the current rate limit for this contract instance
|
@@ -58,6 +60,18 @@ class RLNContract {
|
|
58
60
|
getRateLimit() {
|
59
61
|
return this.rateLimit;
|
60
62
|
}
|
63
|
+
/**
|
64
|
+
* Gets the contract address
|
65
|
+
*/
|
66
|
+
get address() {
|
67
|
+
return this.contract.address;
|
68
|
+
}
|
69
|
+
/**
|
70
|
+
* Gets the contract provider
|
71
|
+
*/
|
72
|
+
get provider() {
|
73
|
+
return this.contract.provider;
|
74
|
+
}
|
61
75
|
/**
|
62
76
|
* Gets the minimum allowed rate limit from the contract
|
63
77
|
* @returns Promise<number> The minimum rate limit in messages per epoch
|
@@ -120,10 +134,16 @@ class RLNContract {
|
|
120
134
|
}
|
121
135
|
get membersRemovedFilter() {
|
122
136
|
if (!this._membersRemovedFilter) {
|
123
|
-
throw Error("
|
137
|
+
throw Error("MembersErased filter was not initialized.");
|
124
138
|
}
|
125
139
|
return this._membersRemovedFilter;
|
126
140
|
}
|
141
|
+
get membersExpiredFilter() {
|
142
|
+
if (!this._membersExpiredFilter) {
|
143
|
+
throw Error("MembersExpired filter was not initialized.");
|
144
|
+
}
|
145
|
+
return this._membersExpiredFilter;
|
146
|
+
}
|
127
147
|
async fetchMembers(rlnInstance, options = {}) {
|
128
148
|
const registeredMemberEvents = await queryFilter(this.contract, {
|
129
149
|
fromBlock: this.deployBlock,
|
@@ -135,7 +155,16 @@ class RLNContract {
|
|
135
155
|
...options,
|
136
156
|
membersFilter: this.membersRemovedFilter
|
137
157
|
});
|
138
|
-
const
|
158
|
+
const expiredMemberEvents = await queryFilter(this.contract, {
|
159
|
+
fromBlock: this.deployBlock,
|
160
|
+
...options,
|
161
|
+
membersFilter: this.membersExpiredFilter
|
162
|
+
});
|
163
|
+
const events = [
|
164
|
+
...registeredMemberEvents,
|
165
|
+
...removedMemberEvents,
|
166
|
+
...expiredMemberEvents
|
167
|
+
];
|
139
168
|
this.processEvents(rlnInstance, events);
|
140
169
|
}
|
141
170
|
processEvents(rlnInstance, events) {
|
@@ -145,8 +174,17 @@ class RLNContract {
|
|
145
174
|
if (!evt.args) {
|
146
175
|
return;
|
147
176
|
}
|
148
|
-
if (evt.event === "
|
149
|
-
|
177
|
+
if (evt.event === "MembershipErased" ||
|
178
|
+
evt.event === "MembershipExpired") {
|
179
|
+
// Both MembershipErased and MembershipExpired events should remove members
|
180
|
+
let index = evt.args.index;
|
181
|
+
if (!index) {
|
182
|
+
return;
|
183
|
+
}
|
184
|
+
// Convert index to ethers.BigNumber if it's not already
|
185
|
+
if (typeof index === "number" || typeof index === "string") {
|
186
|
+
index = BigNumber.from(index);
|
187
|
+
}
|
150
188
|
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
151
189
|
if (toRemoveVal != undefined) {
|
152
190
|
toRemoveVal.push(index.toNumber());
|
@@ -174,14 +212,21 @@ class RLNContract {
|
|
174
212
|
if (!evt.args)
|
175
213
|
return;
|
176
214
|
const _idCommitment = evt.args.idCommitment;
|
177
|
-
|
215
|
+
let index = evt.args.index;
|
216
|
+
// Ensure index is an ethers.BigNumber
|
178
217
|
if (!_idCommitment || !index) {
|
179
218
|
return;
|
180
219
|
}
|
220
|
+
// Convert index to ethers.BigNumber if it's not already
|
221
|
+
if (typeof index === "number" || typeof index === "string") {
|
222
|
+
index = BigNumber.from(index);
|
223
|
+
}
|
181
224
|
const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
|
182
225
|
rlnInstance.zerokit.insertMember(idCommitment);
|
183
|
-
|
184
|
-
|
226
|
+
// Always store the numeric index as the key, but the BigNumber as the value
|
227
|
+
const numericIndex = index.toNumber();
|
228
|
+
this._members.set(numericIndex, {
|
229
|
+
index, // This is always a BigNumber
|
185
230
|
idCommitment: _idCommitment
|
186
231
|
});
|
187
232
|
});
|
@@ -202,34 +247,78 @@ class RLNContract {
|
|
202
247
|
});
|
203
248
|
}
|
204
249
|
subscribeToMembers(rlnInstance) {
|
205
|
-
this.contract.on(this.membersFilter, (_idCommitment,
|
250
|
+
this.contract.on(this.membersFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
206
251
|
this.processEvents(rlnInstance, [event]);
|
207
252
|
});
|
208
|
-
this.contract.on(this.membersRemovedFilter, (_idCommitment, _index, event) => {
|
253
|
+
this.contract.on(this.membersRemovedFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
254
|
+
this.processEvents(rlnInstance, [event]);
|
255
|
+
});
|
256
|
+
this.contract.on(this.membersExpiredFilter, (_idCommitment, _membershipRateLimit, _index, event) => {
|
209
257
|
this.processEvents(rlnInstance, [event]);
|
210
258
|
});
|
211
259
|
}
|
212
260
|
async registerWithIdentity(identity) {
|
213
261
|
try {
|
262
|
+
console.log("registerWithIdentity - starting registration process");
|
263
|
+
console.log("registerWithIdentity - identity:", identity);
|
264
|
+
console.log("registerWithIdentity - IDCommitmentBigInt:", identity.IDCommitmentBigInt.toString());
|
265
|
+
console.log("registerWithIdentity - rate limit:", this.rateLimit);
|
214
266
|
log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
|
267
|
+
// Check if the ID commitment is already registered
|
268
|
+
const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt.toString());
|
269
|
+
if (existingIndex) {
|
270
|
+
console.error(`ID commitment is already registered with index ${existingIndex}`);
|
271
|
+
throw new Error(`ID commitment is already registered with index ${existingIndex}`);
|
272
|
+
}
|
273
|
+
// Check if there's enough remaining rate limit
|
274
|
+
const remainingRateLimit = await this.getRemainingTotalRateLimit();
|
275
|
+
if (remainingRateLimit < this.rateLimit) {
|
276
|
+
console.error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
|
277
|
+
throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
|
278
|
+
}
|
279
|
+
console.log("registerWithIdentity - calling contract.register");
|
215
280
|
const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit: 300000 });
|
281
|
+
console.log("registerWithIdentity - txRegisterResponse:", txRegisterResponse);
|
282
|
+
console.log("registerWithIdentity - hash:", txRegisterResponse.hash);
|
283
|
+
console.log("registerWithIdentity - waiting for transaction confirmation...");
|
216
284
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
285
|
+
console.log("registerWithIdentity - txRegisterReceipt:", txRegisterReceipt);
|
286
|
+
console.log("registerWithIdentity - transaction status:", txRegisterReceipt.status);
|
287
|
+
console.log("registerWithIdentity - block number:", txRegisterReceipt.blockNumber);
|
288
|
+
console.log("registerWithIdentity - gas used:", txRegisterReceipt.gasUsed.toString());
|
289
|
+
// Check transaction status
|
290
|
+
if (txRegisterReceipt.status === 0) {
|
291
|
+
console.error("Transaction failed on-chain");
|
292
|
+
throw new Error("Transaction failed on-chain");
|
293
|
+
}
|
217
294
|
const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
|
295
|
+
console.log("registerWithIdentity - memberRegistered event:", memberRegistered);
|
218
296
|
if (!memberRegistered || !memberRegistered.args) {
|
219
|
-
log
|
297
|
+
console.log("registerWithIdentity - ERROR: no memberRegistered event found");
|
298
|
+
console.log("registerWithIdentity - all events:", txRegisterReceipt.events);
|
299
|
+
console.error("Failed to register membership: No MembershipRegistered event found");
|
220
300
|
return undefined;
|
221
301
|
}
|
302
|
+
console.log("registerWithIdentity - memberRegistered args:", memberRegistered.args);
|
222
303
|
const decodedData = {
|
223
304
|
idCommitment: memberRegistered.args.idCommitment,
|
224
|
-
|
305
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
225
306
|
index: memberRegistered.args.index
|
226
307
|
};
|
308
|
+
console.log("registerWithIdentity - decodedData:", decodedData);
|
309
|
+
console.log("registerWithIdentity - index:", decodedData.index.toString());
|
310
|
+
console.log("registerWithIdentity - membershipRateLimit:", decodedData.membershipRateLimit.toString());
|
227
311
|
log.info(`Successfully registered membership with index ${decodedData.index} ` +
|
228
|
-
`and rate limit ${decodedData.
|
312
|
+
`and rate limit ${decodedData.membershipRateLimit}`);
|
313
|
+
console.log("registerWithIdentity - getting network information");
|
229
314
|
const network = await this.contract.provider.getNetwork();
|
315
|
+
console.log("registerWithIdentity - network:", network);
|
316
|
+
console.log("registerWithIdentity - chainId:", network.chainId);
|
230
317
|
const address = this.contract.address;
|
318
|
+
console.log("registerWithIdentity - contract address:", address);
|
231
319
|
const membershipId = decodedData.index.toNumber();
|
232
|
-
|
320
|
+
console.log("registerWithIdentity - membershipId:", membershipId);
|
321
|
+
const result = {
|
233
322
|
identity,
|
234
323
|
membership: {
|
235
324
|
address,
|
@@ -237,10 +326,39 @@ class RLNContract {
|
|
237
326
|
chainId: network.chainId
|
238
327
|
}
|
239
328
|
};
|
329
|
+
console.log("registerWithIdentity - returning result:", result);
|
330
|
+
return result;
|
240
331
|
}
|
241
332
|
catch (error) {
|
242
|
-
log
|
243
|
-
|
333
|
+
console.log("registerWithIdentity - ERROR:", error);
|
334
|
+
// Improved error handling to decode contract errors
|
335
|
+
if (error instanceof Error) {
|
336
|
+
const errorMessage = error.message;
|
337
|
+
console.log("registerWithIdentity - error message:", errorMessage);
|
338
|
+
console.log("registerWithIdentity - error stack:", error.stack);
|
339
|
+
// Try to extract more specific error information
|
340
|
+
if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
|
341
|
+
console.error("Registration failed: Cannot exceed maximum total rate limit");
|
342
|
+
}
|
343
|
+
else if (errorMessage.includes("InvalidIdCommitment")) {
|
344
|
+
console.error("Registration failed: Invalid ID commitment");
|
345
|
+
}
|
346
|
+
else if (errorMessage.includes("InvalidMembershipRateLimit")) {
|
347
|
+
console.error("Registration failed: Invalid membership rate limit");
|
348
|
+
}
|
349
|
+
else if (errorMessage.includes("execution reverted")) {
|
350
|
+
// Generic revert
|
351
|
+
console.error("Contract execution reverted. Check contract requirements.");
|
352
|
+
}
|
353
|
+
else {
|
354
|
+
console.error(`Error in registerWithIdentity: ${errorMessage}`);
|
355
|
+
}
|
356
|
+
}
|
357
|
+
else {
|
358
|
+
console.error("Unknown error in registerWithIdentity");
|
359
|
+
}
|
360
|
+
// Re-throw the error to allow callers to handle it
|
361
|
+
throw error;
|
244
362
|
}
|
245
363
|
}
|
246
364
|
/**
|
@@ -276,11 +394,11 @@ class RLNContract {
|
|
276
394
|
}
|
277
395
|
const decodedData = {
|
278
396
|
idCommitment: memberRegistered.args.idCommitment,
|
279
|
-
|
397
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
280
398
|
index: memberRegistered.args.index
|
281
399
|
};
|
282
400
|
log.info(`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
283
|
-
`Rate limit: ${decodedData.
|
401
|
+
`Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
|
284
402
|
const network = await this.contract.provider.getNetwork();
|
285
403
|
const address = this.contract.address;
|
286
404
|
const membershipId = decodedData.index.toNumber();
|
@@ -341,17 +459,22 @@ class RLNContract {
|
|
341
459
|
}
|
342
460
|
}
|
343
461
|
async extendMembership(idCommitment) {
|
344
|
-
|
462
|
+
const tx = await this.contract.extendMemberships([idCommitment]);
|
463
|
+
return await tx.wait();
|
345
464
|
}
|
346
465
|
async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
|
347
|
-
|
466
|
+
const tx = await this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
|
467
|
+
return await tx.wait();
|
348
468
|
}
|
349
469
|
async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
|
350
470
|
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
351
471
|
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
352
472
|
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
|
353
473
|
}
|
354
|
-
|
474
|
+
console.log("registering membership", idCommitment, rateLimit);
|
475
|
+
const txn = this.contract.register(idCommitment, rateLimit, []);
|
476
|
+
console.log("txn", txn);
|
477
|
+
return txn;
|
355
478
|
}
|
356
479
|
async getMemberIndex(idCommitment) {
|
357
480
|
try {
|
@@ -1,7 +1,3 @@
|
|
1
|
-
import { arrayify } from '../../../node_modules/@ethersproject/bytes/lib.esm/index.js';
|
2
|
-
import { keccak256 } from '../../../node_modules/@ethersproject/keccak256/lib.esm/index.js';
|
3
|
-
import { Poseidon as m } from '../../../node_modules/@iden3/js-crypto/dist/browser/esm/index.js';
|
4
|
-
import { streamXOR } from '../../../node_modules/@stablelib/chacha/lib/chacha.js';
|
5
1
|
import { buildBigIntFromUint8Array } from './utils/bytes.js';
|
6
2
|
import './utils/epoch.js';
|
7
3
|
|
@@ -29,26 +25,6 @@ class IdentityCredential {
|
|
29
25
|
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
|
30
26
|
return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
|
31
27
|
}
|
32
|
-
static generateSeeded(signature) {
|
33
|
-
// Generate deterministic seed from signature
|
34
|
-
const seed = arrayify(keccak256(signature));
|
35
|
-
// Use ChaCha for deterministic randomness (as in Rust code)
|
36
|
-
const nonce = new Uint8Array(12);
|
37
|
-
const idSecretHash = new Uint8Array(32);
|
38
|
-
streamXOR(seed, nonce, idSecretHash, idSecretHash);
|
39
|
-
// Convert to bigint for Poseidon
|
40
|
-
const secretBigInt = BigInt("0x" + Buffer.from(idSecretHash).toString("hex"));
|
41
|
-
// Generate commitment using Poseidon
|
42
|
-
const idCommitmentBigInt = m.hash([secretBigInt]);
|
43
|
-
// Convert commitment back to Uint8Array
|
44
|
-
const idCommitment = arrayify("0x" + idCommitmentBigInt.toString(16).padStart(64, "0"));
|
45
|
-
// Generate deterministic trapdoor and nullifier from the secret hash
|
46
|
-
const idTrapdoor = new Uint8Array(32);
|
47
|
-
const idNullifier = new Uint8Array(32);
|
48
|
-
streamXOR(idSecretHash, nonce, idTrapdoor, idTrapdoor);
|
49
|
-
streamXOR(idTrapdoor, nonce, idNullifier, idNullifier);
|
50
|
-
return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
|
51
|
-
}
|
52
28
|
}
|
53
29
|
|
54
30
|
export { IdentityCredential };
|
@@ -21,27 +21,43 @@ import { Logger } from '../../utils/dist/logger/index.js';
|
|
21
21
|
import '../../core/dist/lib/metadata/metadata.js';
|
22
22
|
import __wbg_init, { init_panic_hook, newRLN } from '../../../node_modules/@waku/zerokit-rln-wasm/rln_wasm.js';
|
23
23
|
import { createRLNEncoder, createRLNDecoder } from './codec.js';
|
24
|
+
import { DEFAULT_RATE_LIMIT, SEPOLIA_CONTRACT } from './contract/constants.js';
|
24
25
|
import { RLNContract } from './contract/rln_contract.js';
|
25
|
-
import { SEPOLIA_CONTRACT } from './contract/constants.js';
|
26
|
-
import { IdentityCredential } from './identity.js';
|
27
26
|
import { Keystore } from './keystore/keystore.js';
|
28
27
|
import verificationKey from './resources/verification_key.js';
|
29
28
|
import { builder } from './resources/witness_calculator.js';
|
30
29
|
import { extractMetaMaskSigner } from './utils/metamask.js';
|
31
30
|
import './utils/epoch.js';
|
32
31
|
import { Zerokit } from './zerokit.js';
|
33
|
-
import { arrayify } from '../../../node_modules/@ethersproject/bytes/lib.esm/index.js';
|
34
32
|
|
35
33
|
const log = new Logger("waku:rln");
|
36
34
|
async function loadWitnessCalculator() {
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
try {
|
36
|
+
const url = new URL("./resources/rln.wasm", import.meta.url);
|
37
|
+
const response = await fetch(url);
|
38
|
+
if (!response.ok) {
|
39
|
+
throw new Error(`Failed to fetch witness calculator: ${response.status} ${response.statusText}`);
|
40
|
+
}
|
41
|
+
return await builder(new Uint8Array(await response.arrayBuffer()), false);
|
42
|
+
}
|
43
|
+
catch (error) {
|
44
|
+
log.error("Error loading witness calculator:", error);
|
45
|
+
throw new Error(`Failed to load witness calculator: ${error instanceof Error ? error.message : String(error)}`);
|
46
|
+
}
|
40
47
|
}
|
41
48
|
async function loadZkey() {
|
42
|
-
|
43
|
-
|
44
|
-
|
49
|
+
try {
|
50
|
+
const url = new URL("./resources/rln_final.zkey", import.meta.url);
|
51
|
+
const response = await fetch(url);
|
52
|
+
if (!response.ok) {
|
53
|
+
throw new Error(`Failed to fetch zkey: ${response.status} ${response.statusText}`);
|
54
|
+
}
|
55
|
+
return new Uint8Array(await response.arrayBuffer());
|
56
|
+
}
|
57
|
+
catch (error) {
|
58
|
+
log.error("Error loading zkey:", error);
|
59
|
+
throw new Error(`Failed to load zkey: ${error instanceof Error ? error.message : String(error)}`);
|
60
|
+
}
|
45
61
|
}
|
46
62
|
/**
|
47
63
|
* Create an instance of RLN
|
@@ -58,7 +74,7 @@ async function create() {
|
|
58
74
|
const vkey = stringEncoder.encode(JSON.stringify(verificationKey));
|
59
75
|
const DEPTH = 20;
|
60
76
|
const zkRLN = newRLN(DEPTH, zkey, vkey);
|
61
|
-
const zerokit = new Zerokit(zkRLN, witnessCalculator);
|
77
|
+
const zerokit = new Zerokit(zkRLN, witnessCalculator, DEFAULT_RATE_LIMIT);
|
62
78
|
return new RLNInstance(zerokit);
|
63
79
|
}
|
64
80
|
catch (error) {
|
@@ -99,7 +115,7 @@ class RLNInstance {
|
|
99
115
|
this._contract = await RLNContract.init(this, {
|
100
116
|
address: address,
|
101
117
|
signer: signer,
|
102
|
-
rateLimit: options.rateLimit
|
118
|
+
rateLimit: options.rateLimit ?? this.zerokit.getRateLimit
|
103
119
|
});
|
104
120
|
this.started = true;
|
105
121
|
}
|
@@ -146,10 +162,12 @@ class RLNInstance {
|
|
146
162
|
if (!this.contract) {
|
147
163
|
throw Error("RLN Contract is not initialized.");
|
148
164
|
}
|
149
|
-
let identity = "identity" in options
|
165
|
+
let identity = "identity" in options && options.identity;
|
150
166
|
if ("signature" in options) {
|
151
|
-
identity =
|
167
|
+
identity = this.zerokit.generateSeededIdentityCredential(options.signature);
|
152
168
|
}
|
169
|
+
// eslint-disable-next-line no-console
|
170
|
+
console.log("registering membership", identity);
|
153
171
|
if (!identity) {
|
154
172
|
throw Error("Missing signature or identity to register membership.");
|
155
173
|
}
|
@@ -182,12 +200,12 @@ class RLNInstance {
|
|
182
200
|
throw Error("Failed to verify chain coordinates: no contract initialized.");
|
183
201
|
}
|
184
202
|
const registryAddress = credentials.membership.address;
|
185
|
-
const currentRegistryAddress = this._contract.
|
203
|
+
const currentRegistryAddress = this._contract.address;
|
186
204
|
if (registryAddress !== currentRegistryAddress) {
|
187
205
|
throw Error(`Failed to verify chain coordinates: credentials contract address=${registryAddress} is not equal to registryContract address=${currentRegistryAddress}`);
|
188
206
|
}
|
189
207
|
const chainId = credentials.membership.chainId;
|
190
|
-
const network = await this._contract.
|
208
|
+
const network = await this._contract.provider.getNetwork();
|
191
209
|
const currentChainId = network.chainId;
|
192
210
|
if (chainId !== currentChainId) {
|
193
211
|
throw Error(`Failed to verify chain coordinates: credentials chainID=${chainId} is not equal to registryContract chainID=${currentChainId}`);
|
@@ -8,9 +8,20 @@ import { epochIntToBytes, dateToEpoch } from './utils/epoch.js';
|
|
8
8
|
class Zerokit {
|
9
9
|
zkRLN;
|
10
10
|
witnessCalculator;
|
11
|
-
|
11
|
+
rateLimit;
|
12
|
+
constructor(zkRLN, witnessCalculator, rateLimit = DEFAULT_RATE_LIMIT) {
|
12
13
|
this.zkRLN = zkRLN;
|
13
14
|
this.witnessCalculator = witnessCalculator;
|
15
|
+
this.rateLimit = rateLimit;
|
16
|
+
}
|
17
|
+
get getZkRLN() {
|
18
|
+
return this.zkRLN;
|
19
|
+
}
|
20
|
+
get getWitnessCalculator() {
|
21
|
+
return this.witnessCalculator;
|
22
|
+
}
|
23
|
+
get getRateLimit() {
|
24
|
+
return this.rateLimit;
|
14
25
|
}
|
15
26
|
generateIdentityCredentials() {
|
16
27
|
const memKeys = generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
@@ -39,32 +50,33 @@ class Zerokit {
|
|
39
50
|
getMerkleRoot() {
|
40
51
|
return getRoot(this.zkRLN);
|
41
52
|
}
|
42
|
-
serializeMessage(uint8Msg, memIndex, epoch, idKey, rateLimit
|
53
|
+
serializeMessage(uint8Msg, memIndex, epoch, idKey, rateLimit) {
|
43
54
|
// calculate message length
|
44
55
|
const msgLen = writeUIntLE(new Uint8Array(8), uint8Msg.length, 0, 8);
|
45
56
|
const memIndexBytes = writeUIntLE(new Uint8Array(8), memIndex, 0, 8);
|
46
|
-
const rateLimitBytes = writeUIntLE(new Uint8Array(8), rateLimit, 0, 8);
|
57
|
+
const rateLimitBytes = writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8);
|
47
58
|
// [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> | rate_limit<8> ]
|
48
59
|
return concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg, rateLimitBytes);
|
49
60
|
}
|
50
|
-
async generateRLNProof(msg, index, epoch, idSecretHash, rateLimit
|
61
|
+
async generateRLNProof(msg, index, epoch, idSecretHash, rateLimit) {
|
51
62
|
if (epoch === undefined) {
|
52
63
|
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
53
64
|
}
|
54
65
|
else if (epoch instanceof Date) {
|
55
66
|
epoch = epochIntToBytes(dateToEpoch(epoch));
|
56
67
|
}
|
68
|
+
const effectiveRateLimit = rateLimit ?? this.rateLimit;
|
57
69
|
if (epoch.length !== 32)
|
58
70
|
throw new Error("invalid epoch");
|
59
71
|
if (idSecretHash.length !== 32)
|
60
72
|
throw new Error("invalid id secret hash");
|
61
73
|
if (index < 0)
|
62
74
|
throw new Error("index must be >= 0");
|
63
|
-
if (
|
64
|
-
|
75
|
+
if (effectiveRateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
76
|
+
effectiveRateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
65
77
|
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
|
66
78
|
}
|
67
|
-
const serialized_msg = this.serializeMessage(msg, index, epoch, idSecretHash,
|
79
|
+
const serialized_msg = this.serializeMessage(msg, index, epoch, idSecretHash, effectiveRateLimit);
|
68
80
|
const rlnWitness = getSerializedRLNWitness(this.zkRLN, serialized_msg);
|
69
81
|
const inputs = RLNWitnessToJson(this.zkRLN, rlnWitness);
|
70
82
|
const calculatedWitness = await this.witnessCalculator.calculateWitness(inputs, false);
|
@@ -81,9 +93,7 @@ class Zerokit {
|
|
81
93
|
}
|
82
94
|
// calculate message length
|
83
95
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
84
|
-
const rateLimitBytes = rateLimit
|
85
|
-
? writeUIntLE(new Uint8Array(8), rateLimit, 0, 8)
|
86
|
-
: new Uint8Array(8); // Zero if not specified
|
96
|
+
const rateLimitBytes = writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8);
|
87
97
|
return verifyRLNProof(this.zkRLN, concatenate(pBytes, msgLen, msg, rateLimitBytes));
|
88
98
|
}
|
89
99
|
verifyWithRoots(proof, msg, roots, rateLimit) {
|
@@ -96,9 +106,7 @@ class Zerokit {
|
|
96
106
|
}
|
97
107
|
// calculate message length
|
98
108
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
99
|
-
const rateLimitBytes = rateLimit
|
100
|
-
? writeUIntLE(new Uint8Array(8), rateLimit, 0, 8)
|
101
|
-
: new Uint8Array(8); // Zero if not specified
|
109
|
+
const rateLimitBytes = writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8);
|
102
110
|
const rootsBytes = concatenate(...roots);
|
103
111
|
return verifyWithRoots(this.zkRLN, concatenate(pBytes, msgLen, msg, rateLimitBytes), rootsBytes);
|
104
112
|
}
|
@@ -112,9 +120,7 @@ class Zerokit {
|
|
112
120
|
}
|
113
121
|
// calculate message length
|
114
122
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
115
|
-
const rateLimitBytes = rateLimit
|
116
|
-
? writeUIntLE(new Uint8Array(8), rateLimit, 0, 8)
|
117
|
-
: new Uint8Array(8); // Zero if not specified
|
123
|
+
const rateLimitBytes = writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8);
|
118
124
|
return verifyWithRoots(this.zkRLN, concatenate(pBytes, msgLen, msg, rateLimitBytes), new Uint8Array());
|
119
125
|
}
|
120
126
|
}
|