Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ impl pallet_staking::Config for Runtime {
// Alternatively, use pallet_staking::UseNominatorsMap<Runtime> to just use the nominators map.
// Note that the aforementioned does not scale to a very large number of nominators.
type SortedListProvider = BagsList;
type MaxUnlockingChunks = ConstU32<32>;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type BenchmarkingConfig = StakingBenchmarkingConfig;
}
Expand Down
1 change: 1 addition & 0 deletions frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ impl pallet_staking::Config for Test {
type ElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type GenesisElectionProvider = Self::ElectionProvider;
type SortedListProvider = pallet_staking::UseNominatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
}
Expand Down
1 change: 1 addition & 0 deletions 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 ElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type GenesisElectionProvider = Self::ElectionProvider;
type SortedListProvider = pallet_staking::UseNominatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
}
Expand Down
1 change: 1 addition & 0 deletions frame/offences/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ impl pallet_staking::Config for Test {
type ElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type GenesisElectionProvider = Self::ElectionProvider;
type SortedListProvider = pallet_staking::UseNominatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
}
Expand Down
1 change: 1 addition & 0 deletions frame/session/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ impl pallet_staking::Config for Test {
type OffendingValidatorsThreshold = ();
type ElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type GenesisElectionProvider = Self::ElectionProvider;
type MaxUnlockingChunks = ConstU32<32>;
type SortedListProvider = pallet_staking::UseNominatorsMap<Self>;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
Expand Down
10 changes: 5 additions & 5 deletions frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ benchmarks! {
}

rebond {
let l in 1 .. MAX_UNLOCKING_CHUNKS as u32;
let l in 1 .. MaxUnlockingChunks::get() as u32;

// clean up any existing state.
clear_validators_and_nominators::<T>();
Expand Down Expand Up @@ -652,7 +652,7 @@ benchmarks! {
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();

for _ in 0 .. l {
staking_ledger.unlocking.push(unlock_chunk.clone())
staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap()
}
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
let original_bonded: BalanceOf<T> = staking_ledger.active;
Expand Down Expand Up @@ -702,7 +702,7 @@ benchmarks! {
stash: stash.clone(),
active: T::Currency::minimum_balance() - One::one(),
total: T::Currency::minimum_balance() - One::one(),
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
};
Ledger::<T>::insert(&controller, l);
Expand Down Expand Up @@ -788,15 +788,15 @@ benchmarks! {

#[extra]
do_slash {
let l in 1 .. MAX_UNLOCKING_CHUNKS as u32;
let l in 1 .. MaxUnlockingChunks::get() as u32;
let (stash, controller) = create_stash_controller::<T>(0, 100, Default::default())?;
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
value: 1u32.into(),
era: EraIndex::zero(),
};
for _ in 0 .. l {
staking_ledger.unlocking.push(unlock_chunk.clone())
staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap();
}
Ledger::<T>::insert(controller, staking_ledger);
let slash_amount = T::Currency::minimum_balance() * 10u32.into();
Expand Down
22 changes: 16 additions & 6 deletions frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ mod pallet;

use codec::{Decode, Encode, HasCompact};
use frame_support::{
parameter_types,
traits::{ConstU32, Currency, Get},
weights::Weight,
BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
Expand Down Expand Up @@ -347,6 +348,10 @@ type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;

parameter_types! {
pub MaxUnlockingChunks: u32 = 32;
}

/// Information regarding the active era (era in used in session).
#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ActiveEraInfo {
Expand Down Expand Up @@ -446,9 +451,10 @@ pub struct StakingLedger<AccountId, Balance: HasCompact> {
/// rounds.
#[codec(compact)]
pub active: Balance,
/// Any balance that is becoming free, which may eventually be transferred out
/// of the stash (assuming it doesn't get slashed first).
pub unlocking: Vec<UnlockChunk<Balance>>,
/// Any balance that is becoming free, which may eventually be transferred out of the stash
/// (assuming it doesn't get slashed first). It is assumed that this will be treated as a first
/// in, first out queue where the new (higher value) eras get pushed on the back.
pub unlocking: BoundedVec<UnlockChunk<Balance>, MaxUnlockingChunks>,
/// List of eras for which the stakers behind a validator have claimed rewards. Only updated
/// for validators.
pub claimed_rewards: Vec<EraIndex>,
Expand All @@ -463,7 +469,7 @@ impl<AccountId, Balance: HasCompact + Copy + Saturating + AtLeast32BitUnsigned +
stash,
total: Zero::zero(),
active: Zero::zero(),
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
}
}
Expand All @@ -472,7 +478,7 @@ impl<AccountId, Balance: HasCompact + Copy + Saturating + AtLeast32BitUnsigned +
/// total by the sum of their balances.
fn consolidate_unlocked(self, current_era: EraIndex) -> Self {
let mut total = self.total;
let unlocking = self
let unlocking: BoundedVec<_, _> = self
.unlocking
.into_iter()
.filter(|chunk| {
Expand All @@ -483,7 +489,11 @@ impl<AccountId, Balance: HasCompact + Copy + Saturating + AtLeast32BitUnsigned +
false
}
})
.collect();
.collect::<Vec<_>>()
.try_into()
.expect(
"filtering items from a bounded vec always leaves length less than bounds. qed",
);

Self {
stash: self.stash,
Expand Down
1 change: 1 addition & 0 deletions frame/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ impl crate::pallet::pallet::Config for Test {
type GenesisElectionProvider = Self::ElectionProvider;
// NOTE: consider a macro and use `UseNominatorsMap<Self>` as well.
type SortedListProvider = BagsList;
type MaxUnlockingChunks = ConstU32<32>;
type BenchmarkingConfig = TestBenchmarkingConfig;
type WeightInfo = ();
}
Expand Down
8 changes: 4 additions & 4 deletions frame/staking/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ impl<T: Config> ElectionDataProvider for Pallet<T> {
stash: voter.clone(),
active: stake,
total: stake,
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
},
);
Expand All @@ -946,7 +946,7 @@ impl<T: Config> ElectionDataProvider for Pallet<T> {
stash: target.clone(),
active: stake,
total: stake,
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
},
);
Expand Down Expand Up @@ -983,7 +983,7 @@ impl<T: Config> ElectionDataProvider for Pallet<T> {
stash: v.clone(),
active: stake,
total: stake,
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
},
);
Expand All @@ -1004,7 +1004,7 @@ impl<T: Config> ElectionDataProvider for Pallet<T> {
stash: v.clone(),
active: stake,
total: stake,
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: vec![],
},
);
Expand Down
40 changes: 29 additions & 11 deletions frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use frame_election_provider_support::SortedListProvider;
use frame_support::{
pallet_prelude::*,
traits::{
Currency, CurrencyToVote, EnsureOrigin, EstimateNextNewSession, Get, LockIdentifier,
LockableCurrency, OnUnbalanced, UnixTime,
Currency, CurrencyToVote, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get,
LockIdentifier, LockableCurrency, OnUnbalanced, UnixTime,
},
weights::Weight,
};
Expand All @@ -40,12 +40,11 @@ pub use impls::*;

use crate::{
log, slashing, weights::WeightInfo, ActiveEraInfo, BalanceOf, EraPayout, EraRewardPoints,
Exposure, Forcing, NegativeImbalanceOf, Nominations, PositiveImbalanceOf, Releases,
RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk,
Exposure, Forcing, MaxUnlockingChunks, NegativeImbalanceOf, Nominations, PositiveImbalanceOf,
Releases, RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk,
ValidatorPrefs,
};

pub const MAX_UNLOCKING_CHUNKS: usize = 32;
const STAKING_ID: LockIdentifier = *b"staking ";

#[frame_support::pallet]
Expand Down Expand Up @@ -157,6 +156,10 @@ pub mod pallet {
/// the bags-list is not desired, [`impls::UseNominatorsMap`] is likely the desired option.
type SortedListProvider: SortedListProvider<Self::AccountId>;

/// The maximum number of `unlocking` chunks a [`StakingLedger`] can have. Effectively
/// determines how many unique eras a staker may be unbonding in.
type MaxUnlockingChunks: Get<u32>;
Copy link
Contributor

Choose a reason for hiding this comment

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

should be exposed to metadata.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will make a quick PR


/// Some parameters of the benchmarking.
type BenchmarkingConfig: BenchmarkingConfig;

Expand Down Expand Up @@ -772,7 +775,7 @@ pub mod pallet {
stash,
total: value,
active: value,
unlocking: vec![],
unlocking: Default::default(),
claimed_rewards: (last_reward_era..current_era).collect(),
};
Self::update_ledger(&controller, &item);
Expand Down Expand Up @@ -837,7 +840,7 @@ pub mod pallet {
/// Once the unlock period is done, you can call `withdraw_unbonded` to actually move
/// the funds out of management ready for transfer.
///
/// No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`)
/// No more than a limited number of unlocking chunks (see `MaxUnlockingChunks`)
/// can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need
/// to be called first to remove some of the chunks (if possible).
///
Expand All @@ -854,7 +857,10 @@ pub mod pallet {
) -> DispatchResult {
let controller = ensure_signed(origin)?;
let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
ensure!(ledger.unlocking.len() < MAX_UNLOCKING_CHUNKS, Error::<T>::NoMoreChunks,);
ensure!(
ledger.unlocking.len() < MaxUnlockingChunks::get() as usize,
Error::<T>::NoMoreChunks,
);

let mut value = value.min(ledger.active);

Expand All @@ -881,7 +887,19 @@ pub mod pallet {

// Note: in case there is no current era it is fine to bond one era more.
let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get();
ledger.unlocking.push(UnlockChunk { value, era });
if let Some(mut chunk) =
ledger.unlocking.last_mut().filter(|chunk| chunk.era == era)
{
// To keep the chunk count down, we only keep one chunk per era. Since
// `unlocking` is a FiFo queue, if a chunk exists for `era` we know that it will
// be the last one.
chunk.value = chunk.value.defensive_saturating_add(value)
} else {
ledger
.unlocking
.try_push(UnlockChunk { value, era })
.map_err(|_| Error::<T>::NoMoreChunks)?;
};
// NOTE: ledger must be updated prior to calling `Self::weight_of`.
Self::update_ledger(&controller, &ledger);

Expand Down Expand Up @@ -1348,10 +1366,10 @@ pub mod pallet {
///
/// # <weight>
/// - Time complexity: O(L), where L is unlocking chunks
/// - Bounded by `MAX_UNLOCKING_CHUNKS`.
/// - Bounded by `MaxUnlockingChunks`.
/// - Storage changes: Can't increase storage, only decrease it.
/// # </weight>
#[pallet::weight(T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32))]
#[pallet::weight(T::WeightInfo::rebond(MaxUnlockingChunks::get() as u32))]
pub fn rebond(
origin: OriginFor<T>,
#[pallet::compact] value: BalanceOf<T>,
Expand Down
Loading