@waku/rln 0.1.0-e52107d → 0.1.1-0c98a26

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 (53) hide show
  1. package/bundle/assets/rln_wasm_bg-a503e304.wasm +0 -0
  2. package/bundle/index.js +23031 -388
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/byte_utils.d.ts +6 -0
  5. package/dist/byte_utils.js +9 -0
  6. package/dist/byte_utils.js.map +1 -1
  7. package/dist/codec.d.ts +5 -3
  8. package/dist/codec.js +8 -2
  9. package/dist/codec.js.map +1 -1
  10. package/dist/constants.d.ts +3 -3
  11. package/dist/constants.js +64 -11
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.js +3 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/keystore/cipher.d.ts +4 -0
  17. package/dist/keystore/cipher.js +28 -0
  18. package/dist/keystore/cipher.js.map +1 -0
  19. package/dist/keystore/credential_validation_generated.d.ts +8 -0
  20. package/dist/keystore/credential_validation_generated.js +121 -0
  21. package/dist/keystore/credential_validation_generated.js.map +1 -0
  22. package/dist/keystore/index.d.ts +2 -0
  23. package/dist/keystore/index.js +3 -0
  24. package/dist/keystore/index.js.map +1 -0
  25. package/dist/keystore/keystore.d.ts +50 -0
  26. package/dist/keystore/keystore.js +193 -0
  27. package/dist/keystore/keystore.js.map +1 -0
  28. package/dist/keystore/keystore_validation_generated.d.ts +8 -0
  29. package/dist/keystore/keystore_validation_generated.js +75 -0
  30. package/dist/keystore/keystore_validation_generated.js.map +1 -0
  31. package/dist/keystore/schema_validator.d.ts +2 -0
  32. package/dist/keystore/schema_validator.js +18 -0
  33. package/dist/keystore/schema_validator.js.map +1 -0
  34. package/dist/keystore/types.d.ts +9 -0
  35. package/dist/keystore/types.js +2 -0
  36. package/dist/keystore/types.js.map +1 -0
  37. package/dist/message.d.ts +3 -2
  38. package/dist/message.js +6 -3
  39. package/dist/message.js.map +1 -1
  40. package/dist/rln.js +4 -12
  41. package/dist/rln.js.map +1 -1
  42. package/dist/rln_contract.d.ts +28 -14
  43. package/dist/rln_contract.js +117 -28
  44. package/dist/rln_contract.js.map +1 -1
  45. package/package.json +24 -9
  46. package/src/byte_utils.ts +10 -0
  47. package/src/codec.ts +10 -2
  48. package/src/constants.ts +65 -11
  49. package/src/index.ts +10 -3
  50. package/src/message.ts +8 -3
  51. package/src/rln.ts +4 -13
  52. package/src/rln_contract.ts +184 -44
  53. package/bundle/assets/rln_wasm_bg-6f96f821.wasm +0 -0
package/src/rln.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { IRateLimitProof } from "@waku/interfaces";
2
- import init, * as zerokitRLN from "@waku/zerokit-rln-wasm";
2
+ import init from "@waku/zerokit-rln-wasm";
3
+ import * as zerokitRLN from "@waku/zerokit-rln-wasm";
3
4
 
4
- import { writeUIntLE } from "./byte_utils.js";
5
+ import { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js";
5
6
  import { dateToEpoch, epochIntToBytes } from "./epoch.js";
6
7
  import verificationKey from "./resources/verification_key.js";
7
8
  import * as wc from "./witness_calculator.js";
@@ -26,16 +27,6 @@ function concatenate(...input: Uint8Array[]): Uint8Array {
26
27
  return result;
27
28
  }
28
29
 
29
- /**
30
- * Transforms Uint8Array into BigInt
31
- * @param array: Uint8Array
32
- * @returns BigInt
33
- */
34
- function buildBigIntFromUint8Array(array: Uint8Array): bigint {
35
- const dataView = new DataView(array.buffer);
36
- return dataView.getBigUint64(0, true);
37
- }
38
-
39
30
  const stringEncoder = new TextEncoder();
40
31
 
41
32
  const DEPTH = 20;
@@ -57,7 +48,7 @@ async function loadZkey(): Promise<Uint8Array> {
57
48
  * @returns RLNInstance
58
49
  */
59
50
  export async function create(): Promise<RLNInstance> {
60
- await init();
51
+ await (init as any)?.();
61
52
  zerokitRLN.init_panic_hook();
62
53
  const witnessCalculator = await loadWitnessCalculator();
63
54
  const zkey = await loadZkey();
@@ -1,18 +1,28 @@
1
+ import { hexToBytes } from "@waku/utils/bytes";
1
2
  import { ethers } from "ethers";
2
3
 
3
- import { RLN_ABI } from "./constants.js";
4
+ import { RLN_REGISTRY_ABI, RLN_STORAGE_ABI } from "./constants.js";
4
5
  import { IdentityCredential, RLNInstance } from "./rln.js";
6
+ import { MerkleRootTracker } from "./root_tracker.js";
5
7
 
6
8
  type Member = {
7
- pubkey: string;
8
- index: number;
9
+ idCommitment: string;
10
+ index: ethers.BigNumber;
9
11
  };
10
12
 
11
- type ContractOptions = {
12
- address: string;
13
- provider: ethers.Signer | ethers.providers.Provider;
13
+ type Provider = ethers.Signer | ethers.providers.Provider;
14
+
15
+ type RLNContractOptions = {
16
+ provider: Provider;
17
+ registryAddress: string;
18
+ };
19
+
20
+ type RLNStorageOptions = {
21
+ storageIndex?: number;
14
22
  };
15
23
 
24
+ type RLNContractInitOptions = RLNContractOptions & RLNStorageOptions;
25
+
16
26
  type FetchMembersOptions = {
17
27
  fromBlock?: number;
18
28
  fetchRange?: number;
@@ -20,34 +30,86 @@ type FetchMembersOptions = {
20
30
  };
21
31
 
22
32
  export class RLNContract {
23
- private _contract: ethers.Contract;
24
- private membersFilter: ethers.EventFilter;
33
+ private registryContract: ethers.Contract;
34
+ private merkleRootTracker: MerkleRootTracker;
25
35
 
26
- private _members: Member[] = [];
36
+ private deployBlock: undefined | number;
37
+ private storageIndex: undefined | number;
38
+ private storageContract: undefined | ethers.Contract;
39
+ private _membersFilter: undefined | ethers.EventFilter;
40
+
41
+ private _members: Map<number, Member> = new Map();
27
42
 
28
43
  public static async init(
29
44
  rlnInstance: RLNInstance,
30
- options: ContractOptions
45
+ options: RLNContractInitOptions
31
46
  ): Promise<RLNContract> {
32
- const rlnContract = new RLNContract(options);
47
+ const rlnContract = new RLNContract(rlnInstance, options);
33
48
 
49
+ await rlnContract.initStorageContract(options.provider);
34
50
  await rlnContract.fetchMembers(rlnInstance);
35
51
  rlnContract.subscribeToMembers(rlnInstance);
36
52
 
37
53
  return rlnContract;
38
54
  }
39
55
 
40
- constructor({ address, provider }: ContractOptions) {
41
- this._contract = new ethers.Contract(address, RLN_ABI, provider);
42
- this.membersFilter = this.contract.filters.MemberRegistered();
56
+ constructor(
57
+ rlnInstance: RLNInstance,
58
+ { registryAddress, provider }: RLNContractOptions
59
+ ) {
60
+ const initialRoot = rlnInstance.getMerkleRoot();
61
+
62
+ this.registryContract = new ethers.Contract(
63
+ registryAddress,
64
+ RLN_REGISTRY_ABI,
65
+ provider
66
+ );
67
+ this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
68
+ }
69
+
70
+ private async initStorageContract(
71
+ provider: Provider,
72
+ options: RLNStorageOptions = {}
73
+ ): Promise<void> {
74
+ const storageIndex = options?.storageIndex
75
+ ? options.storageIndex
76
+ : await this.registryContract.usingStorageIndex();
77
+ const storageAddress = await this.registryContract.storages(storageIndex);
78
+
79
+ if (!storageAddress || storageAddress === ethers.constants.AddressZero) {
80
+ throw Error("No RLN Storage initialized on registry contract.");
81
+ }
82
+
83
+ this.storageIndex = storageIndex;
84
+ this.storageContract = new ethers.Contract(
85
+ storageAddress,
86
+ RLN_STORAGE_ABI,
87
+ provider
88
+ );
89
+ this._membersFilter = this.storageContract.filters.MemberRegistered();
90
+
91
+ this.deployBlock = await this.storageContract.deployedBlockNumber();
43
92
  }
44
93
 
45
94
  public get contract(): ethers.Contract {
46
- return this._contract;
95
+ if (!this.storageContract) {
96
+ throw Error("Storage contract was not initialized");
97
+ }
98
+ return this.storageContract as ethers.Contract;
47
99
  }
48
100
 
49
101
  public get members(): Member[] {
50
- return this._members;
102
+ const sortedMembers = Array.from(this._members.values()).sort(
103
+ (left, right) => left.index.toNumber() - right.index.toNumber()
104
+ );
105
+ return sortedMembers;
106
+ }
107
+
108
+ private get membersFilter(): ethers.EventFilter {
109
+ if (!this._membersFilter) {
110
+ throw Error("Members filter was not initialized.");
111
+ }
112
+ return this._membersFilter as ethers.EventFilter;
51
113
  }
52
114
 
53
115
  public async fetchMembers(
@@ -55,45 +117,99 @@ export class RLNContract {
55
117
  options: FetchMembersOptions = {}
56
118
  ): Promise<void> {
57
119
  const registeredMemberEvents = await queryFilter(this.contract, {
120
+ fromBlock: this.deployBlock,
58
121
  ...options,
59
122
  membersFilter: this.membersFilter,
60
123
  });
61
-
62
- for (const event of registeredMemberEvents) {
63
- this.addMemberFromEvent(rlnInstance, event);
64
- }
124
+ this.processEvents(rlnInstance, registeredMemberEvents);
65
125
  }
66
126
 
67
- public subscribeToMembers(rlnInstance: RLNInstance): void {
68
- this.contract.on(this.membersFilter, (_pubkey, _index, event) =>
69
- this.addMemberFromEvent(rlnInstance, event)
70
- );
127
+ public processEvents(rlnInstance: RLNInstance, events: ethers.Event[]): void {
128
+ const toRemoveTable = new Map<number, number[]>();
129
+ const toInsertTable = new Map<number, ethers.Event[]>();
130
+
131
+ events.forEach((evt) => {
132
+ if (!evt.args) {
133
+ return;
134
+ }
135
+
136
+ if (evt.removed) {
137
+ const index: ethers.BigNumber = evt.args.index;
138
+ const toRemoveVal = toRemoveTable.get(evt.blockNumber);
139
+ if (toRemoveVal != undefined) {
140
+ toRemoveVal.push(index.toNumber());
141
+ toRemoveTable.set(evt.blockNumber, toRemoveVal);
142
+ } else {
143
+ toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
144
+ }
145
+ } else {
146
+ let eventsPerBlock = toInsertTable.get(evt.blockNumber);
147
+ if (eventsPerBlock == undefined) {
148
+ eventsPerBlock = [];
149
+ }
150
+
151
+ eventsPerBlock.push(evt);
152
+ toInsertTable.set(evt.blockNumber, eventsPerBlock);
153
+ }
154
+ });
155
+
156
+ this.removeMembers(rlnInstance, toRemoveTable);
157
+ this.insertMembers(rlnInstance, toInsertTable);
71
158
  }
72
159
 
73
- private addMemberFromEvent(
160
+ private insertMembers(
74
161
  rlnInstance: RLNInstance,
75
- event: ethers.Event
162
+ toInsert: Map<number, ethers.Event[]>
76
163
  ): void {
77
- if (!event.args) {
78
- return;
79
- }
164
+ toInsert.forEach((events: ethers.Event[], blockNumber: number) => {
165
+ events.forEach((evt) => {
166
+ const _idCommitment = evt?.args?.idCommitment;
167
+ const index: ethers.BigNumber = evt?.args?.index;
168
+
169
+ if (!_idCommitment || !index) {
170
+ return;
171
+ }
172
+
173
+ const idCommitment = hexToBytes(_idCommitment?._hex);
174
+ rlnInstance.insertMember(idCommitment);
175
+ this._members.set(index.toNumber(), {
176
+ index,
177
+ idCommitment: _idCommitment?._hex,
178
+ });
179
+ });
180
+
181
+ const currentRoot = rlnInstance.getMerkleRoot();
182
+ this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
183
+ });
184
+ }
80
185
 
81
- const pubkey: string = event.args.pubkey;
82
- const index: number = event.args.index;
186
+ private removeMembers(
187
+ rlnInstance: RLNInstance,
188
+ toRemove: Map<number, number[]>
189
+ ): void {
190
+ const removeDescending = new Map([...toRemove].sort().reverse());
191
+ removeDescending.forEach((indexes: number[], blockNumber: number) => {
192
+ indexes.forEach((index) => {
193
+ if (this._members.has(index)) {
194
+ this._members.delete(index);
195
+ }
196
+ rlnInstance.deleteMember(index);
197
+ });
83
198
 
84
- this.members.push({ index, pubkey });
199
+ this.merkleRootTracker.backFill(blockNumber);
200
+ });
201
+ }
85
202
 
86
- const idCommitment = ethers.utils.zeroPad(
87
- ethers.utils.arrayify(pubkey),
88
- 32
203
+ public subscribeToMembers(rlnInstance: RLNInstance): void {
204
+ this.contract.on(this.membersFilter, (_pubkey, _index, event) =>
205
+ this.processEvents(rlnInstance, [event])
89
206
  );
90
- rlnInstance.insertMember(idCommitment);
91
207
  }
92
208
 
93
209
  public async registerWithSignature(
94
210
  rlnInstance: RLNInstance,
95
211
  signature: string
96
- ): Promise<ethers.Event | undefined> {
212
+ ): Promise<Member | undefined> {
97
213
  const identityCredential =
98
214
  await rlnInstance.generateSeededIdentityCredential(signature);
99
215
 
@@ -102,16 +218,40 @@ export class RLNContract {
102
218
 
103
219
  public async registerWithKey(
104
220
  credential: IdentityCredential
105
- ): Promise<ethers.Event | undefined> {
106
- const depositValue = await this.contract.MEMBERSHIP_DEPOSIT();
107
-
221
+ ): Promise<Member | undefined> {
222
+ if (this.storageIndex === undefined) {
223
+ throw Error(
224
+ "Cannot register credential, no storage contract index found."
225
+ );
226
+ }
108
227
  const txRegisterResponse: ethers.ContractTransaction =
109
- await this.contract.register(credential.IDCommitmentBigInt, {
110
- value: depositValue,
111
- });
228
+ await this.registryContract["register(uint16,uint256)"](
229
+ this.storageIndex,
230
+ credential.IDCommitmentBigInt,
231
+ { gasLimit: 100000 }
232
+ );
112
233
  const txRegisterReceipt = await txRegisterResponse.wait();
113
234
 
114
- return txRegisterReceipt?.events?.[0];
235
+ // assumption: register(uint16,uint256) emits one event
236
+ const memberRegistered = txRegisterReceipt?.events?.[0];
237
+
238
+ if (!memberRegistered) {
239
+ return undefined;
240
+ }
241
+
242
+ const decodedData = this.contract.interface.decodeEventLog(
243
+ "MemberRegistered",
244
+ memberRegistered.data
245
+ );
246
+
247
+ return {
248
+ idCommitment: decodedData.idCommitment,
249
+ index: decodedData.index,
250
+ };
251
+ }
252
+
253
+ public roots(): Uint8Array[] {
254
+ return this.merkleRootTracker.roots();
115
255
  }
116
256
  }
117
257