@waku/rln 0.1.6-27c1236.0 → 0.1.6-2ce706d.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.
@@ -13,8 +13,9 @@ import '../../../../node_modules/multiformats/dist/src/bases/base8.js';
13
13
  import '../../../../node_modules/multiformats/dist/src/bases/identity.js';
14
14
  import '../../../../node_modules/multiformats/dist/src/codecs/json.js';
15
15
  import { Logger } from '../../../utils/dist/logger/index.js';
16
+ import { IdentityCredential } from '../identity.js';
16
17
  import { RLN_ABI } from './abi.js';
17
- import { DEFAULT_Q, DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
18
+ import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
18
19
  import { MembershipState } from './types.js';
19
20
  import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
20
21
  import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
@@ -26,11 +27,6 @@ class RLNBaseContract {
26
27
  rateLimit;
27
28
  minRateLimit;
28
29
  maxRateLimit;
29
- /**
30
- * Default Q value for the RLN contract.
31
- * @see https://github.com/waku-org/waku-rlnv2-contract/blob/b7e9a9b1bc69256a2a3076c1f099b50ce84e7eff/src/WakuRlnV2.sol#L25
32
- */
33
- idCommitmentBigIntLimit = DEFAULT_Q;
34
30
  _members = new Map();
35
31
  _membersFilter;
36
32
  _membershipErasedFilter;
@@ -69,14 +65,13 @@ class RLNBaseContract {
69
65
  */
70
66
  static async create(options) {
71
67
  const instance = new RLNBaseContract(options);
72
- const [min, max, idCommitmentBigIntLimit] = await Promise.all([
68
+ const [min, max] = await Promise.all([
73
69
  instance.contract.minMembershipRateLimit(),
74
70
  instance.contract.maxMembershipRateLimit(),
75
71
  instance.contract.Q()
76
72
  ]);
77
73
  instance.minRateLimit = BigNumber.from(min).toNumber();
78
74
  instance.maxRateLimit = BigNumber.from(max).toNumber();
79
- instance.idCommitmentBigIntLimit = BigInt(idCommitmentBigIntLimit.toString());
80
75
  instance.validateRateLimit(instance.rateLimit);
81
76
  return instance;
82
77
  }
@@ -347,8 +342,9 @@ class RLNBaseContract {
347
342
  async registerWithIdentity(identity) {
348
343
  try {
349
344
  log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
345
+ const idCommitmentBigInt = IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment);
350
346
  // Check if the ID commitment is already registered
351
- const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt);
347
+ const existingIndex = await this.getMemberIndex(idCommitmentBigInt);
352
348
  if (existingIndex) {
353
349
  throw new Error(`ID commitment is already registered with index ${existingIndex}`);
354
350
  }
@@ -357,9 +353,11 @@ class RLNBaseContract {
357
353
  if (remainingRateLimit < this.rateLimit) {
358
354
  throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
359
355
  }
360
- const estimatedGas = await this.contract.estimateGas.register(identity.IDCommitmentBigInt, this.rateLimit, []);
356
+ const estimatedGas = await this.contract.estimateGas.register(idCommitmentBigInt, this.rateLimit, []);
361
357
  const gasLimit = estimatedGas.add(10000);
362
- const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit });
358
+ const txRegisterResponse = await this.contract.register(idCommitmentBigInt, this.rateLimit, [], {
359
+ gasLimit
360
+ });
363
361
  const txRegisterReceipt = await txRegisterResponse.wait();
364
362
  if (txRegisterReceipt.status === 0) {
365
363
  throw new Error("Transaction failed on-chain");
@@ -421,7 +419,7 @@ class RLNBaseContract {
421
419
  async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
422
420
  try {
423
421
  log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
424
- 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)));
422
+ const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment), this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
425
423
  const txRegisterReceipt = await txRegisterResponse.wait();
426
424
  const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
427
425
  if (!memberRegistered || !memberRegistered.args) {
@@ -20,7 +20,7 @@ import { RLNBaseContract } from './contract/rln_base_contract.js';
20
20
  import { IdentityCredential } from './identity.js';
21
21
  import { Keystore } from './keystore/keystore.js';
22
22
  import { extractMetaMaskSigner } from './utils/metamask.js';
23
- import { buildBigIntFromUint8ArrayLE } from './utils/bytes.js';
23
+ import { switchEndianness } from './utils/bytes.js';
24
24
  import './utils/epoch.js';
25
25
 
26
26
  const log = new Logger("waku:credentials");
@@ -198,22 +198,19 @@ class RLNCredentialsManager {
198
198
  const seedBytes = encoder.encode(seed);
199
199
  // Generate deterministic values using HMAC-SHA256
200
200
  // We use different context strings for each component to ensure they're different
201
- const idTrapdoor = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
202
- const idNullifier = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
203
- const combinedBytes = new Uint8Array([...idTrapdoor, ...idNullifier]);
204
- const idSecretHash = sha256(combinedBytes);
205
- const idCommitment = sha256(idSecretHash);
206
- let idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
207
- if (!this.contract) {
208
- throw Error("RLN contract is not initialized");
209
- }
210
- const idCommitmentBigIntLimit = this.contract.idCommitmentBigIntLimit;
211
- if (idCommitmentBigInt >= idCommitmentBigIntLimit) {
212
- log.warn(`ID commitment is greater than Q, reducing it by Q(idCommitmentBigIntLimit): ${idCommitmentBigInt} % ${idCommitmentBigIntLimit}`);
213
- idCommitmentBigInt = idCommitmentBigInt % idCommitmentBigIntLimit;
214
- }
201
+ const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
202
+ const idNullifierBE = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
203
+ const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
204
+ const idSecretHashBE = sha256(combinedBytes);
205
+ const idCommitmentBE = sha256(idSecretHashBE);
206
+ // All hashing functions return big-endian bytes
207
+ // We need to switch to little-endian for the identity credential
208
+ const idTrapdoorLE = switchEndianness(idTrapdoorBE);
209
+ const idNullifierLE = switchEndianness(idNullifierBE);
210
+ const idSecretHashLE = switchEndianness(idSecretHashBE);
211
+ const idCommitmentLE = switchEndianness(idCommitmentBE);
215
212
  log.info("Successfully generated identity credential");
216
- return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
213
+ return new IdentityCredential(idTrapdoorLE, idNullifierLE, idSecretHashLE, idCommitmentLE);
217
214
  }
218
215
  }
219
216
 
@@ -1,18 +1,35 @@
1
- import { buildBigIntFromUint8ArrayLE } from './utils/bytes.js';
2
- import './utils/epoch.js';
1
+ import '../../interfaces/dist/protocols.js';
2
+ import '../../interfaces/dist/connection_manager.js';
3
+ import '../../interfaces/dist/health_indicator.js';
4
+ import '../../../node_modules/multiformats/dist/src/bases/base10.js';
5
+ import '../../../node_modules/multiformats/dist/src/bases/base16.js';
6
+ import '../../../node_modules/multiformats/dist/src/bases/base2.js';
7
+ import '../../../node_modules/multiformats/dist/src/bases/base256emoji.js';
8
+ import '../../../node_modules/multiformats/dist/src/bases/base32.js';
9
+ import '../../../node_modules/multiformats/dist/src/bases/base36.js';
10
+ import '../../../node_modules/multiformats/dist/src/bases/base58.js';
11
+ import '../../../node_modules/multiformats/dist/src/bases/base64.js';
12
+ import '../../../node_modules/multiformats/dist/src/bases/base8.js';
13
+ import '../../../node_modules/multiformats/dist/src/bases/identity.js';
14
+ import '../../../node_modules/multiformats/dist/src/codecs/json.js';
15
+ import { Logger } from '../../utils/dist/logger/index.js';
16
+ import { DEFAULT_Q } from './contract/constants.js';
17
+ import { buildBigIntFromUint8ArrayBE } from './utils/bytes.js';
3
18
 
19
+ const log = new Logger("waku:rln:identity");
4
20
  class IdentityCredential {
5
21
  IDTrapdoor;
6
22
  IDNullifier;
7
23
  IDSecretHash;
8
24
  IDCommitment;
9
- IDCommitmentBigInt;
10
- constructor(IDTrapdoor, IDNullifier, IDSecretHash, IDCommitment, IDCommitmentBigInt) {
25
+ /**
26
+ * All variables are in little-endian format
27
+ */
28
+ constructor(IDTrapdoor, IDNullifier, IDSecretHash, IDCommitment) {
11
29
  this.IDTrapdoor = IDTrapdoor;
12
30
  this.IDNullifier = IDNullifier;
13
31
  this.IDSecretHash = IDSecretHash;
14
32
  this.IDCommitment = IDCommitment;
15
- this.IDCommitmentBigInt = IDCommitmentBigInt;
16
33
  }
17
34
  static fromBytes(memKeys) {
18
35
  if (memKeys.length < 128) {
@@ -22,8 +39,21 @@ class IdentityCredential {
22
39
  const idNullifier = memKeys.subarray(32, 64);
23
40
  const idSecretHash = memKeys.subarray(64, 96);
24
41
  const idCommitment = memKeys.subarray(96, 128);
25
- const idCommitmentBigInt = buildBigIntFromUint8ArrayLE(idCommitment);
26
- return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
42
+ return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment);
43
+ }
44
+ /**
45
+ * Converts an ID commitment from bytes to a BigInt, normalizing it against a limit if needed
46
+ * @param bytes The ID commitment bytes to convert
47
+ * @param limit Optional limit to normalize against (Q value)
48
+ * @returns The ID commitment as a BigInt
49
+ */
50
+ static getIdCommitmentBigInt(bytes, limit = DEFAULT_Q) {
51
+ let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(bytes);
52
+ if (limit && idCommitmentBigIntBE >= limit) {
53
+ log.warn(`ID commitment is greater than Q, reducing it by Q: ${idCommitmentBigIntBE} % ${limit}`);
54
+ idCommitmentBigIntBE = idCommitmentBigIntBE % limit;
55
+ }
56
+ return idCommitmentBigIntBE;
27
57
  }
28
58
  }
29
59
 
@@ -17,7 +17,6 @@ import { Logger } from '../../../utils/dist/logger/index.js';
17
17
  import { sha256 } from '../../../../node_modules/ethereum-cryptography/esm/sha256.js';
18
18
  import { bytesToUtf8 } from '../../../../node_modules/ethereum-cryptography/esm/utils.js';
19
19
  import _ from '../../../../node_modules/lodash/lodash.js';
20
- import { buildBigIntFromUint8ArrayLE } from '../utils/bytes.js';
21
20
  import { keccak256Checksum, decryptEipKeystore } from './cipher.js';
22
21
  import { isKeystoreValid, isCredentialValid } from './schema_validator.js';
23
22
  import { __exports as lib } from '../../../../_virtual/index.js';
@@ -161,22 +160,19 @@ class Keystore {
161
160
  };
162
161
  }
163
162
  static fromBytesToIdentity(bytes) {
164
- function fromLittleEndian(bytes) {
165
- return new Uint8Array(bytes).reverse();
166
- }
167
163
  try {
168
164
  const str = bytesToUtf8(bytes);
169
165
  const obj = JSON.parse(str);
170
- // Use little-endian bytes directly for BigInt conversion (matches storage and contract expectation)
171
166
  const idCommitmentLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idCommitment", []));
167
+ const idTrapdoorLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idTrapdoor", []));
168
+ const idNullifierLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idNullifier", []));
169
+ const idSecretHashLE = Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idSecretHash", []));
172
170
  return {
173
171
  identity: {
174
- IDCommitment: fromLittleEndian(idCommitmentLE),
175
- IDTrapdoor: fromLittleEndian(Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idTrapdoor", []))),
176
- IDNullifier: fromLittleEndian(Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idNullifier", []))),
177
- // Do NOT reverse for BigInt conversion; use little-endian as stored
178
- IDCommitmentBigInt: buildBigIntFromUint8ArrayLE(idCommitmentLE),
179
- IDSecretHash: fromLittleEndian(Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idSecretHash", [])))
172
+ IDCommitment: idCommitmentLE,
173
+ IDTrapdoor: idTrapdoorLE,
174
+ IDNullifier: idNullifierLE,
175
+ IDSecretHash: idSecretHashLE
180
176
  },
181
177
  membership: {
182
178
  treeIndex: _.get(obj, "treeIndex"),
@@ -16,12 +16,12 @@ function concatenate(...input) {
16
16
  }
17
17
  return result;
18
18
  }
19
- // Adapted from https://github.com/feross/buffer
20
- function checkInt(buf, value, offset, ext, max, min) {
21
- if (value > max || value < min)
22
- throw new RangeError('"value" argument is out of bounds');
23
- if (offset + ext > buf.length)
24
- throw new RangeError("Index out of range");
19
+ function switchEndianness(bytes) {
20
+ return new Uint8Array(bytes.reverse());
21
+ }
22
+ function buildBigIntFromUint8ArrayBE(bytes) {
23
+ // Interpret bytes as big-endian
24
+ return bytes.reduce((acc, byte) => (acc << 8n) + BigInt(byte), 0n);
25
25
  }
26
26
  function writeUIntLE(buf, value, offset, byteLength, noAssert) {
27
27
  value = +value;
@@ -39,9 +39,6 @@ function writeUIntLE(buf, value, offset, byteLength, noAssert) {
39
39
  }
40
40
  return buf;
41
41
  }
42
- function buildBigIntFromUint8ArrayLE(bytes) {
43
- return bytes.reduce((acc, byte, i) => acc + BigInt(byte) * (1n << (8n * BigInt(i))), 0n);
44
- }
45
42
  /**
46
43
  * Fills with zeros to set length
47
44
  * @param array little endian Uint8Array
@@ -55,5 +52,12 @@ function zeroPadLE(array, length) {
55
52
  }
56
53
  return result;
57
54
  }
55
+ // Adapted from https://github.com/feross/buffer
56
+ function checkInt(buf, value, offset, ext, max, min) {
57
+ if (value > max || value < min)
58
+ throw new RangeError('"value" argument is out of bounds');
59
+ if (offset + ext > buf.length)
60
+ throw new RangeError("Index out of range");
61
+ }
58
62
 
59
- export { buildBigIntFromUint8ArrayLE, concatenate, writeUIntLE, zeroPadLE };
63
+ export { buildBigIntFromUint8ArrayBE, concatenate, switchEndianness, writeUIntLE, zeroPadLE };