mirror of
https://github.com/logos-messaging/js-rln.git
synced 2026-01-04 06:33:09 +00:00
feat!: restructure package (#98)
* decouple utils, remove global variables * decouple hash utils * decouple proof related stuff * move to utils, move to resources * decouple zerokit * fix spelling * fix mock * remove auto prettier, typo * retur prettier * comma
This commit is contained in:
parent
e1679b6bd9
commit
65b7d8bcbc
@ -35,8 +35,8 @@
|
|||||||
"gen",
|
"gen",
|
||||||
"proto",
|
"proto",
|
||||||
"*.spec.ts",
|
"*.spec.ts",
|
||||||
"src/resources.ts",
|
"src/resources/*",
|
||||||
"src/constants.ts"
|
"src/contract/constants.ts"
|
||||||
],
|
],
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@waku/rln",
|
"name": "@waku/rln",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@waku/rln",
|
"name": "@waku/rln",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chainsafe/bls-keystore": "^3.0.0",
|
"@chainsafe/bls-keystore": "^3.0.0",
|
||||||
|
|||||||
@ -25,10 +25,9 @@ import {
|
|||||||
RLNDecoder,
|
RLNDecoder,
|
||||||
RLNEncoder,
|
RLNEncoder,
|
||||||
} from "./codec.js";
|
} from "./codec.js";
|
||||||
import { epochBytesToInt } from "./epoch.js";
|
import { createRLN } from "./create.js";
|
||||||
import { RlnMessage } from "./message.js";
|
import { RlnMessage } from "./message.js";
|
||||||
|
import { epochBytesToInt } from "./utils/index.js";
|
||||||
import * as rln from "./index.js";
|
|
||||||
|
|
||||||
const TestContentTopic = "/test/1/waku-message/utf8";
|
const TestContentTopic = "/test/1/waku-message/utf8";
|
||||||
const EMPTY_PUBSUB_TOPIC = "";
|
const EMPTY_PUBSUB_TOPIC = "";
|
||||||
@ -44,12 +43,12 @@ const EMPTY_PROTO_MESSAGE = {
|
|||||||
|
|
||||||
describe("RLN codec with version 0", () => {
|
describe("RLN codec with version 0", () => {
|
||||||
it("toWire", async function () {
|
it("toWire", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = createRLNEncoder({
|
const rlnEncoder = createRLNEncoder({
|
||||||
encoder: createEncoder({ contentTopic: TestContentTopic }),
|
encoder: createEncoder({ contentTopic: TestContentTopic }),
|
||||||
@ -73,7 +72,7 @@ describe("RLN codec with version 0", () => {
|
|||||||
))!;
|
))!;
|
||||||
|
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -85,12 +84,12 @@ describe("RLN codec with version 0", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = new RLNEncoder(
|
const rlnEncoder = new RLNEncoder(
|
||||||
createEncoder({ contentTopic: TestContentTopic }),
|
createEncoder({ contentTopic: TestContentTopic }),
|
||||||
@ -114,7 +113,7 @@ describe("RLN codec with version 0", () => {
|
|||||||
expect(msg).to.not.be.undefined;
|
expect(msg).to.not.be.undefined;
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
|
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -128,12 +127,12 @@ 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.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const symKey = generateSymmetricKey();
|
const symKey = generateSymmetricKey();
|
||||||
|
|
||||||
@ -163,7 +162,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
))!;
|
))!;
|
||||||
|
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -175,12 +174,12 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Symmetric, toProtoObj", async function () {
|
it("Symmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const symKey = generateSymmetricKey();
|
const symKey = generateSymmetricKey();
|
||||||
|
|
||||||
@ -209,7 +208,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
expect(msg).to.not.be.undefined;
|
expect(msg).to.not.be.undefined;
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
|
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -221,12 +220,12 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Asymmetric, toWire", async function () {
|
it("Asymmetric, toWire", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const privateKey = generatePrivateKey();
|
const privateKey = generatePrivateKey();
|
||||||
const publicKey = getPublicKey(privateKey);
|
const publicKey = getPublicKey(privateKey);
|
||||||
@ -257,7 +256,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
))!;
|
))!;
|
||||||
|
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -269,12 +268,12 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Asymmetric, toProtoObj", async function () {
|
it("Asymmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const privateKey = generatePrivateKey();
|
const privateKey = generatePrivateKey();
|
||||||
const publicKey = getPublicKey(privateKey);
|
const publicKey = getPublicKey(privateKey);
|
||||||
@ -304,7 +303,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
expect(msg).to.not.be.undefined;
|
expect(msg).to.not.be.undefined;
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
|
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -318,12 +317,12 @@ 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.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = new RLNEncoder(
|
const rlnEncoder = new RLNEncoder(
|
||||||
createEncoder({ contentTopic: TestContentTopic }),
|
createEncoder({ contentTopic: TestContentTopic }),
|
||||||
@ -350,7 +349,7 @@ describe("RLN Codec - epoch", () => {
|
|||||||
expect(msg).to.not.be.undefined;
|
expect(msg).to.not.be.undefined;
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
|
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch!.toString(10).length).to.eq(9);
|
expect(msg.epoch!.toString(10).length).to.eq(9);
|
||||||
@ -374,12 +373,12 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it("toWire", async function () {
|
it("toWire", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = createRLNEncoder({
|
const rlnEncoder = createRLNEncoder({
|
||||||
encoder: createEncoder({ contentTopic: TestContentTopic, metaSetter }),
|
encoder: createEncoder({ contentTopic: TestContentTopic, metaSetter }),
|
||||||
@ -410,7 +409,7 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
expect(msg!.meta).to.deep.eq(expectedMeta);
|
expect(msg!.meta).to.deep.eq(expectedMeta);
|
||||||
|
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
@ -422,12 +421,12 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
|
|
||||||
const rlnEncoder = new RLNEncoder(
|
const rlnEncoder = new RLNEncoder(
|
||||||
createEncoder({ contentTopic: TestContentTopic, metaSetter }),
|
createEncoder({ contentTopic: TestContentTopic, metaSetter }),
|
||||||
@ -458,7 +457,7 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
expect(msg).to.not.be.undefined;
|
expect(msg).to.not.be.undefined;
|
||||||
expect(msg.rateLimitProof).to.not.be.undefined;
|
expect(msg.rateLimitProof).to.not.be.undefined;
|
||||||
|
|
||||||
expect(msg.verify([rlnInstance.getMerkleRoot()])).to.be.true;
|
expect(msg.verify([rlnInstance.zerokit.getMerkleRoot()])).to.be.true;
|
||||||
expect(msg.verifyNoRoot()).to.be.true;
|
expect(msg.verifyNoRoot()).to.be.true;
|
||||||
expect(msg.epoch).to.not.be.undefined;
|
expect(msg.epoch).to.not.be.undefined;
|
||||||
expect(msg.epoch).to.be.gt(0);
|
expect(msg.epoch).to.be.gt(0);
|
||||||
|
|||||||
@ -8,8 +8,9 @@ import type {
|
|||||||
} from "@waku/interfaces";
|
} from "@waku/interfaces";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
|
import type { IdentityCredential } from "./identity.js";
|
||||||
import { RlnMessage, toRLNSignal } from "./message.js";
|
import { RlnMessage, toRLNSignal } from "./message.js";
|
||||||
import { IdentityCredential, RLNInstance } from "./rln.js";
|
import { RLNInstance } from "./rln.js";
|
||||||
|
|
||||||
const log = debug("waku:rln:encoder");
|
const log = debug("waku:rln:encoder");
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ export class RLNEncoder implements IEncoder {
|
|||||||
|
|
||||||
private async generateProof(message: IMessage): Promise<IRateLimitProof> {
|
private async generateProof(message: IMessage): Promise<IRateLimitProof> {
|
||||||
const signal = toRLNSignal(this.contentTopic, message);
|
const signal = toRLNSignal(this.contentTopic, message);
|
||||||
const proof = await this.rlnInstance.generateRLNProof(
|
const proof = await this.rlnInstance.zerokit.generateRLNProof(
|
||||||
signal,
|
signal,
|
||||||
this.index,
|
this.index,
|
||||||
message.timestamp,
|
message.timestamp,
|
||||||
|
|||||||
2
src/contract/index.ts
Normal file
2
src/contract/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { RLNContract } from "./rln_contract.js";
|
||||||
|
export * from "./constants.js";
|
||||||
@ -2,20 +2,23 @@ import chai from "chai";
|
|||||||
import spies from "chai-spies";
|
import spies from "chai-spies";
|
||||||
import * as ethers from "ethers";
|
import * as ethers from "ethers";
|
||||||
|
|
||||||
import * as rln from "./index.js";
|
import { createRLN } from "../create.js";
|
||||||
|
|
||||||
|
import { SEPOLIA_CONTRACT } from "./constants.js";
|
||||||
|
import { RLNContract } from "./rln_contract.js";
|
||||||
|
|
||||||
chai.use(spies);
|
chai.use(spies);
|
||||||
|
|
||||||
describe("RLN Contract abstraction", () => {
|
describe("RLN Contract abstraction", () => {
|
||||||
it("should be able to fetch members from events and store to rln instance", async () => {
|
it("should be able to fetch members from events and store to rln instance", async () => {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
|
|
||||||
rlnInstance.insertMember = () => undefined;
|
rlnInstance.zerokit.insertMember = () => undefined;
|
||||||
const insertMemberSpy = chai.spy.on(rlnInstance, "insertMember");
|
const insertMemberSpy = chai.spy.on(rlnInstance.zerokit, "insertMember");
|
||||||
|
|
||||||
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
const voidSigner = new ethers.VoidSigner(SEPOLIA_CONTRACT.address);
|
||||||
const rlnContract = new rln.RLNContract(rlnInstance, {
|
const rlnContract = new RLNContract(rlnInstance, {
|
||||||
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
registryAddress: SEPOLIA_CONTRACT.address,
|
||||||
signer: voidSigner,
|
signer: voidSigner,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -36,10 +39,10 @@ describe("RLN Contract abstraction", () => {
|
|||||||
const mockSignature =
|
const mockSignature =
|
||||||
"0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c";
|
"0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c";
|
||||||
|
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
const voidSigner = new ethers.VoidSigner(SEPOLIA_CONTRACT.address);
|
||||||
const rlnContract = new rln.RLNContract(rlnInstance, {
|
const rlnContract = new RLNContract(rlnInstance, {
|
||||||
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
registryAddress: SEPOLIA_CONTRACT.address,
|
||||||
signer: voidSigner,
|
signer: voidSigner,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -58,7 +61,7 @@ describe("RLN Contract abstraction", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const identity =
|
const identity =
|
||||||
rlnInstance.generateSeededIdentityCredential(mockSignature);
|
rlnInstance.zerokit.generateSeededIdentityCredential(mockSignature);
|
||||||
await rlnContract.registerWithIdentity(identity);
|
await rlnContract.registerWithIdentity(identity);
|
||||||
|
|
||||||
chai.expect(contractSpy).to.have.been.called();
|
chai.expect(contractSpy).to.have.been.called();
|
||||||
@ -1,11 +1,13 @@
|
|||||||
import { hexToBytes } from "@waku/utils/bytes";
|
import { hexToBytes } from "@waku/utils/bytes";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
import { zeroPadLE } from "./byte_utils.js";
|
import type { IdentityCredential } from "../identity.js";
|
||||||
|
import type { DecryptedCredentials } from "../keystore/index.js";
|
||||||
|
import type { RLNInstance } from "../rln.js";
|
||||||
|
import { MerkleRootTracker } from "../root_tracker.js";
|
||||||
|
import { zeroPadLE } from "../utils/index.js";
|
||||||
|
|
||||||
import { RLN_REGISTRY_ABI, RLN_STORAGE_ABI } from "./constants.js";
|
import { RLN_REGISTRY_ABI, RLN_STORAGE_ABI } from "./constants.js";
|
||||||
import type { DecryptedCredentials } from "./keystore/index.js";
|
|
||||||
import { type IdentityCredential, RLNInstance } from "./rln.js";
|
|
||||||
import { MerkleRootTracker } from "./root_tracker.js";
|
|
||||||
|
|
||||||
type Member = {
|
type Member = {
|
||||||
idCommitment: string;
|
idCommitment: string;
|
||||||
@ -59,7 +61,7 @@ export class RLNContract {
|
|||||||
rlnInstance: RLNInstance,
|
rlnInstance: RLNInstance,
|
||||||
{ registryAddress, signer }: RLNContractOptions
|
{ registryAddress, signer }: RLNContractOptions
|
||||||
) {
|
) {
|
||||||
const initialRoot = rlnInstance.getMerkleRoot();
|
const initialRoot = rlnInstance.zerokit.getMerkleRoot();
|
||||||
|
|
||||||
this.registryContract = new ethers.Contract(
|
this.registryContract = new ethers.Contract(
|
||||||
registryAddress,
|
registryAddress,
|
||||||
@ -180,14 +182,14 @@ export class RLNContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const idCommitment = zeroPadLE(hexToBytes(_idCommitment?._hex), 32);
|
const idCommitment = zeroPadLE(hexToBytes(_idCommitment?._hex), 32);
|
||||||
rlnInstance.insertMember(idCommitment);
|
rlnInstance.zerokit.insertMember(idCommitment);
|
||||||
this._members.set(index.toNumber(), {
|
this._members.set(index.toNumber(), {
|
||||||
index,
|
index,
|
||||||
idCommitment: _idCommitment?._hex,
|
idCommitment: _idCommitment?._hex,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentRoot = rlnInstance.getMerkleRoot();
|
const currentRoot = rlnInstance.zerokit.getMerkleRoot();
|
||||||
this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
|
this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -202,7 +204,7 @@ export class RLNContract {
|
|||||||
if (this._members.has(index)) {
|
if (this._members.has(index)) {
|
||||||
this._members.delete(index);
|
this._members.delete(index);
|
||||||
}
|
}
|
||||||
rlnInstance.deleteMember(index);
|
rlnInstance.zerokit.deleteMember(index);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.merkleRootTracker.backFill(blockNumber);
|
this.merkleRootTracker.backFill(blockNumber);
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { assert, expect } from "chai";
|
import { assert, expect } from "chai";
|
||||||
|
|
||||||
import * as rln from "./index.js";
|
import { createRLN } from "./create.js";
|
||||||
|
|
||||||
describe("js-rln", () => {
|
describe("js-rln", () => {
|
||||||
it("should verify a proof", async function () {
|
it("should verify a proof", async function () {
|
||||||
const rlnInstance = await rln.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
|
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.zerokit.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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
} else {
|
} else {
|
||||||
// create a new key pair
|
// create a new key pair
|
||||||
rlnInstance.insertMember(
|
rlnInstance.zerokit.insertMember(
|
||||||
rlnInstance.generateIdentityCredentials().IDCommitment
|
rlnInstance.zerokit.generateIdentityCredentials().IDCommitment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ describe("js-rln", () => {
|
|||||||
const epoch = new Date();
|
const epoch = new Date();
|
||||||
|
|
||||||
// generating proof
|
// generating proof
|
||||||
const proof = await rlnInstance.generateRLNProof(
|
const proof = await rlnInstance.zerokit.generateRLNProof(
|
||||||
uint8Msg,
|
uint8Msg,
|
||||||
index,
|
index,
|
||||||
epoch,
|
epoch,
|
||||||
@ -42,7 +42,7 @@ describe("js-rln", () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// verify the proof
|
// verify the proof
|
||||||
const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg);
|
const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg);
|
||||||
expect(verifResult).to.be.true;
|
expect(verifResult).to.be.true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
assert.fail(0, 1, "should not have failed proof verification");
|
assert.fail(0, 1, "should not have failed proof verification");
|
||||||
@ -52,16 +52,17 @@ describe("js-rln", () => {
|
|||||||
// Modifying the signal so it's invalid
|
// Modifying the signal so it's invalid
|
||||||
uint8Msg[4] = 4;
|
uint8Msg[4] = 4;
|
||||||
// verify the proof
|
// verify the proof
|
||||||
const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg);
|
const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg);
|
||||||
expect(verifResult).to.be.false;
|
expect(verifResult).to.be.false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const credential = rlnInstance.generateSeededIdentityCredential(seed);
|
const credential =
|
||||||
|
rlnInstance.zerokit.generateSeededIdentityCredential(seed);
|
||||||
|
|
||||||
//peer's index in the Merkle Tree
|
//peer's index in the Merkle Tree
|
||||||
const index = 5;
|
const index = 5;
|
||||||
@ -70,11 +71,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(credential.IDCommitment);
|
rlnInstance.zerokit.insertMember(credential.IDCommitment);
|
||||||
} else {
|
} else {
|
||||||
// create a new key pair
|
// create a new key pair
|
||||||
rlnInstance.insertMember(
|
rlnInstance.zerokit.insertMember(
|
||||||
rlnInstance.generateIdentityCredentials().IDCommitment
|
rlnInstance.zerokit.generateIdentityCredentials().IDCommitment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +89,7 @@ describe("js-rln", () => {
|
|||||||
const epoch = new Date();
|
const epoch = new Date();
|
||||||
|
|
||||||
// generating proof
|
// generating proof
|
||||||
const proof = await rlnInstance.generateRLNProof(
|
const proof = await rlnInstance.zerokit.generateRLNProof(
|
||||||
uint8Msg,
|
uint8Msg,
|
||||||
index,
|
index,
|
||||||
epoch,
|
epoch,
|
||||||
@ -97,7 +98,7 @@ describe("js-rln", () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// verify the proof
|
// verify the proof
|
||||||
const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg);
|
const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg);
|
||||||
expect(verifResult).to.be.true;
|
expect(verifResult).to.be.true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
assert.fail(0, 1, "should not have failed proof verification");
|
assert.fail(0, 1, "should not have failed proof verification");
|
||||||
@ -107,7 +108,7 @@ describe("js-rln", () => {
|
|||||||
// Modifying the signal so it's invalid
|
// Modifying the signal so it's invalid
|
||||||
uint8Msg[4] = 4;
|
uint8Msg[4] = 4;
|
||||||
// verify the proof
|
// verify the proof
|
||||||
const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg);
|
const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg);
|
||||||
expect(verifResult).to.be.false;
|
expect(verifResult).to.be.false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@ -115,10 +116,10 @@ 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.createRLN();
|
const rlnInstance = await createRLN();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
|
const memKeys1 = rlnInstance.zerokit.generateSeededIdentityCredential(seed);
|
||||||
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
|
const memKeys2 = rlnInstance.zerokit.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]);
|
||||||
27
src/identity.ts
Normal file
27
src/identity.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { buildBigIntFromUint8Array } from "./utils/index.js";
|
||||||
|
|
||||||
|
export class IdentityCredential {
|
||||||
|
constructor(
|
||||||
|
public readonly IDTrapdoor: Uint8Array,
|
||||||
|
public readonly IDNullifier: Uint8Array,
|
||||||
|
public readonly IDSecretHash: Uint8Array,
|
||||||
|
public readonly IDCommitment: Uint8Array,
|
||||||
|
public readonly IDCommitmentBigInt: bigint
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
||||||
|
const idTrapdoor = memKeys.subarray(0, 32);
|
||||||
|
const idNullifier = memKeys.subarray(32, 64);
|
||||||
|
const idSecretHash = memKeys.subarray(64, 96);
|
||||||
|
const idCommitment = memKeys.subarray(96);
|
||||||
|
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
||||||
|
|
||||||
|
return new IdentityCredential(
|
||||||
|
idTrapdoor,
|
||||||
|
idNullifier,
|
||||||
|
idSecretHash,
|
||||||
|
idCommitment,
|
||||||
|
idCommitmentBigInt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/index.ts
16
src/index.ts
@ -3,18 +3,15 @@ import {
|
|||||||
RLN_REGISTRY_ABI,
|
RLN_REGISTRY_ABI,
|
||||||
RLN_STORAGE_ABI,
|
RLN_STORAGE_ABI,
|
||||||
SEPOLIA_CONTRACT,
|
SEPOLIA_CONTRACT,
|
||||||
} from "./constants.js";
|
} from "./contract/index.js";
|
||||||
|
import { RLNContract } from "./contract/index.js";
|
||||||
import { createRLN } from "./create.js";
|
import { createRLN } from "./create.js";
|
||||||
|
import { IdentityCredential } from "./identity.js";
|
||||||
import { Keystore } from "./keystore/index.js";
|
import { Keystore } from "./keystore/index.js";
|
||||||
import { extractMetaMaskSigner } from "./metamask.js";
|
import { Proof } from "./proof.js";
|
||||||
import {
|
import { RLNInstance } from "./rln.js";
|
||||||
IdentityCredential,
|
|
||||||
Proof,
|
|
||||||
ProofMetadata,
|
|
||||||
RLNInstance,
|
|
||||||
} from "./rln.js";
|
|
||||||
import { RLNContract } from "./rln_contract.js";
|
|
||||||
import { MerkleRootTracker } from "./root_tracker.js";
|
import { MerkleRootTracker } from "./root_tracker.js";
|
||||||
|
import { extractMetaMaskSigner } from "./utils/index.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createRLN,
|
createRLN,
|
||||||
@ -22,7 +19,6 @@ export {
|
|||||||
RLNInstance,
|
RLNInstance,
|
||||||
IdentityCredential,
|
IdentityCredential,
|
||||||
Proof,
|
Proof,
|
||||||
ProofMetadata,
|
|
||||||
RLNEncoder,
|
RLNEncoder,
|
||||||
RLNDecoder,
|
RLNDecoder,
|
||||||
MerkleRootTracker,
|
MerkleRootTracker,
|
||||||
|
|||||||
@ -7,8 +7,8 @@ chai.use(chaiSubset);
|
|||||||
chai.use(deepEqualInAnyOrder);
|
chai.use(deepEqualInAnyOrder);
|
||||||
chai.use(chaiAsPromised);
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
import { buildBigIntFromUint8Array } from "../byte_utils.js";
|
import { IdentityCredential } from "../identity.js";
|
||||||
import { IdentityCredential } from "../rln.js";
|
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
||||||
|
|
||||||
import { Keystore } from "./keystore.js";
|
import { Keystore } from "./keystore.js";
|
||||||
import type { MembershipInfo } from "./types.js";
|
import type { MembershipInfo } from "./types.js";
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { v4 as uuidV4 } from "uuid";
|
import { v4 as uuidV4 } from "uuid";
|
||||||
|
|
||||||
import { buildBigIntFromUint8Array } from "../byte_utils.js";
|
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
||||||
|
|
||||||
import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
|
import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
|
||||||
import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
|
import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { IdentityCredential } from "../rln.js";
|
import type { IdentityCredential } from "../identity.js";
|
||||||
|
|
||||||
export type MembershipHash = string;
|
export type MembershipHash = string;
|
||||||
export type Sha256Hash = string;
|
export type Sha256Hash = string;
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import type {
|
|||||||
} from "@waku/interfaces";
|
} from "@waku/interfaces";
|
||||||
import * as utils from "@waku/utils/bytes";
|
import * as utils from "@waku/utils/bytes";
|
||||||
|
|
||||||
import { epochBytesToInt } from "./epoch.js";
|
|
||||||
import { RLNInstance } from "./rln.js";
|
import { RLNInstance } from "./rln.js";
|
||||||
|
import { epochBytesToInt } from "./utils/index.js";
|
||||||
|
|
||||||
export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array {
|
export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array {
|
||||||
const contentTopicBytes = utils.utf8ToBytes(contentTopic ?? "");
|
const contentTopicBytes = utils.utf8ToBytes(contentTopic ?? "");
|
||||||
@ -24,7 +24,7 @@ export class RlnMessage<T extends IDecodedMessage> implements IDecodedMessage {
|
|||||||
|
|
||||||
public verify(roots: Uint8Array[]): boolean | undefined {
|
public verify(roots: Uint8Array[]): boolean | undefined {
|
||||||
return this.rateLimitProof
|
return this.rateLimitProof
|
||||||
? this.rlnInstance.verifyWithRoots(
|
? this.rlnInstance.zerokit.verifyWithRoots(
|
||||||
this.rateLimitProof,
|
this.rateLimitProof,
|
||||||
toRLNSignal(this.msg.contentTopic, this.msg),
|
toRLNSignal(this.msg.contentTopic, this.msg),
|
||||||
...roots
|
...roots
|
||||||
@ -34,7 +34,7 @@ export class RlnMessage<T extends IDecodedMessage> implements IDecodedMessage {
|
|||||||
|
|
||||||
public verifyNoRoot(): boolean | undefined {
|
public verifyNoRoot(): boolean | undefined {
|
||||||
return this.rateLimitProof
|
return this.rateLimitProof
|
||||||
? this.rlnInstance.verifyWithNoRoot(
|
? this.rlnInstance.zerokit.verifyWithNoRoot(
|
||||||
this.rateLimitProof,
|
this.rateLimitProof,
|
||||||
toRLNSignal(this.msg.contentTopic, this.msg)
|
toRLNSignal(this.msg.contentTopic, this.msg)
|
||||||
) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
|
) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
|
||||||
|
|||||||
67
src/proof.ts
Normal file
67
src/proof.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import type { IRateLimitProof } from "@waku/interfaces";
|
||||||
|
|
||||||
|
import { concatenate, poseidonHash } from "./utils/index.js";
|
||||||
|
|
||||||
|
const proofOffset = 128;
|
||||||
|
const rootOffset = proofOffset + 32;
|
||||||
|
const epochOffset = rootOffset + 32;
|
||||||
|
const shareXOffset = epochOffset + 32;
|
||||||
|
const shareYOffset = shareXOffset + 32;
|
||||||
|
const nullifierOffset = shareYOffset + 32;
|
||||||
|
const rlnIdentifierOffset = nullifierOffset + 32;
|
||||||
|
|
||||||
|
class ProofMetadata {
|
||||||
|
constructor(
|
||||||
|
public readonly nullifier: Uint8Array,
|
||||||
|
public readonly shareX: Uint8Array,
|
||||||
|
public readonly shareY: Uint8Array,
|
||||||
|
public readonly externalNullifier: Uint8Array
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Proof implements IRateLimitProof {
|
||||||
|
readonly proof: Uint8Array;
|
||||||
|
readonly merkleRoot: Uint8Array;
|
||||||
|
readonly epoch: Uint8Array;
|
||||||
|
readonly shareX: Uint8Array;
|
||||||
|
readonly shareY: Uint8Array;
|
||||||
|
readonly nullifier: Uint8Array;
|
||||||
|
readonly rlnIdentifier: Uint8Array;
|
||||||
|
|
||||||
|
constructor(proofBytes: Uint8Array) {
|
||||||
|
if (proofBytes.length < rlnIdentifierOffset) throw "invalid proof";
|
||||||
|
// parse the proof as proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32>
|
||||||
|
this.proof = proofBytes.subarray(0, proofOffset);
|
||||||
|
this.merkleRoot = proofBytes.subarray(proofOffset, rootOffset);
|
||||||
|
this.epoch = proofBytes.subarray(rootOffset, epochOffset);
|
||||||
|
this.shareX = proofBytes.subarray(epochOffset, shareXOffset);
|
||||||
|
this.shareY = proofBytes.subarray(shareXOffset, shareYOffset);
|
||||||
|
this.nullifier = proofBytes.subarray(shareYOffset, nullifierOffset);
|
||||||
|
this.rlnIdentifier = proofBytes.subarray(
|
||||||
|
nullifierOffset,
|
||||||
|
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 {
|
||||||
|
return concatenate(
|
||||||
|
p.proof,
|
||||||
|
p.merkleRoot,
|
||||||
|
p.epoch,
|
||||||
|
p.shareX,
|
||||||
|
p.shareY,
|
||||||
|
p.nullifier,
|
||||||
|
p.rlnIdentifier
|
||||||
|
);
|
||||||
|
}
|
||||||
323
src/rln.ts
323
src/rln.ts
@ -1,5 +1,4 @@
|
|||||||
import { createDecoder, createEncoder } from "@waku/core";
|
import { createDecoder, createEncoder } from "@waku/core";
|
||||||
import type { IRateLimitProof } from "@waku/interfaces";
|
|
||||||
import type {
|
import type {
|
||||||
ContentTopic,
|
ContentTopic,
|
||||||
IDecodedMessage,
|
IDecodedMessage,
|
||||||
@ -9,45 +8,25 @@ import init from "@waku/zerokit-rln-wasm";
|
|||||||
import * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
import * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
import { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js";
|
import {
|
||||||
import type { RLNDecoder, RLNEncoder } from "./codec.js";
|
createRLNDecoder,
|
||||||
import { createRLNDecoder, createRLNEncoder } from "./codec.js";
|
createRLNEncoder,
|
||||||
import { SEPOLIA_CONTRACT } from "./constants.js";
|
type RLNDecoder,
|
||||||
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
type RLNEncoder,
|
||||||
|
} from "./codec.js";
|
||||||
|
import { RLNContract, SEPOLIA_CONTRACT } from "./contract/index.js";
|
||||||
|
import { IdentityCredential } from "./identity.js";
|
||||||
import { Keystore } from "./keystore/index.js";
|
import { Keystore } from "./keystore/index.js";
|
||||||
import type {
|
import type {
|
||||||
DecryptedCredentials,
|
DecryptedCredentials,
|
||||||
EncryptedCredentials,
|
EncryptedCredentials,
|
||||||
} from "./keystore/index.js";
|
} from "./keystore/index.js";
|
||||||
import { KeystoreEntity, Password } from "./keystore/types.js";
|
import { KeystoreEntity, Password } from "./keystore/types.js";
|
||||||
import { extractMetaMaskSigner } from "./metamask.js";
|
|
||||||
import verificationKey from "./resources/verification_key.js";
|
import verificationKey from "./resources/verification_key.js";
|
||||||
import { RLNContract } from "./rln_contract.js";
|
import * as wc from "./resources/witness_calculator.js";
|
||||||
import * as wc from "./witness_calculator.js";
|
import { WitnessCalculator } from "./resources/witness_calculator.js";
|
||||||
import { WitnessCalculator } from "./witness_calculator.js";
|
import { extractMetaMaskSigner } from "./utils/index.js";
|
||||||
|
import { Zerokit } from "./zerokit.js";
|
||||||
/**
|
|
||||||
* Concatenate Uint8Arrays
|
|
||||||
* @param input
|
|
||||||
* @returns concatenation of all Uint8Array received as input
|
|
||||||
*/
|
|
||||||
function concatenate(...input: Uint8Array[]): Uint8Array {
|
|
||||||
let totalLength = 0;
|
|
||||||
for (const arr of input) {
|
|
||||||
totalLength += arr.length;
|
|
||||||
}
|
|
||||||
const result = new Uint8Array(totalLength);
|
|
||||||
let offset = 0;
|
|
||||||
for (const arr of input) {
|
|
||||||
result.set(arr, offset);
|
|
||||||
offset += arr.length;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stringEncoder = new TextEncoder();
|
|
||||||
|
|
||||||
const DEPTH = 20;
|
|
||||||
|
|
||||||
async function loadWitnessCalculator(): Promise<WitnessCalculator> {
|
async function loadWitnessCalculator(): Promise<WitnessCalculator> {
|
||||||
const url = new URL("./resources/rln.wasm", import.meta.url);
|
const url = new URL("./resources/rln.wasm", import.meta.url);
|
||||||
@ -68,112 +47,18 @@ async function loadZkey(): Promise<Uint8Array> {
|
|||||||
export async function create(): Promise<RLNInstance> {
|
export async function create(): Promise<RLNInstance> {
|
||||||
await (init as any)?.();
|
await (init as any)?.();
|
||||||
zerokitRLN.init_panic_hook();
|
zerokitRLN.init_panic_hook();
|
||||||
|
|
||||||
const witnessCalculator = await loadWitnessCalculator();
|
const witnessCalculator = await loadWitnessCalculator();
|
||||||
const zkey = await loadZkey();
|
const zkey = await loadZkey();
|
||||||
|
|
||||||
|
const stringEncoder = new TextEncoder();
|
||||||
const vkey = stringEncoder.encode(JSON.stringify(verificationKey));
|
const vkey = stringEncoder.encode(JSON.stringify(verificationKey));
|
||||||
|
|
||||||
|
const DEPTH = 20;
|
||||||
const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey);
|
const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey);
|
||||||
return new RLNInstance(zkRLN, witnessCalculator);
|
const zerokit = new Zerokit(zkRLN, witnessCalculator);
|
||||||
}
|
|
||||||
|
|
||||||
export class IdentityCredential {
|
return new RLNInstance(zerokit);
|
||||||
constructor(
|
|
||||||
public readonly IDTrapdoor: Uint8Array,
|
|
||||||
public readonly IDNullifier: Uint8Array,
|
|
||||||
public readonly IDSecretHash: Uint8Array,
|
|
||||||
public readonly IDCommitment: Uint8Array,
|
|
||||||
public readonly IDCommitmentBigInt: bigint
|
|
||||||
) {}
|
|
||||||
|
|
||||||
static fromBytes(memKeys: Uint8Array): IdentityCredential {
|
|
||||||
const idTrapdoor = memKeys.subarray(0, 32);
|
|
||||||
const idNullifier = memKeys.subarray(32, 64);
|
|
||||||
const idSecretHash = memKeys.subarray(64, 96);
|
|
||||||
const idCommitment = memKeys.subarray(96);
|
|
||||||
const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
|
|
||||||
|
|
||||||
return new IdentityCredential(
|
|
||||||
idTrapdoor,
|
|
||||||
idNullifier,
|
|
||||||
idSecretHash,
|
|
||||||
idCommitment,
|
|
||||||
idCommitmentBigInt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const proofOffset = 128;
|
|
||||||
const rootOffset = proofOffset + 32;
|
|
||||||
const epochOffset = rootOffset + 32;
|
|
||||||
const shareXOffset = epochOffset + 32;
|
|
||||||
const shareYOffset = shareXOffset + 32;
|
|
||||||
const nullifierOffset = shareYOffset + 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 {
|
|
||||||
readonly proof: Uint8Array;
|
|
||||||
readonly merkleRoot: Uint8Array;
|
|
||||||
readonly epoch: Uint8Array;
|
|
||||||
readonly shareX: Uint8Array;
|
|
||||||
readonly shareY: Uint8Array;
|
|
||||||
readonly nullifier: Uint8Array;
|
|
||||||
readonly rlnIdentifier: Uint8Array;
|
|
||||||
|
|
||||||
constructor(proofBytes: Uint8Array) {
|
|
||||||
if (proofBytes.length < rlnIdentifierOffset) throw "invalid proof";
|
|
||||||
// parse the proof as proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32>
|
|
||||||
this.proof = proofBytes.subarray(0, proofOffset);
|
|
||||||
this.merkleRoot = proofBytes.subarray(proofOffset, rootOffset);
|
|
||||||
this.epoch = proofBytes.subarray(rootOffset, epochOffset);
|
|
||||||
this.shareX = proofBytes.subarray(epochOffset, shareXOffset);
|
|
||||||
this.shareY = proofBytes.subarray(shareXOffset, shareYOffset);
|
|
||||||
this.nullifier = proofBytes.subarray(shareYOffset, nullifierOffset);
|
|
||||||
this.rlnIdentifier = proofBytes.subarray(
|
|
||||||
nullifierOffset,
|
|
||||||
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 {
|
|
||||||
return concatenate(
|
|
||||||
p.proof,
|
|
||||||
p.merkleRoot,
|
|
||||||
p.epoch,
|
|
||||||
p.shareX,
|
|
||||||
p.shareY,
|
|
||||||
p.nullifier,
|
|
||||||
p.rlnIdentifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StartRLNOptions = {
|
type StartRLNOptions = {
|
||||||
@ -210,10 +95,7 @@ export class RLNInstance {
|
|||||||
private keystore = Keystore.create();
|
private keystore = Keystore.create();
|
||||||
private _credentials: undefined | DecryptedCredentials;
|
private _credentials: undefined | DecryptedCredentials;
|
||||||
|
|
||||||
constructor(
|
constructor(public zerokit: Zerokit) {}
|
||||||
private zkRLN: number,
|
|
||||||
private witnessCalculator: WitnessCalculator
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public get contract(): undefined | RLNContract {
|
public get contract(): undefined | RLNContract {
|
||||||
return this._contract;
|
return this._contract;
|
||||||
@ -321,7 +203,9 @@ export class RLNInstance {
|
|||||||
let identity = "identity" in options && options.identity;
|
let identity = "identity" in options && options.identity;
|
||||||
|
|
||||||
if ("signature" in options) {
|
if ("signature" in options) {
|
||||||
identity = await this.generateSeededIdentityCredential(options.signature);
|
identity = await this.zerokit.generateSeededIdentityCredential(
|
||||||
|
options.signature
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!identity) {
|
if (!identity) {
|
||||||
@ -398,165 +282,4 @@ export class RLNInstance {
|
|||||||
decoder: createDecoder(contentTopic),
|
decoder: createDecoder(contentTopic),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateIdentityCredentials(): IdentityCredential {
|
|
||||||
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
|
||||||
return IdentityCredential.fromBytes(memKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSeededIdentityCredential(seed: string): IdentityCredential {
|
|
||||||
const seedBytes = stringEncoder.encode(seed);
|
|
||||||
// TODO: rename this function in zerokit rln-wasm
|
|
||||||
const memKeys = zerokitRLN.generateSeededExtendedMembershipKey(
|
|
||||||
this.zkRLN,
|
|
||||||
seedBytes
|
|
||||||
);
|
|
||||||
return IdentityCredential.fromBytes(memKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertMember(idCommitment: Uint8Array): void {
|
|
||||||
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 {
|
|
||||||
return zerokitRLN.getRoot(this.zkRLN);
|
|
||||||
}
|
|
||||||
|
|
||||||
serializeMessage(
|
|
||||||
uint8Msg: Uint8Array,
|
|
||||||
memIndex: number,
|
|
||||||
epoch: Uint8Array,
|
|
||||||
idKey: Uint8Array
|
|
||||||
): Uint8Array {
|
|
||||||
// calculate message length
|
|
||||||
const msgLen = writeUIntLE(new Uint8Array(8), uint8Msg.length, 0, 8);
|
|
||||||
|
|
||||||
// Converting index to LE bytes
|
|
||||||
const memIndexBytes = writeUIntLE(new Uint8Array(8), memIndex, 0, 8);
|
|
||||||
|
|
||||||
// [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
|
||||||
return concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateRLNProof(
|
|
||||||
msg: Uint8Array,
|
|
||||||
index: number,
|
|
||||||
epoch: Uint8Array | Date | undefined,
|
|
||||||
idSecretHash: Uint8Array
|
|
||||||
): Promise<IRateLimitProof> {
|
|
||||||
if (epoch == undefined) {
|
|
||||||
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
|
||||||
} else if (epoch instanceof Date) {
|
|
||||||
epoch = epochIntToBytes(dateToEpoch(epoch));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (epoch.length != 32) throw "invalid epoch";
|
|
||||||
if (idSecretHash.length != 32) throw "invalid id secret hash";
|
|
||||||
if (index < 0) throw "index must be >= 0";
|
|
||||||
|
|
||||||
const serialized_msg = this.serializeMessage(
|
|
||||||
msg,
|
|
||||||
index,
|
|
||||||
epoch,
|
|
||||||
idSecretHash
|
|
||||||
);
|
|
||||||
const rlnWitness = zerokitRLN.getSerializedRLNWitness(
|
|
||||||
this.zkRLN,
|
|
||||||
serialized_msg
|
|
||||||
);
|
|
||||||
const inputs = zerokitRLN.RLNWitnessToJson(this.zkRLN, rlnWitness);
|
|
||||||
const calculatedWitness = await this.witnessCalculator.calculateWitness(
|
|
||||||
inputs,
|
|
||||||
false
|
|
||||||
); // no sanity check being used in zerokit
|
|
||||||
|
|
||||||
const proofBytes = zerokitRLN.generate_rln_proof_with_witness(
|
|
||||||
this.zkRLN,
|
|
||||||
calculatedWitness,
|
|
||||||
rlnWitness
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Proof(proofBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyRLNProof(
|
|
||||||
proof: IRateLimitProof | Uint8Array,
|
|
||||||
msg: Uint8Array
|
|
||||||
): boolean {
|
|
||||||
let pBytes: Uint8Array;
|
|
||||||
if (proof instanceof Uint8Array) {
|
|
||||||
pBytes = proof;
|
|
||||||
} else {
|
|
||||||
pBytes = proofToBytes(proof);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate message length
|
|
||||||
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
|
||||||
|
|
||||||
return zerokitRLN.verifyRLNProof(
|
|
||||||
this.zkRLN,
|
|
||||||
concatenate(pBytes, msgLen, msg)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyWithRoots(
|
|
||||||
proof: IRateLimitProof | Uint8Array,
|
|
||||||
msg: Uint8Array,
|
|
||||||
...roots: Array<Uint8Array>
|
|
||||||
): boolean {
|
|
||||||
let pBytes: Uint8Array;
|
|
||||||
if (proof instanceof Uint8Array) {
|
|
||||||
pBytes = proof;
|
|
||||||
} else {
|
|
||||||
pBytes = proofToBytes(proof);
|
|
||||||
}
|
|
||||||
// calculate message length
|
|
||||||
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
|
||||||
|
|
||||||
const rootsBytes = concatenate(...roots);
|
|
||||||
|
|
||||||
return zerokitRLN.verifyWithRoots(
|
|
||||||
this.zkRLN,
|
|
||||||
concatenate(pBytes, msgLen, msg),
|
|
||||||
rootsBytes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyWithNoRoot(
|
|
||||||
proof: IRateLimitProof | Uint8Array,
|
|
||||||
msg: Uint8Array
|
|
||||||
): boolean {
|
|
||||||
let pBytes: Uint8Array;
|
|
||||||
if (proof instanceof Uint8Array) {
|
|
||||||
pBytes = proof;
|
|
||||||
} else {
|
|
||||||
pBytes = proofToBytes(proof);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate message length
|
|
||||||
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
|
||||||
|
|
||||||
return zerokitRLN.verifyWithRoots(
|
|
||||||
this.zkRLN,
|
|
||||||
concatenate(pBytes, msgLen, msg),
|
|
||||||
new Uint8Array()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,23 @@
|
|||||||
// Adapted from https://github.com/feross/buffer
|
/**
|
||||||
|
* Concatenate Uint8Arrays
|
||||||
|
* @param input
|
||||||
|
* @returns concatenation of all Uint8Array received as input
|
||||||
|
*/
|
||||||
|
export function concatenate(...input: Uint8Array[]): Uint8Array {
|
||||||
|
let totalLength = 0;
|
||||||
|
for (const arr of input) {
|
||||||
|
totalLength += arr.length;
|
||||||
|
}
|
||||||
|
const result = new Uint8Array(totalLength);
|
||||||
|
let offset = 0;
|
||||||
|
for (const arr of input) {
|
||||||
|
result.set(arr, offset);
|
||||||
|
offset += arr.length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from https://github.com/feross/buffer
|
||||||
function checkInt(
|
function checkInt(
|
||||||
buf: Uint8Array,
|
buf: Uint8Array,
|
||||||
value: number,
|
value: number,
|
||||||
15
src/utils/hash.ts
Normal file
15
src/utils/hash.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
||||||
|
|
||||||
|
import { concatenate, writeUIntLE } from "./bytes.js";
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
9
src/utils/index.ts
Normal file
9
src/utils/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export { extractMetaMaskSigner } from "./metamask.js";
|
||||||
|
export {
|
||||||
|
concatenate,
|
||||||
|
writeUIntLE,
|
||||||
|
buildBigIntFromUint8Array,
|
||||||
|
zeroPadLE,
|
||||||
|
} from "./bytes.js";
|
||||||
|
export { sha256, poseidonHash } from "./hash.js";
|
||||||
|
export { dateToEpoch, epochIntToBytes, epochBytesToInt } from "./epoch.js";
|
||||||
181
src/zerokit.ts
Normal file
181
src/zerokit.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import type { IRateLimitProof } from "@waku/interfaces";
|
||||||
|
import * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
||||||
|
|
||||||
|
import { IdentityCredential } from "./identity.js";
|
||||||
|
import { Proof, proofToBytes } from "./proof.js";
|
||||||
|
import { WitnessCalculator } from "./resources/witness_calculator.js";
|
||||||
|
import {
|
||||||
|
concatenate,
|
||||||
|
dateToEpoch,
|
||||||
|
epochIntToBytes,
|
||||||
|
writeUIntLE,
|
||||||
|
} from "./utils/index.js";
|
||||||
|
|
||||||
|
export class Zerokit {
|
||||||
|
constructor(
|
||||||
|
private zkRLN: number,
|
||||||
|
private witnessCalculator: WitnessCalculator
|
||||||
|
) {}
|
||||||
|
|
||||||
|
generateIdentityCredentials(): IdentityCredential {
|
||||||
|
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
||||||
|
return IdentityCredential.fromBytes(memKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSeededIdentityCredential(seed: string): IdentityCredential {
|
||||||
|
const stringEncoder = new TextEncoder();
|
||||||
|
const seedBytes = stringEncoder.encode(seed);
|
||||||
|
// TODO: rename this function in zerokit rln-wasm
|
||||||
|
const memKeys = zerokitRLN.generateSeededExtendedMembershipKey(
|
||||||
|
this.zkRLN,
|
||||||
|
seedBytes
|
||||||
|
);
|
||||||
|
return IdentityCredential.fromBytes(memKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertMember(idCommitment: Uint8Array): void {
|
||||||
|
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 {
|
||||||
|
return zerokitRLN.getRoot(this.zkRLN);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeMessage(
|
||||||
|
uint8Msg: Uint8Array,
|
||||||
|
memIndex: number,
|
||||||
|
epoch: Uint8Array,
|
||||||
|
idKey: Uint8Array
|
||||||
|
): Uint8Array {
|
||||||
|
// calculate message length
|
||||||
|
const msgLen = writeUIntLE(new Uint8Array(8), uint8Msg.length, 0, 8);
|
||||||
|
|
||||||
|
// Converting index to LE bytes
|
||||||
|
const memIndexBytes = writeUIntLE(new Uint8Array(8), memIndex, 0, 8);
|
||||||
|
|
||||||
|
// [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||||
|
return concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateRLNProof(
|
||||||
|
msg: Uint8Array,
|
||||||
|
index: number,
|
||||||
|
epoch: Uint8Array | Date | undefined,
|
||||||
|
idSecretHash: Uint8Array
|
||||||
|
): Promise<IRateLimitProof> {
|
||||||
|
if (epoch == undefined) {
|
||||||
|
epoch = epochIntToBytes(dateToEpoch(new Date()));
|
||||||
|
} else if (epoch instanceof Date) {
|
||||||
|
epoch = epochIntToBytes(dateToEpoch(epoch));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epoch.length != 32) throw "invalid epoch";
|
||||||
|
if (idSecretHash.length != 32) throw "invalid id secret hash";
|
||||||
|
if (index < 0) throw "index must be >= 0";
|
||||||
|
|
||||||
|
const serialized_msg = this.serializeMessage(
|
||||||
|
msg,
|
||||||
|
index,
|
||||||
|
epoch,
|
||||||
|
idSecretHash
|
||||||
|
);
|
||||||
|
const rlnWitness = zerokitRLN.getSerializedRLNWitness(
|
||||||
|
this.zkRLN,
|
||||||
|
serialized_msg
|
||||||
|
);
|
||||||
|
const inputs = zerokitRLN.RLNWitnessToJson(this.zkRLN, rlnWitness);
|
||||||
|
const calculatedWitness = await this.witnessCalculator.calculateWitness(
|
||||||
|
inputs,
|
||||||
|
false
|
||||||
|
); // no sanity check being used in zerokit
|
||||||
|
|
||||||
|
const proofBytes = zerokitRLN.generate_rln_proof_with_witness(
|
||||||
|
this.zkRLN,
|
||||||
|
calculatedWitness,
|
||||||
|
rlnWitness
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Proof(proofBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyRLNProof(
|
||||||
|
proof: IRateLimitProof | Uint8Array,
|
||||||
|
msg: Uint8Array
|
||||||
|
): boolean {
|
||||||
|
let pBytes: Uint8Array;
|
||||||
|
if (proof instanceof Uint8Array) {
|
||||||
|
pBytes = proof;
|
||||||
|
} else {
|
||||||
|
pBytes = proofToBytes(proof);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate message length
|
||||||
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
||||||
|
|
||||||
|
return zerokitRLN.verifyRLNProof(
|
||||||
|
this.zkRLN,
|
||||||
|
concatenate(pBytes, msgLen, msg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyWithRoots(
|
||||||
|
proof: IRateLimitProof | Uint8Array,
|
||||||
|
msg: Uint8Array,
|
||||||
|
...roots: Array<Uint8Array>
|
||||||
|
): boolean {
|
||||||
|
let pBytes: Uint8Array;
|
||||||
|
if (proof instanceof Uint8Array) {
|
||||||
|
pBytes = proof;
|
||||||
|
} else {
|
||||||
|
pBytes = proofToBytes(proof);
|
||||||
|
}
|
||||||
|
// calculate message length
|
||||||
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
||||||
|
|
||||||
|
const rootsBytes = concatenate(...roots);
|
||||||
|
|
||||||
|
return zerokitRLN.verifyWithRoots(
|
||||||
|
this.zkRLN,
|
||||||
|
concatenate(pBytes, msgLen, msg),
|
||||||
|
rootsBytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyWithNoRoot(
|
||||||
|
proof: IRateLimitProof | Uint8Array,
|
||||||
|
msg: Uint8Array
|
||||||
|
): boolean {
|
||||||
|
let pBytes: Uint8Array;
|
||||||
|
if (proof instanceof Uint8Array) {
|
||||||
|
pBytes = proof;
|
||||||
|
} else {
|
||||||
|
pBytes = proofToBytes(proof);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate message length
|
||||||
|
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
|
||||||
|
|
||||||
|
return zerokitRLN.verifyWithRoots(
|
||||||
|
this.zkRLN,
|
||||||
|
concatenate(pBytes, msgLen, msg),
|
||||||
|
new Uint8Array()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user