@waku/rln 0.1.1-60a5070 → 0.1.1-77ba0a6
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/README.md +26 -2
- package/bundle/index.js +28303 -4713
- package/dist/.tsbuildinfo +1 -1
- package/dist/byte_utils.d.ts +7 -0
- package/dist/byte_utils.js +13 -0
- package/dist/byte_utils.js.map +1 -1
- package/dist/codec.js +0 -2
- package/dist/codec.js.map +1 -1
- package/dist/create.d.ts +2 -0
- package/dist/create.js +8 -0
- package/dist/create.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -9
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +2 -0
- package/dist/keystore/index.js.map +1 -1
- package/dist/keystore/keystore.d.ts +9 -9
- package/dist/keystore/keystore.js +12 -3
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/keystore/types.d.ts +20 -0
- package/dist/metamask.d.ts +2 -0
- package/dist/metamask.js +11 -0
- package/dist/metamask.js.map +1 -0
- package/dist/rln.d.ts +47 -0
- package/dist/rln.js +107 -0
- package/dist/rln.js.map +1 -1
- package/dist/rln_contract.d.ts +6 -6
- package/dist/rln_contract.js +20 -15
- package/dist/rln_contract.js.map +1 -1
- package/package.json +1 -1
- package/src/byte_utils.ts +14 -0
- package/src/codec.ts +0 -3
- package/src/create.ts +9 -0
- package/src/index.ts +4 -9
- package/src/metamask.ts +16 -0
- package/src/rln.ts +191 -0
- package/src/rln_contract.ts +27 -30
package/src/rln.ts
CHANGED
@@ -1,10 +1,28 @@
|
|
1
|
+
import { createDecoder, createEncoder } from "@waku/core";
|
1
2
|
import type { IRateLimitProof } from "@waku/interfaces";
|
3
|
+
import type {
|
4
|
+
ContentTopic,
|
5
|
+
IDecodedMessage,
|
6
|
+
EncoderOptions as WakuEncoderOptions,
|
7
|
+
} from "@waku/interfaces";
|
2
8
|
import init from "@waku/zerokit-rln-wasm";
|
3
9
|
import * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
10
|
+
import { ethers } from "ethers";
|
4
11
|
|
5
12
|
import { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js";
|
13
|
+
import type { RLNDecoder, RLNEncoder } from "./codec.js";
|
14
|
+
import { createRLNDecoder, createRLNEncoder } from "./codec.js";
|
15
|
+
import { SEPOLIA_CONTRACT } from "./constants.js";
|
6
16
|
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
17
|
+
import { Keystore } from "./keystore/index.js";
|
18
|
+
import type {
|
19
|
+
DecryptedCredentials,
|
20
|
+
EncryptedCredentials,
|
21
|
+
} from "./keystore/index.js";
|
22
|
+
import { Password } from "./keystore/types.js";
|
23
|
+
import { extractMetaMaskSigner } from "./metamask.js";
|
7
24
|
import verificationKey from "./resources/verification_key.js";
|
25
|
+
import { RLNContract } from "./rln_contract.js";
|
8
26
|
import * as wc from "./witness_calculator.js";
|
9
27
|
import { WitnessCalculator } from "./witness_calculator.js";
|
10
28
|
|
@@ -158,12 +176,185 @@ export function sha256(input: Uint8Array): Uint8Array {
|
|
158
176
|
return zerokitRLN.hash(lenPrefixedData);
|
159
177
|
}
|
160
178
|
|
179
|
+
type StartRLNOptions = {
|
180
|
+
/**
|
181
|
+
* If not set - will extract MetaMask account and get signer from it.
|
182
|
+
*/
|
183
|
+
signer?: ethers.Signer;
|
184
|
+
/**
|
185
|
+
* If not set - will use default SEPOLIA_CONTRACT address.
|
186
|
+
*/
|
187
|
+
registryAddress?: string;
|
188
|
+
/**
|
189
|
+
* Credentials to use for generating proofs and connecting to the contract and network.
|
190
|
+
* If provided used for validating the network chainId and connecting to registry contract.
|
191
|
+
*/
|
192
|
+
credentials?: EncryptedCredentials | DecryptedCredentials;
|
193
|
+
};
|
194
|
+
|
195
|
+
type RegisterMembershipOptions =
|
196
|
+
| { signature: string }
|
197
|
+
| { identity: IdentityCredential };
|
198
|
+
|
161
199
|
export class RLNInstance {
|
200
|
+
private started = false;
|
201
|
+
private starting = false;
|
202
|
+
|
203
|
+
private _contract: undefined | RLNContract;
|
204
|
+
private _signer: undefined | ethers.Signer;
|
205
|
+
|
206
|
+
private keystore = Keystore.create();
|
207
|
+
private _credentials: undefined | DecryptedCredentials;
|
208
|
+
|
162
209
|
constructor(
|
163
210
|
private zkRLN: number,
|
164
211
|
private witnessCalculator: WitnessCalculator
|
165
212
|
) {}
|
166
213
|
|
214
|
+
public get contract(): undefined | RLNContract {
|
215
|
+
return this._contract;
|
216
|
+
}
|
217
|
+
|
218
|
+
public get signer(): undefined | ethers.Signer {
|
219
|
+
return this._signer;
|
220
|
+
}
|
221
|
+
|
222
|
+
public async start(options: StartRLNOptions = {}): Promise<void> {
|
223
|
+
if (this.started || this.starting) {
|
224
|
+
return;
|
225
|
+
}
|
226
|
+
|
227
|
+
this.starting = true;
|
228
|
+
|
229
|
+
try {
|
230
|
+
const { signer, registryAddress } = await this.determineStartOptions(
|
231
|
+
options
|
232
|
+
);
|
233
|
+
|
234
|
+
this._signer = signer!;
|
235
|
+
this._contract = await RLNContract.init(this, {
|
236
|
+
registryAddress: registryAddress!,
|
237
|
+
signer: signer!,
|
238
|
+
});
|
239
|
+
this.started = true;
|
240
|
+
} finally {
|
241
|
+
this.starting = false;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
private async determineStartOptions(
|
246
|
+
options: StartRLNOptions
|
247
|
+
): Promise<StartRLNOptions> {
|
248
|
+
const credentials = await this.decryptCredentialsIfNeeded(
|
249
|
+
options.credentials
|
250
|
+
);
|
251
|
+
|
252
|
+
let chainId = credentials?.membership.chainId;
|
253
|
+
const registryAddress =
|
254
|
+
credentials?.membership.address ||
|
255
|
+
options.registryAddress ||
|
256
|
+
SEPOLIA_CONTRACT.address;
|
257
|
+
|
258
|
+
if (registryAddress === SEPOLIA_CONTRACT.address) {
|
259
|
+
chainId = SEPOLIA_CONTRACT.chainId;
|
260
|
+
}
|
261
|
+
|
262
|
+
const signer = options.signer || (await extractMetaMaskSigner());
|
263
|
+
const currentChainId = await signer.getChainId();
|
264
|
+
|
265
|
+
if (chainId && chainId !== currentChainId) {
|
266
|
+
throw Error(
|
267
|
+
`Failed to start RLN contract, chain ID of contract is different from current one: contract-${chainId}, current network-${currentChainId}`
|
268
|
+
);
|
269
|
+
}
|
270
|
+
|
271
|
+
return {
|
272
|
+
signer,
|
273
|
+
registryAddress,
|
274
|
+
credentials: options.credentials,
|
275
|
+
};
|
276
|
+
}
|
277
|
+
|
278
|
+
private async decryptCredentialsIfNeeded(
|
279
|
+
credentials?: EncryptedCredentials | DecryptedCredentials
|
280
|
+
): Promise<undefined | DecryptedCredentials> {
|
281
|
+
if (!credentials) {
|
282
|
+
return;
|
283
|
+
}
|
284
|
+
|
285
|
+
if ("identity" in credentials) {
|
286
|
+
this._credentials = credentials;
|
287
|
+
return credentials;
|
288
|
+
}
|
289
|
+
|
290
|
+
const keystore = Keystore.fromString(credentials.keystore);
|
291
|
+
|
292
|
+
if (!keystore) {
|
293
|
+
throw Error("Failed to start RLN: cannot read Keystore provided.");
|
294
|
+
}
|
295
|
+
|
296
|
+
this.keystore = keystore;
|
297
|
+
this._credentials = await keystore.readCredential(
|
298
|
+
credentials.id,
|
299
|
+
credentials.password
|
300
|
+
);
|
301
|
+
|
302
|
+
return this._credentials;
|
303
|
+
}
|
304
|
+
|
305
|
+
public async registerMembership(
|
306
|
+
options: RegisterMembershipOptions
|
307
|
+
): Promise<undefined | DecryptedCredentials> {
|
308
|
+
if (!this.contract) {
|
309
|
+
throw Error("RLN Contract is not initialized.");
|
310
|
+
}
|
311
|
+
|
312
|
+
let identity = "identity" in options && options.identity;
|
313
|
+
|
314
|
+
if ("signature" in options) {
|
315
|
+
identity = await this.generateSeededIdentityCredential(options.signature);
|
316
|
+
}
|
317
|
+
|
318
|
+
if (!identity) {
|
319
|
+
throw Error("Missing signature or identity to register membership.");
|
320
|
+
}
|
321
|
+
|
322
|
+
return this.contract.registerWithIdentity(identity);
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Changes credentials in use by relying on provided Keystore earlier in rln.start
|
327
|
+
* @param id: string, hash of credentials to select from Keystore
|
328
|
+
* @param password: string or bytes to use to decrypt credentials from Keystore
|
329
|
+
*/
|
330
|
+
public async useCredentials(id: string, password: Password): Promise<void> {
|
331
|
+
this._credentials = await this.keystore?.readCredential(id, password);
|
332
|
+
}
|
333
|
+
|
334
|
+
public createEncoder(options: WakuEncoderOptions): RLNEncoder {
|
335
|
+
if (!this._credentials) {
|
336
|
+
throw Error(
|
337
|
+
"Failed to create Encoder: missing RLN credentials. Use createRLNEncoder directly."
|
338
|
+
);
|
339
|
+
}
|
340
|
+
|
341
|
+
return createRLNEncoder({
|
342
|
+
encoder: createEncoder(options),
|
343
|
+
rlnInstance: this,
|
344
|
+
index: this._credentials.membership.treeIndex,
|
345
|
+
credential: this._credentials.identity,
|
346
|
+
});
|
347
|
+
}
|
348
|
+
|
349
|
+
public createDecoder(
|
350
|
+
contentTopic: ContentTopic
|
351
|
+
): RLNDecoder<IDecodedMessage> {
|
352
|
+
return createRLNDecoder({
|
353
|
+
rlnInstance: this,
|
354
|
+
decoder: createDecoder(contentTopic),
|
355
|
+
});
|
356
|
+
}
|
357
|
+
|
167
358
|
generateIdentityCredentials(): IdentityCredential {
|
168
359
|
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
169
360
|
return IdentityCredential.fromBytes(memKeys);
|
package/src/rln_contract.ts
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
import { hexToBytes } from "@waku/utils/bytes";
|
1
2
|
import { ethers } from "ethers";
|
2
3
|
|
4
|
+
import { zeroPadLE } from "./byte_utils.js";
|
3
5
|
import { RLN_REGISTRY_ABI, RLN_STORAGE_ABI } from "./constants.js";
|
4
|
-
import {
|
6
|
+
import type { DecryptedCredentials } from "./keystore/index.js";
|
7
|
+
import { type IdentityCredential, RLNInstance } from "./rln.js";
|
5
8
|
import { MerkleRootTracker } from "./root_tracker.js";
|
6
9
|
|
7
10
|
type Member = {
|
@@ -9,10 +12,10 @@ type Member = {
|
|
9
12
|
index: ethers.BigNumber;
|
10
13
|
};
|
11
14
|
|
12
|
-
type
|
15
|
+
type Signer = ethers.Signer;
|
13
16
|
|
14
17
|
type RLNContractOptions = {
|
15
|
-
|
18
|
+
signer: Signer;
|
16
19
|
registryAddress: string;
|
17
20
|
};
|
18
21
|
|
@@ -45,7 +48,7 @@ export class RLNContract {
|
|
45
48
|
): Promise<RLNContract> {
|
46
49
|
const rlnContract = new RLNContract(rlnInstance, options);
|
47
50
|
|
48
|
-
await rlnContract.initStorageContract(options.
|
51
|
+
await rlnContract.initStorageContract(options.signer);
|
49
52
|
await rlnContract.fetchMembers(rlnInstance);
|
50
53
|
rlnContract.subscribeToMembers(rlnInstance);
|
51
54
|
|
@@ -54,20 +57,20 @@ export class RLNContract {
|
|
54
57
|
|
55
58
|
constructor(
|
56
59
|
rlnInstance: RLNInstance,
|
57
|
-
{ registryAddress,
|
60
|
+
{ registryAddress, signer }: RLNContractOptions
|
58
61
|
) {
|
59
62
|
const initialRoot = rlnInstance.getMerkleRoot();
|
60
63
|
|
61
64
|
this.registryContract = new ethers.Contract(
|
62
65
|
registryAddress,
|
63
66
|
RLN_REGISTRY_ABI,
|
64
|
-
|
67
|
+
signer
|
65
68
|
);
|
66
69
|
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
|
67
70
|
}
|
68
71
|
|
69
72
|
private async initStorageContract(
|
70
|
-
|
73
|
+
signer: Signer,
|
71
74
|
options: RLNStorageOptions = {}
|
72
75
|
): Promise<void> {
|
73
76
|
const storageIndex = options?.storageIndex
|
@@ -83,7 +86,7 @@ export class RLNContract {
|
|
83
86
|
this.storageContract = new ethers.Contract(
|
84
87
|
storageAddress,
|
85
88
|
RLN_STORAGE_ABI,
|
86
|
-
|
89
|
+
signer
|
87
90
|
);
|
88
91
|
this._membersFilter = this.storageContract.filters.MemberRegistered();
|
89
92
|
|
@@ -169,15 +172,11 @@ export class RLNContract {
|
|
169
172
|
return;
|
170
173
|
}
|
171
174
|
|
172
|
-
const idCommitment =
|
173
|
-
ethers.utils.arrayify(_idCommitment),
|
174
|
-
32
|
175
|
-
);
|
175
|
+
const idCommitment = zeroPadLE(hexToBytes(_idCommitment?._hex), 32);
|
176
176
|
rlnInstance.insertMember(idCommitment);
|
177
177
|
this._members.set(index.toNumber(), {
|
178
178
|
index,
|
179
|
-
idCommitment:
|
180
|
-
_idCommitment?._hex || ethers.utils.hexlify(idCommitment),
|
179
|
+
idCommitment: _idCommitment?._hex,
|
181
180
|
});
|
182
181
|
});
|
183
182
|
|
@@ -209,19 +208,9 @@ export class RLNContract {
|
|
209
208
|
);
|
210
209
|
}
|
211
210
|
|
212
|
-
public async
|
213
|
-
|
214
|
-
|
215
|
-
): Promise<Member | undefined> {
|
216
|
-
const identityCredential =
|
217
|
-
await rlnInstance.generateSeededIdentityCredential(signature);
|
218
|
-
|
219
|
-
return this.registerWithKey(identityCredential);
|
220
|
-
}
|
221
|
-
|
222
|
-
public async registerWithKey(
|
223
|
-
credential: IdentityCredential
|
224
|
-
): Promise<Member | undefined> {
|
211
|
+
public async registerWithIdentity(
|
212
|
+
identity: IdentityCredential
|
213
|
+
): Promise<DecryptedCredentials | undefined> {
|
225
214
|
if (this.storageIndex === undefined) {
|
226
215
|
throw Error(
|
227
216
|
"Cannot register credential, no storage contract index found."
|
@@ -230,7 +219,7 @@ export class RLNContract {
|
|
230
219
|
const txRegisterResponse: ethers.ContractTransaction =
|
231
220
|
await this.registryContract["register(uint16,uint256)"](
|
232
221
|
this.storageIndex,
|
233
|
-
|
222
|
+
identity.IDCommitmentBigInt,
|
234
223
|
{ gasLimit: 100000 }
|
235
224
|
);
|
236
225
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
@@ -247,9 +236,17 @@ export class RLNContract {
|
|
247
236
|
memberRegistered.data
|
248
237
|
);
|
249
238
|
|
239
|
+
const network = await this.registryContract.provider.getNetwork();
|
240
|
+
const address = this.registryContract.address;
|
241
|
+
const membershipId = decodedData.index.toNumber();
|
242
|
+
|
250
243
|
return {
|
251
|
-
|
252
|
-
|
244
|
+
identity,
|
245
|
+
membership: {
|
246
|
+
address,
|
247
|
+
treeIndex: membershipId,
|
248
|
+
chainId: network.chainId,
|
249
|
+
},
|
253
250
|
};
|
254
251
|
}
|
255
252
|
|