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 <[email protected]>
Signed-off-by: Alexandru Gheorghe <[email protected]>
Signed-off-by: Andrei Sandu <[email protected]>
Signed-off-by: Adrian Catangiu <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: divdeploy <[email protected]>
Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: hongkuang <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: gemini132 <[email protected]>
Co-authored-by: Matteo Muraca <[email protected]>
Co-authored-by: Liam Aharon <[email protected]>
Co-authored-by: Kian Paimani <[email protected]>
Co-authored-by: Alexandru Gheorghe <[email protected]>
Co-authored-by: Alessandro Siniscalchi <[email protected]>
Co-authored-by: Andrei Sandu <[email protected]>
Co-authored-by: Ross Bulat <[email protected]>
Co-authored-by: Serban Iorga <[email protected]>
Co-authored-by: s0me0ne-unkn0wn <[email protected]>
Co-authored-by: Sam Johnson <[email protected]>
Co-authored-by: Adrian Catangiu <[email protected]>
Co-authored-by: Javier Viola <[email protected]>
Co-authored-by: Alexandru Vasile <[email protected]>
Co-authored-by: Niklas Adolfsson <[email protected]>
Co-authored-by: Dastan <[email protected]>
Co-authored-by: Clara van Staden <[email protected]>
Co-authored-by: Ron <[email protected]>
Co-authored-by: Vincent Geddes <[email protected]>
Co-authored-by: Svyatoslav Nikolsky <[email protected]>
Co-authored-by: Michal Kucharczyk <[email protected]>
Co-authored-by: Dino Pačandi <[email protected]>
Co-authored-by: Andrei Eres <[email protected]>
Co-authored-by: Alin Dima <[email protected]>
Co-authored-by: Andrei Sandu <[email protected]>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: Branislav Kontur <[email protected]>
Co-authored-by: Sebastian Kunert <[email protected]>
Co-authored-by: gupnik <[email protected]>
Co-authored-by: Vladimir Istyufeev <[email protected]>
Co-authored-by: Lulu <[email protected]>
Co-authored-by: Juan Girini <[email protected]>
Co-authored-by: Francisco Aguirre <[email protected]>
Co-authored-by: Dónal Murray <[email protected]>
Co-authored-by: Shawn Tabrizi <[email protected]>
Co-authored-by: Kutsal Kaan Bilgin <[email protected]>
Co-authored-by: Ermal Kaleci <[email protected]>
Co-authored-by: ordian <[email protected]>
Co-authored-by: divdeploy <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergej Sakac <[email protected]>
Co-authored-by: Squirrel <[email protected]>
Co-authored-by: HongKuang <[email protected]>
Co-authored-by: Tsvetomir Dimitrov <[email protected]>
Co-authored-by: Egor_P <[email protected]>
Co-authored-by: Aaro Altonen <[email protected]>
Co-authored-by: Dmitry Markin <[email protected]>
Co-authored-by: Alexandru Vasile <[email protected]>
Co-authored-by: Léa Narzis <[email protected]>
Co-authored-by: Gonçalo Pestana <[email protected]>
Co-authored-by: georgepisaltu <[email protected]>
Co-authored-by: command-bot <>
Co-authored-by: PG Herveou <[email protected]>
Co-authored-by: jimwfs <[email protected]>
Co-authored-by: jimwfs <[email protected]>
Co-authored-by: polka.dom <[email protected]>
  • 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.