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

[Pools] Fix issues with member migration to DelegateStake #4822

Merged
merged 54 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
11a0c44
migration test
Ank4n Jun 18, 2024
efd3324
fmt
Ank4n Jun 18, 2024
3f8d2fc
undo pool changes
Ank4n Jun 18, 2024
7388573
fixes all NotEnoughFunds error
Ank4n Jun 18, 2024
6634d25
return false if pool does not exist
Ank4n Jun 18, 2024
a3901a9
fix transfer on hold amount should still be held in dst acc
Ank4n Jun 18, 2024
462be1f
fmt
Ank4n Jun 18, 2024
c7e9f6c
unexpected errors check
Ank4n Jun 19, 2024
9a68720
all non staking accounts succeed when given minimum balance
Ank4n Jun 19, 2024
268d0a5
assert migrate tests
Ank4n Jun 19, 2024
000762f
remove clone
Ank4n Jun 21, 2024
a904fc8
uncomment
Ank4n Jun 21, 2024
0c776b8
incrementing provider fixes users migrating with zero balance
Ank4n Jun 29, 2024
699a2ec
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 3, 2024
c3fa887
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 5, 2024
3d2379e
test for clean up accounts
Ank4n Jul 6, 2024
ec9035c
providers are incremented correctly when migration agent/delegators
Ank4n Jul 6, 2024
a9d4595
migrated agents/delegators are also cleaned up correctly
Ank4n Jul 6, 2024
8a27ae0
improve remote tests
Ank4n Jul 6, 2024
8f77ac4
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 7, 2024
7fd484a
add runtime api for total member balance
Ank4n Jul 7, 2024
9925a9f
ensure member can't bond extra if they are in unmigrated state
Ank4n Jul 7, 2024
4a9f35f
gate all calls if non migrated state as defensive measure
Ank4n Jul 7, 2024
bc3ce11
ensure all calls are gated
Ank4n Jul 7, 2024
5b91644
add runtime api for pool balance
Ank4n Jul 10, 2024
6ceb1ba
remove unused imports
Ank4n Jul 10, 2024
de48083
fix import
Ank4n Jul 11, 2024
fd8a451
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 11, 2024
ab2317b
fix test
Ank4n Jul 11, 2024
cfdaf90
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 12, 2024
d5634c4
add prdoc
Ank4n Jul 12, 2024
0da9807
fix prdoc bump
Ank4n Jul 13, 2024
00f7e6f
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 13, 2024
1514c2b
add missing crate to prdoc
Ank4n Jul 13, 2024
3dee9c4
remove unintended file
Ank4n Jul 13, 2024
8c96add
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Jul 18, 2024
2440303
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 6, 2024
95bf173
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 7, 2024
c6eafa5
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 7, 2024
c209d9e
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 10, 2024
cbfbf6f
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 12, 2024
acbb674
refactor based on pr comments
Ank4n Aug 12, 2024
65cecb5
allow migration lower than ED
Ank4n Aug 12, 2024
4081f12
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 13, 2024
8cdde30
move remote tests to another file
Ank4n Aug 13, 2024
99698d6
fix below ed test
Ank4n Aug 13, 2024
5e465f2
revert comment
Ank4n Aug 13, 2024
c38f83c
ensure there is atleast ED to transfer
Ank4n Aug 13, 2024
b3a3263
fix imports
Ank4n Aug 13, 2024
dfcdc42
Update substrate/frame/delegated-staking/src/lib.rs
Ank4n Aug 13, 2024
5082542
Merge branch 'master' into ankan/delegate-stake-remote-tests
Ank4n Aug 13, 2024
bce9803
refactor inc dec of providers
Ank4n Aug 13, 2024
ae9a370
gate extrinsics on top level function
Ank4n Aug 13, 2024
251ba4c
fix clippy
Ank4n Aug 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ use sp_runtime::{
IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify,
},
transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill,
ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill,
};
use sp_staking::SessionIndex;
#[cfg(any(feature = "std", test))]
Expand Down Expand Up @@ -2474,6 +2474,14 @@ sp_api::impl_runtime_apis! {
fn member_needs_delegate_migration(member: AccountId) -> bool {
NominationPools::api_member_needs_delegate_migration(member)
}

fn member_total_balance(member: AccountId) -> Balance {
NominationPools::api_member_total_balance(member)
}

fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance {
NominationPools::api_pool_balance(pool_id)
}
}

impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime {
Expand Down Expand Up @@ -2811,6 +2819,114 @@ mod remote_tests {
.unwrap();
ext.execute_with(|| Runtime::on_runtime_upgrade(UpgradeCheckSelect::PreAndPost));
}

#[tokio::test]
async fn delegate_stake_migration() {
Ank4n marked this conversation as resolved.
Show resolved Hide resolved
// Intended to be run only manually.
if var("RUN_MIGRATION_TESTS").is_err() {
return;
}
use frame_support::assert_ok;
sp_tracing::try_init_simple();

let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into();
let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
let mut ext = Builder::<Block>::default()
.mode(if let Some(state_snapshot) = maybe_state_snapshot {
Mode::OfflineOrElseOnline(
OfflineConfig { state_snapshot: state_snapshot.clone() },
OnlineConfig {
transport,
state_snapshot: Some(state_snapshot),
pallets: vec![
"staking".into(),
"system".into(),
"balances".into(),
"nomination-pools".into(),
"delegated-staking".into(),
],
..Default::default()
},
)
} else {
Mode::Online(OnlineConfig { transport, ..Default::default() })
})
.build()
.await
.unwrap();
ext.execute_with(|| {
// create an account with some balance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to easily reuse this code for other runtimes, should this part onward live in the pallet, generic over <T: Runtime>?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe you are fine with copy-pasting it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By adding it in the westend runtime, I get a runtime configuration closer to mainnets (kusama/polkadot). For example: The nomination pool pallet test runtime config does not even include pallet-staking.

I have been generally reusing this by just pointing to the polkadot or kusama node with ED the only thing that differs between the runtimes relevant for this test. The most accurate test though would be by copy pasting. Does that make sense?

let alice = AccountId::from([1u8; 32]);
use frame_support::traits::Currency;
let _ = Balances::deposit_creating(&alice, 100_000 * UNITS);

// iterate over all pools
pallet_nomination_pools::BondedPools::<Runtime>::iter_keys().for_each(|k| {
if pallet_nomination_pools::Pallet::<Runtime>::api_pool_needs_delegate_migration(k)
{
assert_ok!(
pallet_nomination_pools::Pallet::<Runtime>::migrate_pool_to_delegate_stake(
RuntimeOrigin::signed(alice.clone()).into(),
k,
)
);
}
});

// member migration stats
let mut success = 0;
let mut members_below_ed = 0;
let mut direct_stakers = 0;
let mut unexpected_errors = 0;

// iterate over all pool members
pallet_nomination_pools::PoolMembers::<Runtime>::iter().for_each(|(k, member)| {
if pallet_nomination_pools::Pallet::<Runtime>::api_member_needs_delegate_migration(
k.clone(),
) {
// reasons migrations can fail:
let is_direct_staker = pallet_staking::Bonded::<Runtime>::contains_key(&k);
let member_balance_below_ed = member.total_balance() < ExistentialDeposit::get();

let migration = pallet_nomination_pools::Pallet::<Runtime>::migrate_delegation(
RuntimeOrigin::signed(alice.clone()).into(),
sp_runtime::MultiAddress::Id(k.clone()),
);

if member_balance_below_ed {
// if the member has balance below ED, the migration should fail.
members_below_ed += 1;
assert_eq!(
migration.unwrap_err(),
pallet_nomination_pools::Error::<Runtime>::MinimumBondNotMet.into()
);
} else if is_direct_staker {
// if the member is a direct staker, the migration should fail until pool
// member unstakes all funds from pallet-staking.
direct_stakers += 1;
assert_eq!(
migration.unwrap_err(),
pallet_delegated_staking::Error::<Runtime>::AlreadyStaking.into()
);
} else if migration.is_err() {
unexpected_errors += 1;
log::error!(target: "remote_test", "Unexpected error {:?} while migrating {:?}", migration.unwrap_err(), k);
} else {
success += 1;
}
}
});

log::info!(
target: "remote_test",
"Migration stats: success: {}, members_below_ed: {}, direct_stakers: {}, unexpected_errors: {}",
success,
members_below_ed,
direct_stakers,
unexpected_errors
);
});
}
}

mod clean_state_migration {
Expand Down
25 changes: 25 additions & 0 deletions prdoc/pr_4822.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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: Ensure as many as possible pool members can migrate to `DelegateStake`

doc:
- audience: Runtime Dev
description: |
1. Allows pool members to use their total balance while joining pool with `DelegateStake`.
2. Gates call mutating pool or member in unmigrated state.
3. Runtime apis for reading pool and member balance.

crates:
- name: westend-runtime
bump: minor
- name: kitchensink-runtime
bump: patch
- name: pallet-delegated-staking
bump: patch
- name: pallet-nomination-pools
bump: minor
- name: sp-staking
bump: patch
- name: pallet-nomination-pools-runtime-api
bump: minor
8 changes: 8 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2782,6 +2782,14 @@ impl_runtime_apis! {
fn member_needs_delegate_migration(member: AccountId) -> bool {
NominationPools::api_member_needs_delegate_migration(member)
}

fn member_total_balance(member: AccountId) -> Balance {
NominationPools::api_member_total_balance(member)
}

fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance {
NominationPools::api_pool_balance(pool_id)
}
}

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

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

impl<T: Config> DelegationInterface for Pallet<T> {
type Balance = BalanceOf<T>;
Expand All @@ -36,7 +36,7 @@ impl<T: Config> DelegationInterface for Pallet<T> {
Delegation::<T>::get(&delegator.get()).map(|d| d.amount)
}

/// Delegate funds to an `Agent`.
/// Register a new `Agent` and delegate funds to it.
fn delegate(
who: Delegator<Self::AccountId>,
agent: Agent<Self::AccountId>,
Expand Down
Loading
Loading