Git Product home page Git Product logo

js-rln's Introduction

@waku/rln

This browser library enables the usage of RLN with Waku, as specified in the Waku v2 RLN Relay RFC.

Purpose

RLN Cryptography

The RLN cryptographic function are provided by zerokit. This is imported via the @waku/zerokit-rln-wasm dependencies which contains a WASM extract of zerokit's RLN functions.

Note that RLN Credentials generated with zerokit, and hence @waku/rln, are compatible with semaphore credentials.

Note that the WASM blob uses browser APIs, NodeJS is not supported.

Waku Interfaces

This library implements the IEncoder and IDecoder interfaces of js-waku.

This enables a seamless usage with js-waku applications, as demonstrated in the rln-js example.

Comparison to Existing Work

Rate-Limiting-Nullifier/rlnjs is an existing JavaScript / TypeScript library that already provides RLN cryptographic functionalities for the browser.

The core difference is that @waku/rln uses zerokit for cryptography and provide opinionated interfaces to use RLN specifically in the context of Waku.

Install

npm install @waku/rln

# or with yarn

yarn add @waku/rln

Or to use ESM import directly from a <script> tag:

<script type="module">
  import * as rln from "https://unpkg.com/@waku/[email protected]/bundle/index.js";
</script>

Running example app

git clone https://github.com/waku-org/js-rln

cd js-rln/

npm install

cd example/

npm install  # or yarn

npm start

Browse http://localhost:8080 and open the dev tools console to see the proof being generated and its verification

Usage

Initializing the Library

import * as rln from "@waku/rln";

const rlnInstance = await rln.createRLN();

Starting RLN to listen to a contract

import * as rln from "@waku/rln";

const rlnInstance = await rln.createRLN();
await rlnInstance.start(); // will use default Sepolia contract

Generating RLN Membership Credentials

let credentials = rlnInstance.generateIdentityCredentials();

Generating RLN Membership Keypair Using a Seed

This can be used to generate credentials from a signature hash (e.g. signed by an Ethereum account).

let credentials = rlnInstance.generateSeededIdentityCredentials(seed);

Adding Membership Keys Into Merkle Tree

rlnInstance.insertMember(credentials.IDCommitment);

Registering Membership on a contract

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

// prepare the message
const uint8Msg = Uint8Array.from(
  "Hello World".split("").map((x) => x.charCodeAt())
);

// setting up the epoch (With 0s for the test)
const epoch = new Uint8Array(32);

// generating a proof
const proof = await rlnInstance.generateProof(
  uint8Msg,
  index,
  epoch,
  credentials.IDSecretHash
);

Verifying a Proof

try {
  // verify the proof
  const verificationResult = rlnInstance.verifyProof(proof, uint8Msg);
  console.log("Is proof verified?", verificationResult ? "yes" : "no");
} catch (err) {
  console.log("Invalid proof");
}

Updating Circuit, Verification Key and Zkey

The RLN specs defines the defaults. These values are fixed and should not change. Currently, these resources are being used. If they change, this file needs to be updated in resources.ts which contains these values encoded in base64 in this format:

const verification_key = "...";
const circuit = "..."; // wasm file generated by circom
const zkey = "...";
export {verification_key, circuit, zkey};

A tool like GNU's base64 could be used to encode this data.

Updating Zerokit

  1. Make sure you have NodeJS installed and a C compiler
  2. Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
  1. Compile RLN for wasm
git clone https://github.com/vacp2p/zerokit
cd zerokit/rln-wasm
wasm-pack build --release
  1. Copy pkg/rln* into src/zerokit

Bugs, Questions & Features

If you encounter any bug or would like to propose new features, feel free to open an issue.

For more general discussion, help and latest news, join Vac Discord or Telegram.

License

Licensed and distributed under either of

or

at your option. These files may not be copied, modified, or distributed except according to those terms.

js-rln's People

Contributors

adklempner avatar danisharora099 avatar fryorcraken avatar richard-ramos avatar vpavlin avatar weboko avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

Forkers

vivianjeng

js-rln's Issues

feat: wasm caching

As of now .wasm files are retrieved by using fetch API which relies on server response whether to cache it and how long.

const response = await fetch(url);

const response = await fetch(url);

Problem with it that on simple or misconfigured servers it has no Cache policy and thus getting fetched always.
To fix it we should find a way to enforce caching.

First approach is to leverage IndexDB and put bytes into browser's memory with time-to-live restriction.
Dynamic import won't work here as it works only with ES modules.
LocalStorage cannot be used because of memory limitation.

Objectives

  • use IndexDB to save bytes of wasm;
  • investigate alternative approaches;

RLN in the Browser

Meta issue: vacp2p/research#129

Breakdown

We want (1) , (2) and (3) for DevCon. Order on the other user stories in loose.

1. As a user, I want to join the group

  • Generate RLN credentials
    • Integrate zerokit in js-rln
  • Call the contract to join the group
    • Using web wallet

