Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: simplify API of bootstrapping, connection to MetaMask #92

Merged
merged 7 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,16 @@ Browse http://localhost:8080 and open the dev tools console to see the proof bei
```js
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
Expand All @@ -94,6 +103,17 @@ let credentials = rlnInstance.generateSeededIdentityCredentials(seed);
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

```js
Expand Down
18 changes: 9 additions & 9 deletions src/codec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const EMPTY_PROTO_MESSAGE = {

describe("RLN codec with version 0", () => {
it("toWire", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -85,7 +85,7 @@ describe("RLN codec with version 0", () => {
});

it("toProtoObj", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -128,7 +128,7 @@ describe("RLN codec with version 0", () => {

describe("RLN codec with version 1", () => {
it("Symmetric, toWire", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -175,7 +175,7 @@ describe("RLN codec with version 1", () => {
});

it("Symmetric, toProtoObj", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -221,7 +221,7 @@ describe("RLN codec with version 1", () => {
});

it("Asymmetric, toWire", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -269,7 +269,7 @@ describe("RLN codec with version 1", () => {
});

it("Asymmetric, toProtoObj", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -318,7 +318,7 @@ describe("RLN codec with version 1", () => {

describe("RLN Codec - epoch", () => {
it("toProtoObj", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -374,7 +374,7 @@ describe("RLN codec with version 0 and meta setter", () => {
};

it("toWire", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down Expand Up @@ -422,7 +422,7 @@ describe("RLN codec with version 0 and meta setter", () => {
});

it("toProtoObj", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();
const credential = rlnInstance.generateIdentityCredentials();
const index = 0;
const payload = new Uint8Array([1, 2, 3, 4, 5]);
Expand Down
3 changes: 0 additions & 3 deletions src/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,12 @@ export class RLNEncoder implements IEncoder {

private async generateProof(message: IMessage): Promise<IRateLimitProof> {
const signal = toRLNSignal(this.contentTopic, message);

console.time("proof_gen_timer");
const proof = await this.rlnInstance.generateRLNProof(
signal,
this.index,
message.timestamp,
this.idSecretHash
);
console.timeEnd("proof_gen_timer");
return proof;
}

Expand Down
9 changes: 9 additions & 0 deletions src/create.ts
Original file line number Diff line number Diff line change
@@ -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();
}
6 changes: 3 additions & 3 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as rln from "./index.js";

describe("js-rln", () => {
it("should verify a proof", async function () {
const rlnInstance = await rln.create();
const rlnInstance = await rln.createRLN();

const credential = rlnInstance.generateIdentityCredentials();

Expand Down Expand Up @@ -59,7 +59,7 @@ describe("js-rln", () => {
}
});
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 credential = rlnInstance.generateSeededIdentityCredential(seed);

Expand Down Expand Up @@ -115,7 +115,7 @@ describe("js-rln", () => {
});

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 memKeys1 = rlnInstance.generateSeededIdentityCredential(seed);
const memKeys2 = rlnInstance.generateSeededIdentityCredential(seed);
Expand Down
11 changes: 2 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
RLN_STORAGE_ABI,
SEPOLIA_CONTRACT,
} from "./constants.js";
import { createRLN } from "./create.js";
import { Keystore } from "./keystore/index.js";
import {
IdentityCredential,
Expand All @@ -14,16 +15,8 @@ import {
import { RLNContract } from "./rln_contract.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 {
createRLN,
Keystore,
RLNInstance,
IdentityCredential,
Expand Down
15 changes: 15 additions & 0 deletions src/metamask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ethers } from "ethers";

export const extractMetaMaskAccount =
async (): Promise<ethers.providers.Web3Provider> => {
const ethereum = (window as any).ethereum;

Check warning on line 5 in src/metamask.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

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 changes: 31 additions & 0 deletions src/rln.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { IRateLimitProof } from "@waku/interfaces";
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 { SEPOLIA_CONTRACT } from "./constants.js";
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
import { extractMetaMaskAccount } 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";

Expand Down Expand Up @@ -48,7 +52,7 @@
* @returns RLNInstance
*/
export async function create(): Promise<RLNInstance> {
await (init as any)?.();

Check warning on line 55 in src/rln.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
zerokitRLN.init_panic_hook();
const witnessCalculator = await loadWitnessCalculator();
const zkey = await loadZkey();
Expand Down Expand Up @@ -158,12 +162,39 @@
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 {
private _contract: null | RLNContract = null;

constructor(
private zkRLN: number,
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 {
const memKeys = zerokitRLN.generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm
return IdentityCredential.fromBytes(memKeys);
Expand Down
4 changes: 2 additions & 2 deletions src/rln_contract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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.create();
const rlnInstance = await rln.createRLN();

rlnInstance.insertMember = () => undefined;
const insertMemberSpy = chai.spy.on(rlnInstance, "insertMember");
Expand Down Expand Up @@ -36,7 +36,7 @@ describe("RLN Contract abstraction", () => {
const mockSignature =
"0xdeb8a6b00a8e404deb1f52d3aa72ed7f60a2ff4484c737eedaef18a0aacb2dfb4d5d74ac39bb71fa358cf2eb390565a35b026cc6272f2010d4351e17670311c21c";

const rlnInstance = await rln.create();
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,
Expand Down
Loading