Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit b6d489e

Browse files
gui1117KiChjangshawntabrizi
authored
Fix pallet bags list and doc (#10231)
* fix bags list * improve doc * doc * inner doc * fix test * Update docs in frame/election-provider-support/src/lib.rs * fix staking impl * prepend unsafe to clear and regenerate * fix test Co-authored-by: Keith Yeung <[email protected]> Co-authored-by: Shawn Tabrizi <[email protected]>
1 parent 40f2483 commit b6d489e

File tree

13 files changed

+76
-50
lines changed

13 files changed

+76
-50
lines changed

frame/bags-list/remote-tests/src/migration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub async fn execute<Runtime: RuntimeT, Block: BlockT + DeserializeOwned>(
4747
log::info!(target: LOG_TARGET, "Nominator count: {}", pre_migrate_nominator_count);
4848

4949
// run the actual migration,
50-
let moved = <Runtime as pallet_staking::Config>::SortedListProvider::regenerate(
50+
let moved = <Runtime as pallet_staking::Config>::SortedListProvider::unsafe_regenerate(
5151
pallet_staking::Nominators::<Runtime>::iter().map(|(n, _)| n),
5252
pallet_staking::Pallet::<Runtime>::weight_of_fn(),
5353
);

frame/bags-list/src/benchmarks.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ frame_benchmarking::benchmarks! {
3535
// node in the destination in addition to the work we do otherwise. (2 W/R)
3636

3737
// clear any pre-existing storage.
38-
List::<T>::clear(None);
38+
// NOTE: safe to call outside block production
39+
List::<T>::unsafe_clear();
3940

4041
// define our origin and destination thresholds.
4142
let origin_bag_thresh = T::BagThresholds::get()[0];
@@ -94,7 +95,8 @@ frame_benchmarking::benchmarks! {
9495
// node in the destination in addition to the work we do otherwise. (2 W/R)
9596

9697
// clear any pre-existing storage.
97-
List::<T>::clear(None);
98+
// NOTE: safe to call outside block production
99+
List::<T>::unsafe_clear();
98100

99101
// define our origin and destination thresholds.
100102
let origin_bag_thresh = T::BagThresholds::get()[0];

frame/bags-list/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
//! the weights of accounts via [`frame_election_provider_support::VoteWeightProvider`].
2727
//!
2828
//! This pallet is not configurable at genesis. Whoever uses it should call appropriate functions of
29-
//! the `SortedListProvider` (e.g. `on_insert`, or `regenerate`) at their genesis.
29+
//! the `SortedListProvider` (e.g. `on_insert`, or `unsafe_regenerate`) at their genesis.
3030
//!
3131
//! # Goals
3232
//!
@@ -256,11 +256,14 @@ impl<T: Config> SortedListProvider<T::AccountId> for Pallet<T> {
256256
List::<T>::remove(id)
257257
}
258258

259-
fn regenerate(
259+
fn unsafe_regenerate(
260260
all: impl IntoIterator<Item = T::AccountId>,
261261
weight_of: Box<dyn Fn(&T::AccountId) -> VoteWeight>,
262262
) -> u32 {
263-
List::<T>::regenerate(all, weight_of)
263+
// NOTE: This call is unsafe for the same reason as SortedListProvider::unsafe_regenerate.
264+
// I.e. because it can lead to many storage accesses.
265+
// So it is ok to call it as caller must ensure the conditions.
266+
List::<T>::unsafe_regenerate(all, weight_of)
264267
}
265268

266269
#[cfg(feature = "std")]
@@ -273,8 +276,11 @@ impl<T: Config> SortedListProvider<T::AccountId> for Pallet<T> {
273276
Ok(())
274277
}
275278

276-
fn clear(maybe_count: Option<u32>) -> u32 {
277-
List::<T>::clear(maybe_count)
279+
fn unsafe_clear() {
280+
// NOTE: This call is unsafe for the same reason as SortedListProvider::unsafe_clear.
281+
// I.e. because it can lead to many storage accesses.
282+
// So it is ok to call it as caller must ensure the conditions.
283+
List::<T>::unsafe_clear()
278284
}
279285

280286
#[cfg(feature = "runtime-benchmarks")]

frame/bags-list/src/list/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,15 @@ pub fn notional_bag_for<T: Config>(weight: VoteWeight) -> VoteWeight {
7676
pub struct List<T: Config>(PhantomData<T>);
7777

7878
impl<T: Config> List<T> {
79-
/// Remove all data associated with the list from storage. Parameter `items` is the number of
80-
/// items to clear from the list.
79+
/// Remove all data associated with the list from storage.
8180
///
8281
/// ## WARNING
8382
///
84-
/// `None` will clear all items and should generally not be used in production as it could lead
85-
/// to a very large number of storage accesses.
86-
pub(crate) fn clear(maybe_count: Option<u32>) -> u32 {
87-
crate::ListBags::<T>::remove_all(maybe_count);
88-
let pre = crate::ListNodes::<T>::count();
89-
crate::ListNodes::<T>::remove_all(maybe_count);
90-
let post = crate::ListNodes::<T>::count();
91-
pre.saturating_sub(post)
83+
/// this function should generally not be used in production as it could lead to a very large
84+
/// number of storage accesses.
85+
pub(crate) fn unsafe_clear() {
86+
crate::ListBags::<T>::remove_all(None);
87+
crate::ListNodes::<T>::remove_all();
9288
}
9389

9490
/// Regenerate all of the data from the given ids.
@@ -100,11 +96,14 @@ impl<T: Config> List<T> {
10096
/// pallet using this `List`.
10197
///
10298
/// Returns the number of ids migrated.
103-
pub fn regenerate(
99+
pub fn unsafe_regenerate(
104100
all: impl IntoIterator<Item = T::AccountId>,
105101
weight_of: Box<dyn Fn(&T::AccountId) -> VoteWeight>,
106102
) -> u32 {
107-
Self::clear(None);
103+
// NOTE: This call is unsafe for the same reason as SortedListProvider::unsafe_regenerate.
104+
// I.e. because it can lead to many storage accesses.
105+
// So it is ok to call it as caller must ensure the conditions.
106+
Self::unsafe_clear();
108107
Self::insert_many(all, weight_of)
109108
}
110109

frame/election-provider-support/src/lib.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,15 +333,23 @@ pub trait SortedListProvider<AccountId> {
333333
/// Regenerate this list from scratch. Returns the count of items inserted.
334334
///
335335
/// This should typically only be used at a runtime upgrade.
336-
fn regenerate(
336+
///
337+
/// ## WARNING
338+
///
339+
/// This function should be called with care, regenerate will remove the current list write the
340+
/// new list, which can lead to too many storage accesses, exhausting the block weight.
341+
fn unsafe_regenerate(
337342
all: impl IntoIterator<Item = AccountId>,
338343
weight_of: Box<dyn Fn(&AccountId) -> VoteWeight>,
339344
) -> u32;
340345

341-
/// Remove `maybe_count` number of items from the list. Returns the number of items actually
342-
/// removed. WARNING: removes all items if `maybe_count` is `None`, which should never be done
343-
/// in production settings because it can lead to an unbounded amount of storage accesses.
344-
fn clear(maybe_count: Option<u32>) -> u32;
346+
/// Remove all items from the list.
347+
///
348+
/// ## WARNING
349+
///
350+
/// This function should never be called in production settings because it can lead to an
351+
/// unbounded amount of storage accesses.
352+
fn unsafe_clear();
345353

346354
/// Sanity check internal state of list. Only meant for debug compilation.
347355
fn sanity_check() -> Result<(), &'static str>;

frame/staking/src/migrations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub mod v8 {
4040
if StorageVersion::<T>::get() == crate::Releases::V7_0_0 {
4141
crate::log!(info, "migrating staking to Releases::V8_0_0");
4242

43-
let migrated = T::SortedListProvider::regenerate(
43+
let migrated = T::SortedListProvider::unsafe_regenerate(
4444
Nominators::<T>::iter().map(|(id, _)| id),
4545
Pallet::<T>::weight_of_fn(),
4646
);

frame/staking/src/pallet/impls.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ impl<T: Config> ElectionDataProvider<T::AccountId, BlockNumberFor<T>> for Pallet
971971
<Nominators<T>>::remove_all(None);
972972
<CounterForNominators<T>>::kill();
973973
<CounterForValidators<T>>::kill();
974-
let _ = T::SortedListProvider::clear(None);
974+
T::SortedListProvider::unsafe_clear();
975975
}
976976

977977
#[cfg(feature = "runtime-benchmarks")]
@@ -1299,7 +1299,7 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsMap<T> {
12991299
fn on_remove(_: &T::AccountId) {
13001300
// nothing to do on remove.
13011301
}
1302-
fn regenerate(
1302+
fn unsafe_regenerate(
13031303
_: impl IntoIterator<Item = T::AccountId>,
13041304
_: Box<dyn Fn(&T::AccountId) -> VoteWeight>,
13051305
) -> u32 {
@@ -1309,13 +1309,10 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsMap<T> {
13091309
fn sanity_check() -> Result<(), &'static str> {
13101310
Ok(())
13111311
}
1312-
fn clear(maybe_count: Option<u32>) -> u32 {
1313-
Nominators::<T>::remove_all(maybe_count);
1314-
if let Some(count) = maybe_count {
1315-
CounterForNominators::<T>::mutate(|noms| *noms - count);
1316-
count
1317-
} else {
1318-
CounterForNominators::<T>::take()
1319-
}
1312+
fn unsafe_clear() {
1313+
// NOTE: Caller must ensure this doesn't lead to too many storage accesses. This is a
1314+
// condition of SortedListProvider::unsafe_clear.
1315+
Nominators::<T>::remove_all(None);
1316+
CounterForNominators::<T>::take();
13201317
}
13211318
}

frame/staking/src/testing_utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ pub fn clear_validators_and_nominators<T: Config>() {
4242
// whenever we touch nominators counter we should update `T::SortedListProvider` as well.
4343
Nominators::<T>::remove_all(None);
4444
CounterForNominators::<T>::kill();
45-
let _ = T::SortedListProvider::clear(None);
45+
// NOTE: safe to call outside block production
46+
T::SortedListProvider::unsafe_clear();
4647
}
4748

4849
/// Grab a funded user.

frame/support/src/storage/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,11 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
11331133
crate::storage::storage_prefix(Self::module_prefix(), Self::storage_prefix())
11341134
}
11351135

1136-
/// Remove all value of the storage.
1136+
/// Remove all values of the storage in the overlay and up to `limit` in the backend.
1137+
///
1138+
/// All values in the client overlay will be deleted, if there is some `limit` then up to
1139+
/// `limit` values are deleted from the client backend, if `limit` is none then all values in
1140+
/// the client backend are deleted.
11371141
fn remove_all(limit: Option<u32>) -> sp_io::KillStorageResult {
11381142
sp_io::storage::clear_prefix(&Self::final_prefix(), limit)
11391143
}

frame/support/src/storage/types/counted_map.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ use crate::{
3131
Never,
3232
};
3333
use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen, Ref};
34-
use sp_arithmetic::traits::Bounded;
3534
use sp_runtime::traits::Saturating;
3635
use sp_std::prelude::*;
3736

@@ -263,10 +262,12 @@ where
263262
}
264263

265264
/// Remove all value of the storage.
266-
pub fn remove_all(maybe_limit: Option<u32>) {
267-
let leftover = Self::count().saturating_sub(maybe_limit.unwrap_or_else(Bounded::max_value));
268-
CounterFor::<Prefix>::set(leftover);
269-
<Self as MapWrapper>::Map::remove_all(maybe_limit);
265+
pub fn remove_all() {
266+
// NOTE: it is not possible to remove up to some limit because
267+
// `sp_io::storage::clear_prefix` and `StorageMap::remove_all` don't give the number of
268+
// value removed from the overlay.
269+
CounterFor::<Prefix>::set(0u32);
270+
<Self as MapWrapper>::Map::remove_all(None);
270271
}
271272

272273
/// Iter over all value of the storage.
@@ -678,7 +679,7 @@ mod test {
678679
assert_eq!(A::count(), 2);
679680

680681
// Remove all.
681-
A::remove_all(None);
682+
A::remove_all();
682683

683684
assert_eq!(A::count(), 0);
684685
assert_eq!(A::initialize_counter(), 0);
@@ -909,7 +910,7 @@ mod test {
909910
assert_eq!(B::count(), 2);
910911

911912
// Remove all.
912-
B::remove_all(None);
913+
B::remove_all();
913914

914915
assert_eq!(B::count(), 0);
915916
assert_eq!(B::initialize_counter(), 0);

0 commit comments

Comments
 (0)