@waku/rln 0.1.6-acc9100.0 → 0.1.6-b0a2e39.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/connection_manager/connection_manager.js +1 -0
- package/bundle/packages/core/dist/lib/connection_manager/keep_alive_manager.js +1 -0
- package/bundle/packages/core/dist/lib/filter/filter.js +2 -0
- package/bundle/packages/core/dist/lib/light_push/light_push.js +12 -9
- package/bundle/packages/core/dist/lib/light_push/light_push_v3.js +30 -0
- package/bundle/packages/core/dist/lib/light_push/utils.js +18 -0
- package/bundle/packages/core/dist/lib/message/version_0.js +1 -0
- package/bundle/packages/core/dist/lib/metadata/metadata.js +2 -0
- package/bundle/packages/core/dist/lib/store/store.js +2 -0
- package/bundle/packages/interfaces/dist/light_push_v3.js +29 -0
- package/bundle/packages/interfaces/dist/protocols.js +10 -0
- package/bundle/packages/proto/dist/generated/light_push.js +3 -3
- package/bundle/packages/proto/dist/generated/light_push_v3.js +348 -0
- package/bundle/packages/rln/dist/codec.js +1 -0
- package/bundle/packages/rln/dist/contract/constants.js +1 -7
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +7 -28
- package/bundle/packages/rln/dist/contract/rln_contract.js +1 -0
- package/bundle/packages/rln/dist/credentials_manager.js +15 -16
- package/bundle/packages/rln/dist/identity.js +8 -5
- package/bundle/packages/rln/dist/keystore/keystore.js +12 -15
- package/bundle/packages/rln/dist/message.js +2 -0
- package/bundle/packages/rln/dist/rln.js +2 -0
- package/bundle/packages/rln/dist/utils/bytes.js +16 -14
- package/bundle/packages/rln/dist/utils/epoch.js +1 -0
- package/bundle/packages/utils/dist/common/sharding/index.js +1 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/constants.d.ts +0 -6
- package/dist/contract/constants.js +0 -6
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/rln_base_contract.d.ts +0 -6
- package/dist/contract/rln_base_contract.js +6 -28
- package/dist/contract/rln_base_contract.js.map +1 -1
- package/dist/contract/test-utils.d.ts +39 -0
- package/dist/contract/test-utils.js +118 -0
- package/dist/contract/test-utils.js.map +1 -0
- package/dist/credentials_manager.js +14 -16
- package/dist/credentials_manager.js.map +1 -1
- package/dist/identity.d.ts +2 -4
- package/dist/identity.js +6 -5
- package/dist/identity.js.map +1 -1
- package/dist/keystore/keystore.js +11 -15
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/utils/bytes.d.ts +6 -2
- package/dist/utils/bytes.js +15 -13
- 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 +0 -9
- package/src/contract/rln_base_contract.ts +13 -41
- package/src/contract/test-utils.ts +179 -0
- package/src/credentials_manager.ts +21 -27
- package/src/identity.ts +7 -5
- package/src/keystore/keystore.ts +24 -28
- package/src/utils/bytes.ts +25 -21
- package/src/utils/index.ts +1 -1
package/dist/utils/bytes.d.ts
CHANGED
@@ -4,9 +4,13 @@
|
|
4
4
|
* @returns concatenation of all Uint8Array received as input
|
5
5
|
*/
|
6
6
|
export declare function concatenate(...input: Uint8Array[]): Uint8Array;
|
7
|
-
export declare function switchEndianness(bytes: Uint8Array): Uint8Array;
|
8
|
-
export declare function buildBigIntFromUint8ArrayBE(bytes: Uint8Array): bigint;
|
9
7
|
export declare function writeUIntLE(buf: Uint8Array, value: number, offset: number, byteLength: number, noAssert?: boolean): Uint8Array;
|
8
|
+
/**
|
9
|
+
* Transforms Uint8Array into BigInt
|
10
|
+
* @param array: Uint8Array
|
11
|
+
* @returns BigInt
|
12
|
+
*/
|
13
|
+
export declare function buildBigIntFromUint8Array(array: Uint8Array, byteOffset?: number): bigint;
|
10
14
|
/**
|
11
15
|
* Fills with zeros to set length
|
12
16
|
* @param array little endian Uint8Array
|
package/dist/utils/bytes.js
CHANGED
@@ -16,12 +16,12 @@ export function concatenate(...input) {
|
|
16
16
|
}
|
17
17
|
return result;
|
18
18
|
}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
// Adapted from https://github.com/feross/buffer
|
20
|
+
function checkInt(buf, value, offset, ext, max, min) {
|
21
|
+
if (value > max || value < min)
|
22
|
+
throw new RangeError('"value" argument is out of bounds');
|
23
|
+
if (offset + ext > buf.length)
|
24
|
+
throw new RangeError("Index out of range");
|
25
25
|
}
|
26
26
|
export function writeUIntLE(buf, value, offset, byteLength, noAssert) {
|
27
27
|
value = +value;
|
@@ -39,6 +39,15 @@ export function writeUIntLE(buf, value, offset, byteLength, noAssert) {
|
|
39
39
|
}
|
40
40
|
return buf;
|
41
41
|
}
|
42
|
+
/**
|
43
|
+
* Transforms Uint8Array into BigInt
|
44
|
+
* @param array: Uint8Array
|
45
|
+
* @returns BigInt
|
46
|
+
*/
|
47
|
+
export function buildBigIntFromUint8Array(array, byteOffset = 0) {
|
48
|
+
const dataView = new DataView(array.buffer);
|
49
|
+
return dataView.getBigUint64(byteOffset, true);
|
50
|
+
}
|
42
51
|
/**
|
43
52
|
* Fills with zeros to set length
|
44
53
|
* @param array little endian Uint8Array
|
@@ -52,11 +61,4 @@ export function zeroPadLE(array, length) {
|
|
52
61
|
}
|
53
62
|
return result;
|
54
63
|
}
|
55
|
-
// Adapted from https://github.com/feross/buffer
|
56
|
-
function checkInt(buf, value, offset, ext, max, min) {
|
57
|
-
if (value > max || value < min)
|
58
|
-
throw new RangeError('"value" argument is out of bounds');
|
59
|
-
if (offset + ext > buf.length)
|
60
|
-
throw new RangeError("Index out of range");
|
61
|
-
}
|
62
64
|
//# sourceMappingURL=bytes.js.map
|
package/dist/utils/bytes.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/utils/bytes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAG,KAAmB;IAChD,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,
|
1
|
+
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/utils/bytes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAG,KAAmB;IAChD,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gDAAgD;AAChD,SAAS,QAAQ,CACf,GAAe,EACf,KAAa,EACb,MAAc,EACd,GAAW,EACX,GAAW,EACX,GAAW;IAEX,IAAI,KAAK,GAAG,GAAG,IAAI,KAAK,GAAG,GAAG;QAC5B,MAAM,IAAI,UAAU,CAAC,mCAAmC,CAAC,CAAC;IAC5D,IAAI,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,GAAe,EACf,KAAa,EACb,MAAc,EACd,UAAkB,EAClB,QAAkB;IAElB,KAAK,GAAG,CAAC,KAAK,CAAC;IACf,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC;IACtB,UAAU,GAAG,UAAU,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACjD,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,CAAC,GAAG,UAAU,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1C,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;IACzC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAiB,EACjB,aAAqB,CAAC;IAEtB,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAiB,EAAE,MAAc;IACzD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/utils/index.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
export { extractMetaMaskSigner } from "./metamask.js";
|
2
|
-
export { concatenate, writeUIntLE,
|
2
|
+
export { concatenate, writeUIntLE, buildBigIntFromUint8Array, zeroPadLE } from "./bytes.js";
|
3
3
|
export { sha256, poseidonHash } from "./hash.js";
|
4
4
|
export { dateToEpoch, epochIntToBytes, epochBytesToInt } from "./epoch.js";
|
package/dist/utils/index.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
export { extractMetaMaskSigner } from "./metamask.js";
|
2
|
-
export { concatenate, writeUIntLE,
|
2
|
+
export { concatenate, writeUIntLE, buildBigIntFromUint8Array, zeroPadLE } from "./bytes.js";
|
3
3
|
export { sha256, poseidonHash } from "./hash.js";
|
4
4
|
export { dateToEpoch, epochIntToBytes, epochBytesToInt } from "./epoch.js";
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,WAAW,EACX,WAAW,EACX,
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,WAAW,EACX,WAAW,EACX,yBAAyB,EACzB,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"name":"@waku/rln","version":"0.1.6-
|
1
|
+
{"name":"@waku/rln","version":"0.1.6-b0a2e39.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-b0a2e39.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-b0a2e39.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-b0a2e39.0","@waku/utils":"0.0.24-b0a2e39.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,13 +25,4 @@ 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
|
-
|
37
28
|
export const DEFAULT_RATE_LIMIT = RATE_LIMIT_PARAMS.MAX_RATE;
|
@@ -3,14 +3,9 @@ import { ethers } from "ethers";
|
|
3
3
|
|
4
4
|
import { IdentityCredential } from "../identity.js";
|
5
5
|
import { DecryptedCredentials } from "../keystore/types.js";
|
6
|
-
import { buildBigIntFromUint8ArrayBE } from "../utils/bytes.js";
|
7
6
|
|
8
7
|
import { RLN_ABI } from "./abi.js";
|
9
|
-
import {
|
10
|
-
DEFAULT_Q,
|
11
|
-
DEFAULT_RATE_LIMIT,
|
12
|
-
RATE_LIMIT_PARAMS
|
13
|
-
} from "./constants.js";
|
8
|
+
import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from "./constants.js";
|
14
9
|
import {
|
15
10
|
CustomQueryOptions,
|
16
11
|
FetchMembersOptions,
|
@@ -30,12 +25,6 @@ export class RLNBaseContract {
|
|
30
25
|
private minRateLimit?: number;
|
31
26
|
private maxRateLimit?: number;
|
32
27
|
|
33
|
-
/**
|
34
|
-
* Default Q value for the RLN contract.
|
35
|
-
* @see https://github.com/waku-org/waku-rlnv2-contract/blob/b7e9a9b1bc69256a2a3076c1f099b50ce84e7eff/src/WakuRlnV2.sol#L25
|
36
|
-
*/
|
37
|
-
public idCommitmentBigIntLimit = DEFAULT_Q;
|
38
|
-
|
39
28
|
protected _members: Map<number, Member> = new Map();
|
40
29
|
private _membersFilter: ethers.EventFilter;
|
41
30
|
private _membershipErasedFilter: ethers.EventFilter;
|
@@ -90,8 +79,7 @@ export class RLNBaseContract {
|
|
90
79
|
const instance = new RLNBaseContract(options);
|
91
80
|
const [min, max] = await Promise.all([
|
92
81
|
instance.contract.minMembershipRateLimit(),
|
93
|
-
instance.contract.maxMembershipRateLimit()
|
94
|
-
instance.contract.Q()
|
82
|
+
instance.contract.maxMembershipRateLimit()
|
95
83
|
]);
|
96
84
|
instance.minRateLimit = ethers.BigNumber.from(min).toNumber();
|
97
85
|
instance.maxRateLimit = ethers.BigNumber.from(max).toNumber();
|
@@ -503,23 +491,6 @@ export class RLNBaseContract {
|
|
503
491
|
}
|
504
492
|
}
|
505
493
|
|
506
|
-
private getIdCommitmentBigInt(bytes: Uint8Array): bigint {
|
507
|
-
let idCommitmentBigIntBE = buildBigIntFromUint8ArrayBE(bytes);
|
508
|
-
|
509
|
-
if (!this.contract) {
|
510
|
-
throw Error("RLN contract is not initialized");
|
511
|
-
}
|
512
|
-
|
513
|
-
if (idCommitmentBigIntBE >= this.idCommitmentBigIntLimit) {
|
514
|
-
log.warn(
|
515
|
-
`ID commitment is greater than Q, reducing it by Q(idCommitmentBigIntLimit): ${idCommitmentBigIntBE} % ${this.idCommitmentBigIntLimit}`
|
516
|
-
);
|
517
|
-
idCommitmentBigIntBE =
|
518
|
-
idCommitmentBigIntBE % this.idCommitmentBigIntLimit;
|
519
|
-
}
|
520
|
-
return idCommitmentBigIntBE;
|
521
|
-
}
|
522
|
-
|
523
494
|
public async registerWithIdentity(
|
524
495
|
identity: IdentityCredential
|
525
496
|
): Promise<DecryptedCredentials | undefined> {
|
@@ -528,12 +499,10 @@ export class RLNBaseContract {
|
|
528
499
|
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
|
529
500
|
);
|
530
501
|
|
531
|
-
const idCommitmentBigInt = this.getIdCommitmentBigInt(
|
532
|
-
identity.IDCommitment
|
533
|
-
);
|
534
|
-
|
535
502
|
// Check if the ID commitment is already registered
|
536
|
-
const existingIndex = await this.getMemberIndex(
|
503
|
+
const existingIndex = await this.getMemberIndex(
|
504
|
+
identity.IDCommitmentBigInt
|
505
|
+
);
|
537
506
|
if (existingIndex) {
|
538
507
|
throw new Error(
|
539
508
|
`ID commitment is already registered with index ${existingIndex}`
|
@@ -549,16 +518,19 @@ export class RLNBaseContract {
|
|
549
518
|
}
|
550
519
|
|
551
520
|
const estimatedGas = await this.contract.estimateGas.register(
|
552
|
-
|
521
|
+
identity.IDCommitmentBigInt,
|
553
522
|
this.rateLimit,
|
554
523
|
[]
|
555
524
|
);
|
556
525
|
const gasLimit = estimatedGas.add(10000);
|
557
526
|
|
558
527
|
const txRegisterResponse: ethers.ContractTransaction =
|
559
|
-
await this.contract.register(
|
560
|
-
|
561
|
-
|
528
|
+
await this.contract.register(
|
529
|
+
identity.IDCommitmentBigInt,
|
530
|
+
this.rateLimit,
|
531
|
+
[],
|
532
|
+
{ gasLimit }
|
533
|
+
);
|
562
534
|
|
563
535
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
564
536
|
|
@@ -654,7 +626,7 @@ export class RLNBaseContract {
|
|
654
626
|
permit.v,
|
655
627
|
permit.r,
|
656
628
|
permit.s,
|
657
|
-
|
629
|
+
identity.IDCommitmentBigInt,
|
658
630
|
this.rateLimit,
|
659
631
|
idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
|
660
632
|
);
|
@@ -0,0 +1,179 @@
|
|
1
|
+
import { hexToBytes } from "@waku/utils/bytes";
|
2
|
+
import { expect } from "chai";
|
3
|
+
import * as ethers from "ethers";
|
4
|
+
import sinon from "sinon";
|
5
|
+
|
6
|
+
import type { IdentityCredential } from "../identity.js";
|
7
|
+
|
8
|
+
import { DEFAULT_RATE_LIMIT, LINEA_CONTRACT } from "./constants.js";
|
9
|
+
|
10
|
+
export const mockRateLimits = {
|
11
|
+
minRate: 20,
|
12
|
+
maxRate: 600,
|
13
|
+
maxTotalRate: 1200,
|
14
|
+
currentTotalRate: 500
|
15
|
+
};
|
16
|
+
|
17
|
+
type MockProvider = {
|
18
|
+
getLogs: () => never[];
|
19
|
+
getBlockNumber: () => Promise<number>;
|
20
|
+
getNetwork: () => Promise<{ chainId: number }>;
|
21
|
+
};
|
22
|
+
|
23
|
+
type MockFilters = {
|
24
|
+
MembershipRegistered: () => { address: string };
|
25
|
+
MembershipErased: () => { address: string };
|
26
|
+
MembershipExpired: () => { address: string };
|
27
|
+
};
|
28
|
+
|
29
|
+
export function createMockProvider(): MockProvider {
|
30
|
+
return {
|
31
|
+
getLogs: () => [],
|
32
|
+
getBlockNumber: () => Promise.resolve(1000),
|
33
|
+
getNetwork: () => Promise.resolve({ chainId: 11155111 })
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
export function createMockFilters(): MockFilters {
|
38
|
+
return {
|
39
|
+
MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
|
40
|
+
MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
|
41
|
+
MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
|
42
|
+
};
|
43
|
+
}
|
44
|
+
|
45
|
+
type ContractOverrides = Partial<{
|
46
|
+
filters: Record<string, unknown>;
|
47
|
+
[key: string]: unknown;
|
48
|
+
}>;
|
49
|
+
|
50
|
+
export function createMockRegistryContract(
|
51
|
+
overrides: ContractOverrides = {}
|
52
|
+
): ethers.Contract {
|
53
|
+
const filters = {
|
54
|
+
MembershipRegistered: () => ({ address: LINEA_CONTRACT.address }),
|
55
|
+
MembershipErased: () => ({ address: LINEA_CONTRACT.address }),
|
56
|
+
MembershipExpired: () => ({ address: LINEA_CONTRACT.address })
|
57
|
+
};
|
58
|
+
|
59
|
+
const baseContract = {
|
60
|
+
minMembershipRateLimit: () =>
|
61
|
+
Promise.resolve(ethers.BigNumber.from(mockRateLimits.minRate)),
|
62
|
+
maxMembershipRateLimit: () =>
|
63
|
+
Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxRate)),
|
64
|
+
maxTotalRateLimit: () =>
|
65
|
+
Promise.resolve(ethers.BigNumber.from(mockRateLimits.maxTotalRate)),
|
66
|
+
currentTotalRateLimit: () =>
|
67
|
+
Promise.resolve(ethers.BigNumber.from(mockRateLimits.currentTotalRate)),
|
68
|
+
queryFilter: () => [],
|
69
|
+
provider: createMockProvider(),
|
70
|
+
filters,
|
71
|
+
on: () => ({}),
|
72
|
+
removeAllListeners: () => ({}),
|
73
|
+
register: () => ({
|
74
|
+
wait: () =>
|
75
|
+
Promise.resolve({
|
76
|
+
events: [mockRLNRegisteredEvent()]
|
77
|
+
})
|
78
|
+
}),
|
79
|
+
estimateGas: {
|
80
|
+
register: () => Promise.resolve(ethers.BigNumber.from(100000))
|
81
|
+
},
|
82
|
+
functions: {
|
83
|
+
register: () => Promise.resolve()
|
84
|
+
},
|
85
|
+
getMemberIndex: () => Promise.resolve(null),
|
86
|
+
interface: {
|
87
|
+
getEvent: (eventName: string) => ({
|
88
|
+
name: eventName,
|
89
|
+
format: () => {}
|
90
|
+
})
|
91
|
+
},
|
92
|
+
address: LINEA_CONTRACT.address
|
93
|
+
};
|
94
|
+
|
95
|
+
// Merge overrides while preserving filters
|
96
|
+
const merged = {
|
97
|
+
...baseContract,
|
98
|
+
...overrides,
|
99
|
+
filters: { ...filters, ...(overrides.filters || {}) }
|
100
|
+
};
|
101
|
+
|
102
|
+
return merged as unknown as ethers.Contract;
|
103
|
+
}
|
104
|
+
|
105
|
+
export function mockRLNRegisteredEvent(idCommitment?: string): ethers.Event {
|
106
|
+
return {
|
107
|
+
args: {
|
108
|
+
idCommitment:
|
109
|
+
idCommitment ||
|
110
|
+
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
111
|
+
membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
|
112
|
+
index: ethers.BigNumber.from(1)
|
113
|
+
},
|
114
|
+
event: "MembershipRegistered"
|
115
|
+
} as unknown as ethers.Event;
|
116
|
+
}
|
117
|
+
|
118
|
+
export function formatIdCommitment(idCommitmentBigInt: bigint): string {
|
119
|
+
return "0x" + idCommitmentBigInt.toString(16).padStart(64, "0");
|
120
|
+
}
|
121
|
+
|
122
|
+
export function createRegisterStub(
|
123
|
+
identity: IdentityCredential
|
124
|
+
): sinon.SinonStub {
|
125
|
+
return sinon.stub().callsFake(() => ({
|
126
|
+
wait: () =>
|
127
|
+
Promise.resolve({
|
128
|
+
events: [
|
129
|
+
{
|
130
|
+
event: "MembershipRegistered",
|
131
|
+
args: {
|
132
|
+
idCommitment: formatIdCommitment(identity.IDCommitmentBigInt),
|
133
|
+
membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
|
134
|
+
index: ethers.BigNumber.from(1)
|
135
|
+
}
|
136
|
+
}
|
137
|
+
]
|
138
|
+
})
|
139
|
+
}));
|
140
|
+
}
|
141
|
+
|
142
|
+
export function verifyRegistration(
|
143
|
+
decryptedCredentials: any,
|
144
|
+
identity: IdentityCredential,
|
145
|
+
registerStub: sinon.SinonStub,
|
146
|
+
insertMemberSpy: sinon.SinonStub
|
147
|
+
): void {
|
148
|
+
if (!decryptedCredentials) {
|
149
|
+
throw new Error("Decrypted credentials should not be undefined");
|
150
|
+
}
|
151
|
+
|
152
|
+
// Verify registration call
|
153
|
+
expect(
|
154
|
+
registerStub.calledWith(
|
155
|
+
sinon.match.same(identity.IDCommitmentBigInt),
|
156
|
+
sinon.match.same(DEFAULT_RATE_LIMIT),
|
157
|
+
sinon.match.array,
|
158
|
+
sinon.match.object
|
159
|
+
)
|
160
|
+
).to.be.true;
|
161
|
+
|
162
|
+
// Verify credential properties
|
163
|
+
expect(decryptedCredentials).to.have.property("identity");
|
164
|
+
expect(decryptedCredentials).to.have.property("membership");
|
165
|
+
expect(decryptedCredentials.membership).to.include({
|
166
|
+
address: LINEA_CONTRACT.address,
|
167
|
+
treeIndex: 1
|
168
|
+
});
|
169
|
+
|
170
|
+
// Verify member insertion
|
171
|
+
const expectedIdCommitment = ethers.utils.zeroPad(
|
172
|
+
hexToBytes(formatIdCommitment(identity.IDCommitmentBigInt)),
|
173
|
+
32
|
174
|
+
);
|
175
|
+
expect(insertMemberSpy.callCount).to.equal(1);
|
176
|
+
expect(insertMemberSpy.getCall(0).args[0]).to.deep.equal(
|
177
|
+
expectedIdCommitment
|
178
|
+
);
|
179
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { hmac } from "@noble/hashes/hmac";
|
2
|
-
import { sha256 } from "@noble/hashes/
|
2
|
+
import { sha256 } from "@noble/hashes/sha256";
|
3
3
|
import { Logger } from "@waku/utils";
|
4
4
|
import { ethers } from "ethers";
|
5
5
|
|
@@ -13,7 +13,10 @@ 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 {
|
16
|
+
import {
|
17
|
+
buildBigIntFromUint8Array,
|
18
|
+
extractMetaMaskSigner
|
19
|
+
} from "./utils/index.js";
|
17
20
|
import { Zerokit } from "./zerokit.js";
|
18
21
|
|
19
22
|
const log = new Logger("waku:credentials");
|
@@ -113,9 +116,7 @@ export class RLNCredentialsManager {
|
|
113
116
|
);
|
114
117
|
} else {
|
115
118
|
log.info("Using local implementation to generate identity");
|
116
|
-
identity =
|
117
|
-
options.signature
|
118
|
-
);
|
119
|
+
identity = this.generateSeededIdentityCredential(options.signature);
|
119
120
|
}
|
120
121
|
}
|
121
122
|
|
@@ -248,9 +249,7 @@ export class RLNCredentialsManager {
|
|
248
249
|
* @param seed A string seed to generate the identity from
|
249
250
|
* @returns IdentityCredential
|
250
251
|
*/
|
251
|
-
private
|
252
|
-
seed: string
|
253
|
-
): Promise<IdentityCredential> {
|
252
|
+
private generateSeededIdentityCredential(seed: string): IdentityCredential {
|
254
253
|
log.info("Generating seeded identity credential");
|
255
254
|
// Convert the seed to bytes
|
256
255
|
const encoder = new TextEncoder();
|
@@ -258,31 +257,26 @@ export class RLNCredentialsManager {
|
|
258
257
|
|
259
258
|
// Generate deterministic values using HMAC-SHA256
|
260
259
|
// We use different context strings for each component to ensure they're different
|
261
|
-
const
|
262
|
-
const
|
263
|
-
sha256,
|
264
|
-
seedBytes,
|
265
|
-
encoder.encode("IDNullifier")
|
266
|
-
);
|
260
|
+
const idTrapdoor = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
|
261
|
+
const idNullifier = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
|
267
262
|
|
268
|
-
|
269
|
-
const
|
263
|
+
// Generate IDSecretHash as a hash of IDTrapdoor and IDNullifier
|
264
|
+
const combinedBytes = new Uint8Array([...idTrapdoor, ...idNullifier]);
|
265
|
+
const idSecretHash = sha256(combinedBytes);
|
270
266
|
|
271
|
-
|
267
|
+
// Generate IDCommitment as a hash of IDSecretHash
|
268
|
+
const idCommitment = sha256(idSecretHash);
|
272
269
|
|
273
|
-
//
|
274
|
-
|
275
|
-
const idTrapdoorLE = switchEndianness(idTrapdoorBE);
|
276
|
-
const idNullifierLE = switchEndianness(idNullifierBE);
|
277
|
-
const idSecretHashLE = switchEndianness(idSecretHashBE);
|
278
|
-
const idCommitmentLE = switchEndianness(idCommitmentBE);
|
270
|
+
// Convert IDCommitment to BigInt
|
271
|
+
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
279
272
|
|
280
273
|
log.info("Successfully generated identity credential");
|
281
274
|
return new IdentityCredential(
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
275
|
+
idTrapdoor,
|
276
|
+
idNullifier,
|
277
|
+
idSecretHash,
|
278
|
+
idCommitment,
|
279
|
+
idCommitmentBigInt
|
286
280
|
);
|
287
281
|
}
|
288
282
|
}
|
package/src/identity.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
import { buildBigIntFromUint8Array } from "./utils/index.js";
|
2
|
+
|
1
3
|
export class IdentityCredential {
|
2
|
-
/**
|
3
|
-
* All variables are in little-endian format
|
4
|
-
*/
|
5
4
|
public constructor(
|
6
5
|
public readonly IDTrapdoor: Uint8Array,
|
7
6
|
public readonly IDNullifier: Uint8Array,
|
8
7
|
public readonly IDSecretHash: Uint8Array,
|
9
|
-
public readonly IDCommitment: Uint8Array
|
8
|
+
public readonly IDCommitment: Uint8Array,
|
9
|
+
public readonly IDCommitmentBigInt: bigint
|
10
10
|
) {}
|
11
11
|
|
12
12
|
public static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
@@ -18,12 +18,14 @@ export class IdentityCredential {
|
|
18
18
|
const idNullifier = memKeys.subarray(32, 64);
|
19
19
|
const idSecretHash = memKeys.subarray(64, 96);
|
20
20
|
const idCommitment = memKeys.subarray(96, 128);
|
21
|
+
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
|
21
22
|
|
22
23
|
return new IdentityCredential(
|
23
24
|
idTrapdoor,
|
24
25
|
idNullifier,
|
25
26
|
idSecretHash,
|
26
|
-
idCommitment
|
27
|
+
idCommitment,
|
28
|
+
idCommitmentBigInt
|
27
29
|
);
|
28
30
|
}
|
29
31
|
}
|
package/src/keystore/keystore.ts
CHANGED
@@ -14,6 +14,8 @@ import {
|
|
14
14
|
import _ from "lodash";
|
15
15
|
import { v4 as uuidV4 } from "uuid";
|
16
16
|
|
17
|
+
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
18
|
+
|
17
19
|
import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
|
18
20
|
import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
|
19
21
|
import type {
|
@@ -248,25 +250,26 @@ export class Keystore {
|
|
248
250
|
const str = bytesToUtf8(bytes);
|
249
251
|
const obj = JSON.parse(str);
|
250
252
|
|
251
|
-
|
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
|
-
|
253
|
+
// TODO: add runtime validation of nwaku credentials
|
264
254
|
return {
|
265
255
|
identity: {
|
266
|
-
IDCommitment:
|
267
|
-
|
268
|
-
|
269
|
-
|
256
|
+
IDCommitment: Keystore.fromArraylikeToBytes(
|
257
|
+
_.get(obj, "identityCredential.idCommitment", [])
|
258
|
+
),
|
259
|
+
IDTrapdoor: Keystore.fromArraylikeToBytes(
|
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
|
+
)
|
270
273
|
},
|
271
274
|
membership: {
|
272
275
|
treeIndex: _.get(obj, "treeIndex"),
|
@@ -318,21 +321,14 @@ export class Keystore {
|
|
318
321
|
// follows nwaku implementation
|
319
322
|
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L98
|
320
323
|
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
|
-
|
332
|
-
idSecretHash: Array.from(
|
333
|
-
toLittleEndian(options.identity.IDSecretHash)
|
334
|
-
),
|
335
|
-
idTrapdoor: Array.from(toLittleEndian(options.identity.IDTrapdoor))
|
328
|
+
idCommitment: Array.from(options.identity.IDCommitment),
|
329
|
+
idNullifier: Array.from(options.identity.IDNullifier),
|
330
|
+
idSecretHash: Array.from(options.identity.IDSecretHash),
|
331
|
+
idTrapdoor: Array.from(options.identity.IDTrapdoor)
|
336
332
|
},
|
337
333
|
membershipContract: {
|
338
334
|
chainId: options.membership.chainId,
|