Skip to content

Commit

Permalink
[Staking] Delegators can stake but stakers can't delegate (paritytech…
Browse files Browse the repository at this point in the history
…#4904)

Related: paritytech#4804.
Fixes the try state error in Westend:
https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/6564522.
Passes here:
https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/6580393

## Context
Currently in Kusama and Polkadot, an account can do both, directly
stake, and join a pool.

With the migration of pools to `DelegateStake` (See
paritytech#3905), the funds of pool
members are locked in a different way than for direct stakers.
- Pool member funds uses `holds`.
- `pallet-staking` uses deprecated locks (analogous to freeze) which can
overlap with holds.

An existing delegator can stake directly since pallet-staking only uses
free balance. But once an account becomes staker, we cannot allow them
to be delegator as this risks an account to use already staked (frozen)
funds in pools.

When an account gets into a situation where it is participating in both
pools and staking, it would no longer would be able to add any extra
bond to the pool but they can still withdraw funds.

## Changes
- Add test for the above scenario.
- Removes the assumption that a delegator cannot be a staker.
  • Loading branch information
Ank4n authored and TarekkMA committed Aug 2, 2024
1 parent 51498b4 commit b66bfe8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 6 deletions.
4 changes: 0 additions & 4 deletions substrate/frame/delegated-staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,10 +823,6 @@ impl<T: Config> Pallet<T> {
) -> Result<(), sp_runtime::TryRuntimeError> {
let mut delegation_aggregation = BTreeMap::<T::AccountId, BalanceOf<T>>::new();
for (delegator, delegation) in delegations.iter() {
ensure!(
T::CoreStaking::status(delegator).is_err(),
"delegator should not be directly staked"
);
ensure!(!Self::is_agent(delegator), "delegator cannot be an agent");

delegation_aggregation
Expand Down
43 changes: 41 additions & 2 deletions substrate/frame/delegated-staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use super::*;
use crate::mock::*;
use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold};
use pallet_nomination_pools::{Error as PoolsError, Event as PoolsEvent};
use pallet_staking::Error as StakingError;
use pallet_staking::{Error as StakingError, RewardDestination};
use sp_staking::{Agent, DelegationInterface, Delegator, StakerStatus};

#[test]
Expand Down Expand Up @@ -337,7 +337,6 @@ fn apply_pending_slash() {
/// Integration tests with pallet-staking.
mod staking_integration {
use super::*;
use pallet_staking::RewardDestination;
use sp_staking::Stake;

#[test]
Expand Down Expand Up @@ -1217,6 +1216,46 @@ mod pool_integration {
});
}

#[test]
fn existing_pool_member_can_stake() {
// A pool member is able to stake directly since staking only uses free funds but once a
// staker, they cannot join/add extra bond to the pool. They can still withdraw funds.
ExtBuilder::default().build_and_execute(|| {
start_era(1);
// GIVEN: a pool.
fund(&200, 1000);
let pool_id = create_pool(200, 800);

// WHEN: delegator joins a pool
let delegator = 100;
fund(&delegator, 1000);
assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), 200, pool_id));

// THEN: they can still stake directly.
assert_ok!(Staking::bond(
RuntimeOrigin::signed(delegator),
500,
RewardDestination::Account(101)
));
assert_ok!(Staking::nominate(
RuntimeOrigin::signed(delegator),
vec![GENESIS_VALIDATOR]
));

// The delegator cannot add any extra bond to the pool anymore.
assert_noop!(
Pools::bond_extra(RawOrigin::Signed(delegator).into(), BondExtra::FreeBalance(100)),
Error::<T>::AlreadyStaking
);

// But they can unbond
assert_ok!(Pools::unbond(RawOrigin::Signed(delegator).into(), delegator, 50));
// and withdraw
start_era(4);
assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(delegator).into(), delegator, 0));
});
}

fn create_pool(creator: AccountId, amount: Balance) -> u32 {
fund(&creator, amount * 2);
assert_ok!(Pools::create(
Expand Down

0 comments on commit b66bfe8

Please sign in to comment.