2. As a user, I want to send a message that includes a proof

Would probably use Light Push (but does not really matter)

  • Retrieve state of group (must be latest to be secured)
    • Call contract (every time?)
    • Listen to contract events
  • Generate the proof using zerokit
  • Attach the proof to Waku Message
    • Need to design API/interface so messages are attached every time relay.send or lightpush.push is used.

3. As a end user, I want to use RLN in the web

4. As a rln-js user, I want to my RLN credentials to be securely saved in the browser storage

  • Credentials are saved in the browser

  • Credentials derived from Ethereum Wallet signature

  • This is a nice feature for web-chat users

  • Also an example for developers

  • See how eth-pm saves encryption key

5. As a rln-js user, I want to be able to export and import my RLN credentials

  • Can import text credentials
  • Can export credentials by copy-pasting
  • Can export/import credentials in file format, blocked by vacp2p/rfc#543

6. As a user, I want my node to check proofs on messages

  • Expose interface to verify proof
  • Provide an easy API to automatically drop messages with invalid proofs
  • Provide an easy API to track seen epochs

The message could be received via store, filter or relay.

Note: This could be pass as a generalized "validation callback" method to filter and store. Maybe some helper to avoid too much code.

7. As a user, I want Waku Relay to check proofs on messages and drop them if invalid

Useful once Relay scalability #905 is ready.
Preferably use pubsub validator.

8. As a user, I want to be able to slash spammers

Review if slashing makes sense in the browser once #905 is progressed.

Other requirements

  • create js-rln repo under waku-org
  • Use Apache 2.0 - MIT Licence
  • Use similar tech stake than js-waku:
    • language: TypeScript
    • ESM Only package
    • package manager: npm or pnpm
    • Docs: Typedoc
    • TypeScript to JavaScript: tsc or parcel or vite
    • Bundling (to enable direct usage in <script> tag): rollup or vite
    • Format: prettier + eslint
    • Testing:
      • mocha/chai is used for js-waku, ok to consider more modern alternative, consider aegir
      • Browser run: karma, ok to consider better alternative or aegir (js-waku uses webpack for karma, best to try rollup or whichever bundler is used for prod code).
      • NodeJS: 16 or 18 (js-waku uses 16, tests can't run with 18 due to ESM loader changes, fine to use 18 and backport config fix to js-waku)
    • Dependencies: aim to use similar deps to js-waku for similar purposes (ie, noble, etc). Avoid polyfills.

js-rln keystore is not compatible with nwaku

When generating a keystore with js-rln (using the frontend repo) and then importing it into nwaku it fails to load.

The error that nwaku raises:

Error: unhandled exception: Trying to access value with err Result: keystore error: OS specific error: Incorrect JSON kind. Wanted '{JNull, JString}' in '.membershipContract.chainId' but got 'JInt'. [ResultDefect]

Any idea?

Persist Merkle tree, valid roots and last sync block

Summary

Merkle tree, valid roots and last sync blocks are necessary to generate and validate proof.
This information is retrieved from the blockchain.

Accessing the blockchain represent several challenges:

  • Web3 wallet/ Web3 RPC Provider needs to be present, logged in and ready. Even if no "write" action is necessary it's unsure that read access is always available. Meaning user may have to log in.
  • An extra query before the node can start sending or verifying messages

Proposed solution

Cache the information locally (local storage?). The information is publicly available on the blockchain and does not need to be protected.

Note

  • Need to verify some assumptions. If using a wallet extension or wallet connect, can the Web3 Provider be used to read smart contract data without the user being logged in?
  • Should we look into and provide alternative to the usual Web3 Provider in terms of reading abilities.
  • There are probably some unknowns here (does sled-db run in wasm? should we create an specific persistance mechanism?).
  • This may be a nice to have. We can review and descope later.

feat: better developer experience

This issue is to track improvements to developer experience.

Basic list of improvements:

  • #69
  • expose in @waku/sdk;
  • update js-waku-examples/rln-js;
  • provide basic examples for docs(for @LordGhostX);

Other improvements will be added later after receiving feedback.

feat: expose roots from zerokit

Problem

@waku/rln maintains its roots here by essentially relying on zerokit to give it current root here.

These roots lately are used to verify a proof - which is done again inside of zerokit here.

Solution

Expose from wasm following API:

  • getRoots - gives requested amount of latest roots (default to 5);
  • remove a need to pass roots to verify as they are already inside of zerokit;

getRoots still can be exposed as might be used later or by a consumer.

fix(rln): convert id commitments to uint256 from LE on register

Problem

Zerokit generates and serializes id keys and the corresponding id commitments in little endian. The membership smart contract, instead, expects id commitments to be uint256.

This means that when registering an id commitment generated from zerokit, js-rln should ensure that the value registered corresponds to the uint256 conversion from the id commitment in little endian.

This is not the case at the moment, where no endianess conversion is implemented and commitments are registered "as they are" to the contract. This has the consequences that proofs might result invalid when verified from other implementations.

Example

Zerokit generates

Key: b20ea77f8d11182df896021c9f1c6f82e21694a44ecf1b030461a5b4fc581817
Commitment: b69a4ee2e32152c4b4bf3b677d160b9d19d9ab043a281dd39cca84a6eadafa11

both visualized in their little-endian representation. The value that should be registered to the smart contract is

Commitment: 0x11fadaeaa684ca9cd31d283a04abd9199d0b167d673bbfb4c45221e3e24e9ab6

and not 0xb69a4ee2e32152c4b4bf3b677d160b9d19d9ab043a281dd39cca84a6eadafa11 as happens now.

Acceptance criteria

  • Fix endianess conversion;
  • (Nice to have) visualize Key and Commitment as the uints they represents (i.e. with bytes reversed).

Contract handling helper

Problem

Retrieving membership details and adding them to the RlnInstance is something that any user of js-rln should do and are likely to copy-paste from https://github.com/waku-org/js-waku-examples/blob/b6d9942b113e51298ba9e4405997c3dc52bec908/rln-js/index.html#L259

Same re listening to contract event add new memberships to RlnInstance.

Same re registering membership in contract: https://github.com/waku-org/js-waku-examples/blob/b6d9942b113e51298ba9e4405997c3dc52bec908/rln-js/index.html#L287

Solution

Provide a function helper that does that.

2 possibilities:

  1. Function helper uses ethers, hence we can set ethers as an optional dependency
  2. Function helper gets passed an object that implement an interface so to give the choice to the user between ethers and web3js.

Not sure (2) is straight forward, might be easier to just go with (1) for now

Acceptance Criteria

  • User can call 1 function to retrieve members from contract, add them to RlnInstance and add new members to RlnInstance by listening to contract
  • User can call 1 function to add credentials to contract.

feat: js-rln optimizations

This is a feature request

Problem

@waku/rln is slow. This investigation is done without measuring contract implications as for now it is lower than RLN problems
It takes ~20s to initialize RLN on fast internet and ~40s on 3g internet

Following breakdown will be done on 3g internet

Breakdown:

  1. init rln_wasm_bg.wasm: ~6931.32 ms
  2. zeroRLN hook: 0.034 ms
  3. init rln.wasm(witness calculator): ~3676.94 ms
  4. init rln_final.zkey: ~18692.76 ms
  5. init zerokitRLN.newRLN: 12444.46 ms

What is interesting is that (3) and (4) take significantly less time on fast internet.
(3) and (4) happens on slow internet and most likely because of the size - (3) is 1.2 mb and (4) is 3.2 mb

Further breakdown for (1):

  • fetching of the file is more or less the same regarding internet: 730 ms
  • WebAssembly.instantiateStreaming of the rln_wasm_bg.wasm takes: 6496.60 ms

Further breakdown for (3):

  • fetching wasm file: 572.31 ms
  • reading the file: 3102.37 ms - it seems easy to mitigate

Further breakdown for (4):

  • fetching is more or less the same: 575.53 ms
  • reading the file: 18112.36 ms - it seems easy to mitigate

Further breakdown for (5):

  • nothing to breakdown as it is proxy around zerokit operation - 12444.46 ms
  • this takes the same time on any internet and is the most critical time consumer in our case

Proposed Solutions

  • (1) can be mitigated by optimizingthe artifact (zerokit side);
  • (3) and (4) - try to use instantiateStreaming instead of reading bytes + check if possible to shrink files;
  • (5) should check if operation can be optimized or parameters(zkey/vkey) should be changed, in future should be improved with new version;

Notes

Ref: waku-org/research#45

feat: use rln-js

To reduce problematic dependency on wasm - investigate if it is possible to use rln-js instead of it.

Setup tests with NodeJS

Testing with Karma takes time locally as it needs to bundle every time.

Having the option to use node would make the feedback loop shorter when developing.

Reduce chances of loosing RLN credentials.

Problem

Credentials are purely generated from entropy, meaning that if a user lose access to their key, then they lose access to their RLN membership.

Proposed Solution

A. Seed derivation from signature

  1. Make user sign message M: The signature of this message will be used to generate your RLN credentials. Ensure that this signature is only shared with the RLN dApp.
  2. Hash signature
  3. Use hash as seed for RLN credentials generation

As long as the user has access to their Ethereum private key, they can regenerate their credentials.

They also need to be able to retrieve their membership id.

To do so:

  1. Look at past transactions sent from this wallet
  2. Find transaction sent to RLN contract
  3. Check receipt to get the membership id

B. Secure storage of credentials in cryptkey-like extension

Another strategy is to store the credentials in an extensions.

Acceptance Criteria

  • Implement A

Note

Zerokit now supports seeded keygen: vacp2p/zerokit#56

Strategy B can be done at a later stage.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.