@waku/rln 0.0.6 → 0.0.8-378ad04
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/assets/rln-fb4d7b4b.wasm +0 -0
- package/bundle/assets/rln_final-a641c06e.zkey +0 -0
- package/bundle/assets/rln_wasm_bg-0185b546.wasm +0 -0
- package/bundle/index.js +47343 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/byte_utils.d.ts +1 -0
- package/dist/byte_utils.js +24 -0
- package/dist/byte_utils.js.map +1 -0
- package/dist/codec.d.ts +22 -0
- package/dist/codec.js +58 -0
- package/dist/codec.js.map +1 -0
- package/dist/epoch.d.ts +3 -0
- package/dist/epoch.js +16 -0
- package/dist/epoch.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/message.d.ts +13 -0
- package/dist/message.js +29 -0
- package/dist/message.js.map +1 -0
- package/dist/resources/rln.wasm +0 -0
- package/dist/resources/rln_final.zkey +0 -0
- package/dist/resources/verification_key.d.ts +12 -0
- package/dist/resources/verification_key.js +121 -0
- package/dist/resources/verification_key.js.map +1 -0
- package/dist/rln.d.ts +6 -5
- package/dist/rln.js +40 -61
- package/dist/rln.js.map +1 -1
- package/package.json +6 -4
- package/src/byte_utils.ts +39 -0
- package/src/codec.ts +88 -0
- package/src/epoch.ts +21 -0
- package/src/index.ts +4 -2
- package/src/message.ts +37 -0
- package/src/rln.ts +58 -98
- package/src/witness_calculator.d.ts +5 -1
- package/bundle/rln-b6bbd489.js +0 -1027
- package/dist/resources.d.ts +0 -4
- package/dist/resources.js +0 -5
- package/dist/resources.js.map +0 -1
- package/src/resources.ts +0 -10
package/dist/rln.js
CHANGED
@@ -1,20 +1,8 @@
|
|
1
1
|
import init, * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
2
|
-
import
|
2
|
+
import { writeUIntLE } from "./byte_utils.js";
|
3
|
+
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
4
|
+
import verificationKey from "./resources/verification_key.js";
|
3
5
|
import * as wc from "./witness_calculator.js";
|
4
|
-
/**
|
5
|
-
* Convert a base64 string into uint8Array
|
6
|
-
* @param base64
|
7
|
-
* @returns Uint8Array
|
8
|
-
*/
|
9
|
-
function base64ToUint8Array(base64) {
|
10
|
-
const binary_string = window.atob(base64);
|
11
|
-
const len = binary_string.length;
|
12
|
-
const bytes = new Uint8Array(len);
|
13
|
-
for (let i = 0; i < len; i++) {
|
14
|
-
bytes[i] = binary_string.charCodeAt(i);
|
15
|
-
}
|
16
|
-
return bytes;
|
17
|
-
}
|
18
6
|
/**
|
19
7
|
* Concatenate Uint8Arrays
|
20
8
|
* @param input
|
@@ -33,10 +21,18 @@ function concatenate(...input) {
|
|
33
21
|
}
|
34
22
|
return result;
|
35
23
|
}
|
24
|
+
const stringEncoder = new TextEncoder();
|
36
25
|
const DEPTH = 20;
|
37
|
-
|
38
|
-
const
|
39
|
-
const
|
26
|
+
async function loadWitnessCalculator() {
|
27
|
+
const url = new URL("./resources/rln.wasm", import.meta.url);
|
28
|
+
const response = await fetch(url);
|
29
|
+
return await wc.builder(new Uint8Array(await response.arrayBuffer()), false);
|
30
|
+
}
|
31
|
+
async function loadZkey() {
|
32
|
+
const url = new URL("./resources/rln_final.zkey", import.meta.url);
|
33
|
+
const response = await fetch(url);
|
34
|
+
return new Uint8Array(await response.arrayBuffer());
|
35
|
+
}
|
40
36
|
/**
|
41
37
|
* Create an instance of RLN
|
42
38
|
* @returns RLNInstance
|
@@ -44,43 +40,22 @@ const CIRCUIT = base64ToUint8Array(resources.circuit);
|
|
44
40
|
export async function create() {
|
45
41
|
await init();
|
46
42
|
zerokitRLN.init_panic_hook();
|
47
|
-
const witnessCalculator = await
|
48
|
-
const
|
43
|
+
const witnessCalculator = await loadWitnessCalculator();
|
44
|
+
const zkey = await loadZkey();
|
45
|
+
const vkey = stringEncoder.encode(JSON.stringify(verificationKey));
|
46
|
+
const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey);
|
49
47
|
return new RLNInstance(zkRLN, witnessCalculator);
|
50
48
|
}
|
51
49
|
export class MembershipKey {
|
52
|
-
constructor(
|
53
|
-
this.IDKey =
|
54
|
-
this.IDCommitment =
|
50
|
+
constructor(IDKey, IDCommitment) {
|
51
|
+
this.IDKey = IDKey;
|
52
|
+
this.IDCommitment = IDCommitment;
|
55
53
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
throw new RangeError('"value" argument is out of bounds');
|
61
|
-
if (offset + ext > buf.length)
|
62
|
-
throw new RangeError("Index out of range");
|
63
|
-
}
|
64
|
-
const writeUIntLE = function writeUIntLE(buf, value, offset, byteLength, noAssert) {
|
65
|
-
value = +value;
|
66
|
-
offset = offset >>> 0;
|
67
|
-
byteLength = byteLength >>> 0;
|
68
|
-
if (!noAssert) {
|
69
|
-
const maxBytes = Math.pow(2, 8 * byteLength) - 1;
|
70
|
-
checkInt(buf, value, offset, byteLength, maxBytes, 0);
|
71
|
-
}
|
72
|
-
let mul = 1;
|
73
|
-
let i = 0;
|
74
|
-
buf[offset] = value & 0xff;
|
75
|
-
while (++i < byteLength && (mul *= 0x100)) {
|
76
|
-
buf[offset + i] = (value / mul) & 0xff;
|
54
|
+
static fromBytes(memKeys) {
|
55
|
+
const idKey = memKeys.subarray(0, 32);
|
56
|
+
const idCommitment = memKeys.subarray(32);
|
57
|
+
return new MembershipKey(idKey, idCommitment);
|
77
58
|
}
|
78
|
-
return buf;
|
79
|
-
};
|
80
|
-
const DefaultEpochUnitSeconds = 10; // the rln-relay epoch length in seconds
|
81
|
-
export function toEpoch(timestamp, epochUnitSeconds = DefaultEpochUnitSeconds) {
|
82
|
-
const unix = Math.floor(timestamp.getTime() / 1000 / epochUnitSeconds);
|
83
|
-
return writeUIntLE(new Uint8Array(32), unix, 0, 8);
|
84
59
|
}
|
85
60
|
const proofOffset = 128;
|
86
61
|
const rootOffset = proofOffset + 32;
|
@@ -89,7 +64,7 @@ const shareXOffset = epochOffset + 32;
|
|
89
64
|
const shareYOffset = shareXOffset + 32;
|
90
65
|
const nullifierOffset = shareYOffset + 32;
|
91
66
|
const rlnIdentifierOffset = nullifierOffset + 32;
|
92
|
-
export class
|
67
|
+
export class Proof {
|
93
68
|
constructor(proofBytes) {
|
94
69
|
if (proofBytes.length < rlnIdentifierOffset)
|
95
70
|
throw "invalid proof";
|
@@ -102,9 +77,9 @@ export class RateLimitProof {
|
|
102
77
|
this.nullifier = proofBytes.subarray(shareYOffset, nullifierOffset);
|
103
78
|
this.rlnIdentifier = proofBytes.subarray(nullifierOffset, rlnIdentifierOffset);
|
104
79
|
}
|
105
|
-
|
106
|
-
|
107
|
-
|
80
|
+
}
|
81
|
+
function proofToBytes(p) {
|
82
|
+
return concatenate(p.proof, p.merkleRoot, p.epoch, p.shareX, p.shareY, p.nullifier, p.rlnIdentifier);
|
108
83
|
}
|
109
84
|
export class RLNInstance {
|
110
85
|
constructor(zkRLN, witnessCalculator) {
|
@@ -113,7 +88,7 @@ export class RLNInstance {
|
|
113
88
|
}
|
114
89
|
generateMembershipKey() {
|
115
90
|
const memKeys = zerokitRLN.generateMembershipKey(this.zkRLN);
|
116
|
-
return
|
91
|
+
return MembershipKey.fromBytes(memKeys);
|
117
92
|
}
|
118
93
|
insertMember(idCommitment) {
|
119
94
|
zerokitRLN.insertMember(this.zkRLN, idCommitment);
|
@@ -128,10 +103,10 @@ export class RLNInstance {
|
|
128
103
|
}
|
129
104
|
async generateProof(msg, index, epoch, idKey) {
|
130
105
|
if (epoch == undefined) {
|
131
|
-
epoch =
|
106
|
+
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
132
107
|
}
|
133
108
|
else if (epoch instanceof Date) {
|
134
|
-
epoch =
|
109
|
+
epoch = epochIntToBytes(dateToEpoch(epoch));
|
135
110
|
}
|
136
111
|
if (epoch.length != 32)
|
137
112
|
throw "invalid epoch";
|
@@ -144,13 +119,17 @@ export class RLNInstance {
|
|
144
119
|
const inputs = zerokitRLN.RLNWitnessToJson(this.zkRLN, rlnWitness);
|
145
120
|
const calculatedWitness = await this.witnessCalculator.calculateWitness(inputs, false); // no sanity check being used in zerokit
|
146
121
|
const proofBytes = zerokitRLN.generate_rln_proof_with_witness(this.zkRLN, calculatedWitness, rlnWitness);
|
147
|
-
return new
|
122
|
+
return new Proof(proofBytes);
|
148
123
|
}
|
149
124
|
verifyProof(proof) {
|
150
|
-
|
151
|
-
|
125
|
+
let pBytes;
|
126
|
+
if (proof instanceof Uint8Array) {
|
127
|
+
pBytes = proof;
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
pBytes = proofToBytes(proof);
|
152
131
|
}
|
153
|
-
return zerokitRLN.verifyProof(this.zkRLN,
|
132
|
+
return zerokitRLN.verifyProof(this.zkRLN, pBytes);
|
154
133
|
}
|
155
134
|
}
|
156
135
|
//# sourceMappingURL=rln.js.map
|
package/dist/rln.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"rln.js","sourceRoot":"","sources":["../src/rln.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,EAAE,KAAK,UAAU,MAAM,wBAAwB,CAAC;
|
1
|
+
{"version":3,"file":"rln.js","sourceRoot":"","sources":["../src/rln.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@waku/rln",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.8-378ad04",
|
4
4
|
"description": "Rate Limit Nullifier for js-waku",
|
5
5
|
"types": "./dist/index.d.ts",
|
6
6
|
"module": "./dist/index.js",
|
@@ -34,7 +34,7 @@
|
|
34
34
|
"test:lint": "eslint src --ext .ts",
|
35
35
|
"test:prettier": "prettier \"src/**/*.ts\" \"./*.json\" \"*.*js\" \".github/**/*.yml\" --list-different",
|
36
36
|
"test:spelling": "cspell \"{*.md,.github/*.md,src/**/*.ts}\"",
|
37
|
-
"test:tsc": "tsc",
|
37
|
+
"test:tsc": "tsc -p tsconfig.dev.json",
|
38
38
|
"test:browser": "karma start karma.conf.cjs",
|
39
39
|
"watch:build": "tsc -p tsconfig.json -w",
|
40
40
|
"watch:test": "mocha --watch",
|
@@ -66,6 +66,7 @@
|
|
66
66
|
"@types/uuid": "^8.3.0",
|
67
67
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
68
68
|
"@typescript-eslint/parser": "^5.8.1",
|
69
|
+
"@web/rollup-plugin-import-meta-assets": "^1.0.7",
|
69
70
|
"app-root-path": "^3.0.0",
|
70
71
|
"chai": "^4.3.4",
|
71
72
|
"cspell": "^5.14.0",
|
@@ -75,11 +76,12 @@
|
|
75
76
|
"eslint-plugin-functional": "^4.0.2",
|
76
77
|
"eslint-plugin-import": "^2.25.3",
|
77
78
|
"eslint-plugin-prettier": "^4.0.0",
|
78
|
-
"fast-check": "^2.
|
79
|
+
"fast-check": "^2.25.0",
|
79
80
|
"gh-pages": "^3.2.3",
|
80
81
|
"husky": "^7.0.4",
|
81
82
|
"ignore-loader": "^0.1.2",
|
82
83
|
"isomorphic-fetch": "^3.0.0",
|
84
|
+
"js-waku": "^0.29.0-29436ea",
|
83
85
|
"jsdom": "^19.0.0",
|
84
86
|
"jsdom-global": "^3.0.2",
|
85
87
|
"karma": "^6.3.12",
|
@@ -123,6 +125,6 @@
|
|
123
125
|
]
|
124
126
|
},
|
125
127
|
"dependencies": {
|
126
|
-
"@waku/zerokit-rln-wasm": "^0.0.
|
128
|
+
"@waku/zerokit-rln-wasm": "^0.0.2"
|
127
129
|
}
|
128
130
|
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// Adapted from https://github.com/feross/buffer
|
2
|
+
|
3
|
+
function checkInt(
|
4
|
+
buf: Uint8Array,
|
5
|
+
value: number,
|
6
|
+
offset: number,
|
7
|
+
ext: number,
|
8
|
+
max: number,
|
9
|
+
min: number
|
10
|
+
): void {
|
11
|
+
if (value > max || value < min)
|
12
|
+
throw new RangeError('"value" argument is out of bounds');
|
13
|
+
if (offset + ext > buf.length) throw new RangeError("Index out of range");
|
14
|
+
}
|
15
|
+
|
16
|
+
export function writeUIntLE(
|
17
|
+
buf: Uint8Array,
|
18
|
+
value: number,
|
19
|
+
offset: number,
|
20
|
+
byteLength: number,
|
21
|
+
noAssert?: boolean
|
22
|
+
): Uint8Array {
|
23
|
+
value = +value;
|
24
|
+
offset = offset >>> 0;
|
25
|
+
byteLength = byteLength >>> 0;
|
26
|
+
if (!noAssert) {
|
27
|
+
const maxBytes = Math.pow(2, 8 * byteLength) - 1;
|
28
|
+
checkInt(buf, value, offset, byteLength, maxBytes, 0);
|
29
|
+
}
|
30
|
+
|
31
|
+
let mul = 1;
|
32
|
+
let i = 0;
|
33
|
+
buf[offset] = value & 0xff;
|
34
|
+
while (++i < byteLength && (mul *= 0x100)) {
|
35
|
+
buf[offset + i] = (value / mul) & 0xff;
|
36
|
+
}
|
37
|
+
|
38
|
+
return buf;
|
39
|
+
}
|
package/src/codec.ts
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
import debug from "debug";
|
2
|
+
import { utils } from "js-waku";
|
3
|
+
import {
|
4
|
+
Decoder,
|
5
|
+
Encoder,
|
6
|
+
Message,
|
7
|
+
ProtoMessage,
|
8
|
+
RateLimitProof,
|
9
|
+
} from "js-waku/lib/interfaces";
|
10
|
+
|
11
|
+
import { RlnMessage } from "./message.js";
|
12
|
+
import { MembershipKey, RLNInstance } from "./rln.js";
|
13
|
+
|
14
|
+
const log = debug("waku:message:rln-encoder");
|
15
|
+
|
16
|
+
export class RLNEncoder implements Encoder {
|
17
|
+
public contentTopic: string;
|
18
|
+
private readonly idKey: Uint8Array;
|
19
|
+
|
20
|
+
constructor(
|
21
|
+
private encoder: Encoder,
|
22
|
+
private rlnInstance: RLNInstance,
|
23
|
+
private index: number,
|
24
|
+
membershipKey: MembershipKey
|
25
|
+
) {
|
26
|
+
if (index < 0) throw "invalid membership index";
|
27
|
+
this.idKey = membershipKey.IDKey;
|
28
|
+
this.contentTopic = encoder.contentTopic;
|
29
|
+
}
|
30
|
+
|
31
|
+
async toWire(message: Partial<Message>): Promise<Uint8Array | undefined> {
|
32
|
+
message.rateLimitProof = await this.generateProof(message);
|
33
|
+
|
34
|
+
return this.encoder.toWire(message);
|
35
|
+
}
|
36
|
+
|
37
|
+
async toProtoObj(
|
38
|
+
message: Partial<Message>
|
39
|
+
): Promise<ProtoMessage | undefined> {
|
40
|
+
const protoMessage = await this.encoder.toProtoObj(message);
|
41
|
+
if (!protoMessage) return;
|
42
|
+
|
43
|
+
protoMessage.rateLimitProof = await this.generateProof(message);
|
44
|
+
|
45
|
+
return protoMessage;
|
46
|
+
}
|
47
|
+
|
48
|
+
private async generateProof(
|
49
|
+
message: Partial<Message>
|
50
|
+
): Promise<RateLimitProof> {
|
51
|
+
const signal = toRLNSignal(message);
|
52
|
+
|
53
|
+
console.time("proof_gen_timer");
|
54
|
+
const proof = await this.rlnInstance.generateProof(
|
55
|
+
signal,
|
56
|
+
this.index,
|
57
|
+
message.timestamp,
|
58
|
+
this.idKey
|
59
|
+
);
|
60
|
+
console.timeEnd("proof_gen_timer");
|
61
|
+
return proof;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
export class RLNDecoder<T extends Message> implements Decoder<RlnMessage<T>> {
|
66
|
+
constructor(private rlnInstance: RLNInstance, private decoder: Decoder<T>) {}
|
67
|
+
|
68
|
+
get contentTopic(): string {
|
69
|
+
return this.decoder.contentTopic;
|
70
|
+
}
|
71
|
+
|
72
|
+
fromWireToProtoObj(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
|
73
|
+
const protoMessage = this.decoder.fromWireToProtoObj(bytes);
|
74
|
+
log("Message decoded", protoMessage);
|
75
|
+
return Promise.resolve(protoMessage);
|
76
|
+
}
|
77
|
+
|
78
|
+
async fromProtoObj(proto: ProtoMessage): Promise<RlnMessage<T> | undefined> {
|
79
|
+
const msg: T | undefined = await this.decoder.fromProtoObj(proto);
|
80
|
+
if (!msg) return;
|
81
|
+
return new RlnMessage(this.rlnInstance, msg, proto.rateLimitProof);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
function toRLNSignal(msg: Partial<Message>): Uint8Array {
|
86
|
+
const contentTopicBytes = utils.utf8ToBytes(msg.contentTopic ?? "");
|
87
|
+
return new Uint8Array([...(msg.payload ?? []), ...contentTopicBytes]);
|
88
|
+
}
|
package/src/epoch.ts
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
const DefaultEpochUnitSeconds = 10; // the rln-relay epoch length in seconds
|
2
|
+
|
3
|
+
export function dateToEpoch(
|
4
|
+
timestamp: Date,
|
5
|
+
epochUnitSeconds: number = DefaultEpochUnitSeconds
|
6
|
+
): number {
|
7
|
+
const time = timestamp.getTime();
|
8
|
+
return Math.floor(time / 1000 / epochUnitSeconds);
|
9
|
+
}
|
10
|
+
|
11
|
+
export function epochIntToBytes(epoch: number): Uint8Array {
|
12
|
+
const bytes = new Uint8Array(32);
|
13
|
+
const db = new DataView(bytes.buffer);
|
14
|
+
db.setUint32(0, epoch, true);
|
15
|
+
return bytes;
|
16
|
+
}
|
17
|
+
|
18
|
+
export function epochBytesToInt(bytes: Uint8Array): number {
|
19
|
+
const dv = new DataView(bytes.buffer);
|
20
|
+
return dv.getUint32(0, true);
|
21
|
+
}
|
package/src/index.ts
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
import
|
1
|
+
import { RLNDecoder, RLNEncoder } from "./codec.js";
|
2
|
+
import type { Proof, RLNInstance } from "./rln.js";
|
3
|
+
import { MembershipKey } from "./rln.js";
|
2
4
|
|
3
5
|
// reexport the create function, dynamically imported from rln.ts
|
4
6
|
export async function create(): Promise<RLNInstance> {
|
@@ -9,4 +11,4 @@ export async function create(): Promise<RLNInstance> {
|
|
9
11
|
return await rlnModule.create();
|
10
12
|
}
|
11
13
|
|
12
|
-
export { RLNInstance, MembershipKey,
|
14
|
+
export { RLNInstance, MembershipKey, Proof, RLNEncoder, RLNDecoder };
|
package/src/message.ts
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
import { Message, RateLimitProof } from "js-waku/lib/interfaces";
|
2
|
+
|
3
|
+
import { epochBytesToInt } from "./epoch.js";
|
4
|
+
import { RLNInstance } from "./rln.js";
|
5
|
+
|
6
|
+
export class RlnMessage<T extends Message> implements Message {
|
7
|
+
constructor(
|
8
|
+
public rlnInstance: RLNInstance,
|
9
|
+
public msg: T,
|
10
|
+
public rateLimitProof: RateLimitProof | undefined
|
11
|
+
) {}
|
12
|
+
|
13
|
+
public verify(): boolean | undefined {
|
14
|
+
return this.rateLimitProof
|
15
|
+
? this.rlnInstance.verifyProof(this.rateLimitProof)
|
16
|
+
: undefined;
|
17
|
+
}
|
18
|
+
|
19
|
+
get payload(): Uint8Array | undefined {
|
20
|
+
return this.msg.payload;
|
21
|
+
}
|
22
|
+
|
23
|
+
get contentTopic(): string | undefined {
|
24
|
+
return this.msg.contentTopic;
|
25
|
+
}
|
26
|
+
|
27
|
+
get timestamp(): Date | undefined {
|
28
|
+
return this.msg.timestamp;
|
29
|
+
}
|
30
|
+
|
31
|
+
get epoch(): number | undefined {
|
32
|
+
const bytes = this.msg.rateLimitProof?.epoch;
|
33
|
+
if (!bytes) return;
|
34
|
+
|
35
|
+
return epochBytesToInt(bytes);
|
36
|
+
}
|
37
|
+
}
|
package/src/rln.ts
CHANGED
@@ -1,22 +1,11 @@
|
|
1
1
|
import init, * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
2
|
+
import { RateLimitProof } from "js-waku/lib/interfaces";
|
2
3
|
|
3
|
-
import
|
4
|
+
import { writeUIntLE } from "./byte_utils.js";
|
5
|
+
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
6
|
+
import verificationKey from "./resources/verification_key.js";
|
4
7
|
import * as wc from "./witness_calculator.js";
|
5
|
-
|
6
|
-
/**
|
7
|
-
* Convert a base64 string into uint8Array
|
8
|
-
* @param base64
|
9
|
-
* @returns Uint8Array
|
10
|
-
*/
|
11
|
-
function base64ToUint8Array(base64: string): Uint8Array {
|
12
|
-
const binary_string = window.atob(base64);
|
13
|
-
const len = binary_string.length;
|
14
|
-
const bytes = new Uint8Array(len);
|
15
|
-
for (let i = 0; i < len; i++) {
|
16
|
-
bytes[i] = binary_string.charCodeAt(i);
|
17
|
-
}
|
18
|
-
return bytes;
|
19
|
-
}
|
8
|
+
import { WitnessCalculator } from "./witness_calculator.js";
|
20
9
|
|
21
10
|
/**
|
22
11
|
* Concatenate Uint8Arrays
|
@@ -37,10 +26,21 @@ function concatenate(...input: Uint8Array[]): Uint8Array {
|
|
37
26
|
return result;
|
38
27
|
}
|
39
28
|
|
29
|
+
const stringEncoder = new TextEncoder();
|
30
|
+
|
40
31
|
const DEPTH = 20;
|
41
|
-
|
42
|
-
|
43
|
-
const
|
32
|
+
|
33
|
+
async function loadWitnessCalculator(): Promise<WitnessCalculator> {
|
34
|
+
const url = new URL("./resources/rln.wasm", import.meta.url);
|
35
|
+
const response = await fetch(url);
|
36
|
+
return await wc.builder(new Uint8Array(await response.arrayBuffer()), false);
|
37
|
+
}
|
38
|
+
|
39
|
+
async function loadZkey(): Promise<Uint8Array> {
|
40
|
+
const url = new URL("./resources/rln_final.zkey", import.meta.url);
|
41
|
+
const response = await fetch(url);
|
42
|
+
return new Uint8Array(await response.arrayBuffer());
|
43
|
+
}
|
44
44
|
|
45
45
|
/**
|
46
46
|
* Create an instance of RLN
|
@@ -49,72 +49,26 @@ const CIRCUIT = base64ToUint8Array(resources.circuit);
|
|
49
49
|
export async function create(): Promise<RLNInstance> {
|
50
50
|
await init();
|
51
51
|
zerokitRLN.init_panic_hook();
|
52
|
-
|
53
|
-
const
|
54
|
-
const
|
52
|
+
const witnessCalculator = await loadWitnessCalculator();
|
53
|
+
const zkey = await loadZkey();
|
54
|
+
const vkey = stringEncoder.encode(JSON.stringify(verificationKey));
|
55
|
+
const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey);
|
55
56
|
return new RLNInstance(zkRLN, witnessCalculator);
|
56
57
|
}
|
57
58
|
|
58
59
|
export class MembershipKey {
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
constructor(
|
61
|
+
public readonly IDKey: Uint8Array,
|
62
|
+
public readonly IDCommitment: Uint8Array
|
63
|
+
) {}
|
64
|
+
|
65
|
+
static fromBytes(memKeys: Uint8Array): MembershipKey {
|
66
|
+
const idKey = memKeys.subarray(0, 32);
|
67
|
+
const idCommitment = memKeys.subarray(32);
|
68
|
+
return new MembershipKey(idKey, idCommitment);
|
65
69
|
}
|
66
70
|
}
|
67
71
|
|
68
|
-
// Adapted from https://github.com/feross/buffer
|
69
|
-
|
70
|
-
function checkInt(
|
71
|
-
buf: Uint8Array,
|
72
|
-
value: number,
|
73
|
-
offset: number,
|
74
|
-
ext: number,
|
75
|
-
max: number,
|
76
|
-
min: number
|
77
|
-
): void {
|
78
|
-
if (value > max || value < min)
|
79
|
-
throw new RangeError('"value" argument is out of bounds');
|
80
|
-
if (offset + ext > buf.length) throw new RangeError("Index out of range");
|
81
|
-
}
|
82
|
-
|
83
|
-
const writeUIntLE = function writeUIntLE(
|
84
|
-
buf: Uint8Array,
|
85
|
-
value: number,
|
86
|
-
offset: number,
|
87
|
-
byteLength: number,
|
88
|
-
noAssert?: boolean
|
89
|
-
): Uint8Array {
|
90
|
-
value = +value;
|
91
|
-
offset = offset >>> 0;
|
92
|
-
byteLength = byteLength >>> 0;
|
93
|
-
if (!noAssert) {
|
94
|
-
const maxBytes = Math.pow(2, 8 * byteLength) - 1;
|
95
|
-
checkInt(buf, value, offset, byteLength, maxBytes, 0);
|
96
|
-
}
|
97
|
-
|
98
|
-
let mul = 1;
|
99
|
-
let i = 0;
|
100
|
-
buf[offset] = value & 0xff;
|
101
|
-
while (++i < byteLength && (mul *= 0x100)) {
|
102
|
-
buf[offset + i] = (value / mul) & 0xff;
|
103
|
-
}
|
104
|
-
|
105
|
-
return buf;
|
106
|
-
};
|
107
|
-
|
108
|
-
const DefaultEpochUnitSeconds = 10; // the rln-relay epoch length in seconds
|
109
|
-
|
110
|
-
export function toEpoch(
|
111
|
-
timestamp: Date,
|
112
|
-
epochUnitSeconds: number = DefaultEpochUnitSeconds
|
113
|
-
): Uint8Array {
|
114
|
-
const unix = Math.floor(timestamp.getTime() / 1000 / epochUnitSeconds);
|
115
|
-
return writeUIntLE(new Uint8Array(32), unix, 0, 8);
|
116
|
-
}
|
117
|
-
|
118
72
|
const proofOffset = 128;
|
119
73
|
const rootOffset = proofOffset + 32;
|
120
74
|
const epochOffset = rootOffset + 32;
|
@@ -123,7 +77,7 @@ const shareYOffset = shareXOffset + 32;
|
|
123
77
|
const nullifierOffset = shareYOffset + 32;
|
124
78
|
const rlnIdentifierOffset = nullifierOffset + 32;
|
125
79
|
|
126
|
-
export class RateLimitProof {
|
80
|
+
export class Proof implements RateLimitProof {
|
127
81
|
readonly proof: Uint8Array;
|
128
82
|
readonly merkleRoot: Uint8Array;
|
129
83
|
readonly epoch: Uint8Array;
|
@@ -146,26 +100,29 @@ export class RateLimitProof {
|
|
146
100
|
rlnIdentifierOffset
|
147
101
|
);
|
148
102
|
}
|
103
|
+
}
|
149
104
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
}
|
105
|
+
function proofToBytes(p: RateLimitProof): Uint8Array {
|
106
|
+
return concatenate(
|
107
|
+
p.proof,
|
108
|
+
p.merkleRoot,
|
109
|
+
p.epoch,
|
110
|
+
p.shareX,
|
111
|
+
p.shareY,
|
112
|
+
p.nullifier,
|
113
|
+
p.rlnIdentifier
|
114
|
+
);
|
161
115
|
}
|
162
116
|
|
163
117
|
export class RLNInstance {
|
164
|
-
constructor(
|
118
|
+
constructor(
|
119
|
+
private zkRLN: number,
|
120
|
+
private witnessCalculator: WitnessCalculator
|
121
|
+
) {}
|
165
122
|
|
166
123
|
generateMembershipKey(): MembershipKey {
|
167
124
|
const memKeys = zerokitRLN.generateMembershipKey(this.zkRLN);
|
168
|
-
return
|
125
|
+
return MembershipKey.fromBytes(memKeys);
|
169
126
|
}
|
170
127
|
|
171
128
|
insertMember(idCommitment: Uint8Array): void {
|
@@ -195,9 +152,9 @@ export class RLNInstance {
|
|
195
152
|
idKey: Uint8Array
|
196
153
|
): Promise<RateLimitProof> {
|
197
154
|
if (epoch == undefined) {
|
198
|
-
epoch =
|
155
|
+
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
199
156
|
} else if (epoch instanceof Date) {
|
200
|
-
epoch =
|
157
|
+
epoch = epochIntToBytes(dateToEpoch(epoch));
|
201
158
|
}
|
202
159
|
|
203
160
|
if (epoch.length != 32) throw "invalid epoch";
|
@@ -221,13 +178,16 @@ export class RLNInstance {
|
|
221
178
|
rlnWitness
|
222
179
|
);
|
223
180
|
|
224
|
-
return new
|
181
|
+
return new Proof(proofBytes);
|
225
182
|
}
|
226
183
|
|
227
184
|
verifyProof(proof: RateLimitProof | Uint8Array): boolean {
|
228
|
-
|
229
|
-
|
185
|
+
let pBytes: Uint8Array;
|
186
|
+
if (proof instanceof Uint8Array) {
|
187
|
+
pBytes = proof;
|
188
|
+
} else {
|
189
|
+
pBytes = proofToBytes(proof);
|
230
190
|
}
|
231
|
-
return zerokitRLN.verifyProof(this.zkRLN,
|
191
|
+
return zerokitRLN.verifyProof(this.zkRLN, pBytes);
|
232
192
|
}
|
233
193
|
}
|