feat: use identity credentials, and expose hash, bulk insert and delete members functions

Also update the resources and karma.conf because proof generation is
taking longer than usual
This commit is contained in:
Richard Ramos 2023-04-28 14:26:39 -04:00
parent 7e0966aef7
commit f20bdbb9a9
No known key found for this signature in database
GPG Key ID: 1CE87DB518195760
14 changed files with 259 additions and 193 deletions

View File

@ -70,10 +70,10 @@ import * as rln from "@waku/rln";
const rlnInstance = await rln.create();
```
### Generating RLN Membership Keypair
#### Generating RLN membership credentials
```js
let memKeys = rlnInstance.generateMembershipKey();
let credentials = rlnInstance.generateIdentityCredentials();
```
### Generating RLN Membership Keypair Using a Seed
@ -81,13 +81,13 @@ let memKeys = rlnInstance.generateMembershipKey();
This can be used to generate credentials from a signature hash (e.g. signed by an Ethereum account).
```js
let memKeys = rlnInstance.generateSeededMembershipKey(seed);
let credentials = rlnInstance.generateSeededIdentityCredentials(seed);
```
### Adding Membership Keys Into Merkle Tree
```js
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credentials.IDCommitment);
```
### Generating a Proof
@ -106,7 +106,7 @@ const proof = await rlnInstance.generateProof(
uint8Msg,
index,
epoch,
memKeys.IDKey
credentials.IDSecretHash
);
```

View File

@ -1,7 +1,7 @@
import * as rln from "@waku/rln";
rln.create().then(async rlnInstance => {
let memKeys = rlnInstance.generateMembershipKey();
let credentials = rlnInstance.generateIdentityCredentials();
//peer's index in the Merkle Tree
const index = 5
@ -10,11 +10,11 @@ rln.create().then(async rlnInstance => {
for (let i = 0; i < 10; i++) {
if (i == index) {
// insert the current peer's pk
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credentials.IDCommitment);
} else {
// create a new key pair
let memKeys = rlnInstance.generateMembershipKey(); // TODO: handle error
rlnInstance.insertMember(memKeys.IDCommitment);
let credentials = rlnInstance.generateIdentityCredentials(); // TODO: handle error
rlnInstance.insertMember(credentials.IDCommitment);
}
}
@ -27,7 +27,7 @@ rln.create().then(async rlnInstance => {
console.log("Generating proof...");
console.time("proof_gen_timer");
let proof = await rlnInstance.generateRLNProof(uint8Msg, index, epoch, memKeys.IDKey)
let proof = await rlnInstance.generateRLNProof(uint8Msg, index, epoch, credentials.IDSecretHash)
console.timeEnd("proof_gen_timer");
console.log("Proof", proof)

View File

@ -30,10 +30,11 @@ module.exports = function (config) {
envPreprocessor: ["CI"],
reporters: ["progress"],
browsers: ["ChromeHeadless"],
pingTimeout: 60000,
singleRun: true,
client: {
mocha: {
timeout: 6000, // Default is 2s
timeout: 60000, // Default is 2s
},
},
webpack: {

18
package-lock.json generated
View File

@ -1,16 +1,16 @@
{
"name": "@waku/rln",
"version": "0.0.14",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@waku/rln",
"version": "0.0.14",
"version": "0.1.0",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@waku/utils": "^0.0.4",
"@waku/zerokit-rln-wasm": "^0.0.5",
"@waku/zerokit-rln-wasm": "^0.0.10",
"ethers": "^5.7.2"
},
"devDependencies": {
@ -2942,9 +2942,9 @@
}
},
"node_modules/@waku/zerokit-rln-wasm": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.5.tgz",
"integrity": "sha512-uZHZRk06WrnqJJOVwIIKtsjWf2d6g2JpK8FtC0lHg4JJkOxhJy0pgEIuBCPw8Je4MpF9FCtIO/ww7xicdlC2GA=="
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.10.tgz",
"integrity": "sha512-qegIK1P54mxEp59uTa8C0/zidUffLc2Iee61yiKRIuGJDui2mQ+0V+KzPSPImKpIoqfVLT192EqgZkqPmj8VEw=="
},
"node_modules/@web/rollup-plugin-import-meta-assets": {
"version": "1.0.7",
@ -13629,9 +13629,9 @@
}
},
"@waku/zerokit-rln-wasm": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.5.tgz",
"integrity": "sha512-uZHZRk06WrnqJJOVwIIKtsjWf2d6g2JpK8FtC0lHg4JJkOxhJy0pgEIuBCPw8Je4MpF9FCtIO/ww7xicdlC2GA=="
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/@waku/zerokit-rln-wasm/-/zerokit-rln-wasm-0.0.10.tgz",
"integrity": "sha512-qegIK1P54mxEp59uTa8C0/zidUffLc2Iee61yiKRIuGJDui2mQ+0V+KzPSPImKpIoqfVLT192EqgZkqPmj8VEw=="
},
"@web/rollup-plugin-import-meta-assets": {
"version": "1.0.7",

View File

@ -1,6 +1,6 @@
{
"name": "@waku/rln",
"version": "0.0.14",
"version": "0.1.0",
"description": "Rate Limit Nullifier for js-waku",
"types": "./dist/index.d.ts",
"module": "./dist/index.js",
@ -130,7 +130,7 @@
},
"dependencies": {
"@waku/utils": "^0.0.4",
"@waku/zerokit-rln-wasm": "^0.0.5",
"@waku/zerokit-rln-wasm": "^0.0.10",
"ethers": "^5.7.2"
}
}

View File

@ -35,17 +35,17 @@ const EMPTY_PUBSUB_TOPIC = "";
describe("RLN codec with version 0", () => {
it("toWire", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const rlnEncoder = createRLNEncoder({
encoder: createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
membershipKey: memKeys,
credential,
});
const rlnDecoder = createRLNDecoder({
rlnInstance,
@ -76,17 +76,17 @@ describe("RLN codec with version 0", () => {
it("toProtoObj", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const rlnEncoder = new RLNEncoder(
createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
@ -119,11 +119,11 @@ describe("RLN codec with version 0", () => {
describe("RLN codec with version 1", () => {
it("Symmetric, toWire", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const symKey = generateSymmetricKey();
@ -134,7 +134,7 @@ describe("RLN codec with version 1", () => {
}),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
@ -166,11 +166,11 @@ describe("RLN codec with version 1", () => {
it("Symmetric, toProtoObj", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const symKey = generateSymmetricKey();
@ -181,7 +181,7 @@ describe("RLN codec with version 1", () => {
}),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
@ -212,11 +212,11 @@ describe("RLN codec with version 1", () => {
it("Asymmetric, toWire", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
@ -228,7 +228,7 @@ describe("RLN codec with version 1", () => {
}),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
@ -260,11 +260,11 @@ describe("RLN codec with version 1", () => {
it("Asymmetric, toProtoObj", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
@ -276,7 +276,7 @@ describe("RLN codec with version 1", () => {
}),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
@ -309,17 +309,17 @@ describe("RLN codec with version 1", () => {
describe("RLN Codec - epoch", () => {
it("toProtoObj", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
const rlnEncoder = new RLNEncoder(
createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
memKeys
credential
);
const rlnDecoder = new RLNDecoder(
rlnInstance,

View File

@ -9,21 +9,21 @@ import type {
import debug from "debug";
import { RlnMessage, toRLNSignal } from "./message.js";
import { MembershipKey, RLNInstance } from "./rln.js";
import { IdentityCredential, RLNInstance } from "./rln.js";
const log = debug("waku:rln:encoder");
export class RLNEncoder implements IEncoder {
private readonly idKey: Uint8Array;
private readonly idSecretHash: Uint8Array;
constructor(
private encoder: IEncoder,
private rlnInstance: RLNInstance,
private index: number,
membershipKey: MembershipKey
identityCredential: IdentityCredential
) {
if (index < 0) throw "invalid membership index";
this.idKey = membershipKey.IDKey;
this.idSecretHash = identityCredential.IDSecretHash;
}
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
@ -50,7 +50,7 @@ export class RLNEncoder implements IEncoder {
signal,
this.index,
message.timestamp,
this.idKey
this.idSecretHash
);
console.timeEnd("proof_gen_timer");
return proof;
@ -69,7 +69,7 @@ type RLNEncoderOptions = {
encoder: IEncoder;
rlnInstance: RLNInstance;
index: number;
membershipKey: MembershipKey;
credential: IdentityCredential;
};
export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
@ -77,7 +77,7 @@ export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
options.encoder,
options.rlnInstance,
options.index,
options.membershipKey
options.credential
);
};

View File

@ -6,7 +6,7 @@ describe("js-rln", () => {
it("should verify a proof", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const credential = rlnInstance.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(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
} else {
// create a new key pair
rlnInstance.insertMember(
rlnInstance.generateMembershipKey().IDCommitment
rlnInstance.generateIdentityCredentials().IDCommitment
);
}
}
@ -37,7 +37,7 @@ describe("js-rln", () => {
uint8Msg,
index,
epoch,
memKeys.IDKey
credential.IDSecretHash
);
try {
@ -61,7 +61,7 @@ describe("js-rln", () => {
it("should verify a proof with a seeded membership key generation", async function () {
const rlnInstance = await rln.create();
const seed = "This is a test seed";
const memKeys = rlnInstance.generateSeededMembershipKey(seed);
const credential = rlnInstance.generateSeededIdentityCredential(seed);
//peer's index in the Merkle Tree
const index = 5;
@ -70,11 +70,11 @@ describe("js-rln", () => {
for (let i = 0; i < 10; i++) {
if (i == index) {
// insert the current peer's pk
rlnInstance.insertMember(memKeys.IDCommitment);
rlnInstance.insertMember(credential.IDCommitment);
} else {
// create a new key pair
rlnInstance.insertMember(
rlnInstance.generateMembershipKey().IDCommitment
rlnInstance.generateIdentityCredentials().IDCommitment
);
}
}
@ -92,7 +92,7 @@ describe("js-rln", () => {
uint8Msg,
index,
epoch,
memKeys.IDKey
credential.IDSecretHash
);
try {
@ -116,14 +116,20 @@ describe("js-rln", () => {
it("should generate the same membership key if the same seed is provided", async function () {
const rlnInstance = await rln.create();
const seed = "This is a test seed";
const memKeys1 = rlnInstance.generateSeededMembershipKey(seed);
const memKeys2 = rlnInstance.generateSeededMembershipKey(seed);
const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
memKeys1.IDCommitment.forEach((element, index) => {
expect(element).to.equal(memKeys2.IDCommitment[index]);
});
memKeys1.IDKey.forEach((element, index) => {
expect(element).to.equal(memKeys2.IDKey[index]);
memKeys1.IDNullifier.forEach((element, index) => {
expect(element).to.equal(memKeys2.IDNullifier[index]);
});
memKeys1.IDSecretHash.forEach((element, index) => {
expect(element).to.equal(memKeys2.IDSecretHash[index]);
});
memKeys1.IDTrapdoor.forEach((element, index) => {
expect(element).to.equal(memKeys2.IDTrapdoor[index]);
});
});
});

View File

@ -1,7 +1,11 @@
import { RLNDecoder, RLNEncoder } from "./codec.js";
import { GOERLI_CONTRACT, RLN_ABI } from "./constants.js";
import { Proof, RLNInstance } from "./rln.js";
import { MembershipKey } from "./rln.js";
import {
IdentityCredential,
Proof,
ProofMetadata,
RLNInstance,
} from "./rln.js";
import { RLNContract } from "./rln_contract.js";
// reexport the create function, dynamically imported from rln.ts
@ -15,8 +19,9 @@ export async function create(): Promise<RLNInstance> {
export {
RLNInstance,
MembershipKey,
IdentityCredential,
Proof,
ProofMetadata,
RLNEncoder,
RLNDecoder,
RLNContract,

Binary file not shown.

Binary file not shown.

View File

@ -1,120 +1,112 @@
const verificationKey = {
"protocol": "groth16",
"curve": "bn128",
"nPublic": 6,
"vk_alpha_1": [
"1805378556360488226980822394597799963030511477964155500103132920745199284516",
"11990395240534218699464972016456017378439762088320057798320175886595281336136",
"1"
protocol: "groth16",
curve: "bn128",
nPublic: 6,
vk_alpha_1: [
"20124996762962216725442980738609010303800849578410091356605067053491763969391",
"9118593021526896828671519912099489027245924097793322973632351264852174143923",
"1",
],
"vk_beta_2": [
vk_beta_2: [
[
"11031529986141021025408838211017932346992429731488270384177563837022796743627",
"16042159910707312759082561183373181639420894978640710177581040523252926273854"
"4693952934005375501364248788849686435240706020501681709396105298107971354382",
"14346958885444710485362620645446987998958218205939139994511461437152241966681",
],
[
"20112698439519222240302944148895052359035104222313380895334495118294612255131",
"19441583024670359810872018179190533814486480928824742448673677460151702019379"
"16851772916911573982706166384196538392731905827088356034885868448550849804972",
"823612331030938060799959717749043047845343400798220427319188951998582076532",
],
[
"1",
"0"
]
["1", "0"],
],
"vk_gamma_2": [
vk_gamma_2: [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
"11559732032986387107991004021392285783925812861821192530917403151452391805634",
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
"4082367875863433681332203403145435568316851327593401208105741076214120093531",
],
["1", "0"],
],
vk_delta_2: [
[
"8353516066399360694538747105302262515182301251524941126222712285088022964076",
"9329524012539638256356482961742014315122377605267454801030953882967973561832",
],
[
"16805391589556134376869247619848130874761233086443465978238468412168162326401",
"10111259694977636294287802909665108497237922060047080343914303287629927847739",
],
["1", "0"],
],
vk_alphabeta_12: [
[
[
"12608968655665301215455851857466367636344427685631271961542642719683786103711",
"9849575605876329747382930567422916152871921500826003490242628251047652318086",
],
[
"6322029441245076030714726551623552073612922718416871603535535085523083939021",
"8700115492541474338049149013125102281865518624059015445617546140629435818912",
],
[
"10674973475340072635573101639867487770811074181475255667220644196793546640210",
"2926286967251299230490668407790788696102889214647256022788211245826267484824",
],
],
[
[
"9660441540778523475944706619139394922744328902833875392144658911530830074820",
"19548113127774514328631808547691096362144426239827206966690021428110281506546",
],
[
"1870837942477655969123169532603615788122896469891695773961478956740992497097",
"12536105729661705698805725105036536744930776470051238187456307227425796690780",
],
[
"21811903352654147452884857281720047789720483752548991551595462057142824037334",
"19021616763967199151052893283384285352200445499680068407023236283004353578353",
],
],
],
IC: [
[
"11992897507809711711025355300535923222599547639134311050809253678876341466909",
"17181525095924075896332561978747020491074338784673526378866503154966799128110",
"1",
"0"
]
],
"vk_delta_2": [
[
"1948496782571164085469528023647105317580208688174386157591917599801657832035",
"20445814069256658101339037520922621162739470138213615104905368409238414511981"
],
[
"10024680869920840984813249386422727863826862577760330492647062850849851925340",
"10512156247842686783409460795717734694774542185222602679117887145206209285142"
],
[
"17018665030246167677911144513385572506766200776123272044534328594850561667818",
"18601114175490465275436712413925513066546725461375425769709566180981674884464",
"1",
"0"
]
],
[
"18799470100699658367834559797874857804183288553462108031963980039244731716542",
"13064227487174191981628537974951887429496059857753101852163607049188825592007",
"1",
],
[
"17432501889058124609368103715904104425610382063762621017593209214189134571156",
"13406815149699834788256141097399354592751313348962590382887503595131085938635",
"1",
],
[
"10320964835612716439094703312987075811498239445882526576970512041988148264481",
"9024164961646353611176283204118089412001502110138072989569118393359029324867",
"1",
],
[
"718355081067365548229685160476620267257521491773976402837645005858953849298",
"14635482993933988261008156660773180150752190597753512086153001683711587601974",
"1",
],
[
"11777720285956632126519898515392071627539405001940313098390150593689568177535",
"8483603647274280691250972408211651407952870456587066148445913156086740744515",
"1",
],
],
"vk_alphabeta_12": [
[
[
"5151991366823434428398919091000210787450832786814248297320989361921939794156",
"15735191313289001022885148627913534790382722933676436876510746491415970766821"
],
[
"3387907257437913904447588318761906430938415556102110876587455322225272831272",
"1998779853452712881084781956683721603875246565720647583735935725110674288056"
],
[
"14280074182991498185075387990446437410077692353432005297922275464876153151820",
"17092408446352310039633488224969232803092763095456307462247653153107223117633"
]
],
[
[
"4359046709531668109201634396816565829237358165496082832279660960675584351266",
"4511888308846208349307186938266411423935335853916317436093178288331845821336"
],
[
"11429499807090785857812316277335883295048773373068683863667725283965356423273",
"16232274853200678548795010078253506586114563833318973594428907292096178657392"
],
[
"18068999605870933925311275504102553573815570223888590384919752303726860800970",
"17309569111965782732372130116757295842160193489132771344011460471298173784984"
]
]
],
"IC": [
[
"18693301901828818437917730940595978397160482710354161265484535387752523310572",
"17985273354976640088538673802000794244421192643855111089693820179790551470769",
"1"
],
[
"21164641723988537620541455173278629777250883365474191521194244273980931825942",
"998385854410718613441067082771678946155853656328717326195057262123686425518",
"1"
],
[
"21666968581672145768705229094968410656430989593283335488162701230986314747515",
"17996457608540683483506630273632100555125353447506062045735279661096094677264",
"1"
],
[
"20137761979695192602424300886442379728165712610493092740175904438282083668117",
"19184814924890679891263780109959113289320127263583260218200636509492157834679",
"1"
],
[
"10943171273393803842589314082509655332154393332394322726077270895078286354146",
"10872472035685319847811233167729172672344935625121511932198535224727331126439",
"1"
],
[
"13049169779481227658517545034348883391527506091990880778783387628208561946597",
"10083689369261379027228809473568899816311684698866922944902456565434209079955",
"1"
],
[
"19633516378466409167014413361365552102431118630694133723053441455184566611083",
"8059525100726933978719058611146131904598011633549012007359165766216730722269",
"1"
]
]
}
export default verificationKey
};
export default verificationKey;

View File

@ -66,18 +66,29 @@ export async function create(): Promise<RLNInstance> {
return new RLNInstance(zkRLN, witnessCalculator);
}
export class MembershipKey {
export class IdentityCredential {
constructor(
public readonly IDKey: Uint8Array,
public readonly IDTrapdoor: Uint8Array,
public readonly IDNullifier: Uint8Array,
public readonly IDSecretHash: Uint8Array,
public readonly IDCommitment: Uint8Array,
public readonly IDCommitmentBigInt: bigint
) {}
static fromBytes(memKeys: Uint8Array): MembershipKey {
const idKey = memKeys.subarray(0, 32);
const idCommitment = memKeys.subarray(32);
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 MembershipKey(idKey, idCommitment, idCommitmentBigInt);
return new IdentityCredential(
idTrapdoor,
idNullifier,
idSecretHash,
idCommitment,
idCommitmentBigInt
);
}
}
@ -89,6 +100,14 @@ const shareYOffset = shareXOffset + 32;
const nullifierOffset = shareYOffset + 32;
const 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;
@ -112,6 +131,16 @@ export class Proof implements IRateLimitProof {
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 {
@ -126,30 +155,60 @@ export function proofToBytes(p: IRateLimitProof): Uint8Array {
);
}
export function poseidonHash(...input: Array<Uint8Array>): Uint8Array {
const inputLen = writeUIntLE(new Uint8Array(8), input.length, 0, 8);
const lenPrefixedData = concatenate(inputLen, ...input);
return zerokitRLN.poseidonHash(lenPrefixedData);
}
export function sha256(input: Uint8Array): Uint8Array {
const inputLen = writeUIntLE(new Uint8Array(8), input.length, 0, 8);
const lenPrefixedData = concatenate(inputLen, input);
return zerokitRLN.hash(lenPrefixedData);
}
export class RLNInstance {
constructor(
private zkRLN: number,
private witnessCalculator: WitnessCalculator
) {}
generateMembershipKey(): MembershipKey {
const memKeys = zerokitRLN.generateMembershipKey(this.zkRLN);
return MembershipKey.fromBytes(memKeys);
generateIdentityCredentials(): IdentityCredential {
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
return IdentityCredential.fromBytes(memKeys);
}
generateSeededMembershipKey(seed: string): MembershipKey {
generateSeededIdentityCredential(seed: string): IdentityCredential {
const seedBytes = stringEncoder.encode(seed);
const memKeys = zerokitRLN.generateSeededMembershipKey(
// TODO: rename this function in zerokit rln-wasm
const memKeys = zerokitRLN.generateSeededExtendedMembershipKey(
this.zkRLN,
seedBytes
);
return MembershipKey.fromBytes(memKeys);
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);
}
@ -174,7 +233,7 @@ export class RLNInstance {
msg: Uint8Array,
index: number,
epoch: Uint8Array | Date | undefined,
idKey: Uint8Array
idSecretHash: Uint8Array
): Promise<IRateLimitProof> {
if (epoch == undefined) {
epoch = epochIntToBytes(dateToEpoch(new Date()));
@ -183,10 +242,15 @@ export class RLNInstance {
}
if (epoch.length != 32) throw "invalid epoch";
if (idKey.length != 32) throw "invalid id key";
if (idSecretHash.length != 32) throw "invalid id secret hash";
if (index < 0) throw "index must be >= 0";
const serialized_msg = this.serializeMessage(msg, index, epoch, idKey);
const serialized_msg = this.serializeMessage(
msg,
index,
epoch,
idSecretHash
);
const rlnWitness = zerokitRLN.getSerializedRLNWitness(
this.zkRLN,
serialized_msg
@ -228,7 +292,8 @@ export class RLNInstance {
verifyWithRoots(
proof: IRateLimitProof | Uint8Array,
msg: Uint8Array
msg: Uint8Array,
...roots: Array<Uint8Array>
): boolean {
let pBytes: Uint8Array;
if (proof instanceof Uint8Array) {
@ -236,17 +301,15 @@ export class RLNInstance {
} else {
pBytes = proofToBytes(proof);
}
// calculate message length
const msgLen = writeUIntLE(new Uint8Array(8), msg.length, 0, 8);
// obtain root
const root = zerokitRLN.getRoot(this.zkRLN);
const rootsBytes = concatenate(...roots);
return zerokitRLN.verifyWithRoots(
this.zkRLN,
concatenate(pBytes, msgLen, msg),
root
rootsBytes
);
}

View File

@ -1,7 +1,7 @@
import { ethers } from "ethers";
import { RLN_ABI } from "./constants.js";
import { MembershipKey, RLNInstance } from "./rln.js";
import { IdentityCredential, RLNInstance } from "./rln.js";
type Member = {
pubkey: string;
@ -94,20 +94,19 @@ export class RLNContract {
rlnInstance: RLNInstance,
signature: string
): Promise<ethers.Event | undefined> {
const membershipKey = await rlnInstance.generateSeededMembershipKey(
signature
);
const identityCredential =
await rlnInstance.generateSeededIdentityCredential(signature);
return this.registerWithKey(membershipKey);
return this.registerWithKey(identityCredential);
}
public async registerWithKey(
membershipKey: MembershipKey
credential: IdentityCredential
): Promise<ethers.Event | undefined> {
const depositValue = await this.contract.MEMBERSHIP_DEPOSIT();
const txRegisterResponse: ethers.ContractTransaction =
await this.contract.register(membershipKey.IDCommitmentBigInt, {
await this.contract.register(credential.IDCommitmentBigInt, {
value: depositValue,
});
const txRegisterReceipt = await txRegisterResponse.wait();