Skip to content

Commit

Permalink
Staking: Add deprecate_controller_batch AdminOrigin call (#2589)
Browse files Browse the repository at this point in the history
Partially Addresses #2500

Adds a `deprecate_controller_batch` call to the staking pallet that is
callable by `Root` and `StakingAdmin`. To be used for controller account
deprecation and removed thereafter. Adds
`MaxControllersDeprecationBatch` pallet constant that defines max
possible deprecations per call.

- [x] Add `deprecate_controller_batch` call, and
`MaxControllersInDeprecationBatch` constant.
- [x] Add tests, benchmark, weights. Tests that weight is only consumed
if unique pair.
- [x] Adds `StakingAdmin` origin to staking's `AdminOrigin` type in
westend runtime.
- [x] Determined that worst case 5,900 deprecations does fit into
`maxBlock` `proofSize` and `refTime` in both normal and operational
thresholds, meaning we can deprecate all controllers for each network in
one call.

## Block Weights

By querying `consts.system.blockWeights` we can see that the
`deprecate_controller_batch` weights fit within the `normal` threshold
on Polkadot.

#### `controller_deprecation_batch` where i = 5900:
#### Ref time: 69,933,325,300
#### Proof size: 21,040,390

### Polkadot 

```
// consts.query.blockWeights

maxBlock: {
        refTime: 2,000,000,000,000
        proofSize: 18,446,744,073,709,551,615
}
normal: {
 maxExtrinsic: {
	refTime: 1,479,873,955,000
	proofSize: 13,650,590,614,545,068,195
 }
 maxTotal: {
	refTime: 1,500,000,000,000
	proofSize: 13,835,058,055,282,163,711
 }
}
```

### Kusama

```
// consts.query.blockWeights

  maxBlock: {
    refTime: 2,000,000,000,000
    proofSize: 18,446,744,073,709,551,615
  }
    normal: {
      maxExtrinsic: {
        refTime: 1,479,875,294,000
        proofSize: 13,650,590,614,545,068,195
      }
      maxTotal: {
        refTime: 1,500,000,000,000
        proofSize: 13,835,058,055,282,163,711
      }
}
```

---------

Co-authored-by: command-bot <>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
  • Loading branch information
Ross Bulat and gpestana committed Dec 12, 2023
1 parent c2d45e7 commit 048a9c2
Show file tree
Hide file tree
Showing 19 changed files with 624 additions and 285 deletions.
1 change: 1 addition & 0 deletions polkadot/runtime/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ impl pallet_staking::Config for Runtime {
type TargetList = pallet_staking::UseValidatorsMap<Runtime>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>;
type MaxUnlockingChunks = frame_support::traits::ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<5900>;
type HistoryDepth = frame_support::traits::ConstU32<84>;
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = ();
Expand Down
4 changes: 3 additions & 1 deletion polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ parameter_types! {
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub const MaxControllersInDeprecationBatch: u32 = 751;
}

impl pallet_staking::Config for Runtime {
Expand All @@ -686,7 +687,7 @@ impl pallet_staking::Config for Runtime {
type SessionsPerEra = SessionsPerEra;
type BondingDuration = BondingDuration;
type SlashDeferDuration = SlashDeferDuration;
type AdminOrigin = EnsureRoot<AccountId>;
type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
Expand All @@ -699,6 +700,7 @@ impl pallet_staking::Config for Runtime {
type NominationsQuota = pallet_staking::FixedNominationsQuota<{ MaxNominations::get() }>;
type MaxUnlockingChunks = frame_support::traits::ConstU32<32>;
type HistoryDepth = frame_support::traits::ConstU32<84>;
type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = NominationPools;
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
Expand Down
214 changes: 118 additions & 96 deletions polkadot/runtime/westend/src/weights/pallet_staking.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,7 @@ parameter_types! {
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxControllersInDeprecationBatch: u32 = 5900;
pub OffchainRepeat: BlockNumber = 5;
pub HistoryDepth: u32 = 84;
}
Expand Down Expand Up @@ -674,6 +675,7 @@ impl pallet_staking::Config for Runtime {
// This a placeholder, to be introduced in the next PR as an instance of bags-list
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
type HistoryDepth = HistoryDepth;
type EventListeners = NominationPools;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ impl pallet_staking::Config for Test {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ impl pallet_staking::Config for Test {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ impl pallet_staking::Config for Runtime {
type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>;
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = HistoryDepth;
type EventListeners = ();
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/fast-unstake/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ impl pallet_staking::Config for Runtime {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/grandpa/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ impl pallet_staking::Config for Test {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/nomination-pools/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl pallet_staking::Config for Runtime {
type VoterList = VoterList;
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type MaxUnlockingChunks = ConstU32<32>;
type HistoryDepth = ConstU32<84>;
type EventListeners = Pools;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/nomination-pools/test-staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ impl pallet_staking::Config for Runtime {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type EventListeners = Pools;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/offences/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ impl pallet_staking::Config for Test {
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/root-offences/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ impl pallet_staking::Config for Test {
type NominationsQuota = pallet_staking::FixedNominationsQuota<16>;
type MaxUnlockingChunks = ConstU32<32>;
type HistoryDepth = ConstU32<84>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type VoterList = pallet_staking::UseNominatorsAndValidatorsMap<Self>;
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/session/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ impl pallet_staking::Config for Test {
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
type MaxUnlockingChunks = ConstU32<32>;
type MaxControllersInDeprecationBatch = ConstU32<100>;
type HistoryDepth = ConstU32<84>;
type VoterList = pallet_staking::UseNominatorsAndValidatorsMap<Self>;
type TargetList = pallet_staking::UseValidatorsMap<Self>;
Expand Down
34 changes: 34 additions & 0 deletions substrate/frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use codec::Decode;
use frame_election_provider_support::{bounds::DataProviderBounds, SortedListProvider};
use frame_support::{
pallet_prelude::*,
storage::bounded_vec::BoundedVec,
traits::{Currency, Get, Imbalance, UnfilteredDispatchable},
};
use sp_runtime::{
Expand Down Expand Up @@ -525,6 +526,39 @@ benchmarks! {
assert_eq!(Invulnerables::<T>::get().len(), v as usize);
}

deprecate_controller_batch {
// We pass a dynamic number of controllers to the benchmark, up to
// `MaxControllersInDeprecationBatch`.
let i in 0 .. T::MaxControllersInDeprecationBatch::get();

let mut controllers: Vec<_> = vec![];
let mut stashes: Vec<_> = vec![];
for n in 0..i as u32 {
let (stash, controller) = create_unique_stash_controller::<T>(
n,
100,
RewardDestination::Staked,
false
)?;
controllers.push(controller);
stashes.push(stash);
}
let bounded_controllers: BoundedVec<_, T::MaxControllersInDeprecationBatch> =
BoundedVec::try_from(controllers.clone()).unwrap();
}: _(RawOrigin::Root, bounded_controllers)
verify {
for n in 0..i as u32 {
let stash = &stashes[n as usize];
let controller = &controllers[n as usize];
// Ledger no longer keyed by controller.
assert_eq!(Ledger::<T>::get(controller), None);
// Bonded now maps to the stash.
assert_eq!(Bonded::<T>::get(stash), Some(stash.clone()));
// Ledger is now keyed by stash.
assert_eq!(Ledger::<T>::get(stash).unwrap().stash, *stash);
}
}

force_unstake {
// Slashing Spans
let s in 0 .. MAX_SPANS;
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ parameter_types! {
pub static SlashDeferDuration: EraIndex = 0;
pub static Period: BlockNumber = 5;
pub static Offset: BlockNumber = 0;
pub static MaxControllersInDeprecationBatch: u32 = 5900;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
Expand Down Expand Up @@ -316,6 +317,7 @@ impl crate::pallet::pallet::Config for Test {
type NominationsQuota = WeightedNominationsQuota<16>;
type MaxUnlockingChunks = MaxUnlockingChunks;
type HistoryDepth = HistoryDepth;
type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
type EventListeners = EventListenerMock;
type BenchmarkingConfig = TestBenchmarkingConfig;
type WeightInfo = ();
Expand Down
56 changes: 53 additions & 3 deletions substrate/frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ pub mod pallet {
#[pallet::constant]
type MaxUnlockingChunks: Get<u32>;

/// The maximum amount of controller accounts that can be deprecated in one call.
type MaxControllersInDeprecationBatch: Get<u32>;

/// Something that listens to staking updates and performs actions based on the data it
/// receives.
///
Expand Down Expand Up @@ -1323,18 +1326,17 @@ pub mod pallet {
pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
let stash = ensure_signed(origin)?;

// the bonded map and ledger are mutated directly as this extrinsic is related to a
// The bonded map and ledger are mutated directly as this extrinsic is related to a
// (temporary) passive migration.
Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
let controller = ledger.controller()
.defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
.ok_or(Error::<T>::NotController)?;

if controller == stash {
// stash is already its own controller.
// Stash is already its own controller.
return Err(Error::<T>::AlreadyPaired.into())
}
// update bond and ledger.
<Ledger<T>>::remove(controller);
<Bonded<T>>::insert(&stash, &stash);
<Ledger<T>>::insert(&stash, ledger);
Expand Down Expand Up @@ -1920,6 +1922,54 @@ pub mod pallet {

Ok(Pays::No.into())
}

/// Updates a batch of controller accounts to their corresponding stash account if they are
/// not the same. Ignores any controller accounts that do not exist, and does not operate if
/// the stash and controller are already the same.
///
/// Effects will be felt instantly (as soon as this function is completed successfully).
///
/// The dispatch origin must be `T::AdminOrigin`.
#[pallet::call_index(28)]
#[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
pub fn deprecate_controller_batch(
origin: OriginFor<T>,
controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
) -> DispatchResultWithPostInfo {
T::AdminOrigin::ensure_origin(origin)?;

// Ignore controllers that do not exist or are already the same as stash.
let filtered_batch_with_ledger: Vec<_> = controllers
.iter()
.filter_map(|controller| {
let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
ledger.ok().map_or(None, |ledger| {
// If the controller `RewardDestination` is still the deprecated
// `Controller` variant, skip deprecating this account.
let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
#[allow(deprecated)]
RewardDestination::Controller
};

if ledger.stash != *controller && !payee_deprecated {
Some((controller.clone(), ledger))
} else {
None
}
})
})
.collect();

// Update unique pairs.
for (controller, ledger) in filtered_batch_with_ledger {
let stash = ledger.stash.clone();

<Bonded<T>>::insert(&stash, &stash);
<Ledger<T>>::remove(controller);
<Ledger<T>>::insert(stash, ledger);
}
Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
}
}
}

Expand Down
Loading

0 comments on commit 048a9c2

Please sign in to comment.