From f5316e903dfe9978eeef098dced9a58a20ba0e53 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 15 Dec 2021 01:27:13 +0100 Subject: [PATCH] Fix update_lock (#10485) * Fix update_lock * Fixes * Formatting * add `inc_consumers_without_limits` to session too Co-authored-by: Shawn Tabrizi --- frame/assets/src/lib.rs | 5 ++-- frame/balances/src/lib.rs | 2 +- frame/session/src/lib.rs | 2 +- frame/system/src/lib.rs | 58 ++++++++++++++++++++++++++++++++++----- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index b86661d9fa4fd..6643cc177460c 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -467,8 +467,9 @@ pub mod pallet { BadWitness, /// Minimum balance should be non-zero. MinBalanceZero, - /// No provider reference exists to allow a non-zero balance of a non-self-sufficient - /// asset. + /// Unable to increment the consumer reference counters on the account. Either no provider + /// reference exists to allow a non-zero balance of a non-self-sufficient asset, or the + /// maximum number of consumers has been reached. NoProvider, /// Invalid metadata given. BadMetadata, diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 4471ed91a9afc..6919c66f7211e 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -971,7 +971,7 @@ impl, I: 'static> Pallet { } else { Locks::::insert(who, bounded_locks); if !existed { - if system::Pallet::::inc_consumers(who).is_err() { + if system::Pallet::::inc_consumers_without_limit(who).is_err() { // No providers for the locks. This is impossible under normal circumstances // since the funds that are under the lock will themselves be stored in the // account and therefore will need a reference. diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 0f80494550849..e88f143065052 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -448,7 +448,7 @@ pub mod pallet { for (account, val, keys) in self.keys.iter().cloned() { >::inner_set_keys(&val, keys) .expect("genesis config must not contain duplicates; qed"); - if frame_system::Pallet::::inc_consumers(&account).is_err() { + if frame_system::Pallet::::inc_consumers_without_limit(&account).is_err() { // This will leak a provider reference, however it only happens once (at // genesis) so it's really not a big deal and we assume that the user wants to // do this since it's the only way a non-endowed account can contain a session diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 6bdaf8bd4b505..e167f44ffd88c 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -85,8 +85,8 @@ use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, storage, traits::{ - Contains, EnsureOrigin, Get, HandleLifetime, OnKilledAccount, OnNewAccount, OriginTrait, - PalletInfo, SortedMembers, StoredMap, + ConstU32, Contains, EnsureOrigin, Get, HandleLifetime, OnKilledAccount, OnNewAccount, + OriginTrait, PalletInfo, SortedMembers, StoredMap, }, weights::{ extract_actual_weight, DispatchClass, DispatchInfo, PerDispatchClass, RuntimeDbWeight, @@ -154,6 +154,36 @@ impl SetCode for () { } } +/// Numeric limits over the ability to add a consumer ref using `inc_consumers`. +pub trait ConsumerLimits { + /// The number of consumers over which `inc_consumers` will cease to work. + fn max_consumers() -> RefCount; + /// The maximum number of additional consumers expected to be over be added at once using + /// `inc_consumers_without_limit`. + /// + /// Note: This is not enforced and it's up to the chain's author to ensure this reflects the + /// actual situation. + fn max_overflow() -> RefCount; +} + +impl ConsumerLimits for ConstU32 { + fn max_consumers() -> RefCount { + Z + } + fn max_overflow() -> RefCount { + Z + } +} + +impl, MaxOverflow: Get> ConsumerLimits for (MaxNormal, MaxOverflow) { + fn max_consumers() -> RefCount { + MaxNormal::get() + } + fn max_overflow() -> RefCount { + MaxOverflow::get() + } +} + #[frame_support::pallet] pub mod pallet { use crate::{self as frame_system, pallet_prelude::*, *}; @@ -310,8 +340,7 @@ pub mod pallet { type OnSetCode: SetCode; /// The maximum number of consumers allowed on a single account. - #[pallet::constant] - type MaxConsumers: Get; + type MaxConsumers: ConsumerLimits; } #[pallet::pallet] @@ -1122,11 +1151,12 @@ impl Pallet { /// Increment the reference counter on an account. /// - /// The account `who`'s `providers` must be non-zero or this will return an error. + /// The account `who`'s `providers` must be non-zero and the current number of consumers must + /// be less than `MaxConsumers::max_consumers()` or this will return an error. pub fn inc_consumers(who: &T::AccountId) -> Result<(), DispatchError> { Account::::try_mutate(who, |a| { if a.providers > 0 { - if a.consumers < T::MaxConsumers::get() { + if a.consumers < T::MaxConsumers::max_consumers() { a.consumers = a.consumers.saturating_add(1); Ok(()) } else { @@ -1138,6 +1168,20 @@ impl Pallet { }) } + /// Increment the reference counter on an account, ignoring the `MaxConsumers` limits. + /// + /// The account `who`'s `providers` must be non-zero or this will return an error. + pub fn inc_consumers_without_limit(who: &T::AccountId) -> Result<(), DispatchError> { + Account::::try_mutate(who, |a| { + if a.providers > 0 { + a.consumers = a.consumers.saturating_add(1); + Ok(()) + } else { + Err(DispatchError::NoProviders) + } + }) + } + /// Decrement the reference counter on an account. This *MUST* only be done once for every time /// you called `inc_consumers` on `who`. pub fn dec_consumers(who: &T::AccountId) { @@ -1172,7 +1216,7 @@ impl Pallet { /// True if the account has at least one provider reference. pub fn can_inc_consumer(who: &T::AccountId) -> bool { let a = Account::::get(who); - a.providers > 0 && a.consumers < T::MaxConsumers::get() + a.providers > 0 && a.consumers < T::MaxConsumers::max_consumers() } /// Deposits an event into this block's event record.