@waku/rln 0.1.5-053bb95.0 → 0.1.5-4adf870.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 (49) hide show
  1. package/bundle/_virtual/utils.js +2 -2
  2. package/bundle/_virtual/utils2.js +2 -2
  3. package/bundle/packages/rln/dist/contract/constants.js +5 -2
  4. package/bundle/packages/rln/dist/contract/rln_base_contract.js +246 -116
  5. package/bundle/packages/rln/dist/contract/rln_contract.js +74 -89
  6. package/bundle/packages/rln/dist/credentials_manager.js +2 -2
  7. package/bundle/packages/rln/dist/identity.js +0 -8
  8. package/bundle/packages/rln/dist/keystore/keystore.js +28 -19
  9. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
  10. package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
  11. package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +1 -1
  12. package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +1 -1
  13. package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +1 -1
  14. package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +1 -1
  15. package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +1 -1
  16. package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +1 -1
  17. package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +1 -1
  18. package/dist/.tsbuildinfo +1 -1
  19. package/dist/contract/constants.d.ts +1 -1
  20. package/dist/contract/constants.js +1 -1
  21. package/dist/contract/constants.js.map +1 -1
  22. package/dist/contract/rln_base_contract.d.ts +30 -22
  23. package/dist/contract/rln_base_contract.js +246 -116
  24. package/dist/contract/rln_base_contract.js.map +1 -1
  25. package/dist/contract/rln_contract.d.ts +4 -24
  26. package/dist/contract/rln_contract.js +74 -89
  27. package/dist/contract/rln_contract.js.map +1 -1
  28. package/dist/credentials_manager.js +2 -2
  29. package/dist/credentials_manager.js.map +1 -1
  30. package/dist/identity.d.ts +0 -1
  31. package/dist/identity.js +0 -8
  32. package/dist/identity.js.map +1 -1
  33. package/dist/keystore/keystore.d.ts +1 -0
  34. package/dist/keystore/keystore.js +28 -19
  35. package/dist/keystore/keystore.js.map +1 -1
  36. package/dist/keystore/types.d.ts +1 -1
  37. package/package.json +1 -1
  38. package/src/contract/constants.ts +1 -1
  39. package/src/contract/rln_base_contract.ts +392 -185
  40. package/src/contract/rln_contract.ts +95 -120
  41. package/src/credentials_manager.ts +2 -2
  42. package/src/identity.ts +0 -9
  43. package/src/keystore/keystore.ts +46 -31
  44. package/src/keystore/types.ts +1 -1
  45. package/bundle/packages/rln/dist/contract/errors.js +0 -62
  46. package/dist/contract/errors.d.ts +0 -30
  47. package/dist/contract/errors.js +0 -61
  48. package/dist/contract/errors.js.map +0 -1
  49. package/src/contract/errors.ts +0 -75
@@ -5,122 +5,107 @@ 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 { ContractStateError } from './errors.js';
9
8
  import { RLNBaseContract } from './rln_base_contract.js';
9
+ import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
10
10
 
11
11
  const log = new Logger("waku:rln:contract");
12
12
  class RLNContract extends RLNBaseContract {
13
13
  instance;
14
14
  merkleRootTracker;
15
- lastSyncedBlock = 0;
16
15
  /**
17
16
  * Asynchronous initializer for RLNContract.
18
17
  * Allows injecting a mocked contract for testing purposes.
19
18
  */
20
19
  static async init(rlnInstance, options) {
21
20
  const rlnContract = new RLNContract(rlnInstance, options);
22
- await rlnContract.syncState();
23
21
  return rlnContract;
24
22
  }
25
- /**
26
- * Override base contract method to keep Merkle tree in sync
27
- * Registers a new membership with the given commitment and rate limit
28
- */
29
- async registerMembership(idCommitment, rateLimit = this.getRateLimit()) {
30
- await super.registerMembership(idCommitment, rateLimit);
31
- await this.syncState();
32
- }
33
- /**
34
- * Override base contract method to keep Merkle tree in sync
35
- * Erases an existing membership from the contract
36
- */
37
- async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
38
- await super.eraseMembership(idCommitment, eraseFromMembershipSet);
39
- await this.syncState();
40
- }
41
- /**
42
- * Gets the current Merkle root
43
- * Returns the latest valid root or empty array if no roots exist
44
- */
45
- async getMerkleRoot() {
46
- await this.syncState();
47
- const roots = this.merkleRootTracker.roots();
48
- return roots.length > 0 ? roots[0] : new Uint8Array();
49
- }
50
23
  constructor(rlnInstance, options) {
51
24
  super(options);
52
25
  this.instance = rlnInstance;
53
26
  const initialRoot = rlnInstance.zerokit.getMerkleRoot();
54
27
  this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
55
28
  }
56
- /**
57
- * Syncs the local Merkle tree with the current contract state
58
- */
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;
29
+ processEvents(events) {
30
+ const toRemoveTable = new Map();
31
+ const toInsertTable = new Map();
32
+ events.forEach((evt) => {
33
+ if (!evt.args) {
66
34
  return;
67
35
  }
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);
36
+ if (evt.event === "MembershipErased" ||
37
+ evt.event === "MembershipExpired") {
38
+ let index = evt.args.index;
39
+ if (!index) {
40
+ return;
76
41
  }
77
- catch (error) {
78
- // Ignore errors for non-existent members
79
- continue;
42
+ if (typeof index === "number" || typeof index === "string") {
43
+ index = BigNumber.from(index);
44
+ }
45
+ else {
46
+ log.error("Index is not a number or string", {
47
+ index,
48
+ event: evt
49
+ });
50
+ return;
51
+ }
52
+ const toRemoveVal = toRemoveTable.get(evt.blockNumber);
53
+ if (toRemoveVal != undefined) {
54
+ toRemoveVal.push(index.toNumber());
55
+ toRemoveTable.set(evt.blockNumber, toRemoveVal);
56
+ }
57
+ else {
58
+ toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
80
59
  }
81
60
  }
82
- // Insert all members
83
- for (const member of members) {
84
- const idCommitment = zeroPadLE(hexToBytes(member.idCommitment), 32);
85
- this.instance.zerokit.insertMember(idCommitment);
61
+ else if (evt.event === "MembershipRegistered") {
62
+ let eventsPerBlock = toInsertTable.get(evt.blockNumber);
63
+ if (eventsPerBlock == undefined) {
64
+ eventsPerBlock = [];
65
+ }
66
+ eventsPerBlock.push(evt);
67
+ toInsertTable.set(evt.blockNumber, eventsPerBlock);
86
68
  }
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}`);
92
- }
93
- catch (error) {
94
- log.error("Failed to sync state", error);
95
- throw new ContractStateError("Failed to sync contract state");
96
- }
69
+ });
70
+ this.removeMembers(this.instance, toRemoveTable);
71
+ this.insertMembers(this.instance, toInsertTable);
97
72
  }
98
- /**
99
- * Syncs new members added between fromBlock and toBlock
100
- */
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());
123
- }
73
+ insertMembers(rlnInstance, toInsert) {
74
+ toInsert.forEach((events, blockNumber) => {
75
+ events.forEach((evt) => {
76
+ if (!evt.args)
77
+ return;
78
+ const _idCommitment = evt.args.idCommitment;
79
+ let index = evt.args.index;
80
+ if (!_idCommitment || !index) {
81
+ return;
82
+ }
83
+ if (typeof index === "number" || typeof index === "string") {
84
+ index = BigNumber.from(index);
85
+ }
86
+ const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
87
+ rlnInstance.zerokit.insertMember(idCommitment);
88
+ const numericIndex = index.toNumber();
89
+ this._members.set(numericIndex, {
90
+ index,
91
+ idCommitment: _idCommitment
92
+ });
93
+ });
94
+ const currentRoot = rlnInstance.zerokit.getMerkleRoot();
95
+ this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
96
+ });
97
+ }
98
+ removeMembers(rlnInstance, toRemove) {
99
+ const removeDescending = new Map([...toRemove].reverse());
100
+ removeDescending.forEach((indexes, blockNumber) => {
101
+ indexes.forEach((index) => {
102
+ if (this._members.has(index)) {
103
+ this._members.delete(index);
104
+ rlnInstance.zerokit.deleteMember(index);
105
+ }
106
+ });
107
+ this.merkleRootTracker.backFill(blockNumber);
108
+ });
124
109
  }
125
110
  }
126
111
 
@@ -132,7 +132,7 @@ class RLNCredentialsManager {
132
132
  const signer = options.signer || (await extractMetaMaskSigner());
133
133
  const currentChainId = await signer.getChainId();
134
134
  log.info(`Current chain ID: ${currentChainId}`);
135
- if (chainId && chainId !== currentChainId.toString()) {
135
+ if (chainId && chainId !== currentChainId) {
136
136
  log.error(`Chain ID mismatch: contract=${chainId}, current=${currentChainId}`);
137
137
  throw Error(`Failed to start RLN contract, chain ID of contract is different from current one: contract-${chainId}, current network-${currentChainId}`);
138
138
  }
@@ -181,7 +181,7 @@ class RLNCredentialsManager {
181
181
  const chainId = credentials.membership.chainId;
182
182
  const network = await this.contract.provider.getNetwork();
183
183
  const currentChainId = network.chainId;
184
- if (chainId !== currentChainId.toString()) {
184
+ if (chainId !== currentChainId) {
185
185
  throw Error(`Failed to verify chain coordinates: credentials chainID=${chainId} is not equal to registryContract chainID=${currentChainId}`);
186
186
  }
187
187
  }
@@ -25,14 +25,6 @@ class IdentityCredential {
25
25
  const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
26
26
  return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
27
27
  }
28
- toJSON() {
29
- return {
30
- idTrapdoor: Array.from(this.IDTrapdoor),
31
- idNullifier: Array.from(this.IDNullifier),
32
- idSecretHash: Array.from(this.IDSecretHash),
33
- idCommitment: Array.from(this.IDCommitment)
34
- };
35
- }
36
28
  }
37
29
 
38
30
  export { IdentityCredential };
@@ -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 { IdentityCredential } from '../identity.js';
21
20
  import { buildBigIntFromUint8Array } from '../utils/bytes.js';
22
21
  import { keccak256Checksum, decryptEipKeystore } from './cipher.js';
23
22
  import { isKeystoreValid, isCredentialValid } from './schema_validator.js';
@@ -165,20 +164,20 @@ class Keystore {
165
164
  try {
166
165
  const str = bytesToUtf8(bytes);
167
166
  const obj = JSON.parse(str);
168
- // Get identity fields from named object
169
- const { idTrapdoor, idNullifier, idSecretHash, idCommitment } = _.get(obj, "identityCredential", {});
170
- const idTrapdoorArray = new Uint8Array(idTrapdoor || []);
171
- const idNullifierArray = new Uint8Array(idNullifier || []);
172
- const idSecretHashArray = new Uint8Array(idSecretHash || []);
173
- const idCommitmentArray = new Uint8Array(idCommitment || []);
174
- const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitmentArray);
167
+ // TODO: add runtime validation of nwaku credentials
175
168
  return {
176
- identity: new IdentityCredential(idTrapdoorArray, idNullifierArray, idSecretHashArray, idCommitmentArray, idCommitmentBigInt),
169
+ identity: {
170
+ IDCommitment: Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idCommitment", [])),
171
+ IDTrapdoor: Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idTrapdoor", [])),
172
+ IDNullifier: Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idNullifier", [])),
173
+ IDCommitmentBigInt: buildBigIntFromUint8Array(Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idCommitment", []))),
174
+ IDSecretHash: Keystore.fromArraylikeToBytes(_.get(obj, "identityCredential.idSecretHash", []))
175
+ },
177
176
  membership: {
178
177
  treeIndex: _.get(obj, "treeIndex"),
179
178
  chainId: _.get(obj, "membershipContract.chainId"),
180
179
  address: _.get(obj, "membershipContract.address"),
181
- rateLimit: _.get(obj, "userMessageLimit")
180
+ rateLimit: _.get(obj, "membershipContract.rateLimit")
182
181
  }
183
182
  };
184
183
  }
@@ -187,6 +186,17 @@ class Keystore {
187
186
  return;
188
187
  }
189
188
  }
189
+ static fromArraylikeToBytes(obj) {
190
+ const bytes = [];
191
+ let index = 0;
192
+ let lastElement = obj[index];
193
+ while (lastElement !== undefined) {
194
+ bytes.push(lastElement);
195
+ index += 1;
196
+ lastElement = obj[index];
197
+ }
198
+ return new Uint8Array(bytes);
199
+ }
190
200
  // follows nwaku implementation
191
201
  // https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
192
202
  static computeMembershipHash(info) {
@@ -196,18 +206,17 @@ class Keystore {
196
206
  // https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L98
197
207
  static fromIdentityToBytes(options) {
198
208
  return utf8ToBytes(JSON.stringify({
199
- membershipContract: {
200
- chainId: options.membership.chainId,
201
- address: options.membership.address
202
- },
203
209
  treeIndex: options.membership.treeIndex,
204
210
  identityCredential: {
205
- idTrapdoor: Array.from(options.identity.IDTrapdoor),
206
- idNullifier: Array.from(options.identity.IDNullifier),
207
- idSecretHash: Array.from(options.identity.IDSecretHash),
208
- idCommitment: Array.from(options.identity.IDCommitment)
211
+ idCommitment: options.identity.IDCommitment,
212
+ idNullifier: options.identity.IDNullifier,
213
+ idSecretHash: options.identity.IDSecretHash,
214
+ idTrapdoor: options.identity.IDTrapdoor
209
215
  },
210
- userMessageLimit: options.membership.rateLimit
216
+ membershipContract: {
217
+ chainId: options.membership.chainId,
218
+ address: options.membership.address
219
+ }
211
220
  }));
212
221
  }
213
222
  }
@@ -1,6 +1,6 @@
1
1
  import { __exports as random } from '../../../../../../../_virtual/random.js';
2
2
  import '../../../../@noble/hashes/utils.js';
3
- import { __exports as utils } from '../../../../../../../_virtual/utils2.js';
3
+ import { __exports as utils } from '../../../../../../../_virtual/utils.js';
4
4
 
5
5
  Object.defineProperty(random, "__esModule", { value: true });
6
6
  random.getRandomBytes = random.getRandomBytesSync = void 0;
@@ -1,10 +1,10 @@
1
1
  import { commonjsGlobal } from '../../../../../../../_virtual/_commonjsHelpers.js';
2
2
  import { commonjsRequire } from '../../../../../../../_virtual/_commonjs-dynamic-modules.js';
3
- import { __module as utils } from '../../../../../../../_virtual/utils.js';
3
+ import { __module as utils } from '../../../../../../../_virtual/utils2.js';
4
4
  import '../../../../@noble/hashes/_assert.js';
5
5
  import '../../../../@noble/hashes/utils.js';
6
6
  import { __exports as _assert } from '../../../../../../../_virtual/_assert.js';
7
- import { __exports as utils$1 } from '../../../../../../../_virtual/utils2.js';
7
+ import { __exports as utils$1 } from '../../../../../../../_virtual/utils.js';
8
8
 
9
9
  utils.exports;
10
10
 
@@ -1,7 +1,7 @@
1
1
  import { __exports as _sha2 } from '../../../../../_virtual/_sha2.js';
2
2
  import './_assert.js';
3
3
  import './utils.js';
4
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
4
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
5
5
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
6
6
 
7
7
  Object.defineProperty(_sha2, "__esModule", { value: true });
@@ -1,7 +1,7 @@
1
1
  import { __exports as hmac } from '../../../../../_virtual/hmac.js';
2
2
  import './_assert.js';
3
3
  import './utils.js';
4
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
4
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
5
5
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
6
6
 
7
7
  (function (exports) {
@@ -2,7 +2,7 @@ import { __exports as pbkdf2$1 } from '../../../../../_virtual/pbkdf22.js';
2
2
  import './_assert.js';
3
3
  import './hmac.js';
4
4
  import './utils.js';
5
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
5
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
6
6
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
7
7
  import { __exports as hmac } from '../../../../../_virtual/hmac.js';
8
8
 
@@ -3,7 +3,7 @@ import './_assert.js';
3
3
  import './sha256.js';
4
4
  import './pbkdf2.js';
5
5
  import './utils.js';
6
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
6
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
7
7
  import { __exports as _assert } from '../../../../../_virtual/_assert.js';
8
8
  import { __exports as pbkdf2 } from '../../../../../_virtual/pbkdf22.js';
9
9
  import { __exports as sha256 } from '../../../../../_virtual/sha2562.js';
@@ -2,7 +2,7 @@ import { __exports as sha256 } from '../../../../../_virtual/sha2562.js';
2
2
  import './_sha2.js';
3
3
  import './utils.js';
4
4
  import { __exports as _sha2 } from '../../../../../_virtual/_sha2.js';
5
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
5
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
6
6
 
7
7
  Object.defineProperty(sha256, "__esModule", { value: true });
8
8
  sha256.sha224 = sha256.sha256 = void 0;
@@ -4,7 +4,7 @@ import './_u64.js';
4
4
  import './utils.js';
5
5
  import { __exports as _u64 } from '../../../../../_virtual/_u64.js';
6
6
  import { __exports as _sha2 } from '../../../../../_virtual/_sha2.js';
7
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
7
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
8
8
 
9
9
  Object.defineProperty(sha512, "__esModule", { value: true });
10
10
  sha512.sha384 = sha512.sha512_256 = sha512.sha512_224 = sha512.sha512 = sha512.SHA512 = void 0;
@@ -1,4 +1,4 @@
1
- import { __exports as utils } from '../../../../../_virtual/utils2.js';
1
+ import { __exports as utils } from '../../../../../_virtual/utils.js';
2
2
  import './cryptoBrowser.js';
3
3
  import { __exports as cryptoBrowser } from '../../../../../_virtual/cryptoBrowser.js';
4
4