@waku/rln 0.1.6-7fba26d.0 → 0.1.6-86bbf5b.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.
- package/bundle/_virtual/utils.js +2 -2
- package/bundle/_virtual/utils2.js +2 -2
- package/bundle/index.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/_sha2.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/hmac.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/pbkdf2.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/scrypt.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/sha256.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/sha512.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/@noble/hashes/utils.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
- package/bundle/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
- package/bundle/packages/core/dist/lib/message/version_0.js +1 -4
- package/bundle/packages/rln/dist/contract/constants.js +7 -1
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +10 -21
- package/bundle/packages/rln/dist/credentials_manager.js +14 -16
- package/bundle/packages/rln/dist/identity.js +40 -7
- package/bundle/packages/rln/dist/keystore/keystore.js +15 -11
- package/bundle/packages/rln/dist/message.js +11 -0
- package/bundle/packages/rln/dist/utils/bytes.js +37 -16
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/constants.d.ts +6 -0
- package/dist/contract/constants.js +6 -0
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/rln_base_contract.d.ts +0 -10
- package/dist/contract/rln_base_contract.js +10 -21
- package/dist/contract/rln_base_contract.js.map +1 -1
- package/dist/credentials_manager.js +14 -16
- package/dist/credentials_manager.js.map +1 -1
- package/dist/identity.d.ts +11 -2
- package/dist/identity.js +26 -6
- package/dist/identity.js.map +1 -1
- package/dist/keystore/keystore.js +15 -11
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/message.d.ts +5 -4
- package/dist/message.js +2 -0
- package/dist/message.js.map +1 -1
- package/dist/utils/bytes.d.ts +12 -5
- package/dist/utils/bytes.js +36 -15
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contract/constants.ts +9 -0
- package/src/contract/rln_base_contract.ts +12 -30
- package/src/credentials_manager.ts +21 -24
- package/src/identity.ts +41 -6
- package/src/keystore/keystore.ts +28 -24
- package/src/message.ts +7 -4
- package/src/utils/bytes.ts +46 -25
- package/src/utils/index.ts +1 -1
- package/dist/contract/test-utils.d.ts +0 -39
- package/dist/contract/test-utils.js +0 -118
- package/dist/contract/test-utils.js.map +0 -1
- package/src/contract/test-utils.ts +0 -179
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"name":"@waku/rln","version":"0.1.6-
|
1
|
+
{"name":"@waku/rln","version":"0.1.6-86bbf5b.0","description":"RLN (Rate Limiting Nullifier) implementation for Waku","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"type":"module","homepage":"https://github.com/waku-org/js-waku/tree/master/packages/rln#readme","repository":{"type":"git","url":"https://github.com/waku-org/js-waku.git"},"bugs":{"url":"https://github.com/waku-org/js-waku/issues"},"license":"MIT OR Apache-2.0","keywords":["waku","rln","rate-limiting","privacy","web3"],"scripts":{"build":"run-s build:**","build:copy":"mkdir -p dist/resources && cp -r src/resources/* dist/resources/","build:esm":"tsc","build:bundle":"rollup --config rollup.config.js","fix":"run-s fix:*","fix:lint":"eslint src *.js --fix","check":"run-s check:*","check:tsc":"tsc -p tsconfig.dev.json","check:lint":"eslint \"src/!(resources)/**/*.{ts,js}\" *.js","check:spelling":"cspell \"{README.md,src/**/*.ts}\"","test":"NODE_ENV=test run-s test:*","test:browser":"karma start karma.conf.cjs","watch:build":"tsc -p tsconfig.json -w","watch:test":"mocha --watch","prepublish":"npm run build","reset-hard":"git clean -dfx -e .idea && git reset --hard && npm i && npm run build"},"engines":{"node":">=20"},"devDependencies":{"@rollup/plugin-commonjs":"^25.0.7","@rollup/plugin-json":"^6.0.0","@rollup/plugin-node-resolve":"^15.2.3","@types/chai":"^5.0.1","@types/chai-spies":"^1.0.6","@waku/interfaces":"0.0.31-86bbf5b.0","@types/deep-equal-in-any-order":"^1.0.4","@types/lodash":"^4.17.15","@types/sinon":"^17.0.3","@waku/build-utils":"^1.0.0","@waku/message-encryption":"0.0.34-86bbf5b.0","deep-equal-in-any-order":"^2.0.6","fast-check":"^3.23.2","rollup-plugin-copy":"^3.5.0"},"files":["dist","bundle","src/**/*.ts","!**/*.spec.*","!**/*.json","CHANGELOG.md","LICENSE","README.md"],"dependencies":{"@chainsafe/bls-keystore":"3.0.0","@waku/core":"0.0.36-86bbf5b.0","@waku/utils":"0.0.24-86bbf5b.0","@noble/hashes":"^1.2.0","@waku/zerokit-rln-wasm":"^0.0.13","ethereum-cryptography":"^3.1.0","ethers":"^5.7.2","lodash":"^4.17.21","uuid":"^11.0.5","chai":"^5.1.2","chai-as-promised":"^8.0.1","chai-spies":"^1.1.0","chai-subset":"^1.6.0","sinon":"^19.0.2"}}
|
@@ -25,4 +25,13 @@ export const RATE_LIMIT_PARAMS = {
|
|
25
25
|
EPOCH_LENGTH: 600 // Epoch length in seconds (10 minutes)
|
26
26
|
} as const;
|
27
27
|
|
28
|
+
/**
|
29
|
+
* Default Q value for the RLN contract
|
30
|
+
* This is the upper bound for the ID commitment
|
31
|
+
* @see https://github.com/waku-org/specs/blob/master/standards/core/rln-contract.md#implementation-suggestions
|
32
|
+
*/
|
33
|
+
export const DEFAULT_Q = BigInt(
|
34
|
+
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
|
35
|
+
);
|
36
|
+
|
28
37
|
export const DEFAULT_RATE_LIMIT = RATE_LIMIT_PARAMS.MAX_RATE;
|
@@ -25,12 +25,6 @@ export class RLNBaseContract {
|
|
25
25
|
private minRateLimit?: number;
|
26
26
|
private maxRateLimit?: number;
|
27
27
|
|
28
|
-
/**
|
29
|
-
* Default Q value for the RLN contract.
|
30
|
-
* @see https://github.com/waku-org/waku-rlnv2-contract/blob/b7e9a9b1bc69256a2a3076c1f099b50ce84e7eff/src/WakuRlnV2.sol#L25
|
31
|
-
*/
|
32
|
-
private Q: undefined | bigint;
|
33
|
-
|
34
28
|
protected _members: Map<number, Member> = new Map();
|
35
29
|
private _membersFilter: ethers.EventFilter;
|
36
30
|
private _membershipErasedFilter: ethers.EventFilter;
|
@@ -85,7 +79,8 @@ export class RLNBaseContract {
|
|
85
79
|
const instance = new RLNBaseContract(options);
|
86
80
|
const [min, max] = await Promise.all([
|
87
81
|
instance.contract.minMembershipRateLimit(),
|
88
|
-
instance.contract.maxMembershipRateLimit()
|
82
|
+
instance.contract.maxMembershipRateLimit(),
|
83
|
+
instance.contract.Q()
|
89
84
|
]);
|
90
85
|
instance.minRateLimit = ethers.BigNumber.from(min).toNumber();
|
91
86
|
instance.maxRateLimit = ethers.BigNumber.from(max).toNumber();
|
@@ -94,17 +89,6 @@ export class RLNBaseContract {
|
|
94
89
|
return instance;
|
95
90
|
}
|
96
91
|
|
97
|
-
/**
|
98
|
-
* Fetches and caches the Q value from the contract.
|
99
|
-
* @returns Promise<bigint> The Q value from the contract
|
100
|
-
*/
|
101
|
-
public async getQ(): Promise<bigint> {
|
102
|
-
if (this.Q !== undefined) return this.Q;
|
103
|
-
const q = await this.contract.Q();
|
104
|
-
this.Q = BigInt(q.toString());
|
105
|
-
return this.Q;
|
106
|
-
}
|
107
|
-
|
108
92
|
/**
|
109
93
|
* Gets the current rate limit for this contract instance
|
110
94
|
*/
|
@@ -507,7 +491,6 @@ export class RLNBaseContract {
|
|
507
491
|
log.error(`Error in withdraw: ${(error as Error).message}`);
|
508
492
|
}
|
509
493
|
}
|
510
|
-
|
511
494
|
public async registerWithIdentity(
|
512
495
|
identity: IdentityCredential
|
513
496
|
): Promise<DecryptedCredentials | undefined> {
|
@@ -516,10 +499,12 @@ export class RLNBaseContract {
|
|
516
499
|
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
|
517
500
|
);
|
518
501
|
|
519
|
-
|
520
|
-
|
521
|
-
identity.IDCommitmentBigInt
|
502
|
+
const idCommitmentBigInt = IdentityCredential.getIdCommitmentBigInt(
|
503
|
+
identity.IDCommitment
|
522
504
|
);
|
505
|
+
|
506
|
+
// Check if the ID commitment is already registered
|
507
|
+
const existingIndex = await this.getMemberIndex(idCommitmentBigInt);
|
523
508
|
if (existingIndex) {
|
524
509
|
throw new Error(
|
525
510
|
`ID commitment is already registered with index ${existingIndex}`
|
@@ -535,19 +520,16 @@ export class RLNBaseContract {
|
|
535
520
|
}
|
536
521
|
|
537
522
|
const estimatedGas = await this.contract.estimateGas.register(
|
538
|
-
|
523
|
+
idCommitmentBigInt,
|
539
524
|
this.rateLimit,
|
540
525
|
[]
|
541
526
|
);
|
542
527
|
const gasLimit = estimatedGas.add(10000);
|
543
528
|
|
544
529
|
const txRegisterResponse: ethers.ContractTransaction =
|
545
|
-
await this.contract.register(
|
546
|
-
|
547
|
-
|
548
|
-
[],
|
549
|
-
{ gasLimit }
|
550
|
-
);
|
530
|
+
await this.contract.register(idCommitmentBigInt, this.rateLimit, [], {
|
531
|
+
gasLimit
|
532
|
+
});
|
551
533
|
|
552
534
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
553
535
|
|
@@ -643,7 +625,7 @@ export class RLNBaseContract {
|
|
643
625
|
permit.v,
|
644
626
|
permit.r,
|
645
627
|
permit.s,
|
646
|
-
identity.
|
628
|
+
IdentityCredential.getIdCommitmentBigInt(identity.IDCommitment),
|
647
629
|
this.rateLimit,
|
648
630
|
idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
|
649
631
|
);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { hmac } from "@noble/hashes/hmac";
|
2
|
-
import { sha256 } from "@noble/hashes/
|
2
|
+
import { sha256 } from "@noble/hashes/sha2";
|
3
3
|
import { Logger } from "@waku/utils";
|
4
4
|
import { ethers } from "ethers";
|
5
5
|
|
@@ -13,10 +13,7 @@ import type {
|
|
13
13
|
} from "./keystore/index.js";
|
14
14
|
import { KeystoreEntity, Password } from "./keystore/types.js";
|
15
15
|
import { RegisterMembershipOptions, StartRLNOptions } from "./types.js";
|
16
|
-
import {
|
17
|
-
buildBigIntFromUint8Array,
|
18
|
-
extractMetaMaskSigner
|
19
|
-
} from "./utils/index.js";
|
16
|
+
import { extractMetaMaskSigner, switchEndianness } from "./utils/index.js";
|
20
17
|
import { Zerokit } from "./zerokit.js";
|
21
18
|
|
22
19
|
const log = new Logger("waku:credentials");
|
@@ -261,31 +258,31 @@ export class RLNCredentialsManager {
|
|
261
258
|
|
262
259
|
// Generate deterministic values using HMAC-SHA256
|
263
260
|
// We use different context strings for each component to ensure they're different
|
264
|
-
const
|
265
|
-
const
|
261
|
+
const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
|
262
|
+
const idNullifierBE = hmac(
|
263
|
+
sha256,
|
264
|
+
seedBytes,
|
265
|
+
encoder.encode("IDNullifier")
|
266
|
+
);
|
266
267
|
|
267
|
-
const combinedBytes = new Uint8Array([...
|
268
|
-
const
|
268
|
+
const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
|
269
|
+
const idSecretHashBE = sha256(combinedBytes);
|
269
270
|
|
270
|
-
const
|
271
|
+
const idCommitmentBE = sha256(idSecretHashBE);
|
271
272
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
const
|
278
|
-
if (idCommitmentBigInt >= Q) {
|
279
|
-
idCommitmentBigInt = idCommitmentBigInt % Q;
|
280
|
-
}
|
273
|
+
// All hashing functions return big-endian bytes
|
274
|
+
// We need to switch to little-endian for the identity credential
|
275
|
+
const idTrapdoorLE = switchEndianness(idTrapdoorBE);
|
276
|
+
const idNullifierLE = switchEndianness(idNullifierBE);
|
277
|
+
const idSecretHashLE = switchEndianness(idSecretHashBE);
|
278
|
+
const idCommitmentLE = switchEndianness(idCommitmentBE);
|
281
279
|
|
282
280
|
log.info("Successfully generated identity credential");
|
283
281
|
return new IdentityCredential(
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
idCommitmentBigInt
|
282
|
+
idTrapdoorLE,
|
283
|
+
idNullifierLE,
|
284
|
+
idSecretHashLE,
|
285
|
+
idCommitmentLE
|
289
286
|
);
|
290
287
|
}
|
291
288
|
}
|
package/src/identity.ts
CHANGED
@@ -1,12 +1,22 @@
|
|
1
|
-
import {
|
1
|
+
import { Logger } from "@waku/utils";
|
2
|
+
|
3
|
+
import { DEFAULT_Q } from "./contract/constants.js";
|
4
|
+
import {
|
5
|
+
buildBigIntFromUint8ArrayLE,
|
6
|
+
switchEndiannessBigInt
|
7
|
+
} from "./utils/bytes.js";
|
8
|
+
|
9
|
+
const log = new Logger("waku:rln:identity");
|
2
10
|
|
3
11
|
export class IdentityCredential {
|
12
|
+
/**
|
13
|
+
* All variables are in little-endian format
|
14
|
+
*/
|
4
15
|
public constructor(
|
5
16
|
public readonly IDTrapdoor: Uint8Array,
|
6
17
|
public readonly IDNullifier: Uint8Array,
|
7
18
|
public readonly IDSecretHash: Uint8Array,
|
8
|
-
public readonly IDCommitment: Uint8Array
|
9
|
-
public readonly IDCommitmentBigInt: bigint
|
19
|
+
public readonly IDCommitment: Uint8Array
|
10
20
|
) {}
|
11
21
|
|
12
22
|
public static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
@@ -18,14 +28,39 @@ export class IdentityCredential {
|
|
18
28
|
const idNullifier = memKeys.subarray(32, 64);
|
19
29
|
const idSecretHash = memKeys.subarray(64, 96);
|
20
30
|
const idCommitment = memKeys.subarray(96, 128);
|
21
|
-
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
|
22
31
|
|
23
32
|
return new IdentityCredential(
|
24
33
|
idTrapdoor,
|
25
34
|
idNullifier,
|
26
35
|
idSecretHash,
|
27
|
-
idCommitment
|
28
|
-
idCommitmentBigInt
|
36
|
+
idCommitment
|
29
37
|
);
|
30
38
|
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Converts an ID commitment from bytes to a BigInt, normalizing it against a limit if needed
|
42
|
+
* @param bytes The ID commitment bytes to convert
|
43
|
+
* @param limit Optional limit to normalize against (Q value)
|
44
|
+
* @returns The ID commitment as a BigInt
|
45
|
+
*/
|
46
|
+
public static getIdCommitmentBigInt(
|
47
|
+
idCommitment: Uint8Array,
|
48
|
+
returnType: "big-endian" | "little-endian" = "big-endian",
|
49
|
+
limit: bigint = DEFAULT_Q
|
50
|
+
): bigint {
|
51
|
+
let idCommitmentBigIntLE = buildBigIntFromUint8ArrayLE(idCommitment);
|
52
|
+
|
53
|
+
if (limit && idCommitmentBigIntLE >= limit) {
|
54
|
+
log.warn(
|
55
|
+
`ID commitment is greater than Q, reducing it by Q: ${idCommitmentBigIntLE} % ${limit}`
|
56
|
+
);
|
57
|
+
idCommitmentBigIntLE = idCommitmentBigIntLE % limit;
|
58
|
+
}
|
59
|
+
|
60
|
+
if (returnType === "big-endian") {
|
61
|
+
return switchEndiannessBigInt(idCommitmentBigIntLE);
|
62
|
+
}
|
63
|
+
|
64
|
+
return idCommitmentBigIntLE;
|
65
|
+
}
|
31
66
|
}
|
package/src/keystore/keystore.ts
CHANGED
@@ -14,8 +14,6 @@ import {
|
|
14
14
|
import _ from "lodash";
|
15
15
|
import { v4 as uuidV4 } from "uuid";
|
16
16
|
|
17
|
-
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
18
|
-
|
19
17
|
import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
|
20
18
|
import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
|
21
19
|
import type {
|
@@ -250,26 +248,25 @@ export class Keystore {
|
|
250
248
|
const str = bytesToUtf8(bytes);
|
251
249
|
const obj = JSON.parse(str);
|
252
250
|
|
253
|
-
|
251
|
+
const idCommitmentLE = Keystore.fromArraylikeToBytes(
|
252
|
+
_.get(obj, "identityCredential.idCommitment", [])
|
253
|
+
);
|
254
|
+
const idTrapdoorLE = Keystore.fromArraylikeToBytes(
|
255
|
+
_.get(obj, "identityCredential.idTrapdoor", [])
|
256
|
+
);
|
257
|
+
const idNullifierLE = Keystore.fromArraylikeToBytes(
|
258
|
+
_.get(obj, "identityCredential.idNullifier", [])
|
259
|
+
);
|
260
|
+
const idSecretHashLE = Keystore.fromArraylikeToBytes(
|
261
|
+
_.get(obj, "identityCredential.idSecretHash", [])
|
262
|
+
);
|
263
|
+
|
254
264
|
return {
|
255
265
|
identity: {
|
256
|
-
IDCommitment:
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
_.get(obj, "identityCredential.idTrapdoor", [])
|
261
|
-
),
|
262
|
-
IDNullifier: Keystore.fromArraylikeToBytes(
|
263
|
-
_.get(obj, "identityCredential.idNullifier", [])
|
264
|
-
),
|
265
|
-
IDCommitmentBigInt: buildBigIntFromUint8Array(
|
266
|
-
Keystore.fromArraylikeToBytes(
|
267
|
-
_.get(obj, "identityCredential.idCommitment", [])
|
268
|
-
)
|
269
|
-
),
|
270
|
-
IDSecretHash: Keystore.fromArraylikeToBytes(
|
271
|
-
_.get(obj, "identityCredential.idSecretHash", [])
|
272
|
-
)
|
266
|
+
IDCommitment: idCommitmentLE,
|
267
|
+
IDTrapdoor: idTrapdoorLE,
|
268
|
+
IDNullifier: idNullifierLE,
|
269
|
+
IDSecretHash: idSecretHashLE
|
273
270
|
},
|
274
271
|
membership: {
|
275
272
|
treeIndex: _.get(obj, "treeIndex"),
|
@@ -321,14 +318,21 @@ export class Keystore {
|
|
321
318
|
// follows nwaku implementation
|
322
319
|
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L98
|
323
320
|
private static fromIdentityToBytes(options: KeystoreEntity): Uint8Array {
|
321
|
+
function toLittleEndian(bytes: Uint8Array): Uint8Array {
|
322
|
+
return new Uint8Array(bytes).reverse();
|
323
|
+
}
|
324
324
|
return utf8ToBytes(
|
325
325
|
JSON.stringify({
|
326
326
|
treeIndex: options.membership.treeIndex,
|
327
327
|
identityCredential: {
|
328
|
-
idCommitment: Array.from(
|
329
|
-
|
330
|
-
|
331
|
-
|
328
|
+
idCommitment: Array.from(
|
329
|
+
toLittleEndian(options.identity.IDCommitment)
|
330
|
+
),
|
331
|
+
idNullifier: Array.from(toLittleEndian(options.identity.IDNullifier)),
|
332
|
+
idSecretHash: Array.from(
|
333
|
+
toLittleEndian(options.identity.IDSecretHash)
|
334
|
+
),
|
335
|
+
idTrapdoor: Array.from(toLittleEndian(options.identity.IDTrapdoor))
|
332
336
|
},
|
333
337
|
membershipContract: {
|
334
338
|
chainId: options.membership.chainId,
|
package/src/message.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
import { message } from "@waku/core";
|
1
2
|
import type {
|
2
3
|
IDecodedMessage,
|
3
4
|
IMessage,
|
4
|
-
IRateLimitProof
|
5
|
+
IRateLimitProof,
|
6
|
+
IRlnMessage
|
5
7
|
} from "@waku/interfaces";
|
6
8
|
import * as utils from "@waku/utils/bytes";
|
7
9
|
|
@@ -13,12 +15,13 @@ export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array {
|
|
13
15
|
return new Uint8Array([...(msg.payload ?? []), ...contentTopicBytes]);
|
14
16
|
}
|
15
17
|
|
16
|
-
export class RlnMessage<T extends IDecodedMessage> implements
|
18
|
+
export class RlnMessage<T extends IDecodedMessage> implements IRlnMessage {
|
17
19
|
public pubsubTopic = "";
|
20
|
+
public version = message.version_0.Version;
|
18
21
|
|
19
22
|
public constructor(
|
20
|
-
|
21
|
-
|
23
|
+
private rlnInstance: RLNInstance,
|
24
|
+
private msg: T,
|
22
25
|
public rateLimitProof: IRateLimitProof | undefined
|
23
26
|
) {}
|
24
27
|
|
package/src/utils/bytes.ts
CHANGED
@@ -17,18 +17,38 @@ export function concatenate(...input: Uint8Array[]): Uint8Array {
|
|
17
17
|
return result;
|
18
18
|
}
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
export function switchEndianness(bytes: Uint8Array): Uint8Array {
|
21
|
+
return new Uint8Array(bytes.reverse());
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Builds a BigInt from a little-endian Uint8Array
|
26
|
+
* @param bytes The little-endian bytes to convert
|
27
|
+
* @returns The resulting BigInt in little-endian format
|
28
|
+
*/
|
29
|
+
export function buildBigIntFromUint8ArrayLE(bytes: Uint8Array): bigint {
|
30
|
+
let result = 0n;
|
31
|
+
for (let i = bytes.length - 1; i >= 0; i--) {
|
32
|
+
result = (result << 8n) + BigInt(bytes[i]);
|
33
|
+
}
|
34
|
+
return result;
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Switches endianness of a bigint value
|
39
|
+
* @param value The bigint value to switch endianness for
|
40
|
+
* @returns The bigint value with reversed endianness
|
41
|
+
*/
|
42
|
+
export function switchEndiannessBigInt(value: bigint): bigint {
|
43
|
+
// Convert bigint to byte array
|
44
|
+
const bytes = [];
|
45
|
+
while (value > 0n) {
|
46
|
+
bytes.push(Number(value & 0xffn));
|
47
|
+
value >>= 8n;
|
48
|
+
}
|
49
|
+
|
50
|
+
// Reverse bytes and convert back to bigint
|
51
|
+
return bytes.reverse().reduce((acc, byte) => (acc << 8n) + BigInt(byte), 0n);
|
32
52
|
}
|
33
53
|
|
34
54
|
export function writeUIntLE(
|
@@ -56,19 +76,6 @@ export function writeUIntLE(
|
|
56
76
|
return buf;
|
57
77
|
}
|
58
78
|
|
59
|
-
/**
|
60
|
-
* Transforms Uint8Array into BigInt
|
61
|
-
* @param array: Uint8Array
|
62
|
-
* @returns BigInt
|
63
|
-
*/
|
64
|
-
export function buildBigIntFromUint8Array(
|
65
|
-
array: Uint8Array,
|
66
|
-
byteOffset: number = 0
|
67
|
-
): bigint {
|
68
|
-
const dataView = new DataView(array.buffer);
|
69
|
-
return dataView.getBigUint64(byteOffset, true);
|
70
|
-
}
|
71
|
-
|
72
79
|
/**
|
73
80
|
* Fills with zeros to set length
|
74
81
|
* @param array little endian Uint8Array
|
@@ -82,3 +89,17 @@ export function zeroPadLE(array: Uint8Array, length: number): Uint8Array {
|
|
82
89
|
}
|
83
90
|
return result;
|
84
91
|
}
|
92
|
+
|
93
|
+
// Adapted from https://github.com/feross/buffer
|
94
|
+
function checkInt(
|
95
|
+
buf: Uint8Array,
|
96
|
+
value: number,
|
97
|
+
offset: number,
|
98
|
+
ext: number,
|
99
|
+
max: number,
|
100
|
+
min: number
|
101
|
+
): void {
|
102
|
+
if (value > max || value < min)
|
103
|
+
throw new RangeError('"value" argument is out of bounds');
|
104
|
+
if (offset + ext > buf.length) throw new RangeError("Index out of range");
|
105
|
+
}
|
package/src/utils/index.ts
CHANGED
@@ -1,39 +0,0 @@
|
|
1
|
-
import * as ethers from "ethers";
|
2
|
-
import sinon from "sinon";
|
3
|
-
import type { IdentityCredential } from "../identity.js";
|
4
|
-
export declare const mockRateLimits: {
|
5
|
-
minRate: number;
|
6
|
-
maxRate: number;
|
7
|
-
maxTotalRate: number;
|
8
|
-
currentTotalRate: number;
|
9
|
-
};
|
10
|
-
type MockProvider = {
|
11
|
-
getLogs: () => never[];
|
12
|
-
getBlockNumber: () => Promise<number>;
|
13
|
-
getNetwork: () => Promise<{
|
14
|
-
chainId: number;
|
15
|
-
}>;
|
16
|
-
};
|
17
|
-
type MockFilters = {
|
18
|
-
MembershipRegistered: () => {
|
19
|
-
address: string;
|
20
|
-
};
|
21
|
-
MembershipErased: () => {
|
22
|
-
address: string;
|
23
|
-
};
|
24
|
-
MembershipExpired: () => {
|
25
|
-
address: string;
|
26
|
-
};
|
27
|
-
};
|
28
|
-
export declare function createMockProvider(): MockProvider;
|
29
|
-
export declare function createMockFilters(): MockFilters;
|
30
|
-
type ContractOverrides = Partial<{
|
31
|
-
filters: Record<string, unknown>;
|
32
|
-
[key: string]: unknown;
|
33
|
-
}>;
|
34
|
-
export declare function createMockRegistryContract(overrides?: ContractOverrides): ethers.Contract;
|
35
|
-
export declare function mockRLNRegisteredEvent(idCommitment?: string): ethers.Event;
|
36
|
-
export declare function formatIdCommitment(idCommitmentBigInt: bigint): string;
|
37
|
-
export declare function createRegisterStub(identity: IdentityCredential): sinon.SinonStub;
|
38
|
-
export declare function verifyRegistration(decryptedCredentials: any, identity: IdentityCredential, registerStub: sinon.SinonStub, insertMemberSpy: sinon.SinonStub): void;
|
39
|
-
export {};
|
@@ -1,118 +0,0 @@
|
|
1
|
-
import { hexToBytes } from "@waku/utils/bytes";
|
2
|
-
import { expect } from "chai";
|
3
|
-
import * as ethers from "ethers";
|
4
|
-
import sinon from "sinon";
|
5
|
-
import { DEFAULT_RATE_LIMIT, LINEA_CONTRACT } from "./constants.js";
|
6
|
-
export const mockRateLimits = {
|
7
|
-
minRate: 20,
|
8
|
-
maxRate: 600,
|
9
|
-
maxTotalRate: 1200,
|
10
|
-
currentTotalRate: 500
|
11
|
-
};
|
12
|
-
export function createMockProvider() {
|
13
|
-
return {
|
14
|
-
getLogs: () => [],
|
15
|
-
getBlockNumber: () => Promise.resolve(1000),
|
16
|
-
getNetwork: () => Promise.resolve({ chainId: 11155111 })
|
17
|
-
};
|
18
|
-
}
|
19
|
-
export function createMockFilters() {
|
20
|
-
return {
|
21
|
-
MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
|
22
|
-
MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
|
23
|
-
MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
|
24
|
-
};
|
25
|
-
}
|
26
|
-
export function createMockRegistryContract(overrides = {}) {
|
27
|
-
const filters = {
|
28
|
-
MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
|
29
|
-
MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
|
30
|
-
MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
|
31
|
-
};
|
32
|
-
const baseContract = {
|
33
|
-
minMembershipRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.minRate)),
|
34
|
-
maxMembershipRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxRate)),
|
35
|
-
maxTotalRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxTotalRate)),
|
36
|
-
currentTotalRateLimit: () => Promise.resolve(ethers.BigNumber.from(mockRateLimits.currentTotalRate)),
|
37
|
-
queryFilter: () => [],
|
38
|
-
provider: createMockProvider(),
|
39
|
-
filters,
|
40
|
-
on: () => ({}),
|
41
|
-
removeAllListeners: () => ({}),
|
42
|
-
register: () => ({
|
43
|
-
wait: () => Promise.resolve({
|
44
|
-
events: [mockRLNRegisteredEvent()]
|
45
|
-
})
|
46
|
-
}),
|
47
|
-
estimateGas: {
|
48
|
-
register: () => Promise.resolve(ethers.BigNumber.from(100000))
|
49
|
-
},
|
50
|
-
functions: {
|
51
|
-
register: () => Promise.resolve()
|
52
|
-
},
|
53
|
-
getMemberIndex: () => Promise.resolve(null),
|
54
|
-
interface: {
|
55
|
-
getEvent: (eventName) => ({
|
56
|
-
name: eventName,
|
57
|
-
format: () => { }
|
58
|
-
})
|
59
|
-
},
|
60
|
-
address: LINEA_CONTRACT.address
|
61
|
-
};
|
62
|
-
// Merge overrides while preserving filters
|
63
|
-
const merged = {
|
64
|
-
...baseContract,
|
65
|
-
...overrides,
|
66
|
-
filters: { ...filters, ...(overrides.filters || {}) }
|
67
|
-
};
|
68
|
-
return merged;
|
69
|
-
}
|
70
|
-
export function mockRLNRegisteredEvent(idCommitment) {
|
71
|
-
return {
|
72
|
-
args: {
|
73
|
-
idCommitment: idCommitment ||
|
74
|
-
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
75
|
-
membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
|
76
|
-
index: ethers.BigNumber.from(1)
|
77
|
-
},
|
78
|
-
event: "MembershipRegistered"
|
79
|
-
};
|
80
|
-
}
|
81
|
-
export function formatIdCommitment(idCommitmentBigInt) {
|
82
|
-
return "0x" + idCommitmentBigInt.toString(16).padStart(64, "0");
|
83
|
-
}
|
84
|
-
export function createRegisterStub(identity) {
|
85
|
-
return sinon.stub().callsFake(() => ({
|
86
|
-
wait: () => Promise.resolve({
|
87
|
-
events: [
|
88
|
-
{
|
89
|
-
event: "MembershipRegistered",
|
90
|
-
args: {
|
91
|
-
idCommitment: formatIdCommitment(identity.IDCommitmentBigInt),
|
92
|
-
membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
|
93
|
-
index: ethers.BigNumber.from(1)
|
94
|
-
}
|
95
|
-
}
|
96
|
-
]
|
97
|
-
})
|
98
|
-
}));
|
99
|
-
}
|
100
|
-
export function verifyRegistration(decryptedCredentials, identity, registerStub, insertMemberSpy) {
|
101
|
-
if (!decryptedCredentials) {
|
102
|
-
throw new Error("Decrypted credentials should not be undefined");
|
103
|
-
}
|
104
|
-
// Verify registration call
|
105
|
-
expect(registerStub.calledWith(sinon.match.same(identity.IDCommitmentBigInt), sinon.match.same(DEFAULT_RATE_LIMIT), sinon.match.array, sinon.match.object)).to.be.true;
|
106
|
-
// Verify credential properties
|
107
|
-
expect(decryptedCredentials).to.have.property("identity");
|
108
|
-
expect(decryptedCredentials).to.have.property("membership");
|
109
|
-
expect(decryptedCredentials.membership).to.include({
|
110
|
-
address: LINEA_CONTRACT.address,
|
111
|
-
treeIndex: 1
|
112
|
-
});
|
113
|
-
// Verify member insertion
|
114
|
-
const expectedIdCommitment = ethers.utils.zeroPad(hexToBytes(formatIdCommitment(identity.IDCommitmentBigInt)), 32);
|
115
|
-
expect(insertMemberSpy.callCount).to.equal(1);
|
116
|
-
expect(insertMemberSpy.getCall(0).args[0]).to.deep.equal(expectedIdCommitment);
|
117
|
-
}
|
118
|
-
//# sourceMappingURL=test-utils.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/contract/test-utils.ts"],"names":[],"mappings":"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"}
|