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

Add WAKU2-RLN-CONTRACT spec for mainnet deployment #30

Merged
merged 28 commits into from
Aug 20, 2024
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d2d5352
add first version of WAKU2-RLN-CONTRACT spec
s-tikhomirov Aug 2, 2024
7ea898b
Apply suggestions from code review
s-tikhomirov Aug 5, 2024
7df465d
address some suggestions
s-tikhomirov Aug 5, 2024
d84ac43
remove future work; minor fixes
s-tikhomirov Aug 7, 2024
5f663e3
clarify path to immutability
s-tikhomirov Aug 7, 2024
d61d6b0
major edit; add table of available functionalities
s-tikhomirov Aug 8, 2024
471b479
add state transition diagram
s-tikhomirov Aug 8, 2024
c816007
major edit incl RFC-speak
s-tikhomirov Aug 9, 2024
4a685cd
specify counting total rate limit
s-tikhomirov Aug 9, 2024
9a6114f
split Erased state on whether deposit is withdrawn
s-tikhomirov Aug 9, 2024
03e868c
edit, unify terminology
s-tikhomirov Aug 12, 2024
6d3b946
rename membership owner to holder (avoid confusion with contract owner)
s-tikhomirov Aug 12, 2024
1fe5dfc
structure edits
s-tikhomirov Aug 12, 2024
7ff2c60
minor edits
s-tikhomirov Aug 12, 2024
1d52731
minor fix
s-tikhomirov Aug 12, 2024
9eab39c
fix typo
s-tikhomirov Aug 13, 2024
3cac3f0
minor edits
s-tikhomirov Aug 14, 2024
fead1f5
address some suggestions
s-tikhomirov Aug 15, 2024
2f45184
Update standards/core/rln-contract.md
s-tikhomirov Aug 15, 2024
554758a
clarify pricing parameter
s-tikhomirov Aug 15, 2024
50d7272
Update standards/core/rln-contract.md
s-tikhomirov Aug 16, 2024
2c65185
separate recommendations for contract vs app devs
s-tikhomirov Aug 16, 2024
a178c48
change price of unit rate limit to 0.05 USD
s-tikhomirov Aug 16, 2024
1d97db4
raise max total rate limit to 160 msg/epoch
s-tikhomirov Aug 19, 2024
9caa26c
less strict MUST usage (referring to RLN Relay spec)
s-tikhomirov Aug 19, 2024
ecdd7b3
define holder/keeper distinction
s-tikhomirov Aug 19, 2024
22d86f3
limit the rate limit of one membership to 600 msg / epoch
s-tikhomirov Aug 19, 2024
0d60b6f
style edits
s-tikhomirov Aug 19, 2024
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
310 changes: 310 additions & 0 deletions standards/core/rln-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
---
title: WAKU2-RLN-CONTRACT
name: Waku2 RLN Contract Specification
category: Standards Track
tags: [waku/core-protocol]
editor: Sergei Tikhomirov <[email protected]>
contributors:
---

## Abstract

This document describes membership management within the RLN smart contract, specifically addressing:
- membership-related contract functionality;
- suggested parameter values for the initial mainnet deployment;
- contract governance and upgradability.

Currently, this document focuses solely on membership-related functionality.
It might later evolve into a comprehensive contract specification.

As of August 2024, RLN is deployed only on Sepolia testnet ([source code](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol)).
This document aims to outline the path to its mainnet deployment.

## Syntax

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).

## Background

Rate-Limiting Nullifier (RLN) is a Zero-Knowledge (ZK) based gadget used for privacy-preserving rate limiting in Waku.
The RLN smart contract (referred to as "the contract" hereinafter) is the central component of the RLN architecture.
The contract stores the RLN tree, which contains all current memberships.
Users interact with the contract to manage their memberships
and obtain the necessary data for proof generation and verification.

Message transmission is handled by Waku RLN Relay nodes.
The sender of a message MUST prove its validity according to RLN requirements.
RLN Relay nodes MUST NOT relay invalid messages.
For the full specification of RLN Relay, see See [17/WAKU2-RLN-RELAY](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md).

## Contract overview

The contract MUST provide the following functionalities:
- register a membership;
- extend a membership;
- withdraw a deposit.
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved

A membership _holder_ is the entity that controls the secret associated with the respective RLN commitment.
A membership _keeper_ is the entity that controls the Ethereum address used to register that membership.
The holder and the keeper MAY be different entities for the same membership.
When authorizing membership-related requests,
the contract SHOULD distinguish between the keeper and non-keepers,
and MAY also use additional criteria.

Contract parameters and their RECOMMENDED values for the initial mainnet deployment are as follows:

| Parameter | Symbol | Value | Units |
| ------------------------------------------------------- | --------- | -------- | -------------------- |
| Epoch length | `epoch` | `10` | minutes |
| Maximum total rate limit of all memberships in the tree | `R_{max}` | `160000` | messages per `epoch` |
| Minimum rate limit of one membership | `r_{min}` | `20` | messages per `epoch` |
| Maximum rate limit of one membership | `r_{max}` | `600` | messages per `epoch` |
| Membership price for `1` message per epoch | `p_u` | `0.05` | `USD` |
| Membership expiration term | `T` | `180` | days |
| Membership grace period | `G` | `30` | days |
| Accepted tokens | | `DAI` | |

The pricing function SHOULD be linear in the rate limit per epoch.

## Membership lifecycle

Any existing membership MUST always be in exactly one of the following states:
- _Active_;
- _GracePeriod_;
- _Expired_;
- _ErasedAwaitsWithdrawal_;
- _Erased_.

```mermaid
graph TD;
NonExistent --> |"register"| Active;
Active -.-> |"time T passed"| GracePeriod;
GracePeriod --> |"extend"| Active;
GracePeriod -.-> |"time G passed"| Expired;
GracePeriod --> |"withdraw"| Erased;
Expired --> |"withdraw"| Erased;
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved
Expired --> |"another membership reuses slot"| ErasedAwaitsWithdrawal;
ErasedAwaitsWithdrawal --> |"withdraw"| Erased;
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved

```

State updates triggered by a transaction (e.g., from _GracePeriod_ to _Active_ as a result of `extend`) MUST be applied immediately.
State updates defined by time progression (e.g., from _GracePeriod_ to _Expired_ after time `G`) MAY be applied lazily.
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved

When handling a membership-specific transaction, the contract MUST:
- check whether the state of the involved membership is up-to-date;
- if necessary, update the membership state;
- process the transaction in accordance with the updated membership state.

Memberships MUST be included in the RLN tree according to the following table:

| State | Included in the RLN tree |
| ------------------------ | ------------------------ |
| _Active_ | Yes |
| _GracePeriod_ | Yes |
| _Expired_ | Yes |
| _ErasedAwaitsWithdrawal_ | No |
| _Erased_ | No |

Memberships MUST NOT be transferable.
A user MAY use one Ethereum address to manage multiple memberships.
A user MAY use one Waku node[^1] to manage multiple memberships.

[^1]: No Waku implementation supports managing multiple memberships from one node (as of August 2024).


## Contract functionalities

Availability of membership-specific functionalities[^2] MUST be as follows:

| | Active | GracePeriod | Expired | ErasedAwaitsWithdrawal | Erased |
| --------------------- | ------ | ----------- | ------- | ---------------------- | ------ |
| Send a message | Yes | Yes | Yes | No | No |
| Extend the membership | No | Yes | No | No | No |
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved
| Withdraw the deposit | No | Yes | Yes | Yes | No |
s-tikhomirov marked this conversation as resolved.
Show resolved Hide resolved

[^2]: Sending a message is included here for completeness, although it is part of the RLN Relay protocol and not the contract.

### Register a membership

Membership registration is subject to the following conditions:
- If there are _Expired_ memberships in the RLN tree, the new membership MUST overwrite an _Expired_ membership.
- The new membership SHOULD overwrite the membership that has been _Expired_ for the longest time.
- If a new membership A overwrites an _Expired_ membership B:
- membership B MUST become _ErasedAwaitsWithdrawal_;
- the current total rate limit MUST be decremented by the rate limit of membership B;
- the contract MUST take all necessary steps to ensure that the keeper of membership B can withdraw their deposit later.
- Registration MUST fail if the total rate limit of _Active_, _GracePeriod_, and _Expired_ memberships, including the one being created, would exceed `R_{max}`.
- Registration MUST fail if the requested rate limit for a new membership is lower than `r_{min}` or higher than `r_{max}`.
- The keeper MUST lock up a deposit to register a membership.
- The keeper MUST specify the rate limit[^3] of a membership at registration time.
- The size of the deposit MUST depend on the specified rate limit.
- In case of a successful registration:
- the new membership MUST become _Active_;
- the current total rate limit MUST be incremented by the rate limit of the new membership.
- A membership MUST have an expiration time `T` and a grace period `G`.

[^3]: A user-facing application SHOULD suggest default rate limits to the keeper (see Implementation Suggestions).

### Extend a membership

Extending a membership is subject to the following conditions:
- The extension MUST fail if the membership is in any state other than _GracePeriod_.
- The membership keeper MUST be able to extend their membership.
- Any user other than the membership keeper MUST NOT be able to extend a membership.
- After a successful extension, the membership MUST become _Active_.

### Withdraw the deposit

Deposit withdrawal is subject to the following conditions:
- The membership keeper MUST be able to withdraw their deposit.
- Any user other than the membership keeper MUST NOT be able to withdraw its deposit.
- A deposit MUST be withdrawn in full.
- A withdrawal MUST fail if the membership is not in _GracePeriod_, _Expired_, or _ErasedAwaitsWithdrawal_.
- A membership MUST become _Erased_ after withdrawal.

## Governance and upgradability

At initial mainnet deployment, the contract MUST have an _Owner_.
The _Owner_ MUST be able to change the values of all contract parameters.
The _Owner_ MUST be able to pause any of the following contract functionalities:
- register a membership;
- extend a membership;
- withdraw a deposit.

At some point, the _Owner_ SHOULD renounce their privileges,
and the contract MUST become immutable.
If further upgrades are necessary,
a new contract SHOULD be deployed,
and the membership set SHOULD be migrated.

## Implementation Suggestions

User-facing applications SHOULD suggest one or more rate limits (tiers) to simplify user selection among the following RECOMMENDED options:
- `20` messages per epoch as low-tier;
- `200` messages per epoch as mid-tier;
- `600` messages per epoch as high-tier.

User-facing applications SHOULD save membership expiration dates in a local keystore during registration,
and notify the user when their membership is about to expire.

## Q&A

### Why can't I withdraw a deposit from an _Active_ membership?

The rationale for this limitation is to prevent a usage pattern where users make deposits and withdrawals in quick succession.
Such a pattern could lead to network instability and should be carefully considered if deemed desirable.

### Why can't I extend an _Active_ membership?

Memberships can only be extended during _GracePeriod_.
Extending an _Active_ membership is not allowed.
The rationale is to make possible parameter changes that the contract _Owner_ might make (e.g., for security reasons) applicable to most memberships.

### What if I don't extend my membership within its _GracePeriod_?

If a user does not extend their membership during the _GracePeriod_,
they risk having their _Expired_ membership overwritten.
Generally, users are expected to either extend their membership or withdraw their deposit to avoid this risk.

### Can I send messages when my membership is _Expired_?

An _Expired_ membership allows sending messages for a certain period.
The RLN proof that message senders provide to RLN Relay nodes does not prove the state of the membership,
only its inclusion in the tree.

_Expired_ memberships are not proactively erased from the tree.
An _Expired_ membership is erased only when a new membership overwrites it or when its deposit is withdrawn.
Once erased (i.e., _Erased_ or _ErasedAwaitsWithdrawal_), the membership can no longer be used to send messages.

### Will my deposit be slashed if I exceed the rate limit?

This specification does not include slashing.
The deposit's current purpose is purely to protect the network from denial-of-service attacks through bandwidth capping.

### Do I need an extra deposit to extend my membership?

Membership extension requires no additional deposit.
The opportunity cost of locked-up capital and gas fees for extension transactions make extensions non-free,
which is sufficient for the initial mainnet deployment.

### Why this particular epoch length?

Epoch length is a global parameter defined in the contract.
Rate limits are defined in terms of the maximum allowed messages per epoch.

There is a trade-off between short and long epochs.
Longer epochs accommodate short-term usage peaks better,
but they increase memory requirements for RLN Relay nodes.
An epoch length of `10` minutes was chosen as a reasonable middle ground.

Each message contains a nullifier that proves its validity in terms of RLN.
Each RLN Relay node must store a nullifier log for the current epoch in memory.
A nullifier plus metadata is `128` bytes per message.
With a `10`-minute epoch, a high-tier user with a `1` message per second rate limit generates up to `600 * 128 / 1024 = 75 KiB` of nullifier log data per epoch.
This equates to, approximately:
- `73 MiB` for 1000 users;
- `732 MiB` for 10 thousand users.

### Why is there a cap on the total rate limit?

Total network bandwidth is a limited resource.
To avoid overstretching the network's capabilities for the initial mainnet deployment,
we define a cap `R_{max}` on the total rate limit.

### Why is there a minimum rate limit?

The minimum rate limit `r_{min}` prevents an attack where a large number of tiny memberships cause RLN tree bloat.

### Why is there a maximum rate limit?

The maximum rate limit `r_{max}` prevents any single actor from consuming an excessive portion of the total available rate limit.

However, it is still possible for an attacker to register multiple Ethereum addresses,
and occupy a significant portion of the total rate limit through several memberships.

### Are there bulk discounts for high-rate memberships?

For the initial mainnet deployment, no bulk discounts are offered.
Membership price is linearly proportional to its rate limit.
We choose this pricing scheme for simplicity.
Future work may explore alternative pricing schemes that balance efficiency with centralization risk.

### Why only accept DAI?

When choosing a token to accept, we considered the following criteria:
- a stablecoin, as USD-denominated pricing is familiar for users and requires no oracle;
- popular with high liquidity;
- decentralized;
- reasonably good censorship-resistance.

Based on these criteria, we chose DAI for the initial mainnet deployment.
Other tokens may be added in the future.

## Security / Privacy Considerations

Issuing membership-specific transactions,
such as membership extensions and deposit withdrawals,
publicly associates a membership with an Ethereum address.
However, this association does not compromise the privacy of the relayed messages,
as the protocol does not require the sender to disclose their specific membership to RLN Relay nodes.

To generate an RLN proof, a message sender must obtain a Merkle proof confirming that their membership belongs to the RLN tree.
This proof can be requested directly from the contract.
Requesting the proof through a third-party RPC provider could compromise the sender's privacy,
as the provider might link the requester's Ethereum address, their RLN membership, and the corresponding API key.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

## References

- [Rate-Limiting Nullifier](https://rate-limiting-nullifier.github.io/rln-docs/)
- [11/WAKU2-RELAY](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md)
- [17/WAKU2-RLN-RELAY](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md)





Loading