20 KiB
| title | name | category | tags | editor | contributors | |
|---|---|---|---|---|---|---|
| WAKU2-RLN-CONTRACT | Waku2 RLN Contract Specification | Standards Track |
|
Sergei Tikhomirov <sergei@status.im> |
Abstract
This document describes membership management within the Rate-Limiting Nullifer (RLN) smart contract, specifically addressing:
- membership-related contract functionality;
- suggested parameter values for the initial mainnet deployment;
- contract governance and upgradability.
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 membership set, 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 17/WAKU2-RLN-RELAY.
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). This document aims to outline the path to its mainnet deployment.
Contract overview
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.
The contract MUST provide the following membership-related functionalities (hereinafter, functionalities):
- register a membership;
- extend a membership;
- erase a membership;
- withdraw a deposit.
A membership holder is a role that grants special privileges in the context of membership management.
Each membership MUST have exactly one holder.
The holder role MUST be assigned at membership registration time to the sender
(msg.sender in Solidity semantics) of the registration transaction.
The sender of the registration SHOULD have RLN identity_commitment generated.
For more information see 32/RLN-V1.
Membership registration MAY be initiated by a different entity from the one that controls the RLN identity_secret,
which is associated with the respective RLN identity_commitment.
Therefore, the holder MAY be assigned to an blockchain address that does not control the identity_secret.
When authorizing membership-related requests,
the contract MUST distinguish between the holder and non-holders,
and MAY also implement additional criteria.
The contract MUST support transactions sent directly from externally-owned accounts (EOA). The contract MAY support transactions sent via a chain of contract calls, in which case the last contract in the call chain MAY be designated as the membership holder. The contract MAY also support meta-transactions sent via paymasters or relayers, which MAY require additional authentication-related logic.
Contract parameters and their RECOMMENDED values for the initial mainnet deployment are as follows:
| Parameter | Symbol | Value | Units |
|---|---|---|---|
| Epoch length | t_{ep} |
600 |
seconds |
| Maximum total rate limit of all memberships in the membership set | 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 active state duration | A |
180 |
days |
| Membership grace period duration | G |
30 |
days |
Membership price for 1 message per epoch for period A |
p_u |
0.05 |
USD |
| Accepted tokens | DAI |
The pricing function SHOULD be linear in the rate limit per epoch.
Note: epoch length means the same as period as defined in 17/WAKU2-RLN-RELAY.
This specification uses the term "epoch length" instead of "period" to avoid confusion with "grace period".
Membership lifecycle
Any existing membership MUST always be in exactly one of the following states:
- Active;
- GracePeriod;
- Expired;
- ErasedAwaitsWithdrawal;
- Erased.
The duration of each state MUST include the start timestamp.
The duration of each state MUST exclude the end timestamp.
For example, if a membership is registered at time 0,
and the active state duration A = 5,
the membership is considered to be Active at timestamps 0, 1, 2, 3, and 4.
At timestamp 5, the membership is considered to be in GracePeriod.
graph TD;
NonExistent --> |"register"| Active;
Active -.-> |"time A passed"| GracePeriod;
GracePeriod ==> |"extend"| Active;
GracePeriod -.-> |"time G passed"| Expired;
GracePeriod ==> |"erase"| ErasedAwaitsWithdrawal;
Expired --> |"erase"| ErasedAwaitsWithdrawal;
Expired --> |"reused by a new membership"| ErasedAwaitsWithdrawal;
ErasedAwaitsWithdrawal ==> |"withdraw"| Erased;
Different line types denote the types of state transitions:
| Line type | Triggered by | Requirements |
|---|---|---|
Thick (==) |
Transaction | MUST be initiable by the membership holder and MUST NOT be initiable by other users. |
Thin (--) |
Transaction | MAY be initiable by any user. |
Dotted (-.-) |
Time progression | MAY be applied lazily. |
Transaction-triggered state transitions MUST be applied immediately based on the blockchain tranaction time.
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 membership set according to the following table:
| State | Included in the membership set |
|---|---|
| Active | Yes |
| GracePeriod | Yes |
| Expired | Yes |
| ErasedAwaitsWithdrawal | No |
| Erased | No |
The holder assigned blockchian address MUST NOT be transferable to a different blockchain address. A user MAY use one blockchain address to manage multiple memberships. A user MAY use one Waku node1 to manage multiple memberships.
Contract functionalities
Availability of functionalities[^2] MUST be as follows:
| Active | GracePeriod | Expired | ErasedAwaitsWithdrawal | Erased | |
|---|---|---|---|---|---|
| Extend the membership | No | Yes (holder only) | No | No | No |
| Erase the membership | No | Yes (holder only) | Yes | No | No |
| Withdraw the deposit | No | No | No | Yes (holder only) | No |
[^2] Note: Sending a message is not present in this table because it is part of the RLN Relay protocol and not the contract. For completeness, we note that the membership holder MUST be able to send a message if their membership is Active, in GracePeriod, or Expired. Sending messages with Expired memberships is allowed, because the inclusion (Merkle) proof that the holder provides to RLN Relay only proves that the membership belongs to the membership set, and not that membership's state.
Register a membership
Membership registration is subject to the following requirements:
- The holder MUST specify the requested rate limit
rof a new membership at registration time2. - Registration MUST fail if
r < r_{min}orr > r_{max}. - The holder MUST make an tranasction to the contract, locking the deposit to register a membership.
- The amount of the deposit MUST depend on the specified rate limit.
- In case of a successful registration:
- the new membership MUST become Active;
- the new membership MUST have an active state duration
A > 0and a grace period durationG >= 0; - the current total rate limit MUST be incremented by the rate limit of the new membership.
Reusing the rate limit of Expired memberships
The rate limits are defined as follows:
R_{active}is the total rate limit of all Active memberships;R_{grace_period}is the total rate limit of all GracePeriod memberships;R_{expired}is the total rate limit of all Expired memberships.
The free rate limit that is available without reusing the rate limit of Expired memberships is defined as follows:
R_{free} = R_{max} - R_{active} - R_{grace_period} - R_{expired}
Membership registration is additionally subject to the following requirements:
- If
r <= R_{free}, the new membership MUST be registered (assuming all other necessary conditions hold).- The new membership MAY erase one or multiple Expired memberships and reuse their rate limit.
- If
r > R_{free}:- if
r > R_{free} + R_{expired}, registration MUST fail; - if
r <= R_{free} + R_{expired}, the new membership SHOULD be registered by reusing some Expired memberships.
- if
- The sender of the registration transaction MAY specify a list of Expired memberships to be erased and their rate limit reused.
- If any of the memberships in the list are not Expired, the registration MUST fail.
- If the list is not provided, the contract MAY use any criteria to select Expired memberships to reuse (see Implementation Suggestions).
- If the list is not provided, the registration MAY fail even if the membership set contains Expired membership that, if erased, would free up sufficient rate limit.
- If a new membership A erases an Expired membership B to reuse its rate limit:
- 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 holder of membership B can withdraw their deposit later.
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 holder MUST be able to extend their membership.
- Any user other than the membership holder MUST NOT be able to extend a membership.
- After an extension, the membership MUST become Active.
- After an extension, the membership MUST stay Active for time
g + A, wheregis the remaining time of the GracePeriod after the extension, andAis this membership's active state duration. - The extended membership MUST retain its original parameters, including active state duration
Aand grace period durationG, even if the global default values of such parameters for new memberships have been changed.
Withdraw the deposit
Deposit withdrawal is subject to the following conditions:
- The membership holder MUST be able to withdraw their deposit.
- Any user other than the membership holder 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 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 updated parameter values MUST apply to all new memberships. The parameters of existing memberships MUST NOT change if the Owner updates global parameters. The contract MAY restrict extensions for memberships created before the latest parameter update.
The Owner MUST be able to pause any of the functionalities (see definition above).
At some point, the Owner SHOULD renounce their privileges, and the contract becomes immutable. If further upgrades are necessary, a new contract SHOULD be deployed, and the membership set SHOULD be migrated.
Implementation Suggestions
Membership Set Implementation
The membership set MAY be implemented as a Merkle tree, such as an Incremental Merkle Tree (IMT).
Choosing Which Expired Memberships to Reuse
When registering a new membership, the contract needs to decide which Expired memberships, if any, to reuse. The criteria for this selection can vary depending on the implementation.
Key considerations include:
- To minimize gas costs, it's better to reuse a single high-rate membership rather than multiple low-rate ones.
- To encourage timely deposit withdrawals, it's better to reuse memberships that have been Expired for a long time.
Considerations for User-facing Applications
User-facing applications SHOULD suggest one or more rate limits (tiers) to simplify user selection among the following RECOMMENDED options:
20messages per epoch as low-tier;200messages per epoch as mid-tier;600messages per epoch as high-tier.
User-facing applications SHOULD save membership expiration timestamps 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 the membership is not extended during its GracePeriod,
it becomes Expired and can be erased at any time.
Users are expected to either extend their membership on time to avoid this risk,
or erase them and withdraw their deposit.
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 membership set.
Expired memberships are not immediately erased from the membership set. An Expired membership is erased in the following scenarios:
- the holder erases it to withdraw the deposit;
- a new membership erases it to free up rate limit during registration;
- a public function is called that erases Expired memberships from the given list.
Once in Erased or ErasedAwaitsWithdrawal state, 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 MiBfor 1000 users;732 MiBfor 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 membership set 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 proof that their membership belongs to the membership set. 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.
References
-
No Waku implementation supports managing multiple memberships from one node (as of August 2024). ↩︎
-
A user-facing application SHOULD suggest default rate limits to the holder (see Implementation Suggestions). ↩︎