mirror of
https://github.com/logos-messaging/js-rln.git
synced 2026-01-05 23:23:12 +00:00
feat: simplify API of bootstrapping, connection to MetaMask (#92)
* add MetaMask abstraction, add default start to RLN Instance, expose RLN Contract * remove console timer * improve text * rename to createRLN * update README, fix tests * use Provider type * use provider as it is
This commit is contained in:
parent
18ce994d5e
commit
bafbe01e52
22
README.md
22
README.md
@ -71,7 +71,16 @@ Browse http://localhost:8080 and open the dev tools console to see the proof bei
|
|||||||
```js
|
```js
|
||||||
import * as rln from "@waku/rln";
|
import * as rln from "@waku/rln";
|
||||||
|
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Starting RLN to listen to a contract
|
||||||
|
|
||||||
|
```js
|
||||||
|
import * as rln from "@waku/rln";
|
||||||
|
|
||||||
|
const rlnInstance = await rln.createRLN();
|
||||||
|
await rlnInstance.start(); // will use default Sepolia contract
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Generating RLN Membership Credentials
|
#### Generating RLN Membership Credentials
|
||||||
@ -94,6 +103,17 @@ let credentials = rlnInstance.generateSeededIdentityCredentials(seed);
|
|||||||
rlnInstance.insertMember(credentials.IDCommitment);
|
rlnInstance.insertMember(credentials.IDCommitment);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Registering Membership on a contract
|
||||||
|
|
||||||
|
```js
|
||||||
|
import * as rln from "@waku/rln";
|
||||||
|
|
||||||
|
const rlnInstance = await rln.createRLN();
|
||||||
|
await rlnInstance.start(); // will use default Sepolia contract
|
||||||
|
|
||||||
|
const membershipInfo = await rlnInstance.contract.registerWithKey(credentials);
|
||||||
|
```
|
||||||
|
|
||||||
### Generating a Proof
|
### Generating a Proof
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
@ -44,7 +44,7 @@ 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.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -85,7 +85,7 @@ describe("RLN codec with version 0", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -128,7 +128,7 @@ describe("RLN codec with version 0", () => {
|
|||||||
|
|
||||||
describe("RLN codec with version 1", () => {
|
describe("RLN codec with version 1", () => {
|
||||||
it("Symmetric, toWire", async function () {
|
it("Symmetric, toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -175,7 +175,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Symmetric, toProtoObj", async function () {
|
it("Symmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -221,7 +221,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Asymmetric, toWire", async function () {
|
it("Asymmetric, toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -269,7 +269,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Asymmetric, toProtoObj", async function () {
|
it("Asymmetric, toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -318,7 +318,7 @@ describe("RLN codec with version 1", () => {
|
|||||||
|
|
||||||
describe("RLN Codec - epoch", () => {
|
describe("RLN Codec - epoch", () => {
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -374,7 +374,7 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it("toWire", async function () {
|
it("toWire", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
@ -422,7 +422,7 @@ describe("RLN codec with version 0 and meta setter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("toProtoObj", async function () {
|
it("toProtoObj", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
|||||||
@ -44,15 +44,12 @@ 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);
|
||||||
|
|
||||||
console.time("proof_gen_timer");
|
|
||||||
const proof = await this.rlnInstance.generateRLNProof(
|
const proof = await this.rlnInstance.generateRLNProof(
|
||||||
signal,
|
signal,
|
||||||
this.index,
|
this.index,
|
||||||
message.timestamp,
|
message.timestamp,
|
||||||
this.idSecretHash
|
this.idSecretHash
|
||||||
);
|
);
|
||||||
console.timeEnd("proof_gen_timer");
|
|
||||||
return proof;
|
return proof;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
src/create.ts
Normal file
9
src/create.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { RLNInstance } from "./rln.js";
|
||||||
|
|
||||||
|
export async function createRLN(): Promise<RLNInstance> {
|
||||||
|
// A dependency graph that contains any wasm must all be imported
|
||||||
|
// asynchronously. This file does the single async import, so
|
||||||
|
// that no one else needs to worry about it again.
|
||||||
|
const rlnModule = await import("./rln.js");
|
||||||
|
return rlnModule.create();
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import * as rln from "./index.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.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
|
|
||||||
const credential = rlnInstance.generateIdentityCredentials();
|
const credential = rlnInstance.generateIdentityCredentials();
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ describe("js-rln", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
it("should verify a proof with a seeded membership key generation", async function () {
|
it("should verify a proof with a seeded membership key generation", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const credential = rlnInstance.generateSeededIdentityCredential(seed);
|
const credential = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ describe("js-rln", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should generate the same membership key if the same seed is provided", async function () {
|
it("should generate the same membership key if the same seed is provided", async function () {
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const seed = "This is a test seed";
|
const seed = "This is a test seed";
|
||||||
const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
|
const memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
|
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
|
||||||
|
|||||||
11
src/index.ts
11
src/index.ts
@ -4,6 +4,7 @@ import {
|
|||||||
RLN_STORAGE_ABI,
|
RLN_STORAGE_ABI,
|
||||||
SEPOLIA_CONTRACT,
|
SEPOLIA_CONTRACT,
|
||||||
} from "./constants.js";
|
} from "./constants.js";
|
||||||
|
import { createRLN } from "./create.js";
|
||||||
import { Keystore } from "./keystore/index.js";
|
import { Keystore } from "./keystore/index.js";
|
||||||
import {
|
import {
|
||||||
IdentityCredential,
|
IdentityCredential,
|
||||||
@ -14,16 +15,8 @@ import {
|
|||||||
import { RLNContract } from "./rln_contract.js";
|
import { RLNContract } from "./rln_contract.js";
|
||||||
import { MerkleRootTracker } from "./root_tracker.js";
|
import { MerkleRootTracker } from "./root_tracker.js";
|
||||||
|
|
||||||
// reexport the create function, dynamically imported from rln.ts
|
|
||||||
export async function create(): Promise<RLNInstance> {
|
|
||||||
// A dependency graph that contains any wasm must all be imported
|
|
||||||
// asynchronously. This file does the single async import, so
|
|
||||||
// that no one else needs to worry about it again.
|
|
||||||
const rlnModule = await import("./rln.js");
|
|
||||||
return await rlnModule.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
createRLN,
|
||||||
Keystore,
|
Keystore,
|
||||||
RLNInstance,
|
RLNInstance,
|
||||||
IdentityCredential,
|
IdentityCredential,
|
||||||
|
|||||||
15
src/metamask.ts
Normal file
15
src/metamask.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
export const extractMetaMaskAccount =
|
||||||
|
async (): Promise<ethers.providers.Web3Provider> => {
|
||||||
|
const ethereum = (window as any).ethereum;
|
||||||
|
|
||||||
|
if (!ethereum) {
|
||||||
|
throw Error(
|
||||||
|
"Missing or invalid Ethereum provider. Please install a Web3 wallet such as MetaMask."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ethereum.request({ method: "eth_requestAccounts" });
|
||||||
|
return new ethers.providers.Web3Provider(ethereum, "any");
|
||||||
|
};
|
||||||
31
src/rln.ts
31
src/rln.ts
@ -1,10 +1,14 @@
|
|||||||
import type { IRateLimitProof } from "@waku/interfaces";
|
import type { IRateLimitProof } from "@waku/interfaces";
|
||||||
import init from "@waku/zerokit-rln-wasm";
|
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 { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js";
|
import { buildBigIntFromUint8Array, writeUIntLE } from "./byte_utils.js";
|
||||||
|
import { SEPOLIA_CONTRACT } from "./constants.js";
|
||||||
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
|
||||||
|
import { extractMetaMaskAccount } 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 "./witness_calculator.js";
|
import * as wc from "./witness_calculator.js";
|
||||||
import { WitnessCalculator } from "./witness_calculator.js";
|
import { WitnessCalculator } from "./witness_calculator.js";
|
||||||
|
|
||||||
@ -158,12 +162,39 @@ export function sha256(input: Uint8Array): Uint8Array {
|
|||||||
return zerokitRLN.hash(lenPrefixedData);
|
return zerokitRLN.hash(lenPrefixedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StartRLNOptions = {
|
||||||
|
/**
|
||||||
|
* If not set - will extract MetaMask account and get provider from it.
|
||||||
|
*/
|
||||||
|
provider?: ethers.providers.Provider;
|
||||||
|
/**
|
||||||
|
* If not set - will use default SEPOLIA_CONTRACT address.
|
||||||
|
*/
|
||||||
|
registryAddress?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export class RLNInstance {
|
export class RLNInstance {
|
||||||
|
private _contract: null | RLNContract = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private zkRLN: number,
|
private zkRLN: number,
|
||||||
private witnessCalculator: WitnessCalculator
|
private witnessCalculator: WitnessCalculator
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public get contract(): null | RLNContract {
|
||||||
|
return this._contract;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(options: StartRLNOptions = {}): Promise<void> {
|
||||||
|
const provider = options.provider || (await extractMetaMaskAccount());
|
||||||
|
const registryAddress = options.registryAddress || SEPOLIA_CONTRACT.address;
|
||||||
|
|
||||||
|
this._contract = await RLNContract.init(this, {
|
||||||
|
registryAddress,
|
||||||
|
provider,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
generateIdentityCredentials(): IdentityCredential {
|
generateIdentityCredentials(): IdentityCredential {
|
||||||
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
|
||||||
return IdentityCredential.fromBytes(memKeys);
|
return IdentityCredential.fromBytes(memKeys);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ 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.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
|
|
||||||
rlnInstance.insertMember = () => undefined;
|
rlnInstance.insertMember = () => undefined;
|
||||||
const insertMemberSpy = chai.spy.on(rlnInstance, "insertMember");
|
const insertMemberSpy = chai.spy.on(rlnInstance, "insertMember");
|
||||||
@ -36,7 +36,7 @@ describe("RLN Contract abstraction", () => {
|
|||||||
const mockSignature =
|
const mockSignature =
|
||||||
"0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c";
|
"0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c";
|
||||||
|
|
||||||
const rlnInstance = await rln.create();
|
const rlnInstance = await rln.createRLN();
|
||||||
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
||||||
const rlnContract = new rln.RLNContract(rlnInstance, {
|
const rlnContract = new rln.RLNContract(rlnInstance, {
|
||||||
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user