Skip to content

Commit

Permalink
[Pools] Refactors and runtime apis for DelegateStake (#4537)
Browse files Browse the repository at this point in the history
## Runtime Apis
Introduces the following runtime apis to facilitate dapps and wallets in
integrating with the `DelegateStake` functionalities of the pools
(related: #3905). These
apis are meant to support pool and member migration, as well as lazy
application of pending slashes of pool members.

```rust
fn pool_pending_slash(pool_id: PoolId) -> Balance;
fn member_pending_slash(member: AccountId) -> Balance;
fn pool_needs_delegate_migration(pool_id: PoolId) -> bool;
fn member_needs_delegate_migration(member: AccountId) -> bool;
```

## Refactors
- Introduces newtypes for `Agent`, `Delegator`, `Pool` and
`[Pool]Member`. And refactors `StakeAdapter` and `DelegationInterface`
to accept the above types. This will help make these apis typesafe
against using wrong account type.
- Fixing `DelegationInterface` apis to return optional (instead of
default value if key does not exist).
- Rename struct `Agent` that wraps `AgentLedger` to `AgentOuterLedger`
which is clearer (naming wise) and different from the newtype `Agent`.
- Cleaning up new Pool events (related to `Delegation` feature of pool).

---------

Signed-off-by: Matteo Muraca <mmuraca247@gmail.com>
Signed-off-by: Alexandru Gheorghe <alexandru.gheorghe@parity.io>
Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
Signed-off-by: Adrian Catangiu <adrian@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: divdeploy <chenguangxue@outlook.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: hongkuang <liurenhong@outlook.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: gemini132 <164285545+gemini132@users.noreply.github.com>
Co-authored-by: Matteo Muraca <56828990+muraca@users.noreply.github.com>
Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com>
Co-authored-by: Alessandro Siniscalchi <asiniscalchi@gmail.com>
Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>
Co-authored-by: Ross Bulat <ross@parity.io>
Co-authored-by: Serban Iorga <serban@parity.io>
Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com>
Co-authored-by: Sam Johnson <sam@durosoft.com>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
Co-authored-by: Javier Viola <363911+pepoviola@users.noreply.github.com>
Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Co-authored-by: Dastan <88332432+dastansam@users.noreply.github.com>
Co-authored-by: Clara van Staden <claravanstaden64@gmail.com>
Co-authored-by: Ron <yrong1997@gmail.com>
Co-authored-by: Vincent Geddes <vincent@snowfork.com>
Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>
Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Co-authored-by: Dino Pačandi <3002868+Dinonard@users.noreply.github.com>
Co-authored-by: Andrei Eres <eresav@me.com>
Co-authored-by: Alin Dima <alin@parity.io>
Co-authored-by: Andrei Sandu <andrei-mihail@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com>
Co-authored-by: Vladimir Istyufeev <vladimir@parity.io>
Co-authored-by: Lulu <morgan@parity.io>
Co-authored-by: Juan Girini <juangirini@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Dónal Murray <donal.murray@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Kutsal Kaan Bilgin <kutsalbilgin@gmail.com>
Co-authored-by: Ermal Kaleci <ermalkaleci@gmail.com>
Co-authored-by: ordian <write@reusable.software>
Co-authored-by: divdeploy <166095818+divdeploy@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com>
Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: HongKuang <166261675+HongKuang@users.noreply.github.com>
Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io>
Co-authored-by: Egor_P <egor@parity.io>
Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com>
Co-authored-by: Dmitry Markin <dmitry@markin.tech>
Co-authored-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: Léa Narzis <78718413+lean-apple@users.noreply.github.com>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com>
Co-authored-by: command-bot <>
Co-authored-by: PG Herveou <pgherveou@gmail.com>
Co-authored-by: jimwfs <wqq1479787@163.com>
Co-authored-by: jimwfs <169986508+jimwfs@users.noreply.github.com>
Co-authored-by: polka.dom <polkadotdom@gmail.com>
  • Loading branch information
Show file tree
Hide file tree
Showing 17 changed files with 800 additions and 431 deletions.
16 changes: 16 additions & 0 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,22 @@ sp_api::impl_runtime_apis! {
fn balance_to_points(pool_id: pallet_nomination_pools::PoolId, new_funds: Balance) -> Balance {
NominationPools::api_balance_to_points(pool_id, new_funds)
}

fn pool_pending_slash(pool_id: pallet_nomination_pools::PoolId) -> Balance {
NominationPools::api_pool_pending_slash(pool_id)
}

fn member_pending_slash(member: AccountId) -> Balance {
NominationPools::api_member_pending_slash(member)
}

fn pool_needs_delegate_migration(pool_id: pallet_nomination_pools::PoolId) -> bool {
NominationPools::api_pool_needs_delegate_migration(pool_id)
}

fn member_needs_delegate_migration(member: AccountId) -> bool {
NominationPools::api_member_needs_delegate_migration(member)
}
}

impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime {
Expand Down
27 changes: 27 additions & 0 deletions prdoc/pr_4537.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Runtime apis to help with delegate-stake based Nomination Pools.

doc:
- audience: Runtime User
description: |
Introduces a new set of runtime apis to facilitate dapps and wallets to integrate with delegate-stake
functionalities of Nomination Pools. These apis support pool and member migration, as well as lazy application of
pending slashes of the pool members.

crates:
- name: pallet-nomination-pools
bump: minor
- name: westend-runtime
bump: minor
- name: kitchensink-runtime
bump: minor
- name: pallet-delegated-staking
bump: minor
- name: sp-staking
bump: minor
- name: pallet-nomination-pools-benchmarking
bump: patch
- name: pallet-nomination-pools-runtime-api
bump: minor
16 changes: 16 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2774,6 +2774,22 @@ impl_runtime_apis! {
fn balance_to_points(pool_id: pallet_nomination_pools::PoolId, new_funds: Balance) -> Balance {
NominationPools::api_balance_to_points(pool_id, new_funds)
}

fn pool_pending_slash(pool_id: pallet_nomination_pools::PoolId) -> Balance {
NominationPools::api_pool_pending_slash(pool_id)
}

fn member_pending_slash(member: AccountId) -> Balance {
NominationPools::api_member_pending_slash(member)
}

fn pool_needs_delegate_migration(pool_id: pallet_nomination_pools::PoolId) -> bool {
NominationPools::api_pool_needs_delegate_migration(pool_id)
}

fn member_needs_delegate_migration(member: AccountId) -> bool {
NominationPools::api_member_needs_delegate_migration(member)
}
}

impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime {
Expand Down
75 changes: 35 additions & 40 deletions substrate/frame/delegated-staking/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,80 +19,78 @@
//! Implementations of public traits, namely [`DelegationInterface`] and [`OnStakingUpdate`].

use super::*;
use sp_staking::{DelegationInterface, DelegationMigrator, OnStakingUpdate};
use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate};

impl<T: Config> DelegationInterface for Pallet<T> {
type Balance = BalanceOf<T>;
type AccountId = T::AccountId;

/// Effective balance of the `Agent` account.
fn agent_balance(who: &Self::AccountId) -> Self::Balance {
Agent::<T>::get(who)
.map(|agent| agent.ledger.effective_balance())
.unwrap_or_default()
fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
AgentLedgerOuter::<T>::get(&agent.get())
.map(|a| a.ledger.effective_balance())
.ok()
}

fn delegator_balance(delegator: &Self::AccountId) -> Self::Balance {
Delegation::<T>::get(delegator).map(|d| d.amount).unwrap_or_default()
fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance> {
Delegation::<T>::get(&delegator.get()).map(|d| d.amount)
}

/// Delegate funds to an `Agent`.
fn delegate(
who: &Self::AccountId,
agent: &Self::AccountId,
who: Delegator<Self::AccountId>,
agent: Agent<Self::AccountId>,
reward_account: &Self::AccountId,
amount: Self::Balance,
) -> DispatchResult {
Pallet::<T>::register_agent(
RawOrigin::Signed(agent.clone()).into(),
RawOrigin::Signed(agent.clone().get()).into(),
reward_account.clone(),
)?;

// Delegate the funds from who to the `Agent` account.
Pallet::<T>::delegate_to_agent(RawOrigin::Signed(who.clone()).into(), agent.clone(), amount)
Pallet::<T>::delegate_to_agent(RawOrigin::Signed(who.get()).into(), agent.get(), amount)
}

/// Add more delegation to the `Agent` account.
fn delegate_extra(
who: &Self::AccountId,
agent: &Self::AccountId,
who: Delegator<Self::AccountId>,
agent: Agent<Self::AccountId>,
amount: Self::Balance,
) -> DispatchResult {
Pallet::<T>::delegate_to_agent(RawOrigin::Signed(who.clone()).into(), agent.clone(), amount)
Pallet::<T>::delegate_to_agent(RawOrigin::Signed(who.get()).into(), agent.get(), amount)
}

/// Withdraw delegation of `delegator` to `Agent`.
///
/// If there are funds in `Agent` account that can be withdrawn, then those funds would be
/// unlocked/released in the delegator's account.
fn withdraw_delegation(
delegator: &Self::AccountId,
agent: &Self::AccountId,
delegator: Delegator<Self::AccountId>,
agent: Agent<Self::AccountId>,
amount: Self::Balance,
num_slashing_spans: u32,
) -> DispatchResult {
Pallet::<T>::release_delegation(
RawOrigin::Signed(agent.clone()).into(),
delegator.clone(),
RawOrigin::Signed(agent.get()).into(),
delegator.get(),
amount,
num_slashing_spans,
)
}

/// Returns true if the `Agent` have any slash pending to be applied.
fn has_pending_slash(agent: &Self::AccountId) -> bool {
Agent::<T>::get(agent)
.map(|d| !d.ledger.pending_slash.is_zero())
.unwrap_or(false)
/// Returns pending slash of the `agent`.
fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
AgentLedgerOuter::<T>::get(&agent.get()).map(|d| d.ledger.pending_slash).ok()
}

fn delegator_slash(
agent: &Self::AccountId,
delegator: &Self::AccountId,
agent: Agent<Self::AccountId>,
delegator: Delegator<Self::AccountId>,
value: Self::Balance,
maybe_reporter: Option<Self::AccountId>,
) -> sp_runtime::DispatchResult {
Pallet::<T>::do_slash(agent.clone(), delegator.clone(), value, maybe_reporter)
Pallet::<T>::do_slash(agent, delegator, value, maybe_reporter)
}
}

Expand All @@ -101,32 +99,29 @@ impl<T: Config> DelegationMigrator for Pallet<T> {
type AccountId = T::AccountId;

fn migrate_nominator_to_agent(
agent: &Self::AccountId,
agent: Agent<Self::AccountId>,
reward_account: &Self::AccountId,
) -> DispatchResult {
Pallet::<T>::migrate_to_agent(
RawOrigin::Signed(agent.clone()).into(),
reward_account.clone(),
)
Pallet::<T>::migrate_to_agent(RawOrigin::Signed(agent.get()).into(), reward_account.clone())
}
fn migrate_delegation(
agent: &Self::AccountId,
delegator: &Self::AccountId,
agent: Agent<Self::AccountId>,
delegator: Delegator<Self::AccountId>,
value: Self::Balance,
) -> DispatchResult {
Pallet::<T>::migrate_delegation(
RawOrigin::Signed(agent.clone()).into(),
delegator.clone(),
RawOrigin::Signed(agent.get()).into(),
delegator.get(),
value,
)
}

/// Only used for testing.
#[cfg(feature = "runtime-benchmarks")]
fn drop_agent(agent: &T::AccountId) {
<Agents<T>>::remove(agent);
fn drop_agent(agent: Agent<Self::AccountId>) {
<Agents<T>>::remove(agent.clone().get());
<Delegators<T>>::iter()
.filter(|(_, delegation)| delegation.agent == *agent)
.filter(|(_, delegation)| delegation.agent == agent.clone().get())
.for_each(|(delegator, _)| {
let _ = T::Currency::release_all(
&HoldReason::StakingDelegation.into(),
Expand All @@ -136,7 +131,7 @@ impl<T: Config> DelegationMigrator for Pallet<T> {
<Delegators<T>>::remove(&delegator);
});

T::CoreStaking::migrate_to_direct_staker(agent);
T::CoreStaking::migrate_to_direct_staker(&agent.get());
}
}

Expand All @@ -158,7 +153,7 @@ impl<T: Config> OnStakingUpdate<T::AccountId, BalanceOf<T>> for Pallet<T> {

fn on_withdraw(stash: &T::AccountId, amount: BalanceOf<T>) {
// if there is a withdraw to the agent, then add it to the unclaimed withdrawals.
let _ = Agent::<T>::get(stash)
let _ = AgentLedgerOuter::<T>::get(stash)
// can't do anything if there is an overflow error. Just raise a defensive error.
.and_then(|agent| agent.add_unclaimed_withdraw(amount).defensive())
.map(|agent| agent.save());
Expand Down
Loading

0 comments on commit 795bc77

Please sign in to comment.