@waku/rln 0.1.0 → 0.1.1-0fbf6be

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