diff --git a/.cspell.json b/.cspell.json index b9f02b3..761324c 100644 --- a/.cspell.json +++ b/.cspell.json @@ -35,8 +35,8 @@ "gen", "proto", "*.spec.ts", - "src/resources.ts", - "src/constants.ts" + "src/resources/*", + "src/contract/constants.ts" ], "patterns": [ { diff --git a/package-lock.json b/package-lock.json index 0dbabc3..6beb0a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@waku/rln", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@waku/rln", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT OR Apache-2.0", "dependencies": { "@chainsafe/bls-keystore": "^3.0.0", diff --git a/src/codec.spec.ts b/src/codec.spec.ts index a707b4b..1c46f88 100644 --- a/src/codec.spec.ts +++ b/src/codec.spec.ts @@ -25,10 +25,9 @@ import { RLNDecoder, RLNEncoder, } from "./codec.js"; -import { epochBytesToInt } from "./epoch.js"; +import { createRLN } from "./create.js"; import { RlnMessage } from "./message.js"; - -import * as rln from "./index.js"; +import { epochBytesToInt } from "./utils/index.js"; const TestContentTopic = "/test/1/waku-message/utf8"; const EMPTY_PUBSUB_TOPIC = ""; @@ -44,12 +43,12 @@ const EMPTY_PROTO_MESSAGE = { describe("RLN codec with version 0", () => { it("toWire", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const rlnEncoder = createRLNEncoder({ encoder: createEncoder({ contentTopic: TestContentTopic }), @@ -73,7 +72,7 @@ describe("RLN codec with version 0", () => { ))!; 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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -85,12 +84,12 @@ describe("RLN codec with version 0", () => { }); it("toProtoObj", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const rlnEncoder = new RLNEncoder( createEncoder({ contentTopic: TestContentTopic }), @@ -114,7 +113,7 @@ describe("RLN codec with version 0", () => { expect(msg).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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -128,12 +127,12 @@ describe("RLN codec with version 0", () => { describe("RLN codec with version 1", () => { it("Symmetric, toWire", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const symKey = generateSymmetricKey(); @@ -163,7 +162,7 @@ describe("RLN codec with version 1", () => { ))!; 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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -175,12 +174,12 @@ describe("RLN codec with version 1", () => { }); it("Symmetric, toProtoObj", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const symKey = generateSymmetricKey(); @@ -209,7 +208,7 @@ describe("RLN codec with version 1", () => { expect(msg).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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -221,12 +220,12 @@ describe("RLN codec with version 1", () => { }); it("Asymmetric, toWire", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const privateKey = generatePrivateKey(); const publicKey = getPublicKey(privateKey); @@ -257,7 +256,7 @@ describe("RLN codec with version 1", () => { ))!; 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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -269,12 +268,12 @@ describe("RLN codec with version 1", () => { }); it("Asymmetric, toProtoObj", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const privateKey = generatePrivateKey(); const publicKey = getPublicKey(privateKey); @@ -304,7 +303,7 @@ describe("RLN codec with version 1", () => { expect(msg).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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -318,12 +317,12 @@ describe("RLN codec with version 1", () => { describe("RLN Codec - epoch", () => { it("toProtoObj", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const rlnEncoder = new RLNEncoder( createEncoder({ contentTopic: TestContentTopic }), @@ -350,7 +349,7 @@ describe("RLN Codec - epoch", () => { expect(msg).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.epoch).to.not.be.undefined; 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 () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const rlnEncoder = createRLNEncoder({ 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.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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); @@ -422,12 +421,12 @@ describe("RLN codec with version 0 and meta setter", () => { }); it("toProtoObj", async function () { - const rlnInstance = await rln.createRLN(); - const credential = rlnInstance.generateIdentityCredentials(); + const rlnInstance = await createRLN(); + const credential = rlnInstance.zerokit.generateIdentityCredentials(); const index = 0; const payload = new Uint8Array([1, 2, 3, 4, 5]); - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); const rlnEncoder = new RLNEncoder( 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.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.epoch).to.not.be.undefined; expect(msg.epoch).to.be.gt(0); diff --git a/src/codec.ts b/src/codec.ts index 1a093c7..8a652b8 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -8,8 +8,9 @@ import type { } from "@waku/interfaces"; import debug from "debug"; +import type { IdentityCredential } from "./identity.js"; import { RlnMessage, toRLNSignal } from "./message.js"; -import { IdentityCredential, RLNInstance } from "./rln.js"; +import { RLNInstance } from "./rln.js"; const log = debug("waku:rln:encoder"); @@ -44,7 +45,7 @@ export class RLNEncoder implements IEncoder { private async generateProof(message: IMessage): Promise { const signal = toRLNSignal(this.contentTopic, message); - const proof = await this.rlnInstance.generateRLNProof( + const proof = await this.rlnInstance.zerokit.generateRLNProof( signal, this.index, message.timestamp, diff --git a/src/constants.ts b/src/contract/constants.ts similarity index 100% rename from src/constants.ts rename to src/contract/constants.ts diff --git a/src/contract/index.ts b/src/contract/index.ts new file mode 100644 index 0000000..caa0fec --- /dev/null +++ b/src/contract/index.ts @@ -0,0 +1,2 @@ +export { RLNContract } from "./rln_contract.js"; +export * from "./constants.js"; diff --git a/src/rln_contract.spec.ts b/src/contract/rln_contract.spec.ts similarity index 68% rename from src/rln_contract.spec.ts rename to src/contract/rln_contract.spec.ts index 5af6cfa..51b6a78 100644 --- a/src/rln_contract.spec.ts +++ b/src/contract/rln_contract.spec.ts @@ -2,20 +2,23 @@ import chai from "chai"; import spies from "chai-spies"; 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); describe("RLN Contract abstraction", () => { 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; - const insertMemberSpy = chai.spy.on(rlnInstance, "insertMember"); + rlnInstance.zerokit.insertMember = () => undefined; + const insertMemberSpy = chai.spy.on(rlnInstance.zerokit, "insertMember"); - const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address); - const rlnContract = new rln.RLNContract(rlnInstance, { - registryAddress: rln.SEPOLIA_CONTRACT.address, + const voidSigner = new ethers.VoidSigner(SEPOLIA_CONTRACT.address); + const rlnContract = new RLNContract(rlnInstance, { + registryAddress: SEPOLIA_CONTRACT.address, signer: voidSigner, }); @@ -36,10 +39,10 @@ describe("RLN Contract abstraction", () => { const mockSignature = "0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c"; - const rlnInstance = await rln.createRLN(); - const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address); - const rlnContract = new rln.RLNContract(rlnInstance, { - registryAddress: rln.SEPOLIA_CONTRACT.address, + const rlnInstance = await createRLN(); + const voidSigner = new ethers.VoidSigner(SEPOLIA_CONTRACT.address); + const rlnContract = new RLNContract(rlnInstance, { + registryAddress: SEPOLIA_CONTRACT.address, signer: voidSigner, }); @@ -58,7 +61,7 @@ describe("RLN Contract abstraction", () => { ); const identity = - rlnInstance.generateSeededIdentityCredential(mockSignature); + rlnInstance.zerokit.generateSeededIdentityCredential(mockSignature); await rlnContract.registerWithIdentity(identity); chai.expect(contractSpy).to.have.been.called(); diff --git a/src/rln_contract.ts b/src/contract/rln_contract.ts similarity index 94% rename from src/rln_contract.ts rename to src/contract/rln_contract.ts index b01ac33..bb244ac 100644 --- a/src/rln_contract.ts +++ b/src/contract/rln_contract.ts @@ -1,11 +1,13 @@ import { hexToBytes } from "@waku/utils/bytes"; 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 type { DecryptedCredentials } from "./keystore/index.js"; -import { type IdentityCredential, RLNInstance } from "./rln.js"; -import { MerkleRootTracker } from "./root_tracker.js"; type Member = { idCommitment: string; @@ -59,7 +61,7 @@ export class RLNContract { rlnInstance: RLNInstance, { registryAddress, signer }: RLNContractOptions ) { - const initialRoot = rlnInstance.getMerkleRoot(); + const initialRoot = rlnInstance.zerokit.getMerkleRoot(); this.registryContract = new ethers.Contract( registryAddress, @@ -180,14 +182,14 @@ export class RLNContract { } const idCommitment = zeroPadLE(hexToBytes(_idCommitment?._hex), 32); - rlnInstance.insertMember(idCommitment); + rlnInstance.zerokit.insertMember(idCommitment); this._members.set(index.toNumber(), { index, idCommitment: _idCommitment?._hex, }); }); - const currentRoot = rlnInstance.getMerkleRoot(); + const currentRoot = rlnInstance.zerokit.getMerkleRoot(); this.merkleRootTracker.pushRoot(blockNumber, currentRoot); }); } @@ -202,7 +204,7 @@ export class RLNContract { if (this._members.has(index)) { this._members.delete(index); } - rlnInstance.deleteMember(index); + rlnInstance.zerokit.deleteMember(index); }); this.merkleRootTracker.backFill(blockNumber); diff --git a/src/index.spec.ts b/src/create.spec.ts similarity index 68% rename from src/index.spec.ts rename to src/create.spec.ts index 25a78cd..7dc2e4f 100644 --- a/src/index.spec.ts +++ b/src/create.spec.ts @@ -1,12 +1,12 @@ import { assert, expect } from "chai"; -import * as rln from "./index.js"; +import { createRLN } from "./create.js"; describe("js-rln", () => { 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 const index = 5; @@ -15,11 +15,11 @@ describe("js-rln", () => { for (let i = 0; i < 10; i++) { if (i == index) { // insert the current peer's pk - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); } else { // create a new key pair - rlnInstance.insertMember( - rlnInstance.generateIdentityCredentials().IDCommitment + rlnInstance.zerokit.insertMember( + rlnInstance.zerokit.generateIdentityCredentials().IDCommitment ); } } @@ -33,7 +33,7 @@ describe("js-rln", () => { const epoch = new Date(); // generating proof - const proof = await rlnInstance.generateRLNProof( + const proof = await rlnInstance.zerokit.generateRLNProof( uint8Msg, index, epoch, @@ -42,7 +42,7 @@ describe("js-rln", () => { try { // verify the proof - const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg); + const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg); expect(verifResult).to.be.true; } catch (err) { assert.fail(0, 1, "should not have failed proof verification"); @@ -52,16 +52,17 @@ describe("js-rln", () => { // Modifying the signal so it's invalid uint8Msg[4] = 4; // verify the proof - const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg); + const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg); expect(verifResult).to.be.false; } catch (err) { console.log(err); } }); 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 credential = rlnInstance.generateSeededIdentityCredential(seed); + const credential = + rlnInstance.zerokit.generateSeededIdentityCredential(seed); //peer's index in the Merkle Tree const index = 5; @@ -70,11 +71,11 @@ describe("js-rln", () => { for (let i = 0; i < 10; i++) { if (i == index) { // insert the current peer's pk - rlnInstance.insertMember(credential.IDCommitment); + rlnInstance.zerokit.insertMember(credential.IDCommitment); } else { // create a new key pair - rlnInstance.insertMember( - rlnInstance.generateIdentityCredentials().IDCommitment + rlnInstance.zerokit.insertMember( + rlnInstance.zerokit.generateIdentityCredentials().IDCommitment ); } } @@ -88,7 +89,7 @@ describe("js-rln", () => { const epoch = new Date(); // generating proof - const proof = await rlnInstance.generateRLNProof( + const proof = await rlnInstance.zerokit.generateRLNProof( uint8Msg, index, epoch, @@ -97,7 +98,7 @@ describe("js-rln", () => { try { // verify the proof - const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg); + const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg); expect(verifResult).to.be.true; } catch (err) { assert.fail(0, 1, "should not have failed proof verification"); @@ -107,7 +108,7 @@ describe("js-rln", () => { // Modifying the signal so it's invalid uint8Msg[4] = 4; // verify the proof - const verifResult = rlnInstance.verifyRLNProof(proof, uint8Msg); + const verifResult = rlnInstance.zerokit.verifyRLNProof(proof, uint8Msg); expect(verifResult).to.be.false; } catch (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 () { - const rlnInstance = await rln.createRLN(); + const rlnInstance = await createRLN(); const seed = "This is a test seed"; - const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed); - const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed); + const memKeys1 = rlnInstance.zerokit.generateSeededIdentityCredential(seed); + const memKeys2 = rlnInstance.zerokit.generateSeededIdentityCredential(seed); memKeys1.IDCommitment.forEach((element, index) => { expect(element).to.equal(memKeys2.IDCommitment[index]); diff --git a/src/identity.ts b/src/identity.ts new file mode 100644 index 0000000..06cd436 --- /dev/null +++ b/src/identity.ts @@ -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 + ); + } +} diff --git a/src/index.ts b/src/index.ts index d0eed69..54dcd26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,18 +3,15 @@ import { RLN_REGISTRY_ABI, RLN_STORAGE_ABI, SEPOLIA_CONTRACT, -} from "./constants.js"; +} from "./contract/index.js"; +import { RLNContract } from "./contract/index.js"; import { createRLN } from "./create.js"; +import { IdentityCredential } from "./identity.js"; import { Keystore } from "./keystore/index.js"; -import { extractMetaMaskSigner } from "./metamask.js"; -import { - IdentityCredential, - Proof, - ProofMetadata, - RLNInstance, -} from "./rln.js"; -import { RLNContract } from "./rln_contract.js"; +import { Proof } from "./proof.js"; +import { RLNInstance } from "./rln.js"; import { MerkleRootTracker } from "./root_tracker.js"; +import { extractMetaMaskSigner } from "./utils/index.js"; export { createRLN, @@ -22,7 +19,6 @@ export { RLNInstance, IdentityCredential, Proof, - ProofMetadata, RLNEncoder, RLNDecoder, MerkleRootTracker, diff --git a/src/keystore/keystore.spec.ts b/src/keystore/keystore.spec.ts index 896759d..a926d14 100644 --- a/src/keystore/keystore.spec.ts +++ b/src/keystore/keystore.spec.ts @@ -7,8 +7,8 @@ chai.use(chaiSubset); chai.use(deepEqualInAnyOrder); chai.use(chaiAsPromised); -import { buildBigIntFromUint8Array } from "../byte_utils.js"; -import { IdentityCredential } from "../rln.js"; +import { IdentityCredential } from "../identity.js"; +import { buildBigIntFromUint8Array } from "../utils/bytes.js"; import { Keystore } from "./keystore.js"; import type { MembershipInfo } from "./types.js"; diff --git a/src/keystore/keystore.ts b/src/keystore/keystore.ts index 51714d4..c104de6 100644 --- a/src/keystore/keystore.ts +++ b/src/keystore/keystore.ts @@ -13,7 +13,7 @@ import { import _ from "lodash"; 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 { isCredentialValid, isKeystoreValid } from "./schema_validator.js"; diff --git a/src/keystore/types.ts b/src/keystore/types.ts index 417eb83..9d8e86a 100644 --- a/src/keystore/types.ts +++ b/src/keystore/types.ts @@ -1,4 +1,4 @@ -import type { IdentityCredential } from "../rln.js"; +import type { IdentityCredential } from "../identity.js"; export type MembershipHash = string; export type Sha256Hash = string; diff --git a/src/message.ts b/src/message.ts index bc051a2..869cf80 100644 --- a/src/message.ts +++ b/src/message.ts @@ -5,8 +5,8 @@ import type { } from "@waku/interfaces"; import * as utils from "@waku/utils/bytes"; -import { epochBytesToInt } from "./epoch.js"; import { RLNInstance } from "./rln.js"; +import { epochBytesToInt } from "./utils/index.js"; export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array { const contentTopicBytes = utils.utf8ToBytes(contentTopic ?? ""); @@ -24,7 +24,7 @@ export class RlnMessage implements IDecodedMessage { public verify(roots: Uint8Array[]): boolean | undefined { return this.rateLimitProof - ? this.rlnInstance.verifyWithRoots( + ? this.rlnInstance.zerokit.verifyWithRoots( this.rateLimitProof, toRLNSignal(this.msg.contentTopic, this.msg), ...roots @@ -34,7 +34,7 @@ export class RlnMessage implements IDecodedMessage { public verifyNoRoot(): boolean | undefined { return this.rateLimitProof - ? this.rlnInstance.verifyWithNoRoot( + ? this.rlnInstance.zerokit.verifyWithNoRoot( this.rateLimitProof, toRLNSignal(this.msg.contentTopic, this.msg) ) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed diff --git a/src/proof.ts b/src/proof.ts new file mode 100644 index 0000000..0aea21e --- /dev/null +++ b/src/proof.ts @@ -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 + ); +} diff --git a/src/witness_calculator.d.ts b/src/resources/witness_calculator.d.ts similarity index 100% rename from src/witness_calculator.d.ts rename to src/resources/witness_calculator.d.ts diff --git a/src/witness_calculator.js b/src/resources/witness_calculator.js similarity index 100% rename from src/witness_calculator.js rename to src/resources/witness_calculator.js diff --git a/src/rln.ts b/src/rln.ts index 373f881..c73b566 100644 --- a/src/rln.ts +++ b/src/rln.ts @@ -1,5 +1,4 @@ import { createDecoder, createEncoder } from "@waku/core"; -import type { IRateLimitProof } from "@waku/interfaces"; import type { ContentTopic, IDecodedMessage, @@ -9,45 +8,25 @@ import init from "@waku/zerokit-rln-wasm"; import * as zerokitRLN from "@waku/zerokit-rln-wasm"; import { ethers } from "ethers"; -import { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js"; -import type { RLNDecoder, RLNEncoder } from "./codec.js"; -import { createRLNDecoder, createRLNEncoder } from "./codec.js"; -import { SEPOLIA_CONTRACT } from "./constants.js"; -import { dateToEpoch, epochIntToBytes } from "./epoch.js"; +import { + createRLNDecoder, + createRLNEncoder, + type RLNDecoder, + 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 type { DecryptedCredentials, EncryptedCredentials, } from "./keystore/index.js"; import { KeystoreEntity, Password } from "./keystore/types.js"; -import { extractMetaMaskSigner } from "./metamask.js"; import verificationKey from "./resources/verification_key.js"; -import { RLNContract } from "./rln_contract.js"; -import * as wc from "./witness_calculator.js"; -import { WitnessCalculator } from "./witness_calculator.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; +import * as wc from "./resources/witness_calculator.js"; +import { WitnessCalculator } from "./resources/witness_calculator.js"; +import { extractMetaMaskSigner } from "./utils/index.js"; +import { Zerokit } from "./zerokit.js"; async function loadWitnessCalculator(): Promise { const url = new URL("./resources/rln.wasm", import.meta.url); @@ -68,112 +47,18 @@ async function loadZkey(): Promise { export async function create(): Promise { await (init as any)?.(); zerokitRLN.init_panic_hook(); + const witnessCalculator = await loadWitnessCalculator(); const zkey = await loadZkey(); + + const stringEncoder = new TextEncoder(); const vkey = stringEncoder.encode(JSON.stringify(verificationKey)); + + const DEPTH = 20; const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey); - return new RLNInstance(zkRLN, witnessCalculator); -} + const zerokit = new Zerokit(zkRLN, witnessCalculator); -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 - ); - } -} - -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 { - 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); + return new RLNInstance(zerokit); } type StartRLNOptions = { @@ -210,10 +95,7 @@ export class RLNInstance { private keystore = Keystore.create(); private _credentials: undefined | DecryptedCredentials; - constructor( - private zkRLN: number, - private witnessCalculator: WitnessCalculator - ) {} + constructor(public zerokit: Zerokit) {} public get contract(): undefined | RLNContract { return this._contract; @@ -321,7 +203,9 @@ export class RLNInstance { let identity = "identity" in options && options.identity; if ("signature" in options) { - identity = await this.generateSeededIdentityCredential(options.signature); + identity = await this.zerokit.generateSeededIdentityCredential( + options.signature + ); } if (!identity) { @@ -398,165 +282,4 @@ export class RLNInstance { 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): void { - // serializes a seq of IDCommitments to a byte seq - // the order of serialization is |id_commitment_len<8>|id_commitment| - 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 ] - return concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg); - } - - async generateRLNProof( - msg: Uint8Array, - index: number, - epoch: Uint8Array | Date | undefined, - idSecretHash: Uint8Array - ): Promise { - 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 - ): 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() - ); - } } diff --git a/src/byte_utils.ts b/src/utils/bytes.ts similarity index 77% rename from src/byte_utils.ts rename to src/utils/bytes.ts index 2e7e52b..000488c 100644 --- a/src/byte_utils.ts +++ b/src/utils/bytes.ts @@ -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( buf: Uint8Array, value: number, diff --git a/src/epoch.spec.ts b/src/utils/epoch.spec.ts similarity index 100% rename from src/epoch.spec.ts rename to src/utils/epoch.spec.ts diff --git a/src/epoch.ts b/src/utils/epoch.ts similarity index 100% rename from src/epoch.ts rename to src/utils/epoch.ts diff --git a/src/utils/hash.ts b/src/utils/hash.ts new file mode 100644 index 0000000..78422e2 --- /dev/null +++ b/src/utils/hash.ts @@ -0,0 +1,15 @@ +import * as zerokitRLN from "@waku/zerokit-rln-wasm"; + +import { concatenate, writeUIntLE } from "./bytes.js"; + +export function poseidonHash(...input: Array): 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); +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..b706700 --- /dev/null +++ b/src/utils/index.ts @@ -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"; diff --git a/src/metamask.ts b/src/utils/metamask.ts similarity index 100% rename from src/metamask.ts rename to src/utils/metamask.ts diff --git a/src/zerokit.ts b/src/zerokit.ts new file mode 100644 index 0000000..39d506f --- /dev/null +++ b/src/zerokit.ts @@ -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): void { + // serializes a seq of IDCommitments to a byte seq + // the order of serialization is |id_commitment_len<8>|id_commitment| + 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 ] + return concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg); + } + + async generateRLNProof( + msg: Uint8Array, + index: number, + epoch: Uint8Array | Date | undefined, + idSecretHash: Uint8Array + ): Promise { + 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 + ): 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() + ); + } +}