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

Support nomination-pools to Setheum Staking #844

Open
balqaasem opened this issue Aug 15, 2022 · 0 comments
Open

Support nomination-pools to Setheum Staking #844

balqaasem opened this issue Aug 15, 2022 · 0 comments
Labels
Milestone

Comments

@balqaasem
Copy link
Contributor

Ref: https://github.com/Setheum-Labs/substrate/blob/master/frame/nomination-pools/src/lib.rs


//! # Nomination Pools for Staking Delegation
//!
//! A pallet that allows members to delegate their stake to nominating pools. A nomination pool acts
//! as nominator and nominates validators on the members behalf.
//!
//! # Index
//!
//! * [Key terms](#key-terms)
//! * [Usage](#usage)
//! * [Design](#design)
//!
//! ## Key terms
//!
//!  * pool id: A unique identifier of each pool. Set to u12
//!  * bonded pool: Tracks the distribution of actively staked funds. See [`BondedPool`] and
//! [`BondedPoolInner`].
//! * reward pool: Tracks rewards earned by actively staked funds. See [`RewardPool`] and
//!   [`RewardPools`].
//! * unbonding sub pools: Collection of pools at different phases of the unbonding lifecycle. See
//!   [`SubPools`] and [`SubPoolsStorage`].
//! * members: Accounts that are members of pools. See [`PoolMember`] and [`PoolMembers`].
//! * roles: Administrative roles of each pool, capable of controlling nomination, and the state of
//!   the pool.
//! * point: A unit of measure for a members portion of a pool's funds. Points initially have a
//!   ratio of 1 (as set by `POINTS_TO_BALANCE_INIT_RATIO`) to balance, but as slashing happens,
//!   this can change.
//! * kick: The act of a pool administrator forcibly ejecting a member.
//! * bonded account: A key-less account id derived from the pool id that acts as the bonded
//!   account. This account registers itself as a nominator in the staking system, and follows
//!   exactly the same rules and conditions as a normal staker. Its bond increases or decreases as
//!   members join, it can `nominate` or `chill`, and might not even earn staking rewards if it is
//!   not nominating proper validators.
//! * reward account: A similar key-less account, that is set as the `Payee` account fo the bonded
//!   account for all staking rewards.
//!
//! ## Usage
//!
//! ### Join
//!
//! An account can stake funds with a nomination pool by calling [`Call::join`].
//!
//! ### Claim rewards
//!
//! After joining a pool, a member can claim rewards by calling [`Call::claim_payout`].
//!
//! For design docs see the [reward pool](#reward-pool) section.
//!
//! ### Leave
//!
//! In order to leave, a member must take two steps.
//!
//! First, they must call [`Call::unbond`]. The unbond extrinsic will start the unbonding process by
//! unbonding all or a portion of the members funds.
//!
//! > A member can have up to [`Config::MaxUnbonding`] distinct active unbonding requests.
//!
//! Second, once [`sp_staking::StakingInterface::bonding_duration`] eras have passed, the member can
//! call [`Call::withdraw_unbonded`] to withdraw any funds that are free.
//!
//! For design docs see the [bonded pool](#bonded-pool) and [unbonding sub
//! pools](#unbonding-sub-pools) sections.
//!
//! ### Slashes
//!
//! Slashes are distributed evenly across the bonded pool and the unbonding pools from slash era+1
//! through the slash apply era. Thus, any member who either
//!
//! 1. unbonded, or
//! 2. was actively bonded
//
//! in the aforementioned range of eras will be affected by the slash. A member is slashed pro-rata
//! based on its stake relative to the total slash amount.
//!
//! For design docs see the [slashing](#slashing) section.
//!
//! ### Administration
//!
//! A pool can be created with the [`Call::create`] call. Once created, the pools nominator or root
//! user must call [`Call::nominate`] to start nominating. [`Call::nominate`] can be called at
//! anytime to update validator selection.
//!
//! To help facilitate pool administration the pool has one of three states (see [`PoolState`]):
//!
//! * Open: Anyone can join the pool and no members can be permissionlessly removed.
//! * Blocked: No members can join and some admin roles can kick members. Kicking is not instant,
//!   and follows the same process of `unbond` and then `withdraw_unbonded`. In other words,
//!   administrators can permissionlessly unbond other members.
//! * Destroying: No members can join and all members can be permissionlessly removed with
//!   [`Call::unbond`] and [`Call::withdraw_unbonded`]. Once a pool is in destroying state, it
//!   cannot be reverted to another state.
//!
//! A pool has 4 administrative roles (see [`PoolRoles`]):
//!
//! * Depositor: creates the pool and is the initial member. They can only leave the pool once all
//!   other members have left. Once they fully withdraw their funds, the pool is destroyed.
//! * Nominator: can select which validators the pool nominates.
//! * State-Toggler: can change the pools state and kick members if the pool is blocked.
//! * Root: can change the nominator, state-toggler, or itself and can perform any of the actions
//!   the nominator or state-toggler can.
//!
//! ### Dismantling
//!
//! As noted, a pool is destroyed once
//!
//! 1. First, all members need to fully unbond and withdraw. If the pool state is set to
//!    `Destroying`, this can happen permissionlessly.
//! 2. The depositor itself fully unbonds and withdraws. Note that at this point, based on the
//!    requirements of the staking system, the pool's bonded account's stake might not be able to ge
//!    below a certain threshold as a nominator. At this point, the pool should `chill` itself to
//!    allow the depositor to leave.
//!
//! ## Design
//!
//! _Notes_: this section uses pseudo code to explain general design and does not necessarily
//! reflect the exact implementation. Additionally, a working knowledge of `pallet-staking`'s api is
//! assumed.
//!
//! ### Goals
//!
//! * Maintain network security by upholding integrity of slashing events, sufficiently penalizing
//!   members that where in the pool while it was backing a validator that got slashed.
//! * Maximize scalability in terms of member count.
//!
//! In order to maintain scalability, all operations are independent of the number of members. To do
//! this, delegation specific information is stored local to the member while the pool data
//! structures have bounded datum.
//!
//! ### Bonded pool
//!
//! A bonded pool nominates with its total balance, excluding that which has been withdrawn for
//! unbonding. The total points of a bonded pool are always equal to the sum of points of the
//! delegation members. A bonded pool tracks its points and reads its bonded balance.
//!
//! When a member joins a pool, `amount_transferred` is transferred from the members account to the
//! bonded pools account. Then the pool calls `staking::bond_extra(amount_transferred)` and issues
//! new points which are tracked by the member and added to the bonded pool's points.
//!
//! When the pool already has some balance, we want the value of a point before the transfer to
//! equal the value of a point after the transfer. So, when a member joins a bonded pool with a
//! given `amount_transferred`, we maintain the ratio of bonded balance to points such that:
//!
//! ```text
//! balance_after_transfer / points_after_transfer == balance_before_transfer / points_before_transfer;
//! ```
//!
//! To achieve this, we issue points based on the following:
//!
//! ```text
//! points_issued = (points_before_transfer / balance_before_transfer) * amount_transferred;
//! ```
//!
//! For new bonded pools we can set the points issued per balance arbitrarily. In this
//! implementation we use a 1 points to 1 balance ratio for pool creation (see
//! [`POINTS_TO_BALANCE_INIT_RATIO`]).
//!
//! **Relevant extrinsics:**
//!
//! * [`Call::create`]
//! * [`Call::join`]
//!
//! ### Reward pool
//!
//! When a pool is first bonded it sets up an deterministic, inaccessible account as its reward
//! destination.
//!
//! The reward pool is not really a pool anymore, as it does not track points anymore. Instead, it
//! tracks, a virtual value called `reward_counter`, among a few other values.
//!
//! See [this link](https://hackmd.io/PFGn6wI5TbCmBYoEA_f2Uw) for an in-depth explanation of the
//! reward pool mechanism.
//!
//! **Relevant extrinsics:**
//!
//! * [`Call::claim_payout`]
//!
//! ### Unbonding sub pools
//!
//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in
//! an unbonding pool associated with the active era. If no such pool exists, one is created. To
//! track which unbonding sub pool a member belongs too, a member tracks it's
//! `unbonding_era`.
//!
//! When a member initiates unbonding it's claim on the bonded pool
//! (`balance_to_unbond`) is computed as:
//!
//! ```text
//! balance_to_unbond = (bonded_pool.balance / bonded_pool.points) * member.points;
//! ```
//!
//! If this is the first transfer into an unbonding pool arbitrary amount of points can be issued
//! per balance. In this implementation unbonding pools are initialized with a 1 point to 1 balance
//! ratio (see [`POINTS_TO_BALANCE_INIT_RATIO`]). Otherwise, the unbonding pools hold the same
//! points to balance ratio properties as the bonded pool, so member points in the
//! unbonding pool are issued based on
//!
//! ```text
//! new_points_issued = (points_before_transfer / balance_before_transfer) * balance_to_unbond;
//! ```
//!
//! For scalability, a bound is maintained on the number of unbonding sub pools (see
//! [`TotalUnbondingPools`]). An unbonding pool is removed once its older than `current_era -
//! TotalUnbondingPools`. An unbonding pool is merged into the unbonded pool with
//!
//! ```text
//! unbounded_pool.balance = unbounded_pool.balance + unbonding_pool.balance;
//! unbounded_pool.points = unbounded_pool.points + unbonding_pool.points;
//! ```
//!
//! This scheme "averages" out the points value in the unbonded pool.
//!
//! Once a members `unbonding_era` is older than `current_era -
//! [sp_staking::StakingInterface::bonding_duration]`, it can can cash it's points out of the
//! corresponding unbonding pool. If it's `unbonding_era` is older than `current_era -
//! TotalUnbondingPools`, it can cash it's points from the unbonded pool.
//!
//! **Relevant extrinsics:**
//!
//! * [`Call::unbond`]
//! * [`Call::withdraw_unbonded`]
//!
//! ### Slashing
//!
//! This section assumes that the slash computation is executed by
//! `pallet_staking::StakingLedger::slash`, which passes the information to this pallet via
//! [`sp_staking::OnStakerSlash::on_slash`].
//!
//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool
//! while it was backing a validator that equivocated are punished. Without these measures a
//! member could unbond right after a validator equivocated with no consequences.
//!
//! This strategy is unfair to members who joined after the slash, because they get slashed as
//! well, but spares members who unbond. The latter is much more important for security: if a
//! pool's validators are attacking the network, their members need to unbond fast! Avoiding
//! slashes gives them an incentive to do that if validators get repeatedly slashed.
//!
//! To be fair to joiners, this implementation also need joining pools, which are actively staking,
//! in addition to the unbonding pools. For maintenance simplicity these are not implemented.
//! Related: <https://github.com/paritytech/substrate/issues/10860>
//!
//! **Relevant methods:**
//!
//! * [`Pallet::on_slash`]
//!
//! ### Limitations
//!
//! * PoolMembers cannot vote with their staked funds because they are transferred into the pools
//!   account. In the future this can be overcome by allowing the members to vote with their bonded
//!   funds via vote splitting.
//! * PoolMembers cannot quickly transfer to another pool if they do no like nominations, instead
//!   they must wait for the unbonding duration.

@balqaasem balqaasem changed the title Support 'nomination-pools' to Setheum Staking Support nomination-pools to Setheum Staking Sep 3, 2022
@balqaasem balqaasem added this to the Mainnet milestone Jan 8, 2024
@balqaasem balqaasem removed the pools label Jan 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant