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 13 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
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;
}
Comment thread
emostov marked this conversation as resolved.

/// 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 pushing on the back.
Comment thread
emostov marked this conversation as resolved.
Outdated
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()
Comment thread
emostov marked this conversation as resolved.
.expect(
"filtering items from a bounded vec always leaves length less than bounds. qed",
);

Self {
stash: self.stash,
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
34 changes: 24 additions & 10 deletions frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use frame_support::{
};
use frame_system::{ensure_root, ensure_signed, offchain::SendTransactionTypes, pallet_prelude::*};
use sp_runtime::{
traits::{CheckedSub, SaturatedConversion, StaticLookup, Zero},
traits::{CheckedSub, SaturatedConversion, Saturating, StaticLookup, Zero},
DispatchError, Perbill, Percent,
};
use sp_staking::{EraIndex, SessionIndex};
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 @@ -771,7 +770,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 @@ -836,7 +835,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 @@ -853,7 +852,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 @@ -880,7 +882,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.saturating_add(value)
Comment thread
ggwpez marked this conversation as resolved.
Outdated
} else {
ledger
.unlocking
.try_push(UnlockChunk { value, era })
.map_err(|_| Error::<T>::NoMoreChunks)?;
Comment thread
ggwpez marked this conversation as resolved.
};
// NOTE: ledger must be updated prior to calling `Self::weight_of`.
Self::update_ledger(&controller, &ledger);

Expand Down Expand Up @@ -1347,10 +1361,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