diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e70adc6aa7238..ff47424a13a67 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -562,6 +562,7 @@ impl pallet_staking::Config for Runtime { // Alternatively, use pallet_staking::UseNominatorsMap 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; type BenchmarkingConfig = StakingBenchmarkingConfig; } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 2e4b6023f1e5f..152ec5ab206e7 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -198,6 +198,7 @@ impl pallet_staking::Config for Test { type ElectionProvider = onchain::OnChainSequentialPhragmen; type GenesisElectionProvider = Self::ElectionProvider; type SortedListProvider = pallet_staking::UseNominatorsMap; + type MaxUnlockingChunks = ConstU32<32>; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index d07f3136d9a0d..9dac33f979841 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -206,6 +206,7 @@ impl pallet_staking::Config for Test { type ElectionProvider = onchain::OnChainSequentialPhragmen; type GenesisElectionProvider = Self::ElectionProvider; type SortedListProvider = pallet_staking::UseNominatorsMap; + type MaxUnlockingChunks = ConstU32<32>; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 3b5e640867c5a..22c9af0f4c3cd 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -176,6 +176,7 @@ impl pallet_staking::Config for Test { type ElectionProvider = onchain::OnChainSequentialPhragmen; type GenesisElectionProvider = Self::ElectionProvider; type SortedListProvider = pallet_staking::UseNominatorsMap; + type MaxUnlockingChunks = ConstU32<32>; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 37305437ca095..19f5e81de4f17 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -182,6 +182,7 @@ impl pallet_staking::Config for Test { type ElectionProvider = onchain::OnChainSequentialPhragmen; type GenesisElectionProvider = Self::ElectionProvider; type SortedListProvider = pallet_staking::UseNominatorsMap; + type MaxUnlockingChunks = ConstU32<32>; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/frame/staking/README.md b/frame/staking/README.md index bbd5bd18f6e81..1b98cc7bf627b 100644 --- a/frame/staking/README.md +++ b/frame/staking/README.md @@ -184,7 +184,7 @@ Validators and nominators are rewarded at the end of each era. The total reward calculated using the era duration and the staking rate (the total amount of tokens staked by nominators and validators, divided by the total token supply). It aims to incentivize toward a defined staking rate. The full specification can be found -[here](https://research.web3.foundation/en/latest/polkadot/economics/1-token-economics.html#inflation-model). +[here](https://w3f-research.readthedocs.io/en/latest/polkadot/overview/2-token-economics.html#inflation-model). Total reward is split among validators and their nominators depending on the number of points they received during the era. Points are added to a validator using @@ -229,7 +229,7 @@ call can be used to actually withdraw the funds. Note that there is a limitation to the number of fund-chunks that can be scheduled to be unlocked in the future via [`unbond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.unbond). In case this maximum -(`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful +(`MaxUnlockingChunks`) is reached, the bonded account _must_ first wait until a successful call to `withdraw_unbonded` to remove some of the chunks. ### Election Algorithm diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 564172d912413..5df4ccdcb165b 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -618,7 +618,7 @@ benchmarks! { } rebond { - let l in 1 .. MAX_UNLOCKING_CHUNKS as u32; + let l in 1 .. ::MaxUnlockingChunks::get(); // clean up any existing state. clear_validators_and_nominators::(); @@ -652,7 +652,7 @@ benchmarks! { let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); for _ in 0 .. l { - staking_ledger.unlocking.push(unlock_chunk.clone()) + staking_ledger.unlocking.try_push(unlock_chunk.clone()).expect("Size is smaller than MaxUnlockingChunks, qed"); } Ledger::::insert(controller.clone(), staking_ledger.clone()); let original_bonded: BalanceOf = staking_ledger.active; @@ -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::::insert(&controller, l); @@ -788,7 +788,7 @@ benchmarks! { #[extra] do_slash { - let l in 1 .. MAX_UNLOCKING_CHUNKS as u32; + let l in 1 .. ::MaxUnlockingChunks::get(); let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { @@ -796,7 +796,7 @@ benchmarks! { era: EraIndex::zero(), }; for _ in 0 .. l { - staking_ledger.unlocking.push(unlock_chunk.clone()) + staking_ledger.unlocking.try_push(unlock_chunk.clone()).expect("Size is smaller than MaxUnlockingChunks, qed"); } Ledger::::insert(controller, staking_ledger); let slash_amount = T::Currency::minimum_balance() * 10u32.into(); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index f1d4cd1e07c2a..30d5afd428dcb 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -209,7 +209,7 @@ //! calculated using the era duration and the staking rate (the total amount of tokens staked by //! nominators and validators, divided by the total token supply). It aims to incentivize toward a //! defined staking rate. The full specification can be found -//! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model). +//! [here](https://w3f-research.readthedocs.io/en/latest/polkadot/overview/2-token-economics.html#inflation-model). //! //! Total reward is split among validators and their nominators depending on the number of points //! they received during the era. Points are added to a validator using @@ -254,7 +254,7 @@ //! //! Note that there is a limitation to the number of fund-chunks that can be scheduled to be //! unlocked in the future via [`unbond`](Call::unbond). In case this maximum -//! (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful +//! (`MaxUnlockingChunks`) is reached, the bonded account _must_ first wait until a successful //! call to `withdraw_unbonded` to remove some of the chunks. //! //! ### Election Algorithm @@ -301,9 +301,10 @@ mod pallet; use codec::{Decode, Encode, HasCompact}; use frame_support::{ - traits::{ConstU32, Currency, Get}, + pallet_prelude::ConstU32, + traits::{Currency, Get}, weights::Weight, - BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, + BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; use scale_info::TypeInfo; use sp_runtime::{ @@ -434,8 +435,15 @@ pub struct UnlockChunk { } /// The ledger of a (bonded) stash. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct StakingLedger { +/// `UnlockingLimit` is the size limit of the `WeakBoundedVec` representing `unlocking`. +#[derive(PartialEqNoBound, EqNoBound, CloneNoBound, Encode, Decode, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(UnlockingLimit))] +pub struct StakingLedger +where + AccountId: Eq + Clone, + Balance: HasCompact + Eq + Clone, + UnlockingLimit: Get, +{ /// The stash account whose balance is actually locked and at stake. pub stash: AccountId, /// The total amount of the stash's balance that we are currently accounting for. @@ -448,14 +456,17 @@ pub struct StakingLedger { 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>, + pub unlocking: BoundedVec, UnlockingLimit>, /// List of eras for which the stakers behind a validator have claimed rewards. Only updated /// for validators. pub claimed_rewards: Vec, } -impl - StakingLedger +impl StakingLedger +where + AccountId: Eq + Clone, + Balance: HasCompact + Copy + Saturating + AtLeast32BitUnsigned + Zero + Eq + Clone, + UnlockingLimit: Get, { /// Initializes the default object using the given `validator`. pub fn default_from(stash: AccountId) -> Self { @@ -463,7 +474,7 @@ impl>>() + .try_into() + .expect("unlocking vector only reduced in size here."); Self { stash: self.stash, @@ -522,9 +535,11 @@ impl StakingLedger +impl StakingLedger where - Balance: AtLeast32BitUnsigned + Saturating + Copy, + AccountId: Eq + Clone, + Balance: AtLeast32BitUnsigned + Saturating + Copy + Eq + Clone, + UnlockingLimit: Get, { /// Slash the validator for a given amount of balance. This can grow the value /// of the slash in the case that the validator has less than `minimum_balance` diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 95f305dfdd22a..12d3804b4e303 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -271,6 +271,7 @@ impl crate::pallet::pallet::Config for Test { type GenesisElectionProvider = Self::ElectionProvider; // NOTE: consider a macro and use `UseNominatorsMap` as well. type SortedListProvider = BagsList; + type MaxUnlockingChunks = ConstU32<32>; type BenchmarkingConfig = TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index ae20550cd40b6..414886aa937f6 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -203,7 +203,7 @@ impl Pallet { /// This will also update the stash lock. pub(crate) fn update_ledger( controller: &T::AccountId, - ledger: &StakingLedger>, + ledger: &StakingLedger, T::MaxUnlockingChunks>, ) { T::Currency::set_lock(STAKING_ID, &ledger.stash, ledger.total, WithdrawReasons::all()); >::insert(controller, ledger); @@ -928,7 +928,7 @@ impl ElectionDataProvider for Pallet { stash: voter.clone(), active: stake, total: stake, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); @@ -946,7 +946,7 @@ impl ElectionDataProvider for Pallet { stash: target.clone(), active: stake, total: stake, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); @@ -983,7 +983,7 @@ impl ElectionDataProvider for Pallet { stash: v.clone(), active: stake, total: stake, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); @@ -1004,7 +1004,7 @@ impl ElectionDataProvider for Pallet { stash: v.clone(), active: stake, total: stake, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 2a870fda063d3..d24bb96734b7c 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -45,7 +45,6 @@ use crate::{ ValidatorPrefs, }; -pub const MAX_UNLOCKING_CHUNKS: usize = 32; const STAKING_ID: LockIdentifier = *b"staking "; #[frame_support::pallet] @@ -147,6 +146,12 @@ pub mod pallet { #[pallet::constant] type MaxNominatorRewardedPerValidator: Get; + /// Note that there is a limitation to the number of fund-chunks that can be scheduled to be + /// unlocked in the future via [`unbond`](Call::unbond). In case this maximum + /// (`MaxUnlockingChunks`) is reached, the bonded account _must_ first wait until a + /// successful call to `withdraw_unbonded` to remove some of the chunks. + type MaxUnlockingChunks: Get; + /// The fraction of the validator set that is safe to be offending. /// After the threshold is reached a new era will be forced. type OffendingValidatorsThreshold: Get; @@ -218,8 +223,12 @@ pub mod pallet { /// Map from all (unlocked) "controller" accounts to the info regarding the staking. #[pallet::storage] #[pallet::getter(fn ledger)] - pub type Ledger = - StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger>>; + pub type Ledger = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + StakingLedger, T::MaxUnlockingChunks>, + >; /// Where the reward payment should be made. Keyed by stash. #[pallet::storage] @@ -771,7 +780,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); @@ -836,7 +845,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). /// @@ -853,7 +862,6 @@ pub mod pallet { ) -> DispatchResult { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - ensure!(ledger.unlocking.len() < MAX_UNLOCKING_CHUNKS, Error::::NoMoreChunks,); let mut value = value.min(ledger.active); @@ -880,7 +888,10 @@ 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 }); + ledger + .unlocking + .try_push(UnlockChunk { value, era }) + .map_err(|_| Error::::NoMoreChunks)?; // NOTE: ledger must be updated prior to calling `Self::weight_of`. Self::update_ledger(&controller, &ledger); @@ -1347,10 +1358,10 @@ pub mod pallet { /// /// # /// - 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. /// # - #[pallet::weight(T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32))] + #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get()))] pub fn rebond( origin: OriginFor, #[pallet::compact] value: BalanceOf, diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 538b75ead340b..58dea16217ec4 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -20,7 +20,7 @@ use super::{Event, *}; use frame_election_provider_support::{ElectionProvider, SortedListProvider, Support}; use frame_support::{ - assert_noop, assert_ok, + assert_noop, assert_ok, bounded_vec, dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{Currency, Get, ReservableCurrency}, @@ -104,7 +104,7 @@ fn basic_setup_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] }) ); @@ -115,7 +115,7 @@ fn basic_setup_works() { stash: 21, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] }) ); @@ -138,7 +138,7 @@ fn basic_setup_works() { stash: 101, total: 500, active: 500, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] }) ); @@ -382,7 +382,7 @@ fn staking_should_work() { stash: 3, total: 1500, active: 1500, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![0], }) ); @@ -936,7 +936,7 @@ fn reward_destination_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -959,7 +959,7 @@ fn reward_destination_works() { stash: 11, total: 1000 + total_payout_0, active: 1000 + total_payout_0, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![0], }) ); @@ -987,7 +987,7 @@ fn reward_destination_works() { stash: 11, total: 1000 + total_payout_0, active: 1000 + total_payout_0, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![0, 1], }) ); @@ -1016,7 +1016,7 @@ fn reward_destination_works() { stash: 11, total: 1000 + total_payout_0, active: 1000 + total_payout_0, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![0, 1, 2], }) ); @@ -1081,7 +1081,7 @@ fn bond_extra_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1098,7 +1098,7 @@ fn bond_extra_works() { stash: 11, total: 1000 + 100, active: 1000 + 100, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1112,7 +1112,7 @@ fn bond_extra_works() { stash: 11, total: 1000000, active: 1000000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1150,7 +1150,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1168,7 +1168,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 1000 + 100, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1189,7 +1189,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 1000 + 100, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1207,7 +1207,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk { value: 1000, era: 2 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], claimed_rewards: vec![] }), ); @@ -1220,7 +1220,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk { value: 1000, era: 2 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], claimed_rewards: vec![] }), ); @@ -1236,7 +1236,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk { value: 1000, era: 2 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], claimed_rewards: vec![] }), ); @@ -1252,7 +1252,7 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 100, active: 100, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] }), ); @@ -1263,7 +1263,8 @@ fn bond_extra_and_withdraw_unbonded_works() { fn too_many_unbond_calls_should_not_work() { ExtBuilder::default().build_and_execute(|| { // locked at era 0 until 3 - for _ in 0..MAX_UNLOCKING_CHUNKS - 1 { + let max: u32 = ::MaxUnlockingChunks::get(); + for _ in 0..max - 1 { assert_ok!(Staking::unbond(Origin::signed(10), 1)); } @@ -1310,7 +1311,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1329,7 +1330,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 100, - unlocking: vec![UnlockChunk { value: 900, era: 2 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 900, era: 2 + 3 }], claimed_rewards: vec![], }) ); @@ -1342,7 +1343,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1355,7 +1356,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 100, - unlocking: vec![UnlockChunk { value: 900, era: 5 }], + unlocking: bounded_vec![UnlockChunk { value: 900, era: 5 }], claimed_rewards: vec![], }) ); @@ -1368,7 +1369,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 600, - unlocking: vec![UnlockChunk { value: 400, era: 5 }], + unlocking: bounded_vec![UnlockChunk { value: 400, era: 5 }], claimed_rewards: vec![], }) ); @@ -1381,7 +1382,7 @@ fn rebond_works() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1396,10 +1397,10 @@ fn rebond_works() { stash: 11, total: 1000, active: 100, - unlocking: vec![ - UnlockChunk { value: 300, era: 5 }, + unlocking: bounded_vec![ UnlockChunk { value: 300, era: 5 }, UnlockChunk { value: 300, era: 5 }, + UnlockChunk { value: 300, era: 5 } ], claimed_rewards: vec![], }) @@ -1413,9 +1414,9 @@ fn rebond_works() { stash: 11, total: 1000, active: 600, - unlocking: vec![ + unlocking: bounded_vec![ UnlockChunk { value: 300, era: 5 }, - UnlockChunk { value: 100, era: 5 }, + UnlockChunk { value: 100, era: 5 } ], claimed_rewards: vec![], }) @@ -1443,7 +1444,7 @@ fn rebond_is_fifo() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1458,7 +1459,7 @@ fn rebond_is_fifo() { stash: 11, total: 1000, active: 600, - unlocking: vec![UnlockChunk { value: 400, era: 2 + 3 },], + unlocking: bounded_vec![UnlockChunk { value: 400, era: 2 + 3 }], claimed_rewards: vec![], }) ); @@ -1473,9 +1474,9 @@ fn rebond_is_fifo() { stash: 11, total: 1000, active: 300, - unlocking: vec![ + unlocking: bounded_vec![ UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 300, era: 3 + 3 }, + UnlockChunk { value: 300, era: 3 + 3 } ], claimed_rewards: vec![], }) @@ -1491,10 +1492,10 @@ fn rebond_is_fifo() { stash: 11, total: 1000, active: 100, - unlocking: vec![ + unlocking: bounded_vec![ UnlockChunk { value: 400, era: 2 + 3 }, UnlockChunk { value: 300, era: 3 + 3 }, - UnlockChunk { value: 200, era: 4 + 3 }, + UnlockChunk { value: 200, era: 4 + 3 } ], claimed_rewards: vec![], }) @@ -1508,9 +1509,9 @@ fn rebond_is_fifo() { stash: 11, total: 1000, active: 500, - unlocking: vec![ + unlocking: bounded_vec![ UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 100, era: 3 + 3 }, + UnlockChunk { value: 100, era: 3 + 3 } ], claimed_rewards: vec![], }) @@ -1540,7 +1541,7 @@ fn rebond_emits_right_value_in_event() { stash: 11, total: 1000, active: 100, - unlocking: vec![UnlockChunk { value: 900, era: 1 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }], claimed_rewards: vec![], }) ); @@ -1553,7 +1554,7 @@ fn rebond_emits_right_value_in_event() { stash: 11, total: 1000, active: 200, - unlocking: vec![UnlockChunk { value: 800, era: 1 + 3 }], + unlocking: bounded_vec![UnlockChunk { value: 800, era: 1 + 3 }], claimed_rewards: vec![], }) ); @@ -1568,7 +1569,7 @@ fn rebond_emits_right_value_in_event() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -1604,7 +1605,7 @@ fn reward_to_stake_works() { stash: 21, total: 69, active: 69, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); @@ -1665,7 +1666,7 @@ fn reap_stash_works() { stash: 11, total: 5, active: 5, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }, ); @@ -1784,7 +1785,7 @@ fn bond_with_no_staked_value() { stash: 1, active: 0, total: 5, - unlocking: vec![UnlockChunk { value: 5, era: 3 }], + unlocking: bounded_vec![UnlockChunk { value: 5, era: 3 }], claimed_rewards: vec![], }) ); @@ -3354,7 +3355,7 @@ fn test_payout_stakers() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![1] }) ); @@ -3376,7 +3377,7 @@ fn test_payout_stakers() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: (1..=14).collect() }) ); @@ -3397,7 +3398,7 @@ fn test_payout_stakers() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![15, 98] }) ); @@ -3412,7 +3413,7 @@ fn test_payout_stakers() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![15, 23, 42, 69, 98] }) ); @@ -3607,7 +3608,7 @@ fn bond_during_era_correctly_populates_claimed_rewards() { stash: 9, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![], }) ); @@ -3619,7 +3620,7 @@ fn bond_during_era_correctly_populates_claimed_rewards() { stash: 11, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: (0..5).collect(), }) ); @@ -3631,7 +3632,7 @@ fn bond_during_era_correctly_populates_claimed_rewards() { stash: 13, total: 1000, active: 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: (15..99).collect(), }) ); @@ -3850,7 +3851,7 @@ fn cannot_rebond_to_lower_than_ed() { stash: 21, total: 10 * 1000, active: 10 * 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] } ); @@ -3864,7 +3865,7 @@ fn cannot_rebond_to_lower_than_ed() { stash: 21, total: 10 * 1000, active: 0, - unlocking: vec![UnlockChunk { value: 10 * 1000, era: 3 }], + unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], claimed_rewards: vec![] } ); @@ -3887,7 +3888,7 @@ fn cannot_bond_extra_to_lower_than_ed() { stash: 21, total: 10 * 1000, active: 10 * 1000, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] } ); @@ -3901,7 +3902,7 @@ fn cannot_bond_extra_to_lower_than_ed() { stash: 21, total: 10 * 1000, active: 0, - unlocking: vec![UnlockChunk { value: 10 * 1000, era: 3 }], + unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], claimed_rewards: vec![] } ); @@ -3928,7 +3929,7 @@ fn do_not_die_when_active_is_ed() { stash: 21, total: 1000 * ed, active: 1000 * ed, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] } ); @@ -3945,7 +3946,7 @@ fn do_not_die_when_active_is_ed() { stash: 21, total: ed, active: ed, - unlocking: vec![], + unlocking: Default::default(), claimed_rewards: vec![] } ); diff --git a/frame/support/src/storage/bounded_vec.rs b/frame/support/src/storage/bounded_vec.rs index 9298a5d98b003..75be41647c424 100644 --- a/frame/support/src/storage/bounded_vec.rs +++ b/frame/support/src/storage/bounded_vec.rs @@ -26,9 +26,9 @@ use crate::{ use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use core::{ ops::{Deref, Index, IndexMut}, - slice::SliceIndex, + slice::{IterMut, SliceIndex}, }; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_std::{marker::PhantomData, ops::RangeBounds, prelude::*, vec::Drain}; /// A bounded vector. /// @@ -128,6 +128,24 @@ impl BoundedVec { self.0.remove(index) } + /// Exactly the same semantics as [`Vec::drain`]. + pub fn drain(&mut self, range: R) -> Drain<'_, T> + where + R: RangeBounds, + { + self.0.drain(range) + } + + /// Exactly the same semantics as the underlying [`[]::last_mut`]. + pub fn last_mut(&mut self) -> Option<&mut T> { + self.0.last_mut() + } + + /// Exactly the same semantics as [`[]::iter_mut`]. + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + self.0.iter_mut() + } + /// Exactly the same semantics as [`Vec::swap_remove`]. /// /// # Panics