mirror of
https://github.com/logos-messaging/js-rln.git
synced 2026-01-04 06:33:09 +00:00
feat: use identity credentials, and expose hash, bulk insert and delete members functions (#58)
* feat: use identity credentials, and expose hash, bulk insert and delete members functions * feat: merkle root tracker
This commit is contained in:
parent
7e0966aef7
commit
e52107dcf8
10
README.md
10
README.md
@ -70,10 +70,10 @@ import * as rln from "@waku/rln";
|
|||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generating RLN Membership Keypair
|
#### Generating RLN Membership Credentials
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let memKeys = rlnInstance.generateMembershipKey();
|
let credentials = rlnInstance.generateIdentityCredentials();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generating RLN Membership Keypair Using a Seed
|
### Generating RLN Membership Keypair Using a Seed
|
||||||
@ -81,13 +81,13 @@ let memKeys = rlnInstance.generateMembershipKey();
|
|||||||
This can be used to generate credentials from a signature hash (e.g. signed by an Ethereum account).
|
This can be used to generate credentials from a signature hash (e.g. signed by an Ethereum account).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let memKeys = rlnInstance.generateSeededMembershipKey(seed);
|
let credentials = rlnInstance.generateSeededIdentityCredentials(seed);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Adding Membership Keys Into Merkle Tree
|
### Adding Membership Keys Into Merkle Tree
|
||||||
|
|
||||||
```js
|
```js
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credentials.IDCommitment);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generating a Proof
|
### Generating a Proof
|
||||||
@ -106,7 +106,7 @@ const proof = await rlnInstance.generateProof(
|
|||||||
uint8Msg,
|
uint8Msg,
|
||||||
index,
|
index,
|
||||||
epoch,
|
epoch,
|
||||||
memKeys.IDKey
|
credentials.IDSecretHash
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import * as rln from "@waku/rln";
|
import * as rln from "@waku/rln";
|
||||||
|
|
||||||
rln.create().then(async rlnInstance => {
|
rln.create().then(async rlnInstance => {
|
||||||
let memKeys = rlnInstance.generateMembershipKey();
|
const credentials = rlnInstance.generateIdentityCredentials();
|
||||||
|
|
||||||
//peer's index in the Merkle Tree
|
//peer's index in the Merkle Tree
|
||||||
const index = 5
|
const index = 5
|
||||||
@ -10,11 +10,11 @@ rln.create().then(async rlnInstance => {
|
|||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
if (i == index) {
|
if (i == index) {
|
||||||
// insert the current peer's pk
|
// insert the current peer's pk
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credentials.IDCommitment);
|
||||||
} else {
|
} else {
|
||||||
// create a new key pair
|
// create a new key pair
|
||||||
let memKeys = rlnInstance.generateMembershipKey(); // TODO: handle error
|
const credentials = rlnInstance.generateIdentityCredentials(); // TODO: handle error
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credentials.IDCommitment);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ rln.create().then(async rlnInstance => {
|
|||||||
|
|
||||||
console.log("Generating proof...");
|
console.log("Generating proof...");
|
||||||
console.time("proof_gen_timer");
|
console.time("proof_gen_timer");
|
||||||
let proof = await rlnInstance.generateRLNProof(uint8Msg, index, epoch, memKeys.IDKey)
|
let proof = await rlnInstance.generateRLNProof(uint8Msg, index, epoch, credentials.IDSecretHash)
|
||||||
console.timeEnd("proof_gen_timer");
|
console.timeEnd("proof_gen_timer");
|
||||||
console.log("Proof", proof)
|
console.log("Proof", proof)
|
||||||
|
|
||||||
|
|||||||
@ -30,10 +30,11 @@ module.exports = function (config) {
|
|||||||
envPreprocessor: ["CI"],
|
envPreprocessor: ["CI"],
|
||||||
reporters: ["progress"],
|
reporters: ["progress"],
|
||||||
browsers: ["ChromeHeadless"],
|
browsers: ["ChromeHeadless"],
|
||||||
|
pingTimeout: 60000,
|
||||||
singleRun: true,
|
singleRun: true,
|
||||||
client: {
|
client: {
|
||||||
mocha: {
|
mocha: {
|
||||||
timeout: 6000, // Default is 2s
|
timeout: 60000, // Default is 2s
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
webpack: {
|
webpack: {
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@waku/rln",
|
"name": "@waku/rln",
|
||||||
"version": "0.0.14",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@waku/rln",
|
"name": "@waku/rln",
|
||||||
"version": "0.0.14",
|
"version": "0.1.0",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/utils": "^0.0.4",
|
"@waku/utils": "^0.0.4",
|
||||||
"@waku/zerokit-rln-wasm": "^0.0.5",
|
"@waku/zerokit-rln-wasm": "^0.0.10",
|
||||||
"ethers": "^5.7.2"
|
"ethers": "^5.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -2942,9 +2942,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/zerokit-rln-wasm": {
|
"node_modules/@waku/zerokit-rln-wasm": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.10.tgz",
|
||||||
"integrity": "sha512-uZHZRk06WrnqJJOVwIIKtsjWf2d6g2JpK8FtC0lHg4JJkOxhJy0pgEIuBCPw8Je4MpF9FCtIO/ww7xicdlC2GA=="
|
"integrity": "sha512-qegIK1P54mxEp59uTa8C0/zidUffLc2Iee61yiKRIuGJDui2mQ+0V+KzPSPImKpIoqfVLT192EqgZkqPmj8VEw=="
|
||||||
},
|
},
|
||||||
"node_modules/@web/rollup-plugin-import-meta-assets": {
|
"node_modules/@web/rollup-plugin-import-meta-assets": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
@ -13629,9 +13629,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@waku/zerokit-rln-wasm": {
|
"@waku/zerokit-rln-wasm": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.10.tgz",
|
||||||
"integrity": "sha512-uZHZRk06WrnqJJOVwIIKtsjWf2d6g2JpK8FtC0lHg4JJkOxhJy0pgEIuBCPw8Je4MpF9FCtIO/ww7xicdlC2GA=="
|
"integrity": "sha512-qegIK1P54mxEp59uTa8C0/zidUffLc2Iee61yiKRIuGJDui2mQ+0V+KzPSPImKpIoqfVLT192EqgZkqPmj8VEw=="
|
||||||
},
|
},
|
||||||
"@web/rollup-plugin-import-meta-assets": {
|
"@web/rollup-plugin-import-meta-assets": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@waku/rln",
|
"name": "@waku/rln",
|
||||||
"version": "0.0.14",
|
"version": "0.1.0",
|
||||||
"description": "Rate Limit Nullifier for js-waku",
|
"description": "Rate Limit Nullifier for js-waku",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@ -130,7 +130,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/utils": "^0.0.4",
|
"@waku/utils": "^0.0.4",
|
||||||
"@waku/zerokit-rln-wasm": "^0.0.5",
|
"@waku/zerokit-rln-wasm": "^0.0.10",
|
||||||
"ethers": "^5.7.2"
|
"ethers": "^5.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,17 +35,17 @@ const EMPTY_PUBSUB_TOPIC = "";
|
|||||||
describe("RLN codec with version 0", () => {
|
describe("RLN codec with version 0", () => {
|
||||||
it("toWire", async function () {
|
it("toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = createRLNEncoder({
|
const rlnEncoder = createRLNEncoder({
|
||||||
encoder: createEncoder({ contentTopic: TestContentTopic }),
|
encoder: createEncoder({ contentTopic: TestContentTopic }),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
membershipKey: memKeys,
|
credential,
|
||||||
});
|
});
|
||||||
const rlnDecoder = createRLNDecoder({
|
const rlnDecoder = createRLNDecoder({
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -76,17 +76,17 @@ describe("RLN codec with version 0", () => {
|
|||||||
|
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = new RLNEncoder(
|
const rlnEncoder = new RLNEncoder(
|
||||||
createEncoder({ contentTopic: TestContentTopic }),
|
createEncoder({ contentTopic: TestContentTopic }),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -119,11 +119,11 @@ describe("RLN codec with version 0", () => {
|
|||||||
describe("RLN codec with version 1", () => {
|
describe("RLN codec with version 1", () => {
|
||||||
it("Symmetric, toWire", async function () {
|
it("Symmetric, toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const symKey = generateSymmetricKey();
|
const symKey = generateSymmetricKey();
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
}),
|
}),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -166,11 +166,11 @@ describe("RLN codec with version 1", () => {
|
|||||||
|
|
||||||
it("Symmetric, toProtoObj", async function () {
|
it("Symmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const symKey = generateSymmetricKey();
|
const symKey = generateSymmetricKey();
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
}),
|
}),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -212,11 +212,11 @@ describe("RLN codec with version 1", () => {
|
|||||||
|
|
||||||
it("Asymmetric, toWire", async function () {
|
it("Asymmetric, toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const privateKey = generatePrivateKey();
|
const privateKey = generatePrivateKey();
|
||||||
const publicKey = getPublicKey(privateKey);
|
const publicKey = getPublicKey(privateKey);
|
||||||
@ -228,7 +228,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
}),
|
}),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -260,11 +260,11 @@ describe("RLN codec with version 1", () => {
|
|||||||
|
|
||||||
it("Asymmetric, toProtoObj", async function () {
|
it("Asymmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const privateKey = generatePrivateKey();
|
const privateKey = generatePrivateKey();
|
||||||
const publicKey = getPublicKey(privateKey);
|
const publicKey = getPublicKey(privateKey);
|
||||||
@ -276,7 +276,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
}),
|
}),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
@ -309,17 +309,17 @@ describe("RLN codec with version 1", () => {
|
|||||||
describe("RLN Codec - epoch", () => {
|
describe("RLN Codec - epoch", () => {
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = new RLNEncoder(
|
const rlnEncoder = new RLNEncoder(
|
||||||
createEncoder({ contentTopic: TestContentTopic }),
|
createEncoder({ contentTopic: TestContentTopic }),
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
index,
|
index,
|
||||||
memKeys
|
credential
|
||||||
);
|
);
|
||||||
const rlnDecoder = new RLNDecoder(
|
const rlnDecoder = new RLNDecoder(
|
||||||
rlnInstance,
|
rlnInstance,
|
||||||
|
|||||||
14
src/codec.ts
14
src/codec.ts
@ -9,21 +9,21 @@ import type {
|
|||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { RlnMessage, toRLNSignal } from "./message.js";
|
import { RlnMessage, toRLNSignal } from "./message.js";
|
||||||
import { MembershipKey, RLNInstance } from "./rln.js";
|
import { IdentityCredential, RLNInstance } from "./rln.js";
|
||||||
|
|
||||||
const log = debug("waku:rln:encoder");
|
const log = debug("waku:rln:encoder");
|
||||||
|
|
||||||
export class RLNEncoder implements IEncoder {
|
export class RLNEncoder implements IEncoder {
|
||||||
private readonly idKey: Uint8Array;
|
private readonly idSecretHash: Uint8Array;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private encoder: IEncoder,
|
private encoder: IEncoder,
|
||||||
private rlnInstance: RLNInstance,
|
private rlnInstance: RLNInstance,
|
||||||
private index: number,
|
private index: number,
|
||||||
membershipKey: MembershipKey
|
identityCredential: IdentityCredential
|
||||||
) {
|
) {
|
||||||
if (index < 0) throw "invalid membership index";
|
if (index < 0) throw "invalid membership index";
|
||||||
this.idKey = membershipKey.IDKey;
|
this.idSecretHash = identityCredential.IDSecretHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
|
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
|
||||||
@ -50,7 +50,7 @@ export class RLNEncoder implements IEncoder {
|
|||||||
signal,
|
signal,
|
||||||
this.index,
|
this.index,
|
||||||
message.timestamp,
|
message.timestamp,
|
||||||
this.idKey
|
this.idSecretHash
|
||||||
);
|
);
|
||||||
console.timeEnd("proof_gen_timer");
|
console.timeEnd("proof_gen_timer");
|
||||||
return proof;
|
return proof;
|
||||||
@ -69,7 +69,7 @@ type RLNEncoderOptions = {
|
|||||||
encoder: IEncoder;
|
encoder: IEncoder;
|
||||||
rlnInstance: RLNInstance;
|
rlnInstance: RLNInstance;
|
||||||
index: number;
|
index: number;
|
||||||
membershipKey: MembershipKey;
|
credential: IdentityCredential;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
|
export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
|
||||||
@ -77,7 +77,7 @@ export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
|
|||||||
options.encoder,
|
options.encoder,
|
||||||
options.rlnInstance,
|
options.rlnInstance,
|
||||||
options.index,
|
options.index,
|
||||||
options.membershipKey
|
options.credential
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ describe("js-rln", () => {
|
|||||||
it("should verify a proof", async function () {
|
it("should verify a proof", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
|
|
||||||
const memKeys = rlnInstance.generateMembershipKey();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
|
|
||||||
//peer's index in the Merkle Tree
|
//peer's index in the Merkle Tree
|
||||||
const index = 5;
|
const index = 5;
|
||||||
@ -15,11 +15,11 @@ describe("js-rln", () => {
|
|||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
if (i == index) {
|
if (i == index) {
|
||||||
// insert the current peer's pk
|
// insert the current peer's pk
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
} else {
|
} else {
|
||||||
// create a new key pair
|
// create a new key pair
|
||||||
rlnInstance.insertMember(
|
rlnInstance.insertMember(
|
||||||
rlnInstance.generateMembershipKey().IDCommitment
|
rlnInstance.generateIdentityCredentials().IDCommitment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ describe("js-rln", () => {
|
|||||||
uint8Msg,
|
uint8Msg,
|
||||||
index,
|
index,
|
||||||
epoch,
|
epoch,
|
||||||
memKeys.IDKey
|
credential.IDSecretHash
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -61,7 +61,7 @@ describe("js-rln", () => {
|
|||||||
it("should verify a proof with a seeded membership key generation", async function () {
|
it("should verify a proof with a seeded membership key generation", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const memKeys = rlnInstance.generateSeededMembershipKey(seed);
|
const credential = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
|
|
||||||
//peer's index in the Merkle Tree
|
//peer's index in the Merkle Tree
|
||||||
const index = 5;
|
const index = 5;
|
||||||
@ -70,11 +70,11 @@ describe("js-rln", () => {
|
|||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
if (i == index) {
|
if (i == index) {
|
||||||
// insert the current peer's pk
|
// insert the current peer's pk
|
||||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
rlnInstance.insertMember(credential.IDCommitment);
|
||||||
} else {
|
} else {
|
||||||
// create a new key pair
|
// create a new key pair
|
||||||
rlnInstance.insertMember(
|
rlnInstance.insertMember(
|
||||||
rlnInstance.generateMembershipKey().IDCommitment
|
rlnInstance.generateIdentityCredentials().IDCommitment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ describe("js-rln", () => {
|
|||||||
uint8Msg,
|
uint8Msg,
|
||||||
index,
|
index,
|
||||||
epoch,
|
epoch,
|
||||||
memKeys.IDKey
|
credential.IDSecretHash
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -116,14 +116,20 @@ describe("js-rln", () => {
|
|||||||
it("should generate the same membership key if the same seed is provided", async function () {
|
it("should generate the same membership key if the same seed is provided", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.create();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const memKeys1 = rlnInstance.generateSeededMembershipKey(seed);
|
const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
const memKeys2 = rlnInstance.generateSeededMembershipKey(seed);
|
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
|
|
||||||
memKeys1.IDCommitment.forEach((element, index) => {
|
memKeys1.IDCommitment.forEach((element, index) => {
|
||||||
expect(element).to.equal(memKeys2.IDCommitment[index]);
|
expect(element).to.equal(memKeys2.IDCommitment[index]);
|
||||||
});
|
});
|
||||||
memKeys1.IDKey.forEach((element, index) => {
|
memKeys1.IDNullifier.forEach((element, index) => {
|
||||||
expect(element).to.equal(memKeys2.IDKey[index]);
|
expect(element).to.equal(memKeys2.IDNullifier[index]);
|
||||||
|
});
|
||||||
|
memKeys1.IDSecretHash.forEach((element, index) => {
|
||||||
|
expect(element).to.equal(memKeys2.IDSecretHash[index]);
|
||||||
|
});
|
||||||
|
memKeys1.IDTrapdoor.forEach((element, index) => {
|
||||||
|
expect(element).to.equal(memKeys2.IDTrapdoor[index]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
13
src/index.ts
13
src/index.ts
@ -1,8 +1,13 @@
|
|||||||
import { RLNDecoder, RLNEncoder } from "./codec.js";
|
import { RLNDecoder, RLNEncoder } from "./codec.js";
|
||||||
import { GOERLI_CONTRACT, RLN_ABI } from "./constants.js";
|
import { GOERLI_CONTRACT, RLN_ABI } from "./constants.js";
|
||||||
import { Proof, RLNInstance } from "./rln.js";
|
import {
|
||||||
import { MembershipKey } from "./rln.js";
|
IdentityCredential,
|
||||||
|
Proof,
|
||||||
|
ProofMetadata,
|
||||||
|
RLNInstance,
|
||||||
|
} from "./rln.js";
|
||||||
import { RLNContract } from "./rln_contract.js";
|
import { RLNContract } from "./rln_contract.js";
|
||||||
|
import { MerkleRootTracker } from "./root_tracker.js";
|
||||||
|
|
||||||
// reexport the create function, dynamically imported from rln.ts
|
// reexport the create function, dynamically imported from rln.ts
|
||||||
export async function create(): Promise<RLNInstance> {
|
export async function create(): Promise<RLNInstance> {
|
||||||
@ -15,10 +20,12 @@ export async function create(): Promise<RLNInstance> {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
RLNInstance,
|
RLNInstance,
|
||||||
MembershipKey,
|
IdentityCredential,
|
||||||
Proof,
|
Proof,
|
||||||
|
ProofMetadata,
|
||||||
RLNEncoder,
|
RLNEncoder,
|
||||||
RLNDecoder,
|
RLNDecoder,
|
||||||
|
MerkleRootTracker,
|
||||||
RLNContract,
|
RLNContract,
|
||||||
RLN_ABI,
|
RLN_ABI,
|
||||||
GOERLI_CONTRACT,
|
GOERLI_CONTRACT,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,120 +1,112 @@
|
|||||||
const verificationKey = {
|
const verificationKey = {
|
||||||
"protocol": "groth16",
|
protocol: "groth16",
|
||||||
"curve": "bn128",
|
curve: "bn128",
|
||||||
"nPublic": 6,
|
nPublic: 6,
|
||||||
"vk_alpha_1": [
|
vk_alpha_1: [
|
||||||
"1805378556360488226980822394597799963030511477964155500103132920745199284516",
|
"20124996762962216725442980738609010303800849578410091356605067053491763969391",
|
||||||
"11990395240534218699464972016456017378439762088320057798320175886595281336136",
|
"9118593021526896828671519912099489027245924097793322973632351264852174143923",
|
||||||
"1"
|
"1",
|
||||||
],
|
],
|
||||||
"vk_beta_2": [
|
vk_beta_2: [
|
||||||
[
|
[
|
||||||
"11031529986141021025408838211017932346992429731488270384177563837022796743627",
|
"4693952934005375501364248788849686435240706020501681709396105298107971354382",
|
||||||
"16042159910707312759082561183373181639420894978640710177581040523252926273854"
|
"14346958885444710485362620645446987998958218205939139994511461437152241966681",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"20112698439519222240302944148895052359035104222313380895334495118294612255131",
|
"16851772916911573982706166384196538392731905827088356034885868448550849804972",
|
||||||
"19441583024670359810872018179190533814486480928824742448673677460151702019379"
|
"823612331030938060799959717749043047845343400798220427319188951998582076532",
|
||||||
],
|
],
|
||||||
[
|
["1", "0"],
|
||||||
"1",
|
|
||||||
"0"
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
"vk_gamma_2": [
|
vk_gamma_2: [
|
||||||
[
|
[
|
||||||
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||||
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
"11559732032986387107991004021392285783925812861821192530917403151452391805634",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||||
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
"4082367875863433681332203403145435568316851327593401208105741076214120093531",
|
||||||
|
],
|
||||||
|
["1", "0"],
|
||||||
|
],
|
||||||
|
vk_delta_2: [
|
||||||
|
[
|
||||||
|
"8353516066399360694538747105302262515182301251524941126222712285088022964076",
|
||||||
|
"9329524012539638256356482961742014315122377605267454801030953882967973561832",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
"16805391589556134376869247619848130874761233086443465978238468412168162326401",
|
||||||
|
"10111259694977636294287802909665108497237922060047080343914303287629927847739",
|
||||||
|
],
|
||||||
|
["1", "0"],
|
||||||
|
],
|
||||||
|
vk_alphabeta_12: [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"12608968655665301215455851857466367636344427685631271961542642719683786103711",
|
||||||
|
"9849575605876329747382930567422916152871921500826003490242628251047652318086",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"6322029441245076030714726551623552073612922718416871603535535085523083939021",
|
||||||
|
"8700115492541474338049149013125102281865518624059015445617546140629435818912",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"10674973475340072635573101639867487770811074181475255667220644196793546640210",
|
||||||
|
"2926286967251299230490668407790788696102889214647256022788211245826267484824",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"9660441540778523475944706619139394922744328902833875392144658911530830074820",
|
||||||
|
"19548113127774514328631808547691096362144426239827206966690021428110281506546",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1870837942477655969123169532603615788122896469891695773961478956740992497097",
|
||||||
|
"12536105729661705698805725105036536744930776470051238187456307227425796690780",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"21811903352654147452884857281720047789720483752548991551595462057142824037334",
|
||||||
|
"19021616763967199151052893283384285352200445499680068407023236283004353578353",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
IC: [
|
||||||
|
[
|
||||||
|
"11992897507809711711025355300535923222599547639134311050809253678876341466909",
|
||||||
|
"17181525095924075896332561978747020491074338784673526378866503154966799128110",
|
||||||
"1",
|
"1",
|
||||||
"0"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"vk_delta_2": [
|
|
||||||
[
|
|
||||||
"1948496782571164085469528023647105317580208688174386157591917599801657832035",
|
|
||||||
"20445814069256658101339037520922621162739470138213615104905368409238414511981"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10024680869920840984813249386422727863826862577760330492647062850849851925340",
|
|
||||||
"10512156247842686783409460795717734694774542185222602679117887145206209285142"
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
"17018665030246167677911144513385572506766200776123272044534328594850561667818",
|
||||||
|
"18601114175490465275436712413925513066546725461375425769709566180981674884464",
|
||||||
"1",
|
"1",
|
||||||
"0"
|
],
|
||||||
]
|
[
|
||||||
|
"18799470100699658367834559797874857804183288553462108031963980039244731716542",
|
||||||
|
"13064227487174191981628537974951887429496059857753101852163607049188825592007",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"17432501889058124609368103715904104425610382063762621017593209214189134571156",
|
||||||
|
"13406815149699834788256141097399354592751313348962590382887503595131085938635",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"10320964835612716439094703312987075811498239445882526576970512041988148264481",
|
||||||
|
"9024164961646353611176283204118089412001502110138072989569118393359029324867",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"718355081067365548229685160476620267257521491773976402837645005858953849298",
|
||||||
|
"14635482993933988261008156660773180150752190597753512086153001683711587601974",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"11777720285956632126519898515392071627539405001940313098390150593689568177535",
|
||||||
|
"8483603647274280691250972408211651407952870456587066148445913156086740744515",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
],
|
],
|
||||||
"vk_alphabeta_12": [
|
};
|
||||||
[
|
|
||||||
[
|
export default verificationKey;
|
||||||
"5151991366823434428398919091000210787450832786814248297320989361921939794156",
|
|
||||||
"15735191313289001022885148627913534790382722933676436876510746491415970766821"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"3387907257437913904447588318761906430938415556102110876587455322225272831272",
|
|
||||||
"1998779853452712881084781956683721603875246565720647583735935725110674288056"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"14280074182991498185075387990446437410077692353432005297922275464876153151820",
|
|
||||||
"17092408446352310039633488224969232803092763095456307462247653153107223117633"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"4359046709531668109201634396816565829237358165496082832279660960675584351266",
|
|
||||||
"4511888308846208349307186938266411423935335853916317436093178288331845821336"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"11429499807090785857812316277335883295048773373068683863667725283965356423273",
|
|
||||||
"16232274853200678548795010078253506586114563833318973594428907292096178657392"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"18068999605870933925311275504102553573815570223888590384919752303726860800970",
|
|
||||||
"17309569111965782732372130116757295842160193489132771344011460471298173784984"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"IC": [
|
|
||||||
[
|
|
||||||
"18693301901828818437917730940595978397160482710354161265484535387752523310572",
|
|
||||||
"17985273354976640088538673802000794244421192643855111089693820179790551470769",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"21164641723988537620541455173278629777250883365474191521194244273980931825942",
|
|
||||||
"998385854410718613441067082771678946155853656328717326195057262123686425518",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"21666968581672145768705229094968410656430989593283335488162701230986314747515",
|
|
||||||
"17996457608540683483506630273632100555125353447506062045735279661096094677264",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"20137761979695192602424300886442379728165712610493092740175904438282083668117",
|
|
||||||
"19184814924890679891263780109959113289320127263583260218200636509492157834679",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10943171273393803842589314082509655332154393332394322726077270895078286354146",
|
|
||||||
"10872472035685319847811233167729172672344935625121511932198535224727331126439",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"13049169779481227658517545034348883391527506091990880778783387628208561946597",
|
|
||||||
"10083689369261379027228809473568899816311684698866922944902456565434209079955",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"19633516378466409167014413361365552102431118630694133723053441455184566611083",
|
|
||||||
"8059525100726933978719058611146131904598011633549012007359165766216730722269",
|
|
||||||
"1"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
export default verificationKey
|
|
||||||
|
|||||||
103
src/rln.ts
103
src/rln.ts
@ -66,18 +66,29 @@ export async function create(): Promise<RLNInstance> {
|
|||||||
return new RLNInstance(zkRLN, witnessCalculator);
|
return new RLNInstance(zkRLN, witnessCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MembershipKey {
|
export class IdentityCredential {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly IDKey: Uint8Array,
|
public readonly IDTrapdoor: Uint8Array,
|
||||||
|
public readonly IDNullifier: Uint8Array,
|
||||||
|
public readonly IDSecretHash: Uint8Array,
|
||||||
public readonly IDCommitment: Uint8Array,
|
public readonly IDCommitment: Uint8Array,
|
||||||
public readonly IDCommitmentBigInt: bigint
|
public readonly IDCommitmentBigInt: bigint
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static fromBytes(memKeys: Uint8Array): MembershipKey {
|
static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
||||||
const idKey = memKeys.subarray(0, 32);
|
const idTrapdoor = memKeys.subarray(0, 32);
|
||||||
const idCommitment = memKeys.subarray(32);
|
const idNullifier = memKeys.subarray(32, 64);
|
||||||
|
const idSecretHash = memKeys.subarray(64, 96);
|
||||||
|
const idCommitment = memKeys.subarray(96);
|
||||||
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
||||||
return new MembershipKey(idKey, idCommitment, idCommitmentBigInt);
|
|
||||||
|
return new IdentityCredential(
|
||||||
|
idTrapdoor,
|
||||||
|
idNullifier,
|
||||||
|
idSecretHash,
|
||||||
|
idCommitment,
|
||||||
|
idCommitmentBigInt
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +100,14 @@ const shareYOffset = shareXOffset + 32;
|
|||||||
const nullifierOffset = shareYOffset + 32;
|
const nullifierOffset = shareYOffset + 32;
|
||||||
const rlnIdentifierOffset = nullifierOffset + 32;
|
const rlnIdentifierOffset = nullifierOffset + 32;
|
||||||
|
|
||||||
|
export class ProofMetadata {
|
||||||
|
constructor(
|
||||||
|
public readonly nullifier: Uint8Array,
|
||||||
|
public readonly shareX: Uint8Array,
|
||||||
|
public readonly shareY: Uint8Array,
|
||||||
|
public readonly externalNullifier: Uint8Array
|
||||||
|
) {}
|
||||||
|
}
|
||||||
export class Proof implements IRateLimitProof {
|
export class Proof implements IRateLimitProof {
|
||||||
readonly proof: Uint8Array;
|
readonly proof: Uint8Array;
|
||||||
readonly merkleRoot: Uint8Array;
|
readonly merkleRoot: Uint8Array;
|
||||||
@ -112,6 +131,16 @@ export class Proof implements IRateLimitProof {
|
|||||||
rlnIdentifierOffset
|
rlnIdentifierOffset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extractMetadata(): ProofMetadata {
|
||||||
|
const externalNullifier = poseidonHash(this.epoch, this.rlnIdentifier);
|
||||||
|
return new ProofMetadata(
|
||||||
|
this.nullifier,
|
||||||
|
this.shareX,
|
||||||
|
this.shareY,
|
||||||
|
externalNullifier
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function proofToBytes(p: IRateLimitProof): Uint8Array {
|
export function proofToBytes(p: IRateLimitProof): Uint8Array {
|
||||||
@ -126,30 +155,60 @@ export function proofToBytes(p: IRateLimitProof): Uint8Array {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function poseidonHash(...input: Array<Uint8Array>): Uint8Array {
|
||||||
|
const inputLen = writeUIntLE(new Uint8Array(8), input.length, 0, 8);
|
||||||
|
const lenPrefixedData = concatenate(inputLen, ...input);
|
||||||
|
return zerokitRLN.poseidonHash(lenPrefixedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sha256(input: Uint8Array): Uint8Array {
|
||||||
|
const inputLen = writeUIntLE(new Uint8Array(8), input.length, 0, 8);
|
||||||
|
const lenPrefixedData = concatenate(inputLen, input);
|
||||||
|
return zerokitRLN.hash(lenPrefixedData);
|
||||||
|
}
|
||||||
|
|
||||||
export class RLNInstance {
|
export class RLNInstance {
|
||||||
constructor(
|
constructor(
|
||||||
private zkRLN: number,
|
private zkRLN: number,
|
||||||
private witnessCalculator: WitnessCalculator
|
private witnessCalculator: WitnessCalculator
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
generateMembershipKey(): MembershipKey {
|
generateIdentityCredentials(): IdentityCredential {
|
||||||
const memKeys = zerokitRLN.generateMembershipKey(this.zkRLN);
|
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
||||||
return MembershipKey.fromBytes(memKeys);
|
return IdentityCredential.fromBytes(memKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSeededMembershipKey(seed: string): MembershipKey {
|
generateSeededIdentityCredential(seed: string): IdentityCredential {
|
||||||
const seedBytes = stringEncoder.encode(seed);
|
const seedBytes = stringEncoder.encode(seed);
|
||||||
const memKeys = zerokitRLN.generateSeededMembershipKey(
|
// TODO: rename this function in zerokit rln-wasm
|
||||||
|
const memKeys = zerokitRLN.generateSeededExtendedMembershipKey(
|
||||||
this.zkRLN,
|
this.zkRLN,
|
||||||
seedBytes
|
seedBytes
|
||||||
);
|
);
|
||||||
return MembershipKey.fromBytes(memKeys);
|
return IdentityCredential.fromBytes(memKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertMember(idCommitment: Uint8Array): void {
|
insertMember(idCommitment: Uint8Array): void {
|
||||||
zerokitRLN.insertMember(this.zkRLN, idCommitment);
|
zerokitRLN.insertMember(this.zkRLN, idCommitment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertMembers(index: number, ...idCommitments: Array<Uint8Array>): void {
|
||||||
|
// serializes a seq of IDCommitments to a byte seq
|
||||||
|
// the order of serialization is |id_commitment_len<8>|id_commitment<var>|
|
||||||
|
const idCommitmentLen = writeUIntLE(
|
||||||
|
new Uint8Array(8),
|
||||||
|
idCommitments.length,
|
||||||
|
0,
|
||||||
|
8
|
||||||
|
);
|
||||||
|
const idCommitmentBytes = concatenate(idCommitmentLen, ...idCommitments);
|
||||||
|
zerokitRLN.setLeavesFrom(this.zkRLN, index, idCommitmentBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteMember(index: number): void {
|
||||||
|
zerokitRLN.deleteLeaf(this.zkRLN, index);
|
||||||
|
}
|
||||||
|
|
||||||
getMerkleRoot(): Uint8Array {
|
getMerkleRoot(): Uint8Array {
|
||||||
return zerokitRLN.getRoot(this.zkRLN);
|
return zerokitRLN.getRoot(this.zkRLN);
|
||||||
}
|
}
|
||||||
@ -174,7 +233,7 @@ export class RLNInstance {
|
|||||||
msg: Uint8Array,
|
msg: Uint8Array,
|
||||||
index: number,
|
index: number,
|
||||||
epoch: Uint8Array | Date | undefined,
|
epoch: Uint8Array | Date | undefined,
|
||||||
idKey: Uint8Array
|
idSecretHash: Uint8Array
|
||||||
): Promise<IRateLimitProof> {
|
): Promise<IRateLimitProof> {
|
||||||
if (epoch == undefined) {
|
if (epoch == undefined) {
|
||||||
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
||||||
@ -183,10 +242,15 @@ export class RLNInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (epoch.length != 32) throw "invalid epoch";
|
if (epoch.length != 32) throw "invalid epoch";
|
||||||
if (idKey.length != 32) throw "invalid id key";
|
if (idSecretHash.length != 32) throw "invalid id secret hash";
|
||||||
if (index < 0) throw "index must be >= 0";
|
if (index < 0) throw "index must be >= 0";
|
||||||
|
|
||||||
const serialized_msg = this.serializeMessage(msg, index, epoch, idKey);
|
const serialized_msg = this.serializeMessage(
|
||||||
|
msg,
|
||||||
|
index,
|
||||||
|
epoch,
|
||||||
|
idSecretHash
|
||||||
|
);
|
||||||
const rlnWitness = zerokitRLN.getSerializedRLNWitness(
|
const rlnWitness = zerokitRLN.getSerializedRLNWitness(
|
||||||
this.zkRLN,
|
this.zkRLN,
|
||||||
serialized_msg
|
serialized_msg
|
||||||
@ -228,7 +292,8 @@ export class RLNInstance {
|
|||||||
|
|
||||||
verifyWithRoots(
|
verifyWithRoots(
|
||||||
proof: IRateLimitProof | Uint8Array,
|
proof: IRateLimitProof | Uint8Array,
|
||||||
msg: Uint8Array
|
msg: Uint8Array,
|
||||||
|
...roots: Array<Uint8Array>
|
||||||
): boolean {
|
): boolean {
|
||||||
let pBytes: Uint8Array;
|
let pBytes: Uint8Array;
|
||||||
if (proof instanceof Uint8Array) {
|
if (proof instanceof Uint8Array) {
|
||||||
@ -236,17 +301,15 @@ export class RLNInstance {
|
|||||||
} else {
|
} else {
|
||||||
pBytes = proofToBytes(proof);
|
pBytes = proofToBytes(proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate message length
|
// calculate message length
|
||||||
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
||||||
|
|
||||||
// obtain root
|
const rootsBytes = concatenate(...roots);
|
||||||
const root = zerokitRLN.getRoot(this.zkRLN);
|
|
||||||
|
|
||||||
return zerokitRLN.verifyWithRoots(
|
return zerokitRLN.verifyWithRoots(
|
||||||
this.zkRLN,
|
this.zkRLN,
|
||||||
concatenate(pBytes, msgLen, msg),
|
concatenate(pBytes, msgLen, msg),
|
||||||
root
|
rootsBytes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
import { RLN_ABI } from "./constants.js";
|
import { RLN_ABI } from "./constants.js";
|
||||||
import { MembershipKey, RLNInstance } from "./rln.js";
|
import { IdentityCredential, RLNInstance } from "./rln.js";
|
||||||
|
|
||||||
type Member = {
|
type Member = {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
@ -94,20 +94,19 @@ export class RLNContract {
|
|||||||
rlnInstance: RLNInstance,
|
rlnInstance: RLNInstance,
|
||||||
signature: string
|
signature: string
|
||||||
): Promise<ethers.Event | undefined> {
|
): Promise<ethers.Event | undefined> {
|
||||||
const membershipKey = await rlnInstance.generateSeededMembershipKey(
|
const identityCredential =
|
||||||
signature
|
await rlnInstance.generateSeededIdentityCredential(signature);
|
||||||
);
|
|
||||||
|
|
||||||
return this.registerWithKey(membershipKey);
|
return this.registerWithKey(identityCredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async registerWithKey(
|
public async registerWithKey(
|
||||||
membershipKey: MembershipKey
|
credential: IdentityCredential
|
||||||
): Promise<ethers.Event | undefined> {
|
): Promise<ethers.Event | undefined> {
|
||||||
const depositValue = await this.contract.MEMBERSHIP_DEPOSIT();
|
const depositValue = await this.contract.MEMBERSHIP_DEPOSIT();
|
||||||
|
|
||||||
const txRegisterResponse: ethers.ContractTransaction =
|
const txRegisterResponse: ethers.ContractTransaction =
|
||||||
await this.contract.register(membershipKey.IDCommitmentBigInt, {
|
await this.contract.register(credential.IDCommitmentBigInt, {
|
||||||
value: depositValue,
|
value: depositValue,
|
||||||
});
|
});
|
||||||
const txRegisterReceipt = await txRegisterResponse.wait();
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
||||||
|
|||||||
56
src/root_tracker.spec.ts
Normal file
56
src/root_tracker.spec.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { assert, expect } from "chai";
|
||||||
|
|
||||||
|
import { MerkleRootTracker } from "./root_tracker";
|
||||||
|
|
||||||
|
describe("js-rln", () => {
|
||||||
|
it("should track merkle roots and backfill from block number", async function () {
|
||||||
|
const acceptableRootWindow = 3;
|
||||||
|
|
||||||
|
const tracker = new MerkleRootTracker(
|
||||||
|
acceptableRootWindow,
|
||||||
|
new Uint8Array([0, 0, 0, 0])
|
||||||
|
);
|
||||||
|
expect(tracker.roots()).to.have.length(1);
|
||||||
|
expect(tracker.buffer()).to.have.length(0);
|
||||||
|
expect(tracker.roots()[0]).to.deep.equal(new Uint8Array([0, 0, 0, 0]));
|
||||||
|
|
||||||
|
for (let i = 1; i <= 30; i++) {
|
||||||
|
tracker.pushRoot(i, new Uint8Array([0, 0, 0, i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(tracker.roots()).to.have.length(acceptableRootWindow);
|
||||||
|
expect(tracker.buffer()).to.have.length(20);
|
||||||
|
assert.sameDeepMembers(tracker.roots(), [
|
||||||
|
new Uint8Array([0, 0, 0, 30]),
|
||||||
|
new Uint8Array([0, 0, 0, 29]),
|
||||||
|
new Uint8Array([0, 0, 0, 28]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Buffer should keep track of 20 blocks previous to the current valid merkle root window
|
||||||
|
expect(tracker.buffer()[0]).to.be.eql(new Uint8Array([0, 0, 0, 8]));
|
||||||
|
expect(tracker.buffer()[19]).to.be.eql(new Uint8Array([0, 0, 0, 27]));
|
||||||
|
|
||||||
|
// Remove roots 29 and 30
|
||||||
|
tracker.backFill(29);
|
||||||
|
assert.sameDeepMembers(tracker.roots(), [
|
||||||
|
new Uint8Array([0, 0, 0, 28]),
|
||||||
|
new Uint8Array([0, 0, 0, 27]),
|
||||||
|
new Uint8Array([0, 0, 0, 26]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(tracker.buffer()).to.have.length(18);
|
||||||
|
expect(tracker.buffer()[0]).to.be.eql(new Uint8Array([0, 0, 0, 8]));
|
||||||
|
expect(tracker.buffer()[17]).to.be.eql(new Uint8Array([0, 0, 0, 25]));
|
||||||
|
|
||||||
|
// Remove roots from block 15 onwards. These blocks exists within the buffer
|
||||||
|
tracker.backFill(15);
|
||||||
|
assert.sameDeepMembers(tracker.roots(), [
|
||||||
|
new Uint8Array([0, 0, 0, 14]),
|
||||||
|
new Uint8Array([0, 0, 0, 13]),
|
||||||
|
new Uint8Array([0, 0, 0, 12]),
|
||||||
|
]);
|
||||||
|
expect(tracker.buffer()).to.have.length(4);
|
||||||
|
expect(tracker.buffer()[0]).to.be.eql(new Uint8Array([0, 0, 0, 8]));
|
||||||
|
expect(tracker.buffer()[3]).to.be.eql(new Uint8Array([0, 0, 0, 11]));
|
||||||
|
});
|
||||||
|
});
|
||||||
88
src/root_tracker.ts
Normal file
88
src/root_tracker.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
class RootPerBlock {
|
||||||
|
constructor(public root: Uint8Array, public blockNumber: number) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxBufferSize = 20;
|
||||||
|
|
||||||
|
export class MerkleRootTracker {
|
||||||
|
private validMerkleRoots: Array<RootPerBlock> = new Array<RootPerBlock>();
|
||||||
|
private merkleRootBuffer: Array<RootPerBlock> = new Array<RootPerBlock>();
|
||||||
|
constructor(
|
||||||
|
private acceptableRootWindowSize: number,
|
||||||
|
initialRoot: Uint8Array
|
||||||
|
) {
|
||||||
|
this.pushRoot(0, initialRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
backFill(fromBlockNumber: number): void {
|
||||||
|
if (this.validMerkleRoots.length == 0) return;
|
||||||
|
|
||||||
|
let numBlocks = 0;
|
||||||
|
for (let i = this.validMerkleRoots.length - 1; i >= 0; i--) {
|
||||||
|
if (this.validMerkleRoots[i].blockNumber >= fromBlockNumber) {
|
||||||
|
numBlocks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numBlocks == 0) return;
|
||||||
|
|
||||||
|
const olderBlock = fromBlockNumber < this.validMerkleRoots[0].blockNumber;
|
||||||
|
|
||||||
|
// Remove last roots
|
||||||
|
let rootsToPop = numBlocks;
|
||||||
|
if (this.validMerkleRoots.length < rootsToPop) {
|
||||||
|
rootsToPop = this.validMerkleRoots.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.validMerkleRoots = this.validMerkleRoots.slice(
|
||||||
|
0,
|
||||||
|
this.validMerkleRoots.length - rootsToPop
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.merkleRootBuffer.length == 0) return;
|
||||||
|
|
||||||
|
if (olderBlock) {
|
||||||
|
const idx = this.merkleRootBuffer.findIndex(
|
||||||
|
(x) => x.blockNumber == fromBlockNumber
|
||||||
|
);
|
||||||
|
if (idx > -1) {
|
||||||
|
this.merkleRootBuffer = this.merkleRootBuffer.slice(0, idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backfill the tree's acceptable roots
|
||||||
|
let rootsToRestore =
|
||||||
|
this.acceptableRootWindowSize - this.validMerkleRoots.length;
|
||||||
|
if (this.merkleRootBuffer.length < rootsToRestore) {
|
||||||
|
rootsToRestore = this.merkleRootBuffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < rootsToRestore; i++) {
|
||||||
|
const x = this.merkleRootBuffer.pop();
|
||||||
|
if (x) this.validMerkleRoots.unshift(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushRoot(blockNumber: number, root: Uint8Array): void {
|
||||||
|
this.validMerkleRoots.push(new RootPerBlock(root, blockNumber));
|
||||||
|
|
||||||
|
// Maintain valid merkle root window
|
||||||
|
if (this.validMerkleRoots.length > this.acceptableRootWindowSize) {
|
||||||
|
const x = this.validMerkleRoots.shift();
|
||||||
|
if (x) this.merkleRootBuffer.push(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain merkle root buffer
|
||||||
|
if (this.merkleRootBuffer.length > maxBufferSize) {
|
||||||
|
this.merkleRootBuffer.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roots(): Array<Uint8Array> {
|
||||||
|
return this.validMerkleRoots.map((x) => x.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer(): Array<Uint8Array> {
|
||||||
|
return this.merkleRootBuffer.map((x) => x.root);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user