-
Notifications
You must be signed in to change notification settings - Fork 1
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
RLN on mainnet - Implementation Roadmap #11
Comments
To reduce gas cost and need to interact with the smart contract, my suggestion would be to not have function used to update the membership state, but instead calculate the state (for Active/GracePeriod/Expired). Since we store the timestamp in which a membership is supposed to end, it should be trivial to determine whether a membership is in any of those states. |
Could we skip storing this data? (to reduce gas cost). If this data is informative, the user could obtain such info from the event |
Looking at the specs in https://github.com/waku-org/specs/blob/master/standards/core/rln-contract.md:
What happens if after overwriting the expired membership B, the rate limit available is still insufficient to register the new membership A. Should it attempt to overwrite more expired memberships? EDIT: added a possible solution to this problem i here: https://github.com/waku-org/waku-rlnv1-contract/blob/d0472967827cb58ca1ad74d9a037210a5d204ee1/contracts/Membership.sol#L115-L146 in which i loop over expired memberships. Since this can be potentially an expensive operation, I was thinking to maybe add a |
I agree that we should not proactively apply time-related state updates. In the sentense you quote, I meant an internal (or private?) function that would be called from within other (public) functions that depend on the state of the membership in question. For example:
Does this sound reasonable? Or are you suggesting we don't even store the state explicitly, and only calculate it just-in-time when needed? |
My reasoning for storing the "assigned at" timestamp was that it is needed for tree slot reusal. As per specification:
Thus the contract needs to know, for each Two thoughts here:
|
Great question. From the storage-saving perspective, it doesn't matter whether we overwrite an old or a recently expired membership. The rationale to overwrite the oldest one was to incentivize user B to withdraw: as time progresses, their expired membership becomes more and more likely to get overwritten. However, one could assume that if users forget to withdraw their membersips, it's not because they want to continue using their rate limit beyond its expiry, but rather because they don't care enough to send a withdrawal transaction to retrieve such a small deposit (it might not even recoup the gas fees: a minimal deposit is 1 USD; fees on an L2 should generally be lower, but who knows). I would suggest the following:
What would be most efficient way to choose memberships to overwrite then? I see these options:
Longer term, I could envision a mechanism where user A is incentivized (with a lower registration price) to opt-in to overwriting old slots (and thus consume more gas). Otherwise, we have an undesired consequence that the increased gas cost of looking up expired membership(s) is borne by whoever registers a new membership, and not by the holder of the expired membership who did not withdraw their deposit timely. However, for me this sounds like an overkill for the initial implementation. |
I suggest to not store state explicitly, since we still need to read some information from the membership like The gas cost for reading from storage is What I propose instead is calculate it like this. Do note that I'm assuming the grace period can be updated by the contract owner and would not affect existing memberships (this is something i want to evaluate with you,): MembershipData memory member = memberships[someId];
// If we need to determine if a membership is expired
bool isExpired = member.expirationDate + member.gracePeriod > block.timestamp;
// If we need to determine if a membership is in grace period
uint256 now = block.timestamp;
uint256 expirationDate = member.expirationDate;
bool isGracePeriod = now >= expirationDate && now <= expirationDate + member.gracePeriod; For the other states we can determine them based on existence of data:
|
In the contract I'm proposing we can identify the expired memberships by storing all the memberships in a double-linked list. The
|
I like this approach. I was applying the second option greedily looking for memberships but indeed this can end up being costly, and also have a separate function anyone could call to expire a list of memberships. I can try combining this into the behaviour you suggest. The user could then to obtain offchain the list of expired memberships and choose those whose total rate limit is enough to cover the rate limit they wish to acquire
This part IMO can be skipped, that way we enforce the maximum total rate limit |
Not sure I understand this part: do you mean "erase expired memberships from the tree"?
Hm, we do need an option to use a new slot, at least for the very first membership. What am I missing here? |
I've created a PR waku-org/specs#34 where I make changes to the spec that stem from ongoing discussions. The most substantial change so far concerns the logic aroung Expired memberships' slot reuse. In certain scenarios it may be quite gas-intensive. On the other hand, we're unlikely to hit the limit of tree capacity (2^20 elements) any time soon, whereas gas costs for slot reuse would (unnecesarily?) burden users who register new memberships. I'd suggest this change to the spec (see commit):
|
This document outlines the steps to implement the mainnet-ready RLN smart contract as in the specification based on its current implementation deployed on testnet.
Implementation Stages
We suggest implementing the specification in multiple stages. The primary goal of this stage-based approach is to limit the scope of each stage, simplifying both implementation and testing.
Each stage description includes a membership state transition diagram representing the contract's state at the end of that stage (if it differs from the previous stage). After completing all stages, the diagram must match the one defined in the specification.
Note: membership states don't have to be stored explicitly. It is up to the implementation to either store membership states or derive the current state just-in-time based on timestamps and other data. The only requirement is that the contract behaves in the way described in the specification.
Add Rate-Limiting Constraints
Implement rate-limiting constraints on individual memberships and the entire membership tree.
Suggested steps:
Stub Membership State Management
Introduce the necessary fields and data structures for storing membership states and related data.
Each membership must store:
msg.sender
during registration).Assign the
Active
state to a membership upon successful registration.Time-Related Membership State Updates
Membership state transitions can result from either time progression or user transactions. This stage focuses on time-based state updates.
Two time-based state transitions are implemented:
Active
toGracePeriod
.GracePeriod
toExpired
.Suggested steps:
msg.sender
is the membership holder.onlyActiveState
,onlyGracePeriodState
).Membership Extensions
A membership holder may extend their membership by sending an extension transaction. An extension returns the membership to the
Active
state and is only possible from theGracePeriod
state. This stage excludes deposit-related conditions.Suggested steps:
extend
function.msg.sender
is the membership holder.GracePeriod
, and sufficient time has passed for its transition toExpired
, update the state toExpired
.GracePeriod
.Add Membership Deposit Lock-Up and Withdrawal
To register a membership, a user must lock up a deposit. When a membership enters the
GracePeriod
, the holder may choose to either extend it or withdraw the deposit.Note: In the final state transition diagram, a
withdraw
action fromGracePeriod
leads to theErased
state instead ofExpired
. However, since membership erasure is not yet implemented at this stage, theExpired
state is temporarily reused as the destination for both transitions fromGracePeriod
.Suggested steps:
withdraw
function for deposit withdrawal:msg.sender
is the membership holder.Erase Membership from the Tree After Deposit Withdrawal
A membership is erased either upon deposit withdrawal or when its slot is overwritten by another membership. This stage implements erasing the membership upon withdrawal.
Suggested steps:
withdraw
function so that it transitions fromGracePeriod
toErased
instead ofExpired
.withdraw
from anExpired
membership.Reuse Tree Slots of Expired Memberships
Implement deposit withdrawal after the membership associated with that deposit is erased from the tree.
Suggested steps:
ErasedAwaitsWithdrawal
memberships and their deposits.The state transition diagram should now match the final form as in the specification.
UPD 2024-09-25: a change in the state diagram has been suggested.
The text was updated successfully, but these errors were encountered: