Skip to content

Commit f5673cf

Browse files
authored
[Staking] Currency <> Fungible migration (#5501)
Migrate staking currency from `traits::LockableCurrency` to `traits::fungible::holds`. Resolves part of #226. ## Changes ### Nomination Pool TransferStake is now incompatible with fungible migration as old pools were not meant to have additional ED. Since they are anyways deprecated, removed its usage from all test runtimes. ### Staking - Config: `Currency` becomes of type `Fungible` while `OldCurrency` is the `LockableCurrency` used before. - Lazy migration of accounts. Any ledger update will create a new hold with no extra reads/writes. A permissionless extrinsic `migrate_currency()` releases the old `lock` along with some housekeeping. - Staking now requires ED to be left free. It also adds no consumer to staking accounts. - If hold cannot be applied to all stake, the un-holdable part is force withdrawn from the ledger. ### Delegated Staking The pallet does not add provider for agents anymore. ## Migration stats ### Polkadot Total accounts that can be migrated: 59564 Accounts failing to migrate: 0 Accounts with stake force withdrawn greater than ED: 59 Total force withdrawal: 29591.26 DOT ### Kusama Total accounts that can be migrated: 26311 Accounts failing to migrate: 0 Accounts with stake force withdrawn greater than ED: 48 Total force withdrawal: 1036.05 KSM [Full logs here](https://hackmd.io/@ak0n/BklDuFra0). ## Note about locks (freeze) vs holds With locks or freezes, staking could use total balance of an account. But with holds, the account needs to be left with at least Existential Deposit in free balance. This would also affect nomination pools which till now has been able to stake all funds contributed to it. An alternate version of this PR is #5658 where staking pallet does not add any provider, but means pools and delegated-staking pallet has to provide for these accounts and makes the end to end logic (of provider and consumer ref) lot less intuitive and prone to bug. This PR now introduces requirement for stakers to maintain ED in their free balance. This helps with removing the bug prone incrementing and decrementing of consumers and providers. ## TODO - [x] Test: Vesting + governance locked funds can be staked. - [ ] can `Call::restore_ledger` be removed? @gpestana - [x] Ensure unclaimed withdrawals is not affected by no provider for pool accounts. - [x] Investigate kusama accounts with balance between 0 and ED. - [x] Permissionless call to release lock. - [x] Migration of consumer (dec) and provider (inc) for direct stakers. - [x] force unstake if hold cannot be applied to all stake. - [x] Fix try state checks (it thinks nothing is staked for unmigrated ledgers). - [x] Bench `migrate_currency`. - [x] Virtual Staker migration test. - [x] Ensure total issuance is upto date when minting rewards. ## Followup - #5742 --------- Co-authored-by: command-bot <>
1 parent e056586 commit f5673cf

File tree

47 files changed

+2100
-2466
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2100
-2466
lines changed

Cargo.lock

+1-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ members = [
389389
"substrate/frame/nomination-pools/fuzzer",
390390
"substrate/frame/nomination-pools/runtime-api",
391391
"substrate/frame/nomination-pools/test-delegate-stake",
392-
"substrate/frame/nomination-pools/test-transfer-stake",
393392
"substrate/frame/offences",
394393
"substrate/frame/offences/benchmarking",
395394
"substrate/frame/paged-list",

polkadot/runtime/test-runtime/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -366,11 +366,13 @@ impl onchain::Config for OnChainSeqPhragmen {
366366
const MAX_QUOTA_NOMINATIONS: u32 = 16;
367367

368368
impl pallet_staking::Config for Runtime {
369+
type OldCurrency = Balances;
369370
type Currency = Balances;
370371
type CurrencyBalance = Balance;
371372
type UnixTime = Timestamp;
372373
type CurrencyToVote = polkadot_runtime_common::CurrencyToVote;
373374
type RewardRemainder = ();
375+
type RuntimeHoldReason = RuntimeHoldReason;
374376
type RuntimeEvent = RuntimeEvent;
375377
type Slash = ();
376378
type Reward = ();

polkadot/runtime/westend/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,10 @@ parameter_types! {
728728
}
729729

730730
impl pallet_staking::Config for Runtime {
731+
type OldCurrency = Balances;
731732
type Currency = Balances;
732733
type CurrencyBalance = Balance;
734+
type RuntimeHoldReason = RuntimeHoldReason;
733735
type UnixTime = Timestamp;
734736
type CurrencyToVote = CurrencyToVote;
735737
type RewardRemainder = ();

polkadot/runtime/westend/src/tests.rs

+86-13
Original file line numberDiff line numberDiff line change
@@ -155,25 +155,27 @@ mod remote_tests {
155155

156156
let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into();
157157
let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
158+
let online_config = OnlineConfig {
159+
transport,
160+
state_snapshot: maybe_state_snapshot.clone(),
161+
child_trie: false,
162+
pallets: vec![
163+
"Staking".into(),
164+
"System".into(),
165+
"Balances".into(),
166+
"NominationPools".into(),
167+
"DelegatedStaking".into(),
168+
],
169+
..Default::default()
170+
};
158171
let mut ext = Builder::<Block>::default()
159172
.mode(if let Some(state_snapshot) = maybe_state_snapshot {
160173
Mode::OfflineOrElseOnline(
161174
OfflineConfig { state_snapshot: state_snapshot.clone() },
162-
OnlineConfig {
163-
transport,
164-
state_snapshot: Some(state_snapshot),
165-
pallets: vec![
166-
"staking".into(),
167-
"system".into(),
168-
"balances".into(),
169-
"nomination-pools".into(),
170-
"delegated-staking".into(),
171-
],
172-
..Default::default()
173-
},
175+
online_config,
174176
)
175177
} else {
176-
Mode::Online(OnlineConfig { transport, ..Default::default() })
178+
Mode::Online(online_config)
177179
})
178180
.build()
179181
.await
@@ -241,6 +243,77 @@ mod remote_tests {
241243
);
242244
});
243245
}
246+
247+
#[tokio::test]
248+
async fn staking_curr_fun_migrate() {
249+
// Intended to be run only manually.
250+
if var("RUN_MIGRATION_TESTS").is_err() {
251+
return;
252+
}
253+
sp_tracing::try_init_simple();
254+
255+
let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9944".to_string()).into();
256+
let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
257+
let online_config = OnlineConfig {
258+
transport,
259+
state_snapshot: maybe_state_snapshot.clone(),
260+
child_trie: false,
261+
pallets: vec!["Staking".into(), "System".into(), "Balances".into()],
262+
..Default::default()
263+
};
264+
let mut ext = Builder::<Block>::default()
265+
.mode(if let Some(state_snapshot) = maybe_state_snapshot {
266+
Mode::OfflineOrElseOnline(
267+
OfflineConfig { state_snapshot: state_snapshot.clone() },
268+
online_config,
269+
)
270+
} else {
271+
Mode::Online(online_config)
272+
})
273+
.build()
274+
.await
275+
.unwrap();
276+
ext.execute_with(|| {
277+
// create an account with some balance
278+
let alice = AccountId::from([1u8; 32]);
279+
use frame_support::traits::Currency;
280+
let _ = Balances::deposit_creating(&alice, 100_000 * UNITS);
281+
282+
let mut success = 0;
283+
let mut err = 0;
284+
let mut force_withdraw_acc = 0;
285+
// iterate over all pools
286+
pallet_staking::Ledger::<Runtime>::iter().for_each(|(ctrl, ledger)| {
287+
match pallet_staking::Pallet::<Runtime>::migrate_currency(
288+
RuntimeOrigin::signed(alice.clone()).into(),
289+
ledger.stash.clone(),
290+
) {
291+
Ok(_) => {
292+
let updated_ledger =
293+
pallet_staking::Ledger::<Runtime>::get(&ctrl).expect("ledger exists");
294+
let force_withdraw = ledger.total - updated_ledger.total;
295+
if force_withdraw > 0 {
296+
force_withdraw_acc += force_withdraw;
297+
log::info!(target: "remote_test", "Force withdraw from stash {:?}: value {:?}", ledger.stash, force_withdraw);
298+
}
299+
success += 1;
300+
},
301+
Err(e) => {
302+
log::error!(target: "remote_test", "Error migrating {:?}: {:?}", ledger.stash, e);
303+
err += 1;
304+
},
305+
}
306+
});
307+
308+
log::info!(
309+
target: "remote_test",
310+
"Migration stats: success: {}, err: {}, total force withdrawn stake: {}",
311+
success,
312+
err,
313+
force_withdraw_acc
314+
);
315+
});
316+
}
244317
}
245318

246319
#[test]

0 commit comments

Comments
 (0)