@waku/rln 0.0.12 → 0.0.13-d77370f
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 +37 -16
- package/bundle/assets/{rln_wasm_bg-a7027f94.wasm → rln_wasm_bg-4d82d6a4.wasm} +0 -0
- package/bundle/index.js +22916 -371
- package/dist/.tsbuildinfo +1 -1
- package/dist/codec.js +3 -1
- package/dist/codec.js.map +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/epoch.js +10 -3
- package/dist/epoch.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/message.d.ts +1 -0
- package/dist/message.js +5 -0
- package/dist/message.js.map +1 -1
- package/dist/rln.d.ts +4 -1
- package/dist/rln.js +30 -2
- package/dist/rln.js.map +1 -1
- package/dist/rln_contract.d.ts +24 -0
- package/dist/rln_contract.js +50 -0
- package/dist/rln_contract.js.map +1 -0
- package/package.json +5 -2
- package/src/codec.ts +3 -3
- package/src/constants.ts +14 -0
- package/src/epoch.ts +12 -3
- package/src/index.ts +13 -2
- package/src/message.ts +9 -0
- package/src/rln.ts +44 -2
- package/src/rln_contract.ts +104 -0
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"rln_contract.js","sourceRoot":"","sources":["../src/rln_contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAazC,MAAM,OAAO,WAAW;IAkBtB,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAmB;QAd1C,aAAQ,GAAa,EAAE,CAAC;QAe9B,IAAI,CAAC,SAAS,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAChE,CAAC;IAfM,MAAM,CAAC,KAAK,CAAC,IAAI,CACtB,WAAwB,EACxB,OAAwB;QAExB,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5C,WAAW,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAE5C,OAAO,WAAW,CAAC;IACrB,CAAC;IAOD,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,IAAW,OAAO;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEM,KAAK,CAAC,YAAY,CACvB,WAAwB,EACxB,SAAkB;QAElB,MAAM,sBAAsB,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAC5D,IAAI,CAAC,aAAa,EAClB,SAAS,CACV,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,sBAAsB,EAAE;YAC1C,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;SAC7C;IACH,CAAC;IAEM,kBAAkB,CAAC,WAAwB;QAChD,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAC9D,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,CAC5C,CAAC;IACJ,CAAC;IAEO,kBAAkB,CACxB,WAAwB,EACxB,KAAmB;QAEnB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACf,OAAO;SACR;QAED,MAAM,MAAM,GAAW,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;QACzC,MAAM,KAAK,GAAW,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;QAEvC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAErC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CACvC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAC7B,EAAE,CACH,CAAC;QACF,WAAW,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,WAAwB,EACxB,SAAiB;QAEjB,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,2BAA2B,CACjE,SAAS,CACV,CAAC;QACF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QAE9D,MAAM,kBAAkB,GACtB,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,kBAAkB,EAAE;YAC7D,KAAK,EAAE,YAAY;SACpB,CAAC,CAAC;QACL,MAAM,iBAAiB,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,CAAC;QAE1D,OAAO,iBAAiB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;CACF"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@waku/rln",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.13-d77370f",
|
4
4
|
"description": "Rate Limit Nullifier for js-waku",
|
5
5
|
"types": "./dist/index.d.ts",
|
6
6
|
"module": "./dist/index.js",
|
@@ -59,6 +59,7 @@
|
|
59
59
|
"@size-limit/preset-big-lib": "^8.0.0",
|
60
60
|
"@types/app-root-path": "^1.2.4",
|
61
61
|
"@types/chai": "^4.2.15",
|
62
|
+
"@types/chai-spies": "^1.0.3",
|
62
63
|
"@types/debug": "^4.1.7",
|
63
64
|
"@types/mocha": "^9.1.0",
|
64
65
|
"@types/node": "^17.0.6",
|
@@ -69,6 +70,7 @@
|
|
69
70
|
"@web/rollup-plugin-import-meta-assets": "^1.0.7",
|
70
71
|
"app-root-path": "^3.0.0",
|
71
72
|
"chai": "^4.3.4",
|
73
|
+
"chai-spies": "^1.0.0",
|
72
74
|
"cspell": "^5.14.0",
|
73
75
|
"eslint": "^8.6.0",
|
74
76
|
"eslint-config-prettier": "^8.3.0",
|
@@ -125,6 +127,7 @@
|
|
125
127
|
]
|
126
128
|
},
|
127
129
|
"dependencies": {
|
128
|
-
"@waku/zerokit-rln-wasm": "^0.0.
|
130
|
+
"@waku/zerokit-rln-wasm": "^0.0.5",
|
131
|
+
"ethers": "^5.7.2"
|
129
132
|
}
|
130
133
|
}
|
package/src/codec.ts
CHANGED
@@ -10,7 +10,7 @@ import {
|
|
10
10
|
import { RlnMessage, toRLNSignal } from "./message.js";
|
11
11
|
import { MembershipKey, RLNInstance } from "./rln.js";
|
12
12
|
|
13
|
-
const log = debug("waku:
|
13
|
+
const log = debug("waku:rln:encoder");
|
14
14
|
|
15
15
|
export class RLNEncoder implements Encoder {
|
16
16
|
public contentTopic: string;
|
@@ -30,7 +30,7 @@ export class RLNEncoder implements Encoder {
|
|
30
30
|
async toWire(message: Partial<Message>): Promise<Uint8Array | undefined> {
|
31
31
|
message.contentTopic = this.contentTopic;
|
32
32
|
message.rateLimitProof = await this.generateProof(message);
|
33
|
-
|
33
|
+
log("Proof generated", message.rateLimitProof);
|
34
34
|
return this.encoder.toWire(message);
|
35
35
|
}
|
36
36
|
|
@@ -42,7 +42,7 @@ export class RLNEncoder implements Encoder {
|
|
42
42
|
if (!protoMessage) return;
|
43
43
|
|
44
44
|
protoMessage.rateLimitProof = await this.generateProof(message);
|
45
|
-
|
45
|
+
log("Proof generated", protoMessage.rateLimitProof);
|
46
46
|
return protoMessage;
|
47
47
|
}
|
48
48
|
|
package/src/constants.ts
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
export const RLN_ABI = [
|
2
|
+
"function MEMBERSHIP_DEPOSIT() public view returns(uint256)",
|
3
|
+
"function register(uint256 pubkey) external payable",
|
4
|
+
"function withdraw(uint256 secret, uint256 _pubkeyIndex, address payable receiver) external",
|
5
|
+
"event MemberRegistered(uint256 pubkey, uint256 index)",
|
6
|
+
"event MemberWithdrawn(uint256 pubkey, uint256 index)",
|
7
|
+
];
|
8
|
+
|
9
|
+
export const GOERLI_CONTRACT = {
|
10
|
+
chainId: 5,
|
11
|
+
startBlock: 7109391,
|
12
|
+
address: "0x4252105670fe33d2947e8ead304969849e64f2a6",
|
13
|
+
abi: RLN_ABI,
|
14
|
+
};
|
package/src/epoch.ts
CHANGED
@@ -1,21 +1,30 @@
|
|
1
|
+
import debug from "debug";
|
2
|
+
|
1
3
|
const DefaultEpochUnitSeconds = 10; // the rln-relay epoch length in seconds
|
2
4
|
|
5
|
+
const log = debug("waku:rln:epoch");
|
6
|
+
|
3
7
|
export function dateToEpoch(
|
4
8
|
timestamp: Date,
|
5
9
|
epochUnitSeconds: number = DefaultEpochUnitSeconds
|
6
10
|
): number {
|
7
11
|
const time = timestamp.getTime();
|
8
|
-
|
12
|
+
const epoch = Math.floor(time / 1000 / epochUnitSeconds);
|
13
|
+
log("generated epoch", epoch);
|
14
|
+
return epoch;
|
9
15
|
}
|
10
16
|
|
11
17
|
export function epochIntToBytes(epoch: number): Uint8Array {
|
12
18
|
const bytes = new Uint8Array(32);
|
13
19
|
const db = new DataView(bytes.buffer);
|
14
20
|
db.setUint32(0, epoch, true);
|
21
|
+
log("encoded epoch", epoch, bytes);
|
15
22
|
return bytes;
|
16
23
|
}
|
17
24
|
|
18
25
|
export function epochBytesToInt(bytes: Uint8Array): number {
|
19
|
-
const dv = new DataView(bytes.buffer);
|
20
|
-
|
26
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
27
|
+
const epoch = dv.getUint32(0, true);
|
28
|
+
log("decoded epoch", epoch, bytes);
|
29
|
+
return epoch;
|
21
30
|
}
|
package/src/index.ts
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import { RLNDecoder, RLNEncoder } from "./codec.js";
|
2
|
-
import
|
2
|
+
import { GOERLI_CONTRACT, RLN_ABI } from "./constants.js";
|
3
|
+
import { Proof, RLNInstance } from "./rln.js";
|
3
4
|
import { MembershipKey } from "./rln.js";
|
5
|
+
import { RLNContract } from "./rln_contract.js";
|
4
6
|
|
5
7
|
// reexport the create function, dynamically imported from rln.ts
|
6
8
|
export async function create(): Promise<RLNInstance> {
|
@@ -11,4 +13,13 @@ export async function create(): Promise<RLNInstance> {
|
|
11
13
|
return await rlnModule.create();
|
12
14
|
}
|
13
15
|
|
14
|
-
export {
|
16
|
+
export {
|
17
|
+
RLNInstance,
|
18
|
+
MembershipKey,
|
19
|
+
Proof,
|
20
|
+
RLNEncoder,
|
21
|
+
RLNDecoder,
|
22
|
+
RLNContract,
|
23
|
+
RLN_ABI,
|
24
|
+
GOERLI_CONTRACT,
|
25
|
+
};
|
package/src/message.ts
CHANGED
@@ -22,6 +22,15 @@ export class RlnMessage<T extends Message> implements Message {
|
|
22
22
|
: undefined;
|
23
23
|
}
|
24
24
|
|
25
|
+
public verifyNoRoot(): boolean | undefined {
|
26
|
+
return this.rateLimitProof
|
27
|
+
? this.rlnInstance.verifyWithNoRoot(
|
28
|
+
this.rateLimitProof,
|
29
|
+
toRLNSignal(this)
|
30
|
+
) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
|
31
|
+
: undefined;
|
32
|
+
}
|
33
|
+
|
25
34
|
get payload(): Uint8Array | undefined {
|
26
35
|
return this.msg.payload;
|
27
36
|
}
|
package/src/rln.ts
CHANGED
@@ -26,6 +26,16 @@ function concatenate(...input: Uint8Array[]): Uint8Array {
|
|
26
26
|
return result;
|
27
27
|
}
|
28
28
|
|
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
|
+
|
29
39
|
const stringEncoder = new TextEncoder();
|
30
40
|
|
31
41
|
const DEPTH = 20;
|
@@ -59,13 +69,15 @@ export async function create(): Promise<RLNInstance> {
|
|
59
69
|
export class MembershipKey {
|
60
70
|
constructor(
|
61
71
|
public readonly IDKey: Uint8Array,
|
62
|
-
public readonly IDCommitment: Uint8Array
|
72
|
+
public readonly IDCommitment: Uint8Array,
|
73
|
+
public readonly IDCommitmentBigInt: bigint
|
63
74
|
) {}
|
64
75
|
|
65
76
|
static fromBytes(memKeys: Uint8Array): MembershipKey {
|
66
77
|
const idKey = memKeys.subarray(0, 32);
|
67
78
|
const idCommitment = memKeys.subarray(32);
|
68
|
-
|
79
|
+
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
80
|
+
return new MembershipKey(idKey, idCommitment, idCommitmentBigInt);
|
69
81
|
}
|
70
82
|
}
|
71
83
|
|
@@ -125,6 +137,15 @@ export class RLNInstance {
|
|
125
137
|
return MembershipKey.fromBytes(memKeys);
|
126
138
|
}
|
127
139
|
|
140
|
+
generateSeededMembershipKey(seed: string): MembershipKey {
|
141
|
+
const seedBytes = stringEncoder.encode(seed);
|
142
|
+
const memKeys = zerokitRLN.generateSeededMembershipKey(
|
143
|
+
this.zkRLN,
|
144
|
+
seedBytes
|
145
|
+
);
|
146
|
+
return MembershipKey.fromBytes(memKeys);
|
147
|
+
}
|
148
|
+
|
128
149
|
insertMember(idCommitment: Uint8Array): void {
|
129
150
|
zerokitRLN.insertMember(this.zkRLN, idCommitment);
|
130
151
|
}
|
@@ -225,4 +246,25 @@ export class RLNInstance {
|
|
225
246
|
root
|
226
247
|
);
|
227
248
|
}
|
249
|
+
|
250
|
+
verifyWithNoRoot(
|
251
|
+
proof: RateLimitProof | Uint8Array,
|
252
|
+
msg: Uint8Array
|
253
|
+
): boolean {
|
254
|
+
let pBytes: Uint8Array;
|
255
|
+
if (proof instanceof Uint8Array) {
|
256
|
+
pBytes = proof;
|
257
|
+
} else {
|
258
|
+
pBytes = proofToBytes(proof);
|
259
|
+
}
|
260
|
+
|
261
|
+
// calculate message length
|
262
|
+
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
263
|
+
|
264
|
+
return zerokitRLN.verifyWithRoots(
|
265
|
+
this.zkRLN,
|
266
|
+
concatenate(pBytes, msgLen, msg),
|
267
|
+
new Uint8Array()
|
268
|
+
);
|
269
|
+
}
|
228
270
|
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import { ethers } from "ethers";
|
2
|
+
|
3
|
+
import { RLN_ABI } from "./constants.js";
|
4
|
+
import { RLNInstance } from "./rln.js";
|
5
|
+
|
6
|
+
type Member = {
|
7
|
+
pubkey: string;
|
8
|
+
index: number;
|
9
|
+
};
|
10
|
+
|
11
|
+
type ContractOptions = {
|
12
|
+
address: string;
|
13
|
+
provider: ethers.Signer | ethers.providers.Provider;
|
14
|
+
};
|
15
|
+
|
16
|
+
export class RLNContract {
|
17
|
+
private _contract: ethers.Contract;
|
18
|
+
private membersFilter: ethers.EventFilter;
|
19
|
+
|
20
|
+
private _members: Member[] = [];
|
21
|
+
|
22
|
+
public static async init(
|
23
|
+
rlnInstance: RLNInstance,
|
24
|
+
options: ContractOptions
|
25
|
+
): Promise<RLNContract> {
|
26
|
+
const rlnContract = new RLNContract(options);
|
27
|
+
|
28
|
+
await rlnContract.fetchMembers(rlnInstance);
|
29
|
+
rlnContract.subscribeToMembers(rlnInstance);
|
30
|
+
|
31
|
+
return rlnContract;
|
32
|
+
}
|
33
|
+
|
34
|
+
constructor({ address, provider }: ContractOptions) {
|
35
|
+
this._contract = new ethers.Contract(address, RLN_ABI, provider);
|
36
|
+
this.membersFilter = this.contract.filters.MemberRegistered();
|
37
|
+
}
|
38
|
+
|
39
|
+
public get contract(): ethers.Contract {
|
40
|
+
return this._contract;
|
41
|
+
}
|
42
|
+
|
43
|
+
public get members(): Member[] {
|
44
|
+
return this._members;
|
45
|
+
}
|
46
|
+
|
47
|
+
public async fetchMembers(
|
48
|
+
rlnInstance: RLNInstance,
|
49
|
+
fromBlock?: number
|
50
|
+
): Promise<void> {
|
51
|
+
const registeredMemberEvents = await this.contract.queryFilter(
|
52
|
+
this.membersFilter,
|
53
|
+
fromBlock
|
54
|
+
);
|
55
|
+
|
56
|
+
for (const event of registeredMemberEvents) {
|
57
|
+
this.addMemberFromEvent(rlnInstance, event);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
public subscribeToMembers(rlnInstance: RLNInstance): void {
|
62
|
+
this.contract.on(this.membersFilter, (_pubkey, _index, event) =>
|
63
|
+
this.addMemberFromEvent(rlnInstance, event)
|
64
|
+
);
|
65
|
+
}
|
66
|
+
|
67
|
+
private addMemberFromEvent(
|
68
|
+
rlnInstance: RLNInstance,
|
69
|
+
event: ethers.Event
|
70
|
+
): void {
|
71
|
+
if (!event.args) {
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
|
75
|
+
const pubkey: string = event.args.pubkey;
|
76
|
+
const index: number = event.args.index;
|
77
|
+
|
78
|
+
this.members.push({ index, pubkey });
|
79
|
+
|
80
|
+
const idCommitment = ethers.utils.zeroPad(
|
81
|
+
ethers.utils.arrayify(pubkey),
|
82
|
+
32
|
83
|
+
);
|
84
|
+
rlnInstance.insertMember(idCommitment);
|
85
|
+
}
|
86
|
+
|
87
|
+
public async registerMember(
|
88
|
+
rlnInstance: RLNInstance,
|
89
|
+
signature: string
|
90
|
+
): Promise<ethers.Event | undefined> {
|
91
|
+
const membershipKey = await rlnInstance.generateSeededMembershipKey(
|
92
|
+
signature
|
93
|
+
);
|
94
|
+
const depositValue = await this.contract.MEMBERSHIP_DEPOSIT();
|
95
|
+
|
96
|
+
const txRegisterResponse: ethers.ContractTransaction =
|
97
|
+
await this.contract.register(membershipKey.IDCommitmentBigInt, {
|
98
|
+
value: depositValue,
|
99
|
+
});
|
100
|
+
const txRegisterReceipt = await txRegisterResponse.wait();
|
101
|
+
|
102
|
+
return txRegisterReceipt?.events?.[0];
|
103
|
+
}
|
104
|
+
}
|