mirror of https://github.com/waku-org/js-rln.git
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
|
||||
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
|
||||
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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", () => {
|
||||
it("should verify a proof", async function () {
|
||||
const rlnInstance = await rln.create();
|
||||
const rlnInstance = await rln.createRLN();
|
||||
|
||||
const credential = rlnInstance.generateIdentityCredentials();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
|
11
src/index.ts
11
src/index.ts
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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 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";
|
||||
|
||||
|
@ -158,12 +162,39 @@ export function sha256(input: Uint8Array): Uint8Array {
|
|||
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);
|
||||
|
|
|
@ -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");
|
||||
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue