From 0f6b5734e8ff52199987b496b2ef7bfd51655198 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 27 Mar 2021 14:37:13 +0100 Subject: [PATCH] Repot frame_support::traits; introduce some new currency stuff (#8435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reservable, Transferrable Fungible(s), plus adapters. * Repot into new dir * Imbalances for Fungibles * Repot and balanced fungible. * Clean up names and bridge-over Imbalanced. * Repot frame_support::trait. Finally. * Make build. * Docs * Good errors * Fix tests. Implement fungible::Inspect for Balances. * Implement additional traits for Balances. * Revert UI test "fixes" * Fix UI error * Fix UI test * Fixes * Update lock * Grumbles * Grumbles * Fixes Co-authored-by: Bastian Köcher --- frame/assets/src/lib.rs | 105 +- frame/balances/src/lib.rs | 174 +- frame/support/src/traits.rs | 2380 +---------------- frame/support/src/traits/dispatch.rs | 87 + frame/support/src/traits/filter.rs | 282 ++ frame/support/src/traits/hooks.rs | 349 +++ frame/support/src/traits/members.rs | 142 + frame/support/src/traits/metadata.rs | 168 ++ frame/support/src/traits/misc.rs | 271 ++ frame/support/src/traits/randomness.rs | 54 + frame/support/src/traits/schedule.rs | 133 + frame/support/src/traits/storage.rs | 45 + frame/support/src/traits/stored_map.rs | 141 + frame/support/src/traits/tokens.rs | 28 + frame/support/src/traits/tokens/currency.rs | 208 ++ .../src/traits/tokens/currency/lockable.rs | 104 + .../src/traits/tokens/currency/reservable.rs | 83 + frame/support/src/traits/tokens/fungible.rs | 218 ++ .../src/traits/tokens/fungible/balanced.rs | 363 +++ .../src/traits/tokens/fungible/imbalance.rs | 162 ++ frame/support/src/traits/tokens/fungibles.rs | 143 + .../src/traits/tokens/fungibles/balanced.rs | 375 +++ .../src/traits/tokens/fungibles/imbalance.rs | 169 ++ frame/support/src/traits/tokens/imbalance.rs | 174 ++ .../traits/tokens/imbalance/on_unbalanced.rs | 50 + .../tokens/imbalance/signed_imbalance.rs | 69 + .../traits/tokens/imbalance/split_two_ways.rs | 51 + frame/support/src/traits/tokens/misc.rs | 164 ++ frame/support/src/traits/validation.rs | 242 ++ frame/support/src/traits/voting.rs | 88 + .../genesis_default_not_satisfied.stderr | 18 +- frame/system/src/lib.rs | 13 +- frame/transaction-payment/src/payment.rs | 1 + primitives/runtime/src/lib.rs | 52 + 34 files changed, 4741 insertions(+), 2365 deletions(-) create mode 100644 frame/support/src/traits/dispatch.rs create mode 100644 frame/support/src/traits/filter.rs create mode 100644 frame/support/src/traits/hooks.rs create mode 100644 frame/support/src/traits/members.rs create mode 100644 frame/support/src/traits/metadata.rs create mode 100644 frame/support/src/traits/misc.rs create mode 100644 frame/support/src/traits/randomness.rs create mode 100644 frame/support/src/traits/schedule.rs create mode 100644 frame/support/src/traits/storage.rs create mode 100644 frame/support/src/traits/stored_map.rs create mode 100644 frame/support/src/traits/tokens.rs create mode 100644 frame/support/src/traits/tokens/currency.rs create mode 100644 frame/support/src/traits/tokens/currency/lockable.rs create mode 100644 frame/support/src/traits/tokens/currency/reservable.rs create mode 100644 frame/support/src/traits/tokens/fungible.rs create mode 100644 frame/support/src/traits/tokens/fungible/balanced.rs create mode 100644 frame/support/src/traits/tokens/fungible/imbalance.rs create mode 100644 frame/support/src/traits/tokens/fungibles.rs create mode 100644 frame/support/src/traits/tokens/fungibles/balanced.rs create mode 100644 frame/support/src/traits/tokens/fungibles/imbalance.rs create mode 100644 frame/support/src/traits/tokens/imbalance.rs create mode 100644 frame/support/src/traits/tokens/imbalance/on_unbalanced.rs create mode 100644 frame/support/src/traits/tokens/imbalance/signed_imbalance.rs create mode 100644 frame/support/src/traits/tokens/imbalance/split_two_ways.rs create mode 100644 frame/support/src/traits/tokens/misc.rs create mode 100644 frame/support/src/traits/validation.rs create mode 100644 frame/support/src/traits/voting.rs diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 65630cf1ba565..db7338e36e535 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -139,15 +139,25 @@ use sp_runtime::{ }; use codec::{Encode, Decode, HasCompact}; use frame_support::{ensure, dispatch::{DispatchError, DispatchResult}}; -use frame_support::traits::{Currency, ReservableCurrency, BalanceStatus::Reserved, Fungibles}; +use frame_support::traits::{Currency, ReservableCurrency, BalanceStatus::Reserved}; +use frame_support::traits::tokens::{WithdrawConsequence, DepositConsequence, fungibles}; use frame_system::Config as SystemConfig; + pub use weights::WeightInfo; pub use pallet::*; -impl Fungibles<::AccountId> for Pallet { +impl fungibles::Inspect<::AccountId> for Pallet { type AssetId = T::AssetId; type Balance = T::Balance; + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + Asset::::get(asset).map(|x| x.supply).unwrap_or_else(Zero::zero) + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + Asset::::get(asset).map(|x| x.min_balance).unwrap_or_else(Zero::zero) + } + fn balance( asset: Self::AssetId, who: &::AccountId, @@ -159,24 +169,45 @@ impl Fungibles<::AccountId> for Pallet { asset: Self::AssetId, who: &::AccountId, amount: Self::Balance, - ) -> bool { + ) -> DepositConsequence { Pallet::::can_deposit(asset, who, amount) } + fn can_withdraw( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + Pallet::::can_withdraw(asset, who, amount) + } +} + +impl fungibles::Mutate<::AccountId> for Pallet { fn deposit( asset: Self::AssetId, - who: ::AccountId, + who: &::AccountId, amount: Self::Balance, ) -> DispatchResult { - Pallet::::increase_balance(asset, who, amount, None) + Pallet::::increase_balance(asset, who.clone(), amount, None) } fn withdraw( asset: Self::AssetId, - who: ::AccountId, + who: &::AccountId, amount: Self::Balance, - ) -> DispatchResult { - Pallet::::reduce_balance(asset, who, amount, None) + ) -> Result { + Pallet::::reduce_balance(asset, who.clone(), amount, None) + } +} + +impl fungibles::Transfer for Pallet { + fn transfer( + asset: Self::AssetId, + source: &T::AccountId, + dest: &T::AccountId, + amount: T::Balance, + ) -> Result { + >::transfer(asset, source, dest, amount) } } @@ -671,7 +702,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let who = T::Lookup::lookup(who)?; - Self::reduce_balance(id, who, amount, Some(origin)) + Self::reduce_balance(id, who, amount, Some(origin)).map(|_| ()) } /// Move some assets from the sender account to another. @@ -1380,21 +1411,57 @@ impl Pallet { d.accounts = d.accounts.saturating_sub(1); } - fn can_deposit(id: T::AssetId, who: &T::AccountId, amount: T::Balance) -> bool { + fn can_deposit(id: T::AssetId, who: &T::AccountId, amount: T::Balance) -> DepositConsequence { let details = match Asset::::get(id) { Some(details) => details, - None => return false, + None => return DepositConsequence::UnknownAsset, }; - if details.supply.checked_add(&amount).is_none() { return false } + if details.supply.checked_add(&amount).is_none() { + return DepositConsequence::Overflow + } let account = Account::::get(id, who); - if account.balance.checked_add(&amount).is_none() { return false } + if account.balance.checked_add(&amount).is_none() { + return DepositConsequence::Overflow + } if account.balance.is_zero() { - if amount < details.min_balance { return false } - if !details.is_sufficient && frame_system::Pallet::::providers(who) == 0 { return false } - if details.is_sufficient && details.sufficients.checked_add(1).is_none() { return false } + if amount < details.min_balance { + return DepositConsequence::BelowMinimum + } + if !details.is_sufficient && frame_system::Pallet::::providers(who) == 0 { + return DepositConsequence::CannotCreate + } + if details.is_sufficient && details.sufficients.checked_add(1).is_none() { + return DepositConsequence::Overflow + } } - true + DepositConsequence::Success + } + + fn can_withdraw( + id: T::AssetId, + who: &T::AccountId, + amount: T::Balance, + ) -> WithdrawConsequence { + let details = match Asset::::get(id) { + Some(details) => details, + None => return WithdrawConsequence::UnknownAsset, + }; + if details.supply.checked_sub(&amount).is_none() { + return WithdrawConsequence::Underflow + } + let account = Account::::get(id, who); + if let Some(rest) = account.balance.checked_sub(&amount) { + if rest < details.min_balance { + WithdrawConsequence::ReducedToZero(rest) + } else { + // NOTE: this assumes (correctly) that the token won't be a provider. If that ever + // changes, this will need to change. + WithdrawConsequence::Success + } + } else { + WithdrawConsequence::NoFunds + } } fn increase_balance( @@ -1430,7 +1497,7 @@ impl Pallet { target: T::AccountId, amount: T::Balance, maybe_check_admin: Option, - ) -> DispatchResult { + ) -> Result { Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; if let Some(check_admin) = maybe_check_admin { @@ -1458,7 +1525,7 @@ impl Pallet { d.supply = d.supply.saturating_sub(burned); Self::deposit_event(Event::Burned(id, target, burned)); - Ok(()) + Ok(burned) }) } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index fc4dab7cec4a7..a2e858799b0e4 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -682,6 +682,78 @@ impl, I: 'static> Pallet { } } + fn deposit_consequence( + _who: &T::AccountId, + amount: T::Balance, + account: &AccountData, + ) -> DepositConsequence { + if amount.is_zero() { return DepositConsequence::Success } + + if TotalIssuance::::get().checked_add(&amount).is_none() { + return DepositConsequence::Overflow + } + + let new_total_balance = match account.total().checked_add(&amount) { + Some(x) => x, + None => return DepositConsequence::Overflow, + }; + + if new_total_balance < T::ExistentialDeposit::get() { + return DepositConsequence::BelowMinimum + } + + // NOTE: We assume that we are a provider, so don't need to do any checks in the + // case of account creation. + + DepositConsequence::Success + } + + fn withdraw_consequence( + who: &T::AccountId, + amount: T::Balance, + account: &AccountData, + ) -> WithdrawConsequence { + if amount.is_zero() { return WithdrawConsequence::Success } + + if TotalIssuance::::get().checked_sub(&amount).is_none() { + return WithdrawConsequence::Underflow + } + + let new_total_balance = match account.total().checked_sub(&amount) { + Some(x) => x, + None => return WithdrawConsequence::NoFunds, + }; + + // Provider restriction - total account balance cannot be reduced to zero if it cannot + // sustain the loss of a provider reference. + // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, + // then this will need to adapt accordingly. + let ed = T::ExistentialDeposit::get(); + let success = if new_total_balance < ed { + if frame_system::Pallet::::can_dec_provider(who) { + WithdrawConsequence::ReducedToZero(new_total_balance) + } else { + return WithdrawConsequence::WouldDie + } + } else { + WithdrawConsequence::Success + }; + + // Enough free funds to have them be reduced. + let new_free_balance = match account.free.checked_sub(&amount) { + Some(b) => b, + None => return WithdrawConsequence::NoFunds, + }; + + // Eventual free funds must be no less than the frozen balance. + let min_balance = account.frozen(Reasons::All); + if new_free_balance < min_balance { + return WithdrawConsequence::Frozen + } + + success + } + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce /// `ExistentialDeposit` law, annulling the account as needed. /// @@ -803,6 +875,75 @@ impl, I: 'static> Pallet { } } +use frame_support::traits::tokens::{fungible, DepositConsequence, WithdrawConsequence}; + +impl, I: 'static> fungible::Inspect for Pallet { + type Balance = T::Balance; + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + fn balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).total() + } + fn can_deposit(who: &T::AccountId, amount: Self::Balance) -> DepositConsequence { + Self::deposit_consequence(who, amount, &Self::account(who)) + } + fn can_withdraw(who: &T::AccountId, amount: Self::Balance) -> WithdrawConsequence { + Self::withdraw_consequence(who, amount, &Self::account(who)) + } +} + +impl, I: 'static> fungible::Mutate for Pallet { + fn deposit(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + if amount.is_zero() { return Ok(()) } + Self::try_mutate_account(who, |account, _is_new| -> DispatchResult { + Self::deposit_consequence(who, amount, &account).into_result()?; + account.free += amount; + Ok(()) + })?; + TotalIssuance::::mutate(|t| *t += amount); + Ok(()) + } + + fn withdraw(who: &T::AccountId, amount: Self::Balance) -> Result { + if amount.is_zero() { return Ok(Self::Balance::zero()); } + + let actual = Self::try_mutate_account(who, |account, _is_new| -> Result { + let extra = Self::withdraw_consequence(who, amount, &account).into_result()?; + let actual = amount + extra; + account.free -= actual; + Ok(actual) + })?; + TotalIssuance::::mutate(|t| *t -= actual); + Ok(actual) + } +} + +impl, I: 'static> fungible::Transfer for Pallet { + fn transfer( + source: &T::AccountId, + dest: &T::AccountId, + amount: T::Balance, + ) -> Result { + >::transfer(source, dest, amount) + } +} + +impl, I: 'static> fungible::Unbalanced for Pallet { + fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + Self::mutate_account(who, |account| account.free = amount)?; + Ok(()) + } + + fn set_total_issuance(amount: Self::Balance) { + TotalIssuance::::mutate(|t| *t = amount); + } +} + // wrapping these imbalances in a private module is necessary to ensure absolute privacy // of the inner member. mod imbalances { @@ -811,6 +952,7 @@ mod imbalances { TryDrop, RuntimeDebug, }; use sp_std::mem; + use frame_support::traits::SameOrOther; /// Opaque, move-only struct with private fields that serves as a token denoting that /// funds have been created without any equal and opposite accounting. @@ -844,6 +986,12 @@ mod imbalances { } } + impl, I: 'static> Default for PositiveImbalance { + fn default() -> Self { + Self::zero() + } + } + impl, I: 'static> Imbalance for PositiveImbalance { type Opposite = NegativeImbalance; @@ -874,14 +1022,16 @@ mod imbalances { self.0 = self.0.saturating_add(other.0); mem::forget(other); } - fn offset(self, other: Self::Opposite) -> result::Result { + fn offset(self, other: Self::Opposite) -> SameOrOther { let (a, b) = (self.0, other.0); mem::forget((self, other)); - if a >= b { - Ok(Self(a - b)) + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(NegativeImbalance::new(b - a)) } else { - Err(NegativeImbalance::new(b - a)) + SameOrOther::None } } fn peek(&self) -> T::Balance { @@ -895,6 +1045,12 @@ mod imbalances { } } + impl, I: 'static> Default for NegativeImbalance { + fn default() -> Self { + Self::zero() + } + } + impl, I: 'static> Imbalance for NegativeImbalance { type Opposite = PositiveImbalance; @@ -925,14 +1081,16 @@ mod imbalances { self.0 = self.0.saturating_add(other.0); mem::forget(other); } - fn offset(self, other: Self::Opposite) -> result::Result { + fn offset(self, other: Self::Opposite) -> SameOrOther { let (a, b) = (self.0, other.0); mem::forget((self, other)); - if a >= b { - Ok(Self(a - b)) + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(PositiveImbalance::new(b - a)) } else { - Err(PositiveImbalance::new(b - a)) + SameOrOther::None } } fn peek(&self) -> T::Balance { diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 9f8afdf7c7546..391fa0b538981 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -15,2347 +15,71 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traits for FRAME. +//! Traits and associated utilities for use in the FRAME environment. //! //! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module. -use sp_std::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug}; -use codec::{FullCodec, Codec, Encode, Decode, EncodeLike}; -use sp_core::u32_trait::Value as U32; -use sp_runtime::{ - traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Block as BlockT, BadOrigin, Convert, - MaybeSerializeDeserialize, SaturatedConversion, Saturating, StoredMapError, - UniqueSaturatedFrom, UniqueSaturatedInto, Zero, - }, - BoundToRuntimeAppPublic, ConsensusEngineId, DispatchError, DispatchResult, Percent, - RuntimeAppPublic, RuntimeDebug, +pub mod tokens; +pub use tokens::fungible::{ + Inspect as InspectFungible, Mutate as MutateFungible, Transfer as TransferFungible, + Reserve as ReserveFungible, Balanced as BalancedFungible, Unbalanced as UnbalancedFungible, + ItemOf, }; -use sp_staking::SessionIndex; -use crate::dispatch::Parameter; -use crate::storage::StorageMap; -use crate::weights::Weight; -use bitflags::bitflags; -use impl_trait_for_tuples::impl_for_tuples; - -/// Re-expected for the macro. -#[doc(hidden)] -pub use sp_std::{mem::{swap, take}, cell::RefCell, vec::Vec, boxed::Box}; - -/// A trait for online node inspection in a session. -/// -/// Something that can give information about the current validator set. -pub trait ValidatorSet { - /// Type for representing validator id in a session. - type ValidatorId: Parameter; - /// A type for converting `AccountId` to `ValidatorId`. - type ValidatorIdOf: Convert>; - - /// Returns current session index. - fn session_index() -> SessionIndex; - - /// Returns the active set of validators. - fn validators() -> Vec; -} - -/// [`ValidatorSet`] combined with an identification. -pub trait ValidatorSetWithIdentification: ValidatorSet { - /// Full identification of `ValidatorId`. - type Identification: Parameter; - /// A type for converting `ValidatorId` to `Identification`. - type IdentificationOf: Convert>; -} - -/// A session handler for specific key type. -pub trait OneSessionHandler: BoundToRuntimeAppPublic { - /// The key type expected. - type Key: Decode + Default + RuntimeAppPublic; - - /// The given validator set will be used for the genesis session. - /// It is guaranteed that the given validator set will also be used - /// for the second session, therefore the first call to `on_new_session` - /// should provide the same validator set. - fn on_genesis_session<'a, I: 'a>(validators: I) - where I: Iterator, ValidatorId: 'a; - - /// Session set has changed; act appropriately. Note that this can be called - /// before initialization of your module. - /// - /// `changed` is true when at least one of the session keys - /// or the underlying economic identities/distribution behind one the - /// session keys has changed, false otherwise. - /// - /// The `validators` are the validators of the incoming session, and `queued_validators` - /// will follow. - fn on_new_session<'a, I: 'a>( - changed: bool, - validators: I, - queued_validators: I, - ) where I: Iterator, ValidatorId: 'a; - - /// A notification for end of the session. - /// - /// Note it is triggered before any `SessionManager::end_session` handlers, - /// so we can still affect the validator set. - fn on_before_session_ending() {} - - /// A validator got disabled. Act accordingly until a new session begins. - fn on_disabled(_validator_index: usize); -} - -/// Simple trait for providing a filter over a reference to some type. -pub trait Filter { - /// Determine if a given value should be allowed through the filter (returns `true`) or not. - fn filter(_: &T) -> bool; -} - -impl Filter for () { - fn filter(_: &T) -> bool { true } -} - -/// Trait to add a constraint onto the filter. -pub trait FilterStack: Filter { - /// The type used to archive the stack. - type Stack; - - /// Add a new `constraint` onto the filter. - fn push(constraint: impl Fn(&T) -> bool + 'static); - - /// Removes the most recently pushed, and not-yet-popped, constraint from the filter. - fn pop(); - - /// Clear the filter, returning a value that may be used later to `restore` it. - fn take() -> Self::Stack; - - /// Restore the filter from a previous `take` operation. - fn restore(taken: Self::Stack); -} - -/// Guard type for pushing a constraint to a `FilterStack` and popping when dropped. -pub struct FilterStackGuard, T>(PhantomData<(F, T)>); - -/// Guard type for clearing all pushed constraints from a `FilterStack` and reinstating them when -/// dropped. -pub struct ClearFilterGuard, T>(Option, PhantomData); - -impl, T> FilterStackGuard { - /// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when - /// this instance is dropped. - pub fn new(constraint: impl Fn(&T) -> bool + 'static) -> Self { - F::push(constraint); - Self(PhantomData) - } -} - -impl, T> Drop for FilterStackGuard { - fn drop(&mut self) { - F::pop(); - } -} - -impl, T> ClearFilterGuard { - /// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when - /// this instance is dropped. - pub fn new() -> Self { - Self(Some(F::take()), PhantomData) - } -} - -impl, T> Drop for ClearFilterGuard { - fn drop(&mut self) { - if let Some(taken) = self.0.take() { - F::restore(taken); - } - } -} - -/// Simple trait for providing a filter over a reference to some type, given an instance of itself. -pub trait InstanceFilter: Sized + Send + Sync { - /// Determine if a given value should be allowed through the filter (returns `true`) or not. - fn filter(&self, _: &T) -> bool; - - /// Determines whether `self` matches at least everything that `_o` does. - fn is_superset(&self, _o: &Self) -> bool { false } -} - -impl InstanceFilter for () { - fn filter(&self, _: &T) -> bool { true } - fn is_superset(&self, _o: &Self) -> bool { true } -} - -#[macro_export] -macro_rules! impl_filter_stack { - ($target:ty, $base:ty, $call:ty, $module:ident) => { - #[cfg(feature = "std")] - mod $module { - #[allow(unused_imports)] - use super::*; - use $crate::traits::{swap, take, RefCell, Vec, Box, Filter, FilterStack}; - - thread_local! { - static FILTER: RefCell bool + 'static>>> = RefCell::new(Vec::new()); - } - - impl Filter<$call> for $target { - fn filter(call: &$call) -> bool { - <$base>::filter(call) && - FILTER.with(|filter| filter.borrow().iter().all(|f| f(call))) - } - } - - impl FilterStack<$call> for $target { - type Stack = Vec bool + 'static>>; - fn push(f: impl Fn(&$call) -> bool + 'static) { - FILTER.with(|filter| filter.borrow_mut().push(Box::new(f))); - } - fn pop() { - FILTER.with(|filter| filter.borrow_mut().pop()); - } - fn take() -> Self::Stack { - FILTER.with(|filter| take(filter.borrow_mut().as_mut())) - } - fn restore(mut s: Self::Stack) { - FILTER.with(|filter| swap(filter.borrow_mut().as_mut(), &mut s)); - } - } - } - - #[cfg(not(feature = "std"))] - mod $module { - #[allow(unused_imports)] - use super::*; - use $crate::traits::{swap, take, RefCell, Vec, Box, Filter, FilterStack}; - - struct ThisFilter(RefCell bool + 'static>>>); - // NOTE: Safe only in wasm (guarded above) because there's only one thread. - unsafe impl Send for ThisFilter {} - unsafe impl Sync for ThisFilter {} - - static FILTER: ThisFilter = ThisFilter(RefCell::new(Vec::new())); - - impl Filter<$call> for $target { - fn filter(call: &$call) -> bool { - <$base>::filter(call) && FILTER.0.borrow().iter().all(|f| f(call)) - } - } - - impl FilterStack<$call> for $target { - type Stack = Vec bool + 'static>>; - fn push(f: impl Fn(&$call) -> bool + 'static) { - FILTER.0.borrow_mut().push(Box::new(f)); - } - fn pop() { - FILTER.0.borrow_mut().pop(); - } - fn take() -> Self::Stack { - take(FILTER.0.borrow_mut().as_mut()) - } - fn restore(mut s: Self::Stack) { - swap(FILTER.0.borrow_mut().as_mut(), &mut s); - } - } - } - } -} - -/// Type that provide some integrity tests. -/// -/// This implemented for modules by `decl_module`. -#[impl_for_tuples(30)] -pub trait IntegrityTest { - /// Run integrity test. - /// - /// The test is not executed in a externalities provided environment. - fn integrity_test() {} -} - -#[cfg(test)] -mod test_impl_filter_stack { - use super::*; - - pub struct IsCallable; - pub struct BaseFilter; - impl Filter for BaseFilter { - fn filter(x: &u32) -> bool { x % 2 == 0 } - } - impl_filter_stack!( - crate::traits::test_impl_filter_stack::IsCallable, - crate::traits::test_impl_filter_stack::BaseFilter, - u32, - is_callable - ); - - #[test] - fn impl_filter_stack_should_work() { - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - - IsCallable::push(|x| *x < 42); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - - IsCallable::push(|x| *x % 3 == 0); - assert!(IsCallable::filter(&36)); - assert!(!IsCallable::filter(&40)); - - IsCallable::pop(); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - - let saved = IsCallable::take(); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - - IsCallable::restore(saved); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - - IsCallable::pop(); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - } - - #[test] - fn guards_should_work() { - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - { - let _guard_1 = FilterStackGuard::::new(|x| *x < 42); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - { - let _guard_2 = FilterStackGuard::::new(|x| *x % 3 == 0); - assert!(IsCallable::filter(&36)); - assert!(!IsCallable::filter(&40)); - } - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - { - let _guard_2 = ClearFilterGuard::::new(); - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - } - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(!IsCallable::filter(&42)); - } - assert!(IsCallable::filter(&36)); - assert!(IsCallable::filter(&40)); - assert!(IsCallable::filter(&42)); - assert!(!IsCallable::filter(&43)); - } -} - -/// An abstraction of a value stored within storage, but possibly as part of a larger composite -/// item. -pub trait StoredMap { - /// Get the item, or its default if it doesn't yet exist; we make no distinction between the - /// two. - fn get(k: &K) -> T; - - /// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is - /// returned. It is removed or reset to default value if it has been mutated to `None` - fn try_mutate_exists>( - k: &K, - f: impl FnOnce(&mut Option) -> Result, - ) -> Result; - - // Everything past here has a default implementation. - - /// Mutate the item. - fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> Result { - Self::mutate_exists(k, |maybe_account| match maybe_account { - Some(ref mut account) => f(account), - x @ None => { - let mut account = Default::default(); - let r = f(&mut account); - *x = Some(account); - r - } - }) - } - - /// Mutate the item, removing or resetting to default value if it has been mutated to `None`. - /// - /// This is infallible as long as the value does not get destroyed. - fn mutate_exists( - k: &K, - f: impl FnOnce(&mut Option) -> R, - ) -> Result { - Self::try_mutate_exists(k, |x| -> Result { Ok(f(x)) }) - } - - /// Set the item to something new. - fn insert(k: &K, t: T) -> Result<(), StoredMapError> { Self::mutate(k, |i| *i = t) } - - /// Remove the item or otherwise replace it with its default value; we don't care which. - fn remove(k: &K) -> Result<(), StoredMapError> { Self::mutate_exists(k, |x| *x = None) } -} - -/// A simple, generic one-parameter event notifier/handler. -pub trait HandleLifetime { - /// An account was created. - fn created(_t: &T) -> Result<(), StoredMapError> { Ok(()) } - - /// An account was killed. - fn killed(_t: &T) -> Result<(), StoredMapError> { Ok(()) } -} - -impl HandleLifetime for () {} - -/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this -/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this -/// would break the ability to have custom impls of `StoredValue`. The other workaround is to -/// implement it directly in the macro. -/// -/// This form has the advantage that two additional types are provides, `Created` and `Removed`, -/// which are both generic events that can be tied to handlers to do something in the case of being -/// about to create an account where one didn't previously exist (at all; not just where it used to -/// be the default value), or where the account is being removed or reset back to the default value -/// where previously it did exist (though may have been in a default state). This works well with -/// system module's `CallOnCreatedAccount` and `CallKillAccount`. -pub struct StorageMapShim(sp_std::marker::PhantomData<(S, L, K, T)>); -impl< - S: StorageMap, - L: HandleLifetime, - K: FullCodec, - T: FullCodec + Default, -> StoredMap for StorageMapShim { - fn get(k: &K) -> T { S::get(k) } - fn insert(k: &K, t: T) -> Result<(), StoredMapError> { - if !S::contains_key(&k) { - L::created(k)?; - } - S::insert(k, t); - Ok(()) - } - fn remove(k: &K) -> Result<(), StoredMapError> { - if S::contains_key(&k) { - L::killed(&k)?; - S::remove(k); - } - Ok(()) - } - fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> Result { - if !S::contains_key(&k) { - L::created(k)?; - } - Ok(S::mutate(k, f)) - } - fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> Result { - S::try_mutate_exists(k, |maybe_value| { - let existed = maybe_value.is_some(); - let r = f(maybe_value); - let exists = maybe_value.is_some(); - - if !existed && exists { - L::created(k)?; - } else if existed && !exists { - L::killed(k)?; - } - Ok(r) - }) - } - fn try_mutate_exists>( - k: &K, - f: impl FnOnce(&mut Option) -> Result, - ) -> Result { - S::try_mutate_exists(k, |maybe_value| { - let existed = maybe_value.is_some(); - let r = f(maybe_value)?; - let exists = maybe_value.is_some(); - - if !existed && exists { - L::created(k).map_err(E::from)?; - } else if existed && !exists { - L::killed(k).map_err(E::from)?; - } - Ok(r) - }) - } -} - -/// Something that can estimate at which block the next session rotation will happen (i.e. a new -/// session starts). -/// -/// The accuracy of the estimates is dependent on the specific implementation, but in order to get -/// the best estimate possible these methods should be called throughout the duration of the session -/// (rather than calling once and storing the result). -/// -/// This should be the same logical unit that dictates `ShouldEndSession` to the session module. No -/// assumptions are made about the scheduling of the sessions. -pub trait EstimateNextSessionRotation { - /// Return the average length of a session. - /// - /// This may or may not be accurate. - fn average_session_length() -> BlockNumber; - - /// Return an estimate of the current session progress. - /// - /// None should be returned if the estimation fails to come to an answer. - fn estimate_current_session_progress(now: BlockNumber) -> (Option, Weight); - - /// Return the block number at which the next session rotation is estimated to happen. - /// - /// None should be returned if the estimation fails to come to an answer. - fn estimate_next_session_rotation(now: BlockNumber) -> (Option, Weight); -} - -impl EstimateNextSessionRotation for () { - fn average_session_length() -> BlockNumber { - Zero::zero() - } - - fn estimate_current_session_progress(_: BlockNumber) -> (Option, Weight) { - (None, Zero::zero()) - } - - fn estimate_next_session_rotation(_: BlockNumber) -> (Option, Weight) { - (None, Zero::zero()) - } -} - -/// Something that can estimate at which block scheduling of the next session will happen (i.e when -/// we will try to fetch new validators). -/// -/// This only refers to the point when we fetch the next session details and not when we enact them -/// (for enactment there's `EstimateNextSessionRotation`). With `pallet-session` this should be -/// triggered whenever `SessionManager::new_session` is called. -/// -/// For example, if we are using a staking module this would be the block when the session module -/// would ask staking what the next validator set will be, as such this must always be implemented -/// by the session module. -pub trait EstimateNextNewSession { - /// Return the average length of a session. - /// - /// This may or may not be accurate. - fn average_session_length() -> BlockNumber; - - /// Return the block number at which the next new session is estimated to happen. - /// - /// None should be returned if the estimation fails to come to an answer. - fn estimate_next_new_session(_: BlockNumber) -> (Option, Weight); -} - -impl EstimateNextNewSession for () { - fn average_session_length() -> BlockNumber { - Zero::zero() - } - - fn estimate_next_new_session(_: BlockNumber) -> (Option, Weight) { - (None, Zero::zero()) - } -} - -/// Anything that can have a `::len()` method. -pub trait Len { - /// Return the length of data type. - fn len(&self) -> usize; -} - -impl Len for T where ::IntoIter: ExactSizeIterator { - fn len(&self) -> usize { - self.clone().into_iter().len() - } -} - -/// A trait for querying a single value from a type. -/// -/// It is not required that the value is constant. -pub trait Get { - /// Return the current value. - fn get() -> T; -} - -impl Get for () { - fn get() -> T { T::default() } -} - -/// A trait for querying whether a type can be said to "contain" a value. -pub trait Contains { - /// Return `true` if this "contains" the given value `t`. - fn contains(t: &T) -> bool { Self::sorted_members().binary_search(t).is_ok() } - - /// Get a vector of all members in the set, ordered. - fn sorted_members() -> Vec; - - /// Get the number of items in the set. - fn count() -> usize { Self::sorted_members().len() } - - /// Add an item that would satisfy `contains`. It does not make sure any other - /// state is correctly maintained or generated. - /// - /// **Should be used for benchmarking only!!!** - #[cfg(feature = "runtime-benchmarks")] - fn add(_t: &T) { unimplemented!() } -} - -/// A trait for querying bound for the length of an implementation of `Contains` -pub trait ContainsLengthBound { - /// Minimum number of elements contained - fn min_len() -> usize; - /// Maximum number of elements contained - fn max_len() -> usize; -} - -/// Handler for when a new account has been created. -#[impl_for_tuples(30)] -pub trait OnNewAccount { - /// A new account `who` has been registered. - fn on_new_account(who: &AccountId); -} - -/// The account with the given id was reaped. -#[impl_for_tuples(30)] -pub trait OnKilledAccount { - /// The account with the given id was reaped. - fn on_killed_account(who: &AccountId); -} - -/// A trait for finding the author of a block header based on the `PreRuntime` digests contained -/// within it. -pub trait FindAuthor { - /// Find the author of a block based on the pre-runtime digests. - fn find_author<'a, I>(digests: I) -> Option - where I: 'a + IntoIterator; -} - -impl FindAuthor for () { - fn find_author<'a, I>(_: I) -> Option - where I: 'a + IntoIterator - { - None - } -} - -/// A trait for verifying the seal of a header and returning the author. -pub trait VerifySeal { - /// Verify a header and return the author, if any. - fn verify_seal(header: &Header) -> Result, &'static str>; -} - -/// Something which can compute and check proofs of -/// a historical key owner and return full identification data of that -/// key owner. -pub trait KeyOwnerProofSystem { - /// The proof of membership itself. - type Proof: Codec; - /// The full identification of a key owner and the stash account. - type IdentificationTuple: Codec; - - /// Prove membership of a key owner in the current block-state. - /// - /// This should typically only be called off-chain, since it may be - /// computationally heavy. - /// - /// Returns `Some` iff the key owner referred to by the given `key` is a - /// member of the current set. - fn prove(key: Key) -> Option; - - /// Check a proof of membership on-chain. Return `Some` iff the proof is - /// valid and recent enough to check. - fn check_proof(key: Key, proof: Self::Proof) -> Option; -} - -impl KeyOwnerProofSystem for () { - // The proof and identification tuples is any bottom type to guarantee that the methods of this - // implementation can never be called or return anything other than `None`. - type Proof = crate::Void; - type IdentificationTuple = crate::Void; - - fn prove(_key: Key) -> Option { - None - } - - fn check_proof(_key: Key, _proof: Self::Proof) -> Option { - None - } -} - -/// Handler for when some currency "account" decreased in balance for -/// some reason. -/// -/// The only reason at present for an increase would be for validator rewards, but -/// there may be other reasons in the future or for other chains. -/// -/// Reasons for decreases include: -/// -/// - Someone got slashed. -/// - Someone paid for a transaction to be included. -pub trait OnUnbalanced { - /// Handler for some imbalances. The different imbalances might have different origins or - /// meanings, dependent on the context. Will default to simply calling on_unbalanced for all - /// of them. Infallible. - fn on_unbalanceds(amounts: impl Iterator) where Imbalance: crate::traits::Imbalance { - Self::on_unbalanced(amounts.fold(Imbalance::zero(), |i, x| x.merge(i))) - } - - /// Handler for some imbalance. Infallible. - fn on_unbalanced(amount: Imbalance) { - amount.try_drop().unwrap_or_else(Self::on_nonzero_unbalanced) - } - - /// Actually handle a non-zero imbalance. You probably want to implement this rather than - /// `on_unbalanced`. - fn on_nonzero_unbalanced(amount: Imbalance) { drop(amount); } -} - -impl OnUnbalanced for () {} - -/// Simple boolean for whether an account needs to be kept in existence. -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum ExistenceRequirement { - /// Operation must not result in the account going out of existence. - /// - /// Note this implies that if the account never existed in the first place, then the operation - /// may legitimately leave the account unchanged and still non-existent. - KeepAlive, - /// Operation may result in account going out of existence. - AllowDeath, -} - -/// A type for which some values make sense to be able to drop without further consideration. -pub trait TryDrop: Sized { - /// Drop an instance cleanly. Only works if its value represents "no-operation". - fn try_drop(self) -> Result<(), Self>; -} - -/// A trait for a not-quite Linear Type that tracks an imbalance. -/// -/// Functions that alter account balances return an object of this trait to -/// express how much account balances have been altered in aggregate. If -/// dropped, the currency system will take some default steps to deal with -/// the imbalance (`balances` module simply reduces or increases its -/// total issuance). Your module should generally handle it in some way, -/// good practice is to do so in a configurable manner using an -/// `OnUnbalanced` type for each situation in which your module needs to -/// handle an imbalance. -/// -/// Imbalances can either be Positive (funds were added somewhere without -/// being subtracted elsewhere - e.g. a reward) or Negative (funds deducted -/// somewhere without an equal and opposite addition - e.g. a slash or -/// system fee payment). -/// -/// Since they are unsigned, the actual type is always Positive or Negative. -/// The trait makes no distinction except to define the `Opposite` type. -/// -/// New instances of zero value can be created (`zero`) and destroyed -/// (`drop_zero`). -/// -/// Existing instances can be `split` and merged either consuming `self` with -/// `merge` or mutating `self` with `subsume`. If the target is an `Option`, -/// then `maybe_merge` and `maybe_subsume` might work better. Instances can -/// also be `offset` with an `Opposite` that is less than or equal to in value. -/// -/// You can always retrieve the raw balance value using `peek`. -#[must_use] -pub trait Imbalance: Sized + TryDrop { - /// The oppositely imbalanced type. They come in pairs. - type Opposite: Imbalance; - - /// The zero imbalance. Can be destroyed with `drop_zero`. - fn zero() -> Self; - - /// Drop an instance cleanly. Only works if its `self.value()` is zero. - fn drop_zero(self) -> Result<(), Self>; - - /// Consume `self` and return two independent instances; the first - /// is guaranteed to be at most `amount` and the second will be the remainder. - fn split(self, amount: Balance) -> (Self, Self); - - /// Consume `self` and return two independent instances; the amounts returned will be in - /// approximately the same ratio as `first`:`second`. - /// - /// NOTE: This requires up to `first + second` room for a multiply, and `first + second` should - /// fit into a `u32`. Overflow will safely saturate in both cases. - fn ration(self, first: u32, second: u32) -> (Self, Self) - where Balance: From + Saturating + Div - { - let total: u32 = first.saturating_add(second); - let amount1 = self.peek().saturating_mul(first.into()) / total.into(); - self.split(amount1) - } - - /// Consume self and add its two components, defined by the first component's balance, - /// element-wise to two pre-existing Imbalances. - /// - /// A convenient replacement for `split` and `merge`. - fn split_merge(self, amount: Balance, others: (Self, Self)) -> (Self, Self) { - let (a, b) = self.split(amount); - (a.merge(others.0), b.merge(others.1)) - } - - /// Consume self and add its two components, defined by the ratio `first`:`second`, - /// element-wise to two pre-existing Imbalances. - /// - /// A convenient replacement for `split` and `merge`. - fn ration_merge(self, first: u32, second: u32, others: (Self, Self)) -> (Self, Self) - where Balance: From + Saturating + Div - { - let (a, b) = self.ration(first, second); - (a.merge(others.0), b.merge(others.1)) - } - - /// Consume self and add its two components, defined by the first component's balance, - /// element-wise into two pre-existing Imbalance refs. - /// - /// A convenient replacement for `split` and `subsume`. - fn split_merge_into(self, amount: Balance, others: &mut (Self, Self)) { - let (a, b) = self.split(amount); - others.0.subsume(a); - others.1.subsume(b); - } - - /// Consume self and add its two components, defined by the ratio `first`:`second`, - /// element-wise to two pre-existing Imbalances. - /// - /// A convenient replacement for `split` and `merge`. - fn ration_merge_into(self, first: u32, second: u32, others: &mut (Self, Self)) - where Balance: From + Saturating + Div - { - let (a, b) = self.ration(first, second); - others.0.subsume(a); - others.1.subsume(b); - } - - /// Consume `self` and an `other` to return a new instance that combines - /// both. - fn merge(self, other: Self) -> Self; - - /// Consume self to mutate `other` so that it combines both. Just like `subsume`, only with - /// reversed arguments. - fn merge_into(self, other: &mut Self) { - other.subsume(self) - } - - /// Consume `self` and maybe an `other` to return a new instance that combines - /// both. - fn maybe_merge(self, other: Option) -> Self { - if let Some(o) = other { - self.merge(o) - } else { - self - } - } - - /// Consume an `other` to mutate `self` into a new instance that combines - /// both. - fn subsume(&mut self, other: Self); - - /// Maybe consume an `other` to mutate `self` into a new instance that combines - /// both. - fn maybe_subsume(&mut self, other: Option) { - if let Some(o) = other { - self.subsume(o) - } - } - - /// Consume self and along with an opposite counterpart to return - /// a combined result. - /// - /// Returns `Ok` along with a new instance of `Self` if this instance has a - /// greater value than the `other`. Otherwise returns `Err` with an instance of - /// the `Opposite`. In both cases the value represents the combination of `self` - /// and `other`. - fn offset(self, other: Self::Opposite) -> Result; - - /// The raw value of self. - fn peek(&self) -> Balance; -} - -/// Either a positive or a negative imbalance. -pub enum SignedImbalance>{ - /// A positive imbalance (funds have been created but none destroyed). - Positive(P), - /// A negative imbalance (funds have been destroyed but none created). - Negative(P::Opposite), -} - -impl< - P: Imbalance, - N: Imbalance, - B: AtLeast32BitUnsigned + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default, -> SignedImbalance { - pub fn zero() -> Self { - SignedImbalance::Positive(P::zero()) - } - - pub fn drop_zero(self) -> Result<(), Self> { - match self { - SignedImbalance::Positive(x) => x.drop_zero().map_err(SignedImbalance::Positive), - SignedImbalance::Negative(x) => x.drop_zero().map_err(SignedImbalance::Negative), - } - } - - /// Consume `self` and an `other` to return a new instance that combines - /// both. - pub fn merge(self, other: Self) -> Self { - match (self, other) { - (SignedImbalance::Positive(one), SignedImbalance::Positive(other)) => - SignedImbalance::Positive(one.merge(other)), - (SignedImbalance::Negative(one), SignedImbalance::Negative(other)) => - SignedImbalance::Negative(one.merge(other)), - (SignedImbalance::Positive(one), SignedImbalance::Negative(other)) => - if one.peek() > other.peek() { - SignedImbalance::Positive(one.offset(other).ok().unwrap_or_else(P::zero)) - } else { - SignedImbalance::Negative(other.offset(one).ok().unwrap_or_else(N::zero)) - }, - (one, other) => other.merge(one), - } - } -} - -/// Split an unbalanced amount two ways between a common divisor. -pub struct SplitTwoWays< - Balance, - Imbalance, - Part1, - Target1, - Part2, - Target2, ->(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>); - -impl< - Balance: From + Saturating + Div, - I: Imbalance, - Part1: U32, - Target1: OnUnbalanced, - Part2: U32, - Target2: OnUnbalanced, -> OnUnbalanced for SplitTwoWays -{ - fn on_nonzero_unbalanced(amount: I) { - let total: u32 = Part1::VALUE + Part2::VALUE; - let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into(); - let (imb1, imb2) = amount.split(amount1); - Target1::on_unbalanced(imb1); - Target2::on_unbalanced(imb2); - } -} - -/// Abstraction over a fungible assets system. -pub trait Currency { - /// The balance of an account. - type Balance: AtLeast32BitUnsigned + FullCodec + Copy + MaybeSerializeDeserialize + Debug + - Default; - - /// The opaque token type for an imbalance. This is returned by unbalanced operations - /// and must be dealt with. It may be dropped but cannot be cloned. - type PositiveImbalance: Imbalance; - - /// The opaque token type for an imbalance. This is returned by unbalanced operations - /// and must be dealt with. It may be dropped but cannot be cloned. - type NegativeImbalance: Imbalance; - - // PUBLIC IMMUTABLES - - /// The combined balance of `who`. - fn total_balance(who: &AccountId) -> Self::Balance; - - /// Same result as `slash(who, value)` (but without the side-effects) assuming there are no - /// balance changes in the meantime and only the reserved balance is not taken into account. - fn can_slash(who: &AccountId, value: Self::Balance) -> bool; - - /// The total amount of issuance in the system. - fn total_issuance() -> Self::Balance; - - /// The minimum balance any single account may have. This is equivalent to the `Balances` module's - /// `ExistentialDeposit`. - fn minimum_balance() -> Self::Balance; - - /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will - /// typically be used to reduce an account by the same amount with e.g. `settle`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example - /// in the case of underflow. - fn burn(amount: Self::Balance) -> Self::PositiveImbalance; - - /// Increase the total issuance by `amount` and return the according imbalance. The imbalance - /// will typically be used to increase an account by the same amount with e.g. - /// `resolve_into_existing` or `resolve_creating`. - /// - /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example - /// in the case of overflow. - fn issue(amount: Self::Balance) -> Self::NegativeImbalance; - - /// Produce a pair of imbalances that cancel each other out exactly. - /// - /// This is just the same as burning and issuing the same amount and has no effect on the - /// total issuance. - fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) { - (Self::burn(amount.clone()), Self::issue(amount)) - } - - /// The 'free' balance of a given account. - /// - /// This is the only balance that matters in terms of most operations on tokens. It alone - /// is used to determine the balance when in the contract execution environment. When this - /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is - /// deleted: specifically `FreeBalance`. - /// - /// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets - /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. - fn free_balance(who: &AccountId) -> Self::Balance; - - /// Returns `Ok` iff the account is able to make a withdrawal of the given amount - /// for the given reason. Basically, it's just a dry-run of `withdraw`. - /// - /// `Err(...)` with the reason why not otherwise. - fn ensure_can_withdraw( - who: &AccountId, - _amount: Self::Balance, - reasons: WithdrawReasons, - new_balance: Self::Balance, - ) -> DispatchResult; - - // PUBLIC MUTABLES (DANGEROUS) - - /// Transfer some liquid free balance to another staker. - /// - /// This is a very high-level function. It will ensure all appropriate fees are paid - /// and no imbalance in the system remains. - fn transfer( - source: &AccountId, - dest: &AccountId, - value: Self::Balance, - existence_requirement: ExistenceRequirement, - ) -> DispatchResult; - - /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the - /// free balance. This function cannot fail. - /// - /// The resulting imbalance is the first item of the tuple returned. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then a non-zero second item will be returned. - fn slash( - who: &AccountId, - value: Self::Balance - ) -> (Self::NegativeImbalance, Self::Balance); - - /// Mints `value` to the free balance of `who`. - /// - /// If `who` doesn't exist, nothing is done and an Err returned. - fn deposit_into_existing( - who: &AccountId, - value: Self::Balance - ) -> result::Result; - - /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on - /// success. - fn resolve_into_existing( - who: &AccountId, - value: Self::NegativeImbalance, - ) -> result::Result<(), Self::NegativeImbalance> { - let v = value.peek(); - match Self::deposit_into_existing(who, v) { - Ok(opposite) => Ok(drop(value.offset(opposite))), - _ => Err(value), - } - } - - /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created. - /// - /// Infallible. - fn deposit_creating( - who: &AccountId, - value: Self::Balance, - ) -> Self::PositiveImbalance; - - /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on - /// success. - fn resolve_creating( - who: &AccountId, - value: Self::NegativeImbalance, - ) { - let v = value.peek(); - drop(value.offset(Self::deposit_creating(who, v))); - } - - /// Removes some free balance from `who` account for `reason` if possible. If `liveness` is - /// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining. - /// - /// This checks any locks, vesting, and liquidity requirements. If the removal is not possible, - /// then it returns `Err`. - /// - /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value - /// is `value`. - fn withdraw( - who: &AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result; - - /// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success. - fn settle( - who: &AccountId, - value: Self::PositiveImbalance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result<(), Self::PositiveImbalance> { - let v = value.peek(); - match Self::withdraw(who, v, reasons, liveness) { - Ok(opposite) => Ok(drop(value.offset(opposite))), - _ => Err(value), - } - } - - /// Ensure an account's free balance equals some value; this will create the account - /// if needed. - /// - /// Returns a signed imbalance and status to indicate if the account was successfully updated or update - /// has led to killing of the account. - fn make_free_balance_be( - who: &AccountId, - balance: Self::Balance, - ) -> SignedImbalance; -} - -/// Trait for providing an ERC-20 style set of named fungible assets. -pub trait Fungibles { - /// Means of identifying one asset class from another. - type AssetId: FullCodec + Copy + Default; - /// Scalar type for storing balance of an account. - type Balance: AtLeast32BitUnsigned + FullCodec + Copy + Default; - /// Get the `asset` balance of `who`. - fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; - /// Returns `true` if the `asset` balance of `who` may be increased by `amount`. - fn can_deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> bool; - /// Increase the `asset` balance of `who` by `amount`. - fn deposit(asset: Self::AssetId, who: AccountId, amount: Self::Balance) -> DispatchResult; - /// Attempt to reduce the `asset` balance of `who` by `amount`. - fn withdraw(asset: Self::AssetId, who: AccountId, amount: Self::Balance) -> DispatchResult; -} - -/// Status of funds. -#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] -pub enum BalanceStatus { - /// Funds are free, as corresponding to `free` item in Balances. - Free, - /// Funds are reserved, as corresponding to `reserved` item in Balances. - Reserved, -} - -/// A currency where funds can be reserved from the user. -pub trait ReservableCurrency: Currency { - /// Same result as `reserve(who, value)` (but without the side-effects) assuming there - /// are no balance changes in the meantime. - fn can_reserve(who: &AccountId, value: Self::Balance) -> bool; - - /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. - /// - /// As much funds up to `value` will be deducted as possible. If the reserve balance of `who` - /// is less than `value`, then a non-zero second item will be returned. - fn slash_reserved( - who: &AccountId, - value: Self::Balance - ) -> (Self::NegativeImbalance, Self::Balance); - - /// The amount of the balance of a given account that is externally reserved; this can still get - /// slashed, but gets slashed last of all. - /// - /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens - /// that are still 'owned' by the account holder, but which are suspendable. - /// - /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' - /// is deleted: specifically, `ReservedBalance`. - /// - /// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets - /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. - fn reserved_balance(who: &AccountId) -> Self::Balance; - - /// Moves `value` from balance to reserved balance. - /// - /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will - /// be returned to notify of this. This is different behavior than `unreserve`. - fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult; - - /// Moves up to `value` from reserved balance to free balance. This function cannot fail. - /// - /// As much funds up to `value` will be moved as possible. If the reserve balance of `who` - /// is less than `value`, then the remaining amount will be returned. - /// - /// # NOTES - /// - /// - This is different from `reserve`. - /// - If the remaining reserved balance is less than `ExistentialDeposit`, it will - /// invoke `on_reserved_too_low` and could reap the account. - fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance; - - /// Moves up to `value` from reserved balance of account `slashed` to balance of account - /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be - /// returned. Funds will be placed in either the `free` balance or the `reserved` balance, - /// depending on the `status`. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Ok(non_zero)` will be returned. - fn repatriate_reserved( - slashed: &AccountId, - beneficiary: &AccountId, - value: Self::Balance, - status: BalanceStatus, - ) -> result::Result; -} - -/// An identifier for a lock. Used for disambiguating different locks so that -/// they can be individually replaced or removed. -pub type LockIdentifier = [u8; 8]; - -/// A currency whose accounts can have liquidity restrictions. -pub trait LockableCurrency: Currency { - /// The quantity used to denote time; usually just a `BlockNumber`. - type Moment; - - /// The maximum number of locks a user should have on their account. - type MaxLocks: Get; - - /// Create a new balance lock on account `who`. - /// - /// If the new lock is valid (i.e. not already expired), it will push the struct to - /// the `Locks` vec in storage. Note that you can lock more funds than a user has. - /// - /// If the lock `id` already exists, this will update it. - fn set_lock( - id: LockIdentifier, - who: &AccountId, - amount: Self::Balance, - reasons: WithdrawReasons, - ); - - /// Changes a balance lock (selected by `id`) so that it becomes less liquid in all - /// parameters or creates a new one if it does not exist. - /// - /// Calling `extend_lock` on an existing lock `id` differs from `set_lock` in that it - /// applies the most severe constraints of the two, while `set_lock` replaces the lock - /// with the new parameters. As in, `extend_lock` will set: - /// - maximum `amount` - /// - bitwise mask of all `reasons` - fn extend_lock( - id: LockIdentifier, - who: &AccountId, - amount: Self::Balance, - reasons: WithdrawReasons, - ); - - /// Remove an existing lock. - fn remove_lock( - id: LockIdentifier, - who: &AccountId, - ); -} - -/// A vesting schedule over a currency. This allows a particular currency to have vesting limits -/// applied to it. -pub trait VestingSchedule { - /// The quantity used to denote time; usually just a `BlockNumber`. - type Moment; - - /// The currency that this schedule applies to. - type Currency: Currency; - - /// Get the amount that is currently being vested and cannot be transferred out of this account. - /// Returns `None` if the account has no vesting schedule. - fn vesting_balance(who: &AccountId) -> Option<>::Balance>; - - /// Adds a vesting schedule to a given account. - /// - /// If there already exists a vesting schedule for the given account, an `Err` is returned - /// and nothing is updated. - /// - /// Is a no-op if the amount to be vested is zero. - /// - /// NOTE: This doesn't alter the free balance of the account. - fn add_vesting_schedule( - who: &AccountId, - locked: >::Balance, - per_block: >::Balance, - starting_block: Self::Moment, - ) -> DispatchResult; - - /// Remove a vesting schedule for a given account. - /// - /// NOTE: This doesn't alter the free balance of the account. - fn remove_vesting_schedule(who: &AccountId); -} - -bitflags! { - /// Reasons for moving funds out of an account. - #[derive(Encode, Decode)] - pub struct WithdrawReasons: i8 { - /// In order to pay for (system) transaction costs. - const TRANSACTION_PAYMENT = 0b00000001; - /// In order to transfer ownership. - const TRANSFER = 0b00000010; - /// In order to reserve some funds for a later return or repatriation. - const RESERVE = 0b00000100; - /// In order to pay some other (higher-level) fees. - const FEE = 0b00001000; - /// In order to tip a validator for transaction inclusion. - const TIP = 0b00010000; - } -} - -impl WithdrawReasons { - /// Choose all variants except for `one`. - /// - /// ```rust - /// # use frame_support::traits::WithdrawReasons; - /// # fn main() { - /// assert_eq!( - /// WithdrawReasons::FEE | WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE | WithdrawReasons::TIP, - /// WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT), - /// ); - /// # } - /// ``` - pub fn except(one: WithdrawReasons) -> WithdrawReasons { - let mut flags = Self::all(); - flags.toggle(one); - flags - } -} - -pub trait Time { - type Moment: AtLeast32Bit + Parameter + Default + Copy; - - fn now() -> Self::Moment; -} - -/// Trait to deal with unix time. -pub trait UnixTime { - /// Return duration since `SystemTime::UNIX_EPOCH`. - fn now() -> core::time::Duration; -} - -/// Trait for type that can handle incremental changes to a set of account IDs. -pub trait ChangeMembers { - /// A number of members `incoming` just joined the set and replaced some `outgoing` ones. The - /// new set is given by `new`, and need not be sorted. - /// - /// This resets any previous value of prime. - fn change_members(incoming: &[AccountId], outgoing: &[AccountId], mut new: Vec) { - new.sort(); - Self::change_members_sorted(incoming, outgoing, &new[..]); - } - - /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The - /// new set is thus given by `sorted_new` and **must be sorted**. - /// - /// NOTE: This is the only function that needs to be implemented in `ChangeMembers`. - /// - /// This resets any previous value of prime. - fn change_members_sorted( - incoming: &[AccountId], - outgoing: &[AccountId], - sorted_new: &[AccountId], - ); - - /// Set the new members; they **must already be sorted**. This will compute the diff and use it to - /// call `change_members_sorted`. - /// - /// This resets any previous value of prime. - fn set_members_sorted(new_members: &[AccountId], old_members: &[AccountId]) { - let (incoming, outgoing) = Self::compute_members_diff_sorted(new_members, old_members); - Self::change_members_sorted(&incoming[..], &outgoing[..], &new_members); - } - - /// Compute diff between new and old members; they **must already be sorted**. - /// - /// Returns incoming and outgoing members. - fn compute_members_diff_sorted( - new_members: &[AccountId], - old_members: &[AccountId], - ) -> (Vec, Vec) { - let mut old_iter = old_members.iter(); - let mut new_iter = new_members.iter(); - let mut incoming = Vec::new(); - let mut outgoing = Vec::new(); - let mut old_i = old_iter.next(); - let mut new_i = new_iter.next(); - loop { - match (old_i, new_i) { - (None, None) => break, - (Some(old), Some(new)) if old == new => { - old_i = old_iter.next(); - new_i = new_iter.next(); - } - (Some(old), Some(new)) if old < new => { - outgoing.push(old.clone()); - old_i = old_iter.next(); - } - (Some(old), None) => { - outgoing.push(old.clone()); - old_i = old_iter.next(); - } - (_, Some(new)) => { - incoming.push(new.clone()); - new_i = new_iter.next(); - } - } - } - (incoming, outgoing) - } - - /// Set the prime member. - fn set_prime(_prime: Option) {} - - /// Get the current prime. - fn get_prime() -> Option { - None - } -} - -impl ChangeMembers for () { - fn change_members(_: &[T], _: &[T], _: Vec) {} - fn change_members_sorted(_: &[T], _: &[T], _: &[T]) {} - fn set_members_sorted(_: &[T], _: &[T]) {} - fn set_prime(_: Option) {} -} - -/// Trait for type that can handle the initialization of account IDs at genesis. -pub trait InitializeMembers { - /// Initialize the members to the given `members`. - fn initialize_members(members: &[AccountId]); -} - -impl InitializeMembers for () { - fn initialize_members(_: &[T]) {} -} - -/// A trait that is able to provide randomness. -/// -/// Being a deterministic blockchain, real randomness is difficult to come by, different -/// implementations of this trait will provide different security guarantees. At best, -/// this will be randomness which was hard to predict a long time ago, but that has become -/// easy to predict recently. -pub trait Randomness { - /// Get the most recently determined random seed, along with the time in the past - /// since when it was determinable by chain observers. - /// - /// `subject` is a context identifier and allows you to get a different result to - /// other callers of this function; use it like `random(&b"my context"[..])`. - /// - /// NOTE: The returned seed should only be used to distinguish commitments made before - /// the returned block number. If the block number is too early (i.e. commitments were - /// made afterwards), then ensure no further commitments may be made and repeatedly - /// call this on later blocks until the block number returned is later than the latest - /// commitment. - fn random(subject: &[u8]) -> (Output, BlockNumber); - - /// Get the basic random seed. - /// - /// In general you won't want to use this, but rather `Self::random` which allows - /// you to give a subject for the random result and whose value will be - /// independently low-influence random from any other such seeds. - /// - /// NOTE: The returned seed should only be used to distinguish commitments made before - /// the returned block number. If the block number is too early (i.e. commitments were - /// made afterwards), then ensure no further commitments may be made and repeatedly - /// call this on later blocks until the block number returned is later than the latest - /// commitment. - fn random_seed() -> (Output, BlockNumber) { - Self::random(&[][..]) - } -} - -/// Trait to be used by block producing consensus engine modules to determine -/// how late the current block is (e.g. in a slot-based proposal mechanism how -/// many slots were skipped since the previous block). -pub trait Lateness { - /// Returns a generic measure of how late the current block is compared to - /// its parent. - fn lateness(&self) -> N; -} - -impl Lateness for () { - fn lateness(&self) -> N { - Zero::zero() - } -} - -/// Implementors of this trait provide information about whether or not some validator has -/// been registered with them. The [Session module](../../pallet_session/index.html) is an implementor. -pub trait ValidatorRegistration { - /// Returns true if the provided validator ID has been registered with the implementing runtime - /// module - fn is_registered(id: &ValidatorId) -> bool; -} - -/// Provides information about the pallet setup in the runtime. -/// -/// An implementor should be able to provide information about each pallet that -/// is configured in `construct_runtime!`. -pub trait PalletInfo { - /// Convert the given pallet `P` into its index as configured in the runtime. - fn index() -> Option; - /// Convert the given pallet `P` into its name as configured in the runtime. - fn name() -> Option<&'static str>; -} - -/// The function and pallet name of the Call. -#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug)] -pub struct CallMetadata { - /// Name of the function. - pub function_name: &'static str, - /// Name of the pallet to which the function belongs. - pub pallet_name: &'static str, -} - -/// Gets the function name of the Call. -pub trait GetCallName { - /// Return all function names. - fn get_call_names() -> &'static [&'static str]; - /// Return the function name of the Call. - fn get_call_name(&self) -> &'static str; -} - -/// Gets the metadata for the Call - function name and pallet name. -pub trait GetCallMetadata { - /// Return all module names. - fn get_module_names() -> &'static [&'static str]; - /// Return all function names for the given `module`. - fn get_call_names(module: &str) -> &'static [&'static str]; - /// Return a [`CallMetadata`], containing function and pallet name of the Call. - fn get_call_metadata(&self) -> CallMetadata; -} - -/// The block finalization trait. -/// -/// Implementing this lets you express what should happen for your pallet when the block is ending. -#[impl_for_tuples(30)] -pub trait OnFinalize { - /// The block is being finalized. Implement to have something happen. - /// - /// NOTE: This function is called AFTER ALL extrinsics in a block are applied, - /// including inherent extrinsics. - fn on_finalize(_n: BlockNumber) {} -} - -/// The block's on idle trait. -/// -/// Implementing this lets you express what should happen for your pallet before -/// block finalization (see `on_finalize` hook) in case any remaining weight is left. -pub trait OnIdle { - /// The block is being finalized. - /// Implement to have something happen in case there is leftover weight. - /// Check the passed `remaining_weight` to make sure it is high enough to allow for - /// your pallet's extra computation. - /// - /// NOTE: This function is called AFTER ALL extrinsics - including inherent extrinsics - - /// in a block are applied but before `on_finalize` is executed. - fn on_idle( - _n: BlockNumber, - _remaining_weight: crate::weights::Weight - ) -> crate::weights::Weight { - 0 - } -} - -#[impl_for_tuples(30)] -impl OnIdle for Tuple { - fn on_idle(n: BlockNumber, remaining_weight: crate::weights::Weight) -> crate::weights::Weight { - let mut weight = 0; - for_tuples!( #( - let adjusted_remaining_weight = remaining_weight.saturating_sub(weight); - weight = weight.saturating_add(Tuple::on_idle(n.clone(), adjusted_remaining_weight)); - )* ); - weight - } -} - -/// The block initialization trait. -/// -/// Implementing this lets you express what should happen for your pallet when the block is -/// beginning (right before the first extrinsic is executed). -pub trait OnInitialize { - /// The block is being initialized. Implement to have something happen. - /// - /// Return the non-negotiable weight consumed in the block. - /// - /// NOTE: This function is called BEFORE ANY extrinsic in a block is applied, - /// including inherent extrinsics. Hence for instance, if you runtime includes - /// `pallet_timestamp`, the `timestamp` is not yet up to date at this point. - fn on_initialize(_n: BlockNumber) -> crate::weights::Weight { 0 } -} - -#[impl_for_tuples(30)] -impl OnInitialize for Tuple { - fn on_initialize(n: BlockNumber) -> crate::weights::Weight { - let mut weight = 0; - for_tuples!( #( weight = weight.saturating_add(Tuple::on_initialize(n.clone())); )* ); - weight - } -} +pub use tokens::fungibles::{ + Inspect as InspectFungibles, Mutate as MutateFungibles, Transfer as TransferFungibles, + Reserve as ReserveFungibles, Balanced as BalancedFungibles, Unbalanced as UnbalancedFungibles, +}; +pub use tokens::currency::{ + Currency, LockIdentifier, LockableCurrency, ReservableCurrency, VestingSchedule, +}; +pub use tokens::imbalance::{Imbalance, OnUnbalanced, SignedImbalance}; +pub use tokens::{ExistenceRequirement, WithdrawReasons, BalanceStatus}; -/// A trait that will be called at genesis. -/// -/// Implementing this trait for a pallet let's you express operations that should -/// happen at genesis. It will be called in an externalities provided environment and -/// will see the genesis state after all pallets have written their genesis state. -#[impl_for_tuples(30)] -pub trait OnGenesis { - /// Something that should happen at genesis. - fn on_genesis() {} -} +mod members; +pub use members::{Contains, ContainsLengthBound, InitializeMembers, ChangeMembers}; -/// Prefix to be used (optionally) for implementing [`OnRuntimeUpgradeHelpersExt::storage_key`]. -#[cfg(feature = "try-runtime")] -pub const ON_RUNTIME_UPGRADE_PREFIX: &[u8] = b"__ON_RUNTIME_UPGRADE__"; - -/// Some helper functions for [`OnRuntimeUpgrade`] during `try-runtime` testing. -#[cfg(feature = "try-runtime")] -pub trait OnRuntimeUpgradeHelpersExt { - /// Generate a storage key unique to this runtime upgrade. - /// - /// This can be used to communicate data from pre-upgrade to post-upgrade state and check - /// them. See [`Self::set_temp_storage`] and [`Self::get_temp_storage`]. - #[cfg(feature = "try-runtime")] - fn storage_key(ident: &str) -> [u8; 32] { - let prefix = sp_io::hashing::twox_128(ON_RUNTIME_UPGRADE_PREFIX); - let ident = sp_io::hashing::twox_128(ident.as_bytes()); +mod validation; +pub use validation::{ + ValidatorSet, ValidatorSetWithIdentification, OneSessionHandler, FindAuthor, VerifySeal, + EstimateNextNewSession, EstimateNextSessionRotation, KeyOwnerProofSystem, ValidatorRegistration, + Lateness, +}; - let mut final_key = [0u8; 32]; - final_key[..16].copy_from_slice(&prefix); - final_key[16..].copy_from_slice(&ident); +mod filter; +pub use filter::{ + Filter, FilterStack, FilterStackGuard, ClearFilterGuard, InstanceFilter, IntegrityTest, +}; - final_key - } +mod misc; +pub use misc::{ + Len, Get, GetDefault, HandleLifetime, TryDrop, Time, UnixTime, IsType, IsSubType, ExecuteBlock, + SameOrOther, OnNewAccount, OnKilledAccount, OffchainWorker, +}; - /// Get temporary storage data written by [`Self::set_temp_storage`]. - /// - /// Returns `None` if either the data is unavailable or un-decodable. - /// - /// A `at` storage identifier must be provided to indicate where the storage is being read from. - #[cfg(feature = "try-runtime")] - fn get_temp_storage(at: &str) -> Option { - sp_io::storage::get(&Self::storage_key(at)) - .and_then(|bytes| Decode::decode(&mut &*bytes).ok()) - } +mod stored_map; +pub use stored_map::{StoredMap, StorageMapShim}; +mod randomness; +pub use randomness::Randomness; - /// Write some temporary data to a specific storage that can be read (potentially in - /// post-upgrade hook) via [`Self::get_temp_storage`]. - /// - /// A `at` storage identifier must be provided to indicate where the storage is being written - /// to. - #[cfg(feature = "try-runtime")] - fn set_temp_storage(data: T, at: &str) { - sp_io::storage::set(&Self::storage_key(at), &data.encode()); - } -} +mod metadata; +pub use metadata::{ + CallMetadata, GetCallMetadata, GetCallName, PalletInfo, PalletVersion, GetPalletVersion, + PALLET_VERSION_STORAGE_KEY_POSTFIX, +}; +mod hooks; +pub use hooks::{Hooks, OnGenesis, OnInitialize, OnFinalize, OnIdle, OnRuntimeUpgrade, OnTimestampSet}; #[cfg(feature = "try-runtime")] -impl OnRuntimeUpgradeHelpersExt for U {} - -/// The runtime upgrade trait. -/// -/// Implementing this lets you express what should happen when the runtime upgrades, -/// and changes may need to occur to your module. -pub trait OnRuntimeUpgrade { - /// Perform a module upgrade. - /// - /// # Warning - /// - /// This function will be called before we initialized any runtime state, aka `on_initialize` - /// wasn't called yet. So, information like the block number and any other - /// block local data are not accessible. - /// - /// Return the non-negotiable weight consumed for runtime upgrade. - fn on_runtime_upgrade() -> crate::weights::Weight { - 0 - } - - /// Execute some pre-checks prior to a runtime upgrade. - /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<(), &'static str> { Ok(()) } - - /// Execute some post-checks after a runtime upgrade. - /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. - #[cfg(feature = "try-runtime")] - fn post_upgrade() -> Result<(), &'static str> { Ok(()) } -} - -#[impl_for_tuples(30)] -impl OnRuntimeUpgrade for Tuple { - fn on_runtime_upgrade() -> crate::weights::Weight { - let mut weight = 0; - for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* ); - weight - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<(), &'static str> { - let mut result = Ok(()); - for_tuples!( #( result = result.and(Tuple::pre_upgrade()); )* ); - result - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade() -> Result<(), &'static str> { - let mut result = Ok(()); - for_tuples!( #( result = result.and(Tuple::post_upgrade()); )* ); - result - } -} - -/// Off-chain computation trait. -/// -/// Implementing this trait on a module allows you to perform long-running tasks -/// that make (by default) validators generate transactions that feed results -/// of those long-running computations back on chain. -/// -/// NOTE: This function runs off-chain, so it can access the block state, -/// but cannot preform any alterations. More specifically alterations are -/// not forbidden, but they are not persisted in any way after the worker -/// has finished. -#[impl_for_tuples(30)] -pub trait OffchainWorker { - /// This function is being called after every block import (when fully synced). - /// - /// Implement this and use any of the `Offchain` `sp_io` set of APIs - /// to perform off-chain computations, calls and submit transactions - /// with results to trigger any on-chain changes. - /// Any state alterations are lost and are not persisted. - fn offchain_worker(_n: BlockNumber) {} -} - -pub mod schedule { - use super::*; - - /// Information relating to the period of a scheduled task. First item is the length of the - /// period and the second is the number of times it should be executed in total before the task - /// is considered finished and removed. - pub type Period = (BlockNumber, u32); - - /// Priority with which a call is scheduled. It's just a linear amount with lowest values meaning - /// higher priority. - pub type Priority = u8; - - /// The dispatch time of a scheduled task. - #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] - pub enum DispatchTime { - /// At specified block. - At(BlockNumber), - /// After specified number of blocks. - After(BlockNumber), - } - - /// The highest priority. We invert the value so that normal sorting will place the highest - /// priority at the beginning of the list. - pub const HIGHEST_PRIORITY: Priority = 0; - /// Anything of this value or lower will definitely be scheduled on the block that they ask for, even - /// if it breaches the `MaximumWeight` limitation. - pub const HARD_DEADLINE: Priority = 63; - /// The lowest priority. Most stuff should be around here. - pub const LOWEST_PRIORITY: Priority = 255; - - /// A type that can be used as a scheduler. - pub trait Anon { - /// An address which can be used for removing a scheduled task. - type Address: Codec + Clone + Eq + EncodeLike + Debug; - - /// Schedule a dispatch to happen at the beginning of some block in the future. - /// - /// This is not named. - fn schedule( - when: DispatchTime, - maybe_periodic: Option>, - priority: Priority, - origin: Origin, - call: Call - ) -> Result; - - /// Cancel a scheduled task. If periodic, then it will cancel all further instances of that, - /// also. - /// - /// Will return an error if the `address` is invalid. - /// - /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. - /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. - /// - /// NOTE2: This will not work to cancel periodic tasks after their initial execution. For - /// that, you must name the task explicitly using the `Named` trait. - fn cancel(address: Self::Address) -> Result<(), ()>; - - /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed - /// only if it is executed *before* the currently scheduled block. For periodic tasks, - /// this dispatch is guaranteed to succeed only before the *initial* execution; for - /// others, use `reschedule_named`. - /// - /// Will return an error if the `address` is invalid. - fn reschedule( - address: Self::Address, - when: DispatchTime, - ) -> Result; - - /// Return the next dispatch time for a given task. - /// - /// Will return an error if the `address` is invalid. - fn next_dispatch_time(address: Self::Address) -> Result; - } - - /// A type that can be used as a scheduler. - pub trait Named { - /// An address which can be used for removing a scheduled task. - type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug; - - /// Schedule a dispatch to happen at the beginning of some block in the future. - /// - /// - `id`: The identity of the task. This must be unique and will return an error if not. - fn schedule_named( - id: Vec, - when: DispatchTime, - maybe_periodic: Option>, - priority: Priority, - origin: Origin, - call: Call - ) -> Result; - - /// Cancel a scheduled, named task. If periodic, then it will cancel all further instances - /// of that, also. - /// - /// Will return an error if the `id` is invalid. - /// - /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. - /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. - fn cancel_named(id: Vec) -> Result<(), ()>; - - /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed - /// only if it is executed *before* the currently scheduled block. - fn reschedule_named( - id: Vec, - when: DispatchTime, - ) -> Result; - - /// Return the next dispatch time for a given task. - /// - /// Will return an error if the `id` is invalid. - fn next_dispatch_time(id: Vec) -> Result; - } -} - -/// Some sort of check on the origin is performed by this object. -pub trait EnsureOrigin { - /// A return type. - type Success; - /// Perform the origin check. - fn ensure_origin(o: OuterOrigin) -> result::Result { - Self::try_origin(o).map_err(|_| BadOrigin) - } - /// Perform the origin check. - fn try_origin(o: OuterOrigin) -> result::Result; - - /// Returns an outer origin capable of passing `try_origin` check. - /// - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OuterOrigin; -} - -/// Type that can be dispatched with an origin but without checking the origin filter. -/// -/// Implemented for pallet dispatchable type by `decl_module` and for runtime dispatchable by -/// `construct_runtime` and `impl_outer_dispatch`. -pub trait UnfilteredDispatchable { - /// The origin type of the runtime, (i.e. `frame_system::Config::Origin`). - type Origin; - - /// Dispatch this call but do not check the filter in origin. - fn dispatch_bypass_filter(self, origin: Self::Origin) -> crate::dispatch::DispatchResultWithPostInfo; -} - -/// Methods available on `frame_system::Config::Origin`. -pub trait OriginTrait: Sized { - /// Runtime call type, as in `frame_system::Config::Call` - type Call; - - /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin; - - /// The AccountId used across the system. - type AccountId; - - /// Add a filter to the origin. - fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static); - - /// Reset origin filters to default one, i.e `frame_system::Config::BaseCallFilter`. - fn reset_filter(&mut self); - - /// Replace the caller with caller from the other origin - fn set_caller_from(&mut self, other: impl Into); - - /// Filter the call, if false then call is filtered out. - fn filter_call(&self, call: &Self::Call) -> bool; - - /// Get the caller. - fn caller(&self) -> &Self::PalletsOrigin; - - /// Create with system none origin and `frame-system::Config::BaseCallFilter`. - fn none() -> Self; - - /// Create with system root origin and no filter. - fn root() -> Self; - - /// Create with system signed origin and `frame-system::Config::BaseCallFilter`. - fn signed(by: Self::AccountId) -> Self; -} - -/// Trait to be used when types are exactly same. -/// -/// This allow to convert back and forth from type, a reference and a mutable reference. -pub trait IsType: Into + From { - /// Cast reference. - fn from_ref(t: &T) -> &Self; - - /// Cast reference. - fn into_ref(&self) -> &T; - - /// Cast mutable reference. - fn from_mut(t: &mut T) -> &mut Self; - - /// Cast mutable reference. - fn into_mut(&mut self) -> &mut T; -} - -impl IsType for T { - fn from_ref(t: &T) -> &Self { t } - fn into_ref(&self) -> &T { self } - fn from_mut(t: &mut T) -> &mut Self { t } - fn into_mut(&mut self) -> &mut T { self } -} - -/// An instance of a pallet in the storage. -/// -/// It is required that these instances are unique, to support multiple instances per pallet in the same runtime! -/// -/// E.g. for module MyModule default instance will have prefix "MyModule" and other instances -/// "InstanceNMyModule". -pub trait Instance: 'static { - /// Unique module prefix. E.g. "InstanceNMyModule" or "MyModule" - const PREFIX: &'static str; -} - -/// An instance of a storage in a pallet. -/// -/// Define an instance for an individual storage inside a pallet. -/// The pallet prefix is used to isolate the storage between pallets, and the storage prefix is -/// used to isolate storages inside a pallet. -/// -/// NOTE: These information can be used to define storages in pallet such as a `StorageMap` which -/// can use keys after `twox_128(pallet_prefix())++twox_128(STORAGE_PREFIX)` -pub trait StorageInstance { - /// Prefix of a pallet to isolate it from other pallets. - fn pallet_prefix() -> &'static str; - - /// Prefix given to a storage to isolate from other storages in the pallet. - const STORAGE_PREFIX: &'static str; -} - -/// Implement Get by returning Default for any type that implements Default. -pub struct GetDefault; -impl crate::traits::Get for GetDefault { - fn get() -> T { - T::default() - } -} - -/// A trait similar to `Convert` to convert values from `B` an abstract balance type -/// into u64 and back from u128. (This conversion is used in election and other places where complex -/// calculation over balance type is needed) -/// -/// Total issuance of the currency is passed in, but an implementation of this trait may or may not -/// use it. -/// -/// # WARNING -/// -/// the total issuance being passed in implies that the implementation must be aware of the fact -/// that its values can affect the outcome. This implies that if the vote value is dependent on the -/// total issuance, it should never ber written to storage for later re-use. -pub trait CurrencyToVote { - /// Convert balance to u64. - fn to_vote(value: B, issuance: B) -> u64; - - /// Convert u128 to balance. - fn to_currency(value: u128, issuance: B) -> B; -} - -/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128. -/// -/// The factor is the `(total_issuance / u64::max()).max(1)`, represented as u64. Let's look at the -/// important cases: -/// -/// If the chain's total issuance is less than u64::max(), this will always be 1, which means that -/// the factor will not have any effect. In this case, any account's balance is also less. Thus, -/// both of the conversions are basically an `as`; Any balance can fit in u64. -/// -/// If the chain's total issuance is more than 2*u64::max(), then a factor might be multiplied and -/// divided upon conversion. -pub struct U128CurrencyToVote; - -impl U128CurrencyToVote { - fn factor(issuance: u128) -> u128 { - (issuance / u64::max_value() as u128).max(1) - } -} - -impl CurrencyToVote for U128CurrencyToVote { - fn to_vote(value: u128, issuance: u128) -> u64 { - (value / Self::factor(issuance)).saturated_into() - } - - fn to_currency(value: u128, issuance: u128) -> u128 { - value.saturating_mul(Self::factor(issuance)) - } -} - - -/// A naive implementation of `CurrencyConvert` that simply saturates all conversions. -/// -/// # Warning -/// -/// This is designed to be used mostly for testing. Use with care, and think about the consequences. -pub struct SaturatingCurrencyToVote; - -impl + UniqueSaturatedFrom> CurrencyToVote for SaturatingCurrencyToVote { - fn to_vote(value: B, _: B) -> u64 { - value.unique_saturated_into() - } - - fn to_currency(value: u128, _: B) -> B { - B::unique_saturated_from(value) - } -} - -/// Something that can be checked to be a of sub type `T`. -/// -/// This is useful for enums where each variant encapsulates a different sub type, and -/// you need access to these sub types. -/// -/// For example, in FRAME, this trait is implemented for the runtime `Call` enum. Pallets use this -/// to check if a certain call is an instance of the local pallet's `Call` enum. -/// -/// # Example -/// -/// ``` -/// # use frame_support::traits::IsSubType; -/// -/// enum Test { -/// String(String), -/// U32(u32), -/// } -/// -/// impl IsSubType for Test { -/// fn is_sub_type(&self) -> Option<&String> { -/// match self { -/// Self::String(ref r) => Some(r), -/// _ => None, -/// } -/// } -/// } -/// -/// impl IsSubType for Test { -/// fn is_sub_type(&self) -> Option<&u32> { -/// match self { -/// Self::U32(ref r) => Some(r), -/// _ => None, -/// } -/// } -/// } -/// -/// fn main() { -/// let data = Test::String("test".into()); -/// -/// assert_eq!("test", IsSubType::::is_sub_type(&data).unwrap().as_str()); -/// } -/// ``` -pub trait IsSubType { - /// Returns `Some(_)` if `self` is an instance of sub type `T`. - fn is_sub_type(&self) -> Option<&T>; -} - -/// The pallet hooks trait. Implementing this lets you express some logic to execute. -pub trait Hooks { - /// The block is being finalized. Implement to have something happen. - fn on_finalize(_n: BlockNumber) {} - - /// This will be run when the block is being finalized (before `on_finalize`). - /// Implement to have something happen using the remaining weight. - /// Will not fire if the remaining weight is 0. - /// Return the weight used, the hook will subtract it from current weight used - /// and pass the result to the next `on_idle` hook if it exists. - fn on_idle( - _n: BlockNumber, - _remaining_weight: crate::weights::Weight - ) -> crate::weights::Weight { - 0 - } - - /// The block is being initialized. Implement to have something happen. - /// - /// Return the non-negotiable weight consumed in the block. - fn on_initialize(_n: BlockNumber) -> crate::weights::Weight { 0 } - - /// Perform a module upgrade. - /// - /// NOTE: this doesn't include all pallet logic triggered on runtime upgrade. For instance it - /// doesn't include the write of the pallet version in storage. The final complete logic - /// triggered on runtime upgrade is given by implementation of `OnRuntimeUpgrade` trait by - /// `Pallet`. - /// - /// # Warning - /// - /// This function will be called before we initialized any runtime state, aka `on_initialize` - /// wasn't called yet. So, information like the block number and any other - /// block local data are not accessible. - /// - /// Return the non-negotiable weight consumed for runtime upgrade. - fn on_runtime_upgrade() -> crate::weights::Weight { 0 } - - /// Execute some pre-checks prior to a runtime upgrade. - /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<(), &'static str> { - Ok(()) - } - - /// Execute some post-checks after a runtime upgrade. - /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. - #[cfg(feature = "try-runtime")] - fn post_upgrade() -> Result<(), &'static str> { - Ok(()) - } - - /// Implementing this function on a module allows you to perform long-running tasks - /// that make (by default) validators generate transactions that feed results - /// of those long-running computations back on chain. - /// - /// NOTE: This function runs off-chain, so it can access the block state, - /// but cannot preform any alterations. More specifically alterations are - /// not forbidden, but they are not persisted in any way after the worker - /// has finished. - /// - /// This function is being called after every block import (when fully synced). - /// - /// Implement this and use any of the `Offchain` `sp_io` set of APIs - /// to perform off-chain computations, calls and submit transactions - /// with results to trigger any on-chain changes. - /// Any state alterations are lost and are not persisted. - fn offchain_worker(_n: BlockNumber) {} - - /// Run integrity test. - /// - /// The test is not executed in a externalities provided environment. - fn integrity_test() {} -} - -/// A trait to define the build function of a genesis config, T and I are placeholder for pallet -/// trait and pallet instance. +pub use hooks::{OnRuntimeUpgradeHelpersExt, ON_RUNTIME_UPGRADE_PREFIX}; #[cfg(feature = "std")] -pub trait GenesisBuild: Default + MaybeSerializeDeserialize { - /// The build function is called within an externalities allowing storage APIs. - /// Thus one can write to storage using regular pallet storages. - fn build(&self); - - /// Build the storage using `build` inside default storage. - fn build_storage(&self) -> Result { - let mut storage = Default::default(); - self.assimilate_storage(&mut storage)?; - Ok(storage) - } - - /// Assimilate the storage for this module into pre-existing overlays. - fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { - sp_state_machine::BasicExternalities::execute_with_storage(storage, || { - self.build(); - Ok(()) - }) - } -} - -/// The storage key postfix that is used to store the [`PalletVersion`] per pallet. -/// -/// The full storage key is built by using: -/// Twox128([`PalletInfo::name`]) ++ Twox128([`PALLET_VERSION_STORAGE_KEY_POSTFIX`]) -pub const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; - -/// The version of a pallet. -/// -/// Each pallet version is stored in the state under a fixed key. See -/// [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] for how this key is built. -#[derive(RuntimeDebug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy)] -pub struct PalletVersion { - /// The major version of the pallet. - pub major: u16, - /// The minor version of the pallet. - pub minor: u8, - /// The patch version of the pallet. - pub patch: u8, -} - -impl PalletVersion { - /// Creates a new instance of `Self`. - pub fn new(major: u16, minor: u8, patch: u8) -> Self { - Self { - major, - minor, - patch, - } - } - - /// Returns the storage key for a pallet version. - /// - /// See [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built. - /// - /// Returns `None` if the given `PI` returned a `None` as name for the given - /// `Pallet`. - pub fn storage_key() -> Option<[u8; 32]> { - let pallet_name = PI::name::()?; - - let pallet_name = sp_io::hashing::twox_128(pallet_name.as_bytes()); - let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); - - let mut final_key = [0u8; 32]; - final_key[..16].copy_from_slice(&pallet_name); - final_key[16..].copy_from_slice(&postfix); - - Some(final_key) - } - - /// Put this pallet version into the storage. - /// - /// It will use the storage key that is associated with the given `Pallet`. - /// - /// # Panics - /// - /// This function will panic iff `Pallet` can not be found by `PalletInfo`. - /// In a runtime that is put together using - /// [`construct_runtime!`](crate::construct_runtime) this should never happen. - /// - /// It will also panic if this function isn't executed in an externalities - /// provided environment. - pub fn put_into_storage(&self) { - let key = Self::storage_key::() - .expect("Every active pallet has a name in the runtime; qed"); - - crate::storage::unhashed::put(&key, self); - } -} - -impl sp_std::cmp::PartialOrd for PalletVersion { - fn partial_cmp(&self, other: &Self) -> Option { - let res = self.major - .cmp(&other.major) - .then_with(|| - self.minor - .cmp(&other.minor) - .then_with(|| self.patch.cmp(&other.patch) - )); - - Some(res) - } -} - -/// Provides version information about a pallet. -/// -/// This trait provides two functions for returning the version of a -/// pallet. There is a state where both functions can return distinct versions. -/// See [`GetPalletVersion::storage_version`] for more information about this. -pub trait GetPalletVersion { - /// Returns the current version of the pallet. - fn current_version() -> PalletVersion; - - /// Returns the version of the pallet that is stored in storage. - /// - /// Most of the time this will return the exact same version as - /// [`GetPalletVersion::current_version`]. Only when being in - /// a state after a runtime upgrade happened and the pallet did - /// not yet updated its version in storage, this will return a - /// different(the previous, seen from the time of calling) version. - /// - /// See [`PalletVersion`] for more information. - /// - /// # Note - /// - /// If there was no previous version of the pallet stored in the state, - /// this function returns `None`. - fn storage_version() -> Option; -} - -/// Something that can execute a given block. -/// -/// Executing a block means that all extrinsics in a given block will be executed and the resulting -/// header will be checked against the header of the given block. -pub trait ExecuteBlock { - /// Execute the given `block`. - /// - /// This will execute all extrinsics in the block and check that the resulting header is correct. - /// - /// # Panic - /// - /// Panics when an extrinsics panics or the resulting header doesn't match the expected header. - fn execute_block(block: Block); -} - -/// A trait which is called when the timestamp is set in the runtime. -#[impl_trait_for_tuples::impl_for_tuples(30)] -pub trait OnTimestampSet { - /// Called when the timestamp is set. - fn on_timestamp_set(moment: Moment); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn on_initialize_and_on_runtime_upgrade_weight_merge_works() { - struct Test; - impl OnInitialize for Test { - fn on_initialize(_n: u8) -> crate::weights::Weight { - 10 - } - } - impl OnRuntimeUpgrade for Test { - fn on_runtime_upgrade() -> crate::weights::Weight { - 20 - } - } +pub use hooks::GenesisBuild; - assert_eq!(<(Test, Test)>::on_initialize(0), 20); - assert_eq!(<(Test, Test)>::on_runtime_upgrade(), 40); - } +pub mod schedule; +mod storage; +pub use storage::{Instance, StorageInstance}; - #[test] - fn check_pallet_version_ordering() { - let version = PalletVersion::new(1, 0, 0); - assert!(version > PalletVersion::new(0, 1, 2)); - assert!(version == PalletVersion::new(1, 0, 0)); - assert!(version < PalletVersion::new(1, 0, 1)); - assert!(version < PalletVersion::new(1, 1, 0)); +mod dispatch; +pub use dispatch::{EnsureOrigin, OriginTrait, UnfilteredDispatchable}; - let version = PalletVersion::new(2, 50, 50); - assert!(version < PalletVersion::new(2, 50, 51)); - assert!(version > PalletVersion::new(2, 49, 51)); - assert!(version < PalletVersion::new(3, 49, 51)); - } -} +mod voting; +pub use voting::{CurrencyToVote, SaturatingCurrencyToVote, U128CurrencyToVote}; diff --git a/frame/support/src/traits/dispatch.rs b/frame/support/src/traits/dispatch.rs new file mode 100644 index 0000000000000..29dbaf105a05a --- /dev/null +++ b/frame/support/src/traits/dispatch.rs @@ -0,0 +1,87 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with dispatching calls and the origin from which they are dispatched. + +use crate::dispatch::DispatchResultWithPostInfo; +use sp_runtime::traits::BadOrigin; + +/// Some sort of check on the origin is performed by this object. +pub trait EnsureOrigin { + /// A return type. + type Success; + /// Perform the origin check. + fn ensure_origin(o: OuterOrigin) -> Result { + Self::try_origin(o).map_err(|_| BadOrigin) + } + /// Perform the origin check. + fn try_origin(o: OuterOrigin) -> Result; + + /// Returns an outer origin capable of passing `try_origin` check. + /// + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> OuterOrigin; +} + +/// Type that can be dispatched with an origin but without checking the origin filter. +/// +/// Implemented for pallet dispatchable type by `decl_module` and for runtime dispatchable by +/// `construct_runtime` and `impl_outer_dispatch`. +pub trait UnfilteredDispatchable { + /// The origin type of the runtime, (i.e. `frame_system::Config::Origin`). + type Origin; + + /// Dispatch this call but do not check the filter in origin. + fn dispatch_bypass_filter(self, origin: Self::Origin) -> DispatchResultWithPostInfo; +} + +/// Methods available on `frame_system::Config::Origin`. +pub trait OriginTrait: Sized { + /// Runtime call type, as in `frame_system::Config::Call` + type Call; + + /// The caller origin, overarching type of all pallets origins. + type PalletsOrigin; + + /// The AccountId used across the system. + type AccountId; + + /// Add a filter to the origin. + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static); + + /// Reset origin filters to default one, i.e `frame_system::Config::BaseCallFilter`. + fn reset_filter(&mut self); + + /// Replace the caller with caller from the other origin + fn set_caller_from(&mut self, other: impl Into); + + /// Filter the call, if false then call is filtered out. + fn filter_call(&self, call: &Self::Call) -> bool; + + /// Get the caller. + fn caller(&self) -> &Self::PalletsOrigin; + + /// Create with system none origin and `frame-system::Config::BaseCallFilter`. + fn none() -> Self; + + /// Create with system root origin and no filter. + fn root() -> Self; + + /// Create with system signed origin and `frame-system::Config::BaseCallFilter`. + fn signed(by: Self::AccountId) -> Self; +} diff --git a/frame/support/src/traits/filter.rs b/frame/support/src/traits/filter.rs new file mode 100644 index 0000000000000..f884a8ece72e5 --- /dev/null +++ b/frame/support/src/traits/filter.rs @@ -0,0 +1,282 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits and associated utilities for dealing with abstract constraint filters. + +use sp_std::marker::PhantomData; + +/// Simple trait for providing a filter over a reference to some type. +pub trait Filter { + /// Determine if a given value should be allowed through the filter (returns `true`) or not. + fn filter(_: &T) -> bool; +} + +impl Filter for () { + fn filter(_: &T) -> bool { true } +} + +/// Trait to add a constraint onto the filter. +pub trait FilterStack: Filter { + /// The type used to archive the stack. + type Stack; + + /// Add a new `constraint` onto the filter. + fn push(constraint: impl Fn(&T) -> bool + 'static); + + /// Removes the most recently pushed, and not-yet-popped, constraint from the filter. + fn pop(); + + /// Clear the filter, returning a value that may be used later to `restore` it. + fn take() -> Self::Stack; + + /// Restore the filter from a previous `take` operation. + fn restore(taken: Self::Stack); +} + +/// Guard type for pushing a constraint to a `FilterStack` and popping when dropped. +pub struct FilterStackGuard, T>(PhantomData<(F, T)>); + +/// Guard type for clearing all pushed constraints from a `FilterStack` and reinstating them when +/// dropped. +pub struct ClearFilterGuard, T>(Option, PhantomData); + +impl, T> FilterStackGuard { + /// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when + /// this instance is dropped. + pub fn new(constraint: impl Fn(&T) -> bool + 'static) -> Self { + F::push(constraint); + Self(PhantomData) + } +} + +impl, T> Drop for FilterStackGuard { + fn drop(&mut self) { + F::pop(); + } +} + +impl, T> ClearFilterGuard { + /// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when + /// this instance is dropped. + pub fn new() -> Self { + Self(Some(F::take()), PhantomData) + } +} + +impl, T> Drop for ClearFilterGuard { + fn drop(&mut self) { + if let Some(taken) = self.0.take() { + F::restore(taken); + } + } +} + +/// Simple trait for providing a filter over a reference to some type, given an instance of itself. +pub trait InstanceFilter: Sized + Send + Sync { + /// Determine if a given value should be allowed through the filter (returns `true`) or not. + fn filter(&self, _: &T) -> bool; + + /// Determines whether `self` matches at least everything that `_o` does. + fn is_superset(&self, _o: &Self) -> bool { false } +} + +impl InstanceFilter for () { + fn filter(&self, _: &T) -> bool { true } + fn is_superset(&self, _o: &Self) -> bool { true } +} + +/// Re-expected for the macro. +#[doc(hidden)] +pub use sp_std::{mem::{swap, take}, cell::RefCell, vec::Vec, boxed::Box}; + +#[macro_export] +macro_rules! impl_filter_stack { + ($target:ty, $base:ty, $call:ty, $module:ident) => { + #[cfg(feature = "std")] + mod $module { + #[allow(unused_imports)] + use super::*; + use $crate::traits::filter::{swap, take, RefCell, Vec, Box, Filter, FilterStack}; + + thread_local! { + static FILTER: RefCell bool + 'static>>> = RefCell::new(Vec::new()); + } + + impl Filter<$call> for $target { + fn filter(call: &$call) -> bool { + <$base>::filter(call) && + FILTER.with(|filter| filter.borrow().iter().all(|f| f(call))) + } + } + + impl FilterStack<$call> for $target { + type Stack = Vec bool + 'static>>; + fn push(f: impl Fn(&$call) -> bool + 'static) { + FILTER.with(|filter| filter.borrow_mut().push(Box::new(f))); + } + fn pop() { + FILTER.with(|filter| filter.borrow_mut().pop()); + } + fn take() -> Self::Stack { + FILTER.with(|filter| take(filter.borrow_mut().as_mut())) + } + fn restore(mut s: Self::Stack) { + FILTER.with(|filter| swap(filter.borrow_mut().as_mut(), &mut s)); + } + } + } + + #[cfg(not(feature = "std"))] + mod $module { + #[allow(unused_imports)] + use super::*; + use $crate::traits::{swap, take, RefCell, Vec, Box, Filter, FilterStack}; + + struct ThisFilter(RefCell bool + 'static>>>); + // NOTE: Safe only in wasm (guarded above) because there's only one thread. + unsafe impl Send for ThisFilter {} + unsafe impl Sync for ThisFilter {} + + static FILTER: ThisFilter = ThisFilter(RefCell::new(Vec::new())); + + impl Filter<$call> for $target { + fn filter(call: &$call) -> bool { + <$base>::filter(call) && FILTER.0.borrow().iter().all(|f| f(call)) + } + } + + impl FilterStack<$call> for $target { + type Stack = Vec bool + 'static>>; + fn push(f: impl Fn(&$call) -> bool + 'static) { + FILTER.0.borrow_mut().push(Box::new(f)); + } + fn pop() { + FILTER.0.borrow_mut().pop(); + } + fn take() -> Self::Stack { + take(FILTER.0.borrow_mut().as_mut()) + } + fn restore(mut s: Self::Stack) { + swap(FILTER.0.borrow_mut().as_mut(), &mut s); + } + } + } + } +} + +/// Type that provide some integrity tests. +/// +/// This implemented for modules by `decl_module`. +#[impl_trait_for_tuples::impl_for_tuples(30)] +pub trait IntegrityTest { + /// Run integrity test. + /// + /// The test is not executed in a externalities provided environment. + fn integrity_test() {} +} + +#[cfg(test)] +pub mod test_impl_filter_stack { + use super::*; + + pub struct IsCallable; + pub struct BaseFilter; + impl Filter for BaseFilter { + fn filter(x: &u32) -> bool { x % 2 == 0 } + } + impl_filter_stack!( + crate::traits::filter::test_impl_filter_stack::IsCallable, + crate::traits::filter::test_impl_filter_stack::BaseFilter, + u32, + is_callable + ); + + #[test] + fn impl_filter_stack_should_work() { + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + + IsCallable::push(|x| *x < 42); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + + IsCallable::push(|x| *x % 3 == 0); + assert!(IsCallable::filter(&36)); + assert!(!IsCallable::filter(&40)); + + IsCallable::pop(); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + + let saved = IsCallable::take(); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + + IsCallable::restore(saved); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + + IsCallable::pop(); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + } + + #[test] + fn guards_should_work() { + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + { + let _guard_1 = FilterStackGuard::::new(|x| *x < 42); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + { + let _guard_2 = FilterStackGuard::::new(|x| *x % 3 == 0); + assert!(IsCallable::filter(&36)); + assert!(!IsCallable::filter(&40)); + } + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + { + let _guard_2 = ClearFilterGuard::::new(); + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + } + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(!IsCallable::filter(&42)); + } + assert!(IsCallable::filter(&36)); + assert!(IsCallable::filter(&40)); + assert!(IsCallable::filter(&42)); + assert!(!IsCallable::filter(&43)); + } +} diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs new file mode 100644 index 0000000000000..5f7b35a9ad25c --- /dev/null +++ b/frame/support/src/traits/hooks.rs @@ -0,0 +1,349 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for hooking tasks to events in a blockchain's lifecycle. + +use sp_arithmetic::traits::Saturating; +use sp_runtime::traits::MaybeSerializeDeserialize; +use impl_trait_for_tuples::impl_for_tuples; + +/// The block initialization trait. +/// +/// Implementing this lets you express what should happen for your pallet when the block is +/// beginning (right before the first extrinsic is executed). +pub trait OnInitialize { + /// The block is being initialized. Implement to have something happen. + /// + /// Return the non-negotiable weight consumed in the block. + /// + /// NOTE: This function is called BEFORE ANY extrinsic in a block is applied, + /// including inherent extrinsics. Hence for instance, if you runtime includes + /// `pallet_timestamp`, the `timestamp` is not yet up to date at this point. + fn on_initialize(_n: BlockNumber) -> crate::weights::Weight { 0 } +} + +#[impl_for_tuples(30)] +impl OnInitialize for Tuple { + fn on_initialize(n: BlockNumber) -> crate::weights::Weight { + let mut weight = 0; + for_tuples!( #( weight = weight.saturating_add(Tuple::on_initialize(n.clone())); )* ); + weight + } +} + +/// The block finalization trait. +/// +/// Implementing this lets you express what should happen for your pallet when the block is ending. +#[impl_for_tuples(30)] +pub trait OnFinalize { + /// The block is being finalized. Implement to have something happen. + /// + /// NOTE: This function is called AFTER ALL extrinsics in a block are applied, + /// including inherent extrinsics. + fn on_finalize(_n: BlockNumber) {} +} + +/// The block's on idle trait. +/// +/// Implementing this lets you express what should happen for your pallet before +/// block finalization (see `on_finalize` hook) in case any remaining weight is left. +pub trait OnIdle { + /// The block is being finalized. + /// Implement to have something happen in case there is leftover weight. + /// Check the passed `remaining_weight` to make sure it is high enough to allow for + /// your pallet's extra computation. + /// + /// NOTE: This function is called AFTER ALL extrinsics - including inherent extrinsics - + /// in a block are applied but before `on_finalize` is executed. + fn on_idle( + _n: BlockNumber, + _remaining_weight: crate::weights::Weight + ) -> crate::weights::Weight { + 0 + } +} + +#[impl_for_tuples(30)] +impl OnIdle for Tuple { + fn on_idle(n: BlockNumber, remaining_weight: crate::weights::Weight) -> crate::weights::Weight { + let mut weight = 0; + for_tuples!( #( + let adjusted_remaining_weight = remaining_weight.saturating_sub(weight); + weight = weight.saturating_add(Tuple::on_idle(n.clone(), adjusted_remaining_weight)); + )* ); + weight + } +} + +/// A trait that will be called at genesis. +/// +/// Implementing this trait for a pallet let's you express operations that should +/// happen at genesis. It will be called in an externalities provided environment and +/// will see the genesis state after all pallets have written their genesis state. +#[impl_for_tuples(30)] +pub trait OnGenesis { + /// Something that should happen at genesis. + fn on_genesis() {} +} + +/// Prefix to be used (optionally) for implementing [`OnRuntimeUpgradeHelpersExt::storage_key`]. +#[cfg(feature = "try-runtime")] +pub const ON_RUNTIME_UPGRADE_PREFIX: &[u8] = b"__ON_RUNTIME_UPGRADE__"; + +/// Some helper functions for [`OnRuntimeUpgrade`] during `try-runtime` testing. +#[cfg(feature = "try-runtime")] +pub trait OnRuntimeUpgradeHelpersExt { + /// Generate a storage key unique to this runtime upgrade. + /// + /// This can be used to communicate data from pre-upgrade to post-upgrade state and check + /// them. See [`Self::set_temp_storage`] and [`Self::get_temp_storage`]. + #[cfg(feature = "try-runtime")] + fn storage_key(ident: &str) -> [u8; 32] { + let prefix = sp_io::hashing::twox_128(ON_RUNTIME_UPGRADE_PREFIX); + let ident = sp_io::hashing::twox_128(ident.as_bytes()); + + let mut final_key = [0u8; 32]; + final_key[..16].copy_from_slice(&prefix); + final_key[16..].copy_from_slice(&ident); + + final_key + } + + /// Get temporary storage data written by [`Self::set_temp_storage`]. + /// + /// Returns `None` if either the data is unavailable or un-decodable. + /// + /// A `at` storage identifier must be provided to indicate where the storage is being read from. + #[cfg(feature = "try-runtime")] + fn get_temp_storage(at: &str) -> Option { + sp_io::storage::get(&Self::storage_key(at)) + .and_then(|bytes| codec::Decode::decode(&mut &*bytes).ok()) + } + + /// Write some temporary data to a specific storage that can be read (potentially in + /// post-upgrade hook) via [`Self::get_temp_storage`]. + /// + /// A `at` storage identifier must be provided to indicate where the storage is being written + /// to. + #[cfg(feature = "try-runtime")] + fn set_temp_storage(data: T, at: &str) { + sp_io::storage::set(&Self::storage_key(at), &data.encode()); + } +} + +#[cfg(feature = "try-runtime")] +impl OnRuntimeUpgradeHelpersExt for U {} + +/// The runtime upgrade trait. +/// +/// Implementing this lets you express what should happen when the runtime upgrades, +/// and changes may need to occur to your module. +pub trait OnRuntimeUpgrade { + /// Perform a module upgrade. + /// + /// # Warning + /// + /// This function will be called before we initialized any runtime state, aka `on_initialize` + /// wasn't called yet. So, information like the block number and any other + /// block local data are not accessible. + /// + /// Return the non-negotiable weight consumed for runtime upgrade. + fn on_runtime_upgrade() -> crate::weights::Weight { + 0 + } + + /// Execute some pre-checks prior to a runtime upgrade. + /// + /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { Ok(()) } + + /// Execute some post-checks after a runtime upgrade. + /// + /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { Ok(()) } +} + +#[impl_for_tuples(30)] +impl OnRuntimeUpgrade for Tuple { + fn on_runtime_upgrade() -> crate::weights::Weight { + let mut weight = 0; + for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* ); + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + let mut result = Ok(()); + for_tuples!( #( result = result.and(Tuple::pre_upgrade()); )* ); + result + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { + let mut result = Ok(()); + for_tuples!( #( result = result.and(Tuple::post_upgrade()); )* ); + result + } +} + +/// The pallet hooks trait. Implementing this lets you express some logic to execute. +pub trait Hooks { + /// The block is being finalized. Implement to have something happen. + fn on_finalize(_n: BlockNumber) {} + + /// This will be run when the block is being finalized (before `on_finalize`). + /// Implement to have something happen using the remaining weight. + /// Will not fire if the remaining weight is 0. + /// Return the weight used, the hook will subtract it from current weight used + /// and pass the result to the next `on_idle` hook if it exists. + fn on_idle( + _n: BlockNumber, + _remaining_weight: crate::weights::Weight + ) -> crate::weights::Weight { + 0 + } + + /// The block is being initialized. Implement to have something happen. + /// + /// Return the non-negotiable weight consumed in the block. + fn on_initialize(_n: BlockNumber) -> crate::weights::Weight { 0 } + + /// Perform a module upgrade. + /// + /// NOTE: this doesn't include all pallet logic triggered on runtime upgrade. For instance it + /// doesn't include the write of the pallet version in storage. The final complete logic + /// triggered on runtime upgrade is given by implementation of `OnRuntimeUpgrade` trait by + /// `Pallet`. + /// + /// # Warning + /// + /// This function will be called before we initialized any runtime state, aka `on_initialize` + /// wasn't called yet. So, information like the block number and any other + /// block local data are not accessible. + /// + /// Return the non-negotiable weight consumed for runtime upgrade. + fn on_runtime_upgrade() -> crate::weights::Weight { 0 } + + /// Execute some pre-checks prior to a runtime upgrade. + /// + /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + Ok(()) + } + + /// Execute some post-checks after a runtime upgrade. + /// + /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { + Ok(()) + } + + /// Implementing this function on a module allows you to perform long-running tasks + /// that make (by default) validators generate transactions that feed results + /// of those long-running computations back on chain. + /// + /// NOTE: This function runs off-chain, so it can access the block state, + /// but cannot preform any alterations. More specifically alterations are + /// not forbidden, but they are not persisted in any way after the worker + /// has finished. + /// + /// This function is being called after every block import (when fully synced). + /// + /// Implement this and use any of the `Offchain` `sp_io` set of APIs + /// to perform off-chain computations, calls and submit transactions + /// with results to trigger any on-chain changes. + /// Any state alterations are lost and are not persisted. + fn offchain_worker(_n: BlockNumber) {} + + /// Run integrity test. + /// + /// The test is not executed in a externalities provided environment. + fn integrity_test() {} +} + +/// A trait to define the build function of a genesis config, T and I are placeholder for pallet +/// trait and pallet instance. +#[cfg(feature = "std")] +pub trait GenesisBuild: Default + MaybeSerializeDeserialize { + /// The build function is called within an externalities allowing storage APIs. + /// Thus one can write to storage using regular pallet storages. + fn build(&self); + + /// Build the storage using `build` inside default storage. + fn build_storage(&self) -> Result { + let mut storage = Default::default(); + self.assimilate_storage(&mut storage)?; + Ok(storage) + } + + /// Assimilate the storage for this module into pre-existing overlays. + fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { + sp_state_machine::BasicExternalities::execute_with_storage(storage, || { + self.build(); + Ok(()) + }) + } +} + +/// A trait which is called when the timestamp is set in the runtime. +#[impl_for_tuples(30)] +pub trait OnTimestampSet { + /// Called when the timestamp is set. + fn on_timestamp_set(moment: Moment); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::metadata::PalletVersion; + + #[test] + fn on_initialize_and_on_runtime_upgrade_weight_merge_works() { + struct Test; + impl OnInitialize for Test { + fn on_initialize(_n: u8) -> crate::weights::Weight { + 10 + } + } + impl OnRuntimeUpgrade for Test { + fn on_runtime_upgrade() -> crate::weights::Weight { + 20 + } + } + + assert_eq!(<(Test, Test)>::on_initialize(0), 20); + assert_eq!(<(Test, Test)>::on_runtime_upgrade(), 40); + } + + #[test] + fn check_pallet_version_ordering() { + let version = PalletVersion::new(1, 0, 0); + assert!(version > PalletVersion::new(0, 1, 2)); + assert!(version == PalletVersion::new(1, 0, 0)); + assert!(version < PalletVersion::new(1, 0, 1)); + assert!(version < PalletVersion::new(1, 1, 0)); + + let version = PalletVersion::new(2, 50, 50); + assert!(version < PalletVersion::new(2, 50, 51)); + assert!(version > PalletVersion::new(2, 49, 51)); + assert!(version < PalletVersion::new(3, 49, 51)); + } +} diff --git a/frame/support/src/traits/members.rs b/frame/support/src/traits/members.rs new file mode 100644 index 0000000000000..d3ce6786af8c1 --- /dev/null +++ b/frame/support/src/traits/members.rs @@ -0,0 +1,142 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with the idea of membership. + +use sp_std::prelude::*; + +/// A trait for querying whether a type can be said to "contain" a value. +pub trait Contains { + /// Return `true` if this "contains" the given value `t`. + fn contains(t: &T) -> bool { Self::sorted_members().binary_search(t).is_ok() } + + /// Get a vector of all members in the set, ordered. + fn sorted_members() -> Vec; + + /// Get the number of items in the set. + fn count() -> usize { Self::sorted_members().len() } + + /// Add an item that would satisfy `contains`. It does not make sure any other + /// state is correctly maintained or generated. + /// + /// **Should be used for benchmarking only!!!** + #[cfg(feature = "runtime-benchmarks")] + fn add(_t: &T) { unimplemented!() } +} + +/// A trait for querying bound for the length of an implementation of `Contains` +pub trait ContainsLengthBound { + /// Minimum number of elements contained + fn min_len() -> usize; + /// Maximum number of elements contained + fn max_len() -> usize; +} + +/// Trait for type that can handle the initialization of account IDs at genesis. +pub trait InitializeMembers { + /// Initialize the members to the given `members`. + fn initialize_members(members: &[AccountId]); +} + +impl InitializeMembers for () { + fn initialize_members(_: &[T]) {} +} + +/// Trait for type that can handle incremental changes to a set of account IDs. +pub trait ChangeMembers { + /// A number of members `incoming` just joined the set and replaced some `outgoing` ones. The + /// new set is given by `new`, and need not be sorted. + /// + /// This resets any previous value of prime. + fn change_members(incoming: &[AccountId], outgoing: &[AccountId], mut new: Vec) { + new.sort(); + Self::change_members_sorted(incoming, outgoing, &new[..]); + } + + /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The + /// new set is thus given by `sorted_new` and **must be sorted**. + /// + /// NOTE: This is the only function that needs to be implemented in `ChangeMembers`. + /// + /// This resets any previous value of prime. + fn change_members_sorted( + incoming: &[AccountId], + outgoing: &[AccountId], + sorted_new: &[AccountId], + ); + + /// Set the new members; they **must already be sorted**. This will compute the diff and use it to + /// call `change_members_sorted`. + /// + /// This resets any previous value of prime. + fn set_members_sorted(new_members: &[AccountId], old_members: &[AccountId]) { + let (incoming, outgoing) = Self::compute_members_diff_sorted(new_members, old_members); + Self::change_members_sorted(&incoming[..], &outgoing[..], &new_members); + } + + /// Compute diff between new and old members; they **must already be sorted**. + /// + /// Returns incoming and outgoing members. + fn compute_members_diff_sorted( + new_members: &[AccountId], + old_members: &[AccountId], + ) -> (Vec, Vec) { + let mut old_iter = old_members.iter(); + let mut new_iter = new_members.iter(); + let mut incoming = Vec::new(); + let mut outgoing = Vec::new(); + let mut old_i = old_iter.next(); + let mut new_i = new_iter.next(); + loop { + match (old_i, new_i) { + (None, None) => break, + (Some(old), Some(new)) if old == new => { + old_i = old_iter.next(); + new_i = new_iter.next(); + } + (Some(old), Some(new)) if old < new => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (Some(old), None) => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (_, Some(new)) => { + incoming.push(new.clone()); + new_i = new_iter.next(); + } + } + } + (incoming, outgoing) + } + + /// Set the prime member. + fn set_prime(_prime: Option) {} + + /// Get the current prime. + fn get_prime() -> Option { + None + } +} + +impl ChangeMembers for () { + fn change_members(_: &[T], _: &[T], _: Vec) {} + fn change_members_sorted(_: &[T], _: &[T], _: &[T]) {} + fn set_members_sorted(_: &[T], _: &[T]) {} + fn set_prime(_: Option) {} +} diff --git a/frame/support/src/traits/metadata.rs b/frame/support/src/traits/metadata.rs new file mode 100644 index 0000000000000..ff4507dce9c98 --- /dev/null +++ b/frame/support/src/traits/metadata.rs @@ -0,0 +1,168 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for managing information attached to pallets and their constituents. + +use codec::{Encode, Decode}; +use sp_runtime::RuntimeDebug; + +/// Provides information about the pallet setup in the runtime. +/// +/// An implementor should be able to provide information about each pallet that +/// is configured in `construct_runtime!`. +pub trait PalletInfo { + /// Convert the given pallet `P` into its index as configured in the runtime. + fn index() -> Option; + /// Convert the given pallet `P` into its name as configured in the runtime. + fn name() -> Option<&'static str>; +} + +/// The function and pallet name of the Call. +#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug)] +pub struct CallMetadata { + /// Name of the function. + pub function_name: &'static str, + /// Name of the pallet to which the function belongs. + pub pallet_name: &'static str, +} + +/// Gets the function name of the Call. +pub trait GetCallName { + /// Return all function names. + fn get_call_names() -> &'static [&'static str]; + /// Return the function name of the Call. + fn get_call_name(&self) -> &'static str; +} + +/// Gets the metadata for the Call - function name and pallet name. +pub trait GetCallMetadata { + /// Return all module names. + fn get_module_names() -> &'static [&'static str]; + /// Return all function names for the given `module`. + fn get_call_names(module: &str) -> &'static [&'static str]; + /// Return a [`CallMetadata`], containing function and pallet name of the Call. + fn get_call_metadata(&self) -> CallMetadata; +} + +/// The storage key postfix that is used to store the [`PalletVersion`] per pallet. +/// +/// The full storage key is built by using: +/// Twox128([`PalletInfo::name`]) ++ Twox128([`PALLET_VERSION_STORAGE_KEY_POSTFIX`]) +pub const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; + +/// The version of a pallet. +/// +/// Each pallet version is stored in the state under a fixed key. See +/// [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] for how this key is built. +#[derive(RuntimeDebug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy)] +pub struct PalletVersion { + /// The major version of the pallet. + pub major: u16, + /// The minor version of the pallet. + pub minor: u8, + /// The patch version of the pallet. + pub patch: u8, +} + +impl PalletVersion { + /// Creates a new instance of `Self`. + pub fn new(major: u16, minor: u8, patch: u8) -> Self { + Self { + major, + minor, + patch, + } + } + + /// Returns the storage key for a pallet version. + /// + /// See [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built. + /// + /// Returns `None` if the given `PI` returned a `None` as name for the given + /// `Pallet`. + pub fn storage_key() -> Option<[u8; 32]> { + let pallet_name = PI::name::()?; + + let pallet_name = sp_io::hashing::twox_128(pallet_name.as_bytes()); + let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); + + let mut final_key = [0u8; 32]; + final_key[..16].copy_from_slice(&pallet_name); + final_key[16..].copy_from_slice(&postfix); + + Some(final_key) + } + + /// Put this pallet version into the storage. + /// + /// It will use the storage key that is associated with the given `Pallet`. + /// + /// # Panics + /// + /// This function will panic iff `Pallet` can not be found by `PalletInfo`. + /// In a runtime that is put together using + /// [`construct_runtime!`](crate::construct_runtime) this should never happen. + /// + /// It will also panic if this function isn't executed in an externalities + /// provided environment. + pub fn put_into_storage(&self) { + let key = Self::storage_key::() + .expect("Every active pallet has a name in the runtime; qed"); + + crate::storage::unhashed::put(&key, self); + } +} + +impl sp_std::cmp::PartialOrd for PalletVersion { + fn partial_cmp(&self, other: &Self) -> Option { + let res = self.major + .cmp(&other.major) + .then_with(|| + self.minor + .cmp(&other.minor) + .then_with(|| self.patch.cmp(&other.patch) + )); + + Some(res) + } +} + +/// Provides version information about a pallet. +/// +/// This trait provides two functions for returning the version of a +/// pallet. There is a state where both functions can return distinct versions. +/// See [`GetPalletVersion::storage_version`] for more information about this. +pub trait GetPalletVersion { + /// Returns the current version of the pallet. + fn current_version() -> PalletVersion; + + /// Returns the version of the pallet that is stored in storage. + /// + /// Most of the time this will return the exact same version as + /// [`GetPalletVersion::current_version`]. Only when being in + /// a state after a runtime upgrade happened and the pallet did + /// not yet updated its version in storage, this will return a + /// different(the previous, seen from the time of calling) version. + /// + /// See [`PalletVersion`] for more information. + /// + /// # Note + /// + /// If there was no previous version of the pallet stored in the state, + /// this function returns `None`. + fn storage_version() -> Option; +} diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs new file mode 100644 index 0000000000000..2f219942907d8 --- /dev/null +++ b/frame/support/src/traits/misc.rs @@ -0,0 +1,271 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Smaller traits used in FRAME which don't need their own file. + +use sp_runtime::traits::{StoredMapError, Block as BlockT}; +use sp_arithmetic::traits::AtLeast32Bit; +use crate::dispatch::Parameter; + +/// Anything that can have a `::len()` method. +pub trait Len { + /// Return the length of data type. + fn len(&self) -> usize; +} + +impl Len for T where ::IntoIter: ExactSizeIterator { + fn len(&self) -> usize { + self.clone().into_iter().len() + } +} + +/// A trait for querying a single value from a type. +/// +/// It is not required that the value is constant. +pub trait Get { + /// Return the current value. + fn get() -> T; +} + +impl Get for () { + fn get() -> T { T::default() } +} + +/// Implement Get by returning Default for any type that implements Default. +pub struct GetDefault; +impl Get for GetDefault { + fn get() -> T { + T::default() + } +} + +/// A type for which some values make sense to be able to drop without further consideration. +pub trait TryDrop: Sized { + /// Drop an instance cleanly. Only works if its value represents "no-operation". + fn try_drop(self) -> Result<(), Self>; +} + +/// Return type used when we need to return one of two items, each of the opposite direction or +/// sign, with one (`Same`) being of the same type as the `self` or primary argument of the function +/// that returned it. +pub enum SameOrOther { + /// No item. + None, + /// An item of the same type as the `Self` on which the return function was called. + Same(A), + /// An item of the opposite type to the `Self` on which the return function was called. + Other(B), +} + +impl TryDrop for SameOrOther { + fn try_drop(self) -> Result<(), Self> { + if let SameOrOther::None = self { + Ok(()) + } else { + Err(self) + } + } +} + +impl SameOrOther { + /// Returns `Ok` with the inner value of `Same` if `self` is that, otherwise returns `Err` with + /// `self`. + pub fn try_same(self) -> Result { + match self { + SameOrOther::Same(a) => Ok(a), + x => Err(x), + } + } + + /// Returns `Ok` with the inner value of `Other` if `self` is that, otherwise returns `Err` with + /// `self`. + pub fn try_other(self) -> Result { + match self { + SameOrOther::Other(b) => Ok(b), + x => Err(x), + } + } + + /// Returns `Ok` if `self` is `None`, otherwise returns `Err` with `self`. + pub fn try_none(self) -> Result<(), Self> { + match self { + SameOrOther::None => Ok(()), + x => Err(x), + } + } + + pub fn same(self) -> Result where A: Default { + match self { + SameOrOther::Same(a) => Ok(a), + SameOrOther::None => Ok(A::default()), + SameOrOther::Other(b) => Err(b), + } + } + + pub fn other(self) -> Result where B: Default { + match self { + SameOrOther::Same(a) => Err(a), + SameOrOther::None => Ok(B::default()), + SameOrOther::Other(b) => Ok(b), + } + } +} + +/// Handler for when a new account has been created. +#[impl_trait_for_tuples::impl_for_tuples(30)] +pub trait OnNewAccount { + /// A new account `who` has been registered. + fn on_new_account(who: &AccountId); +} + +/// The account with the given id was reaped. +#[impl_trait_for_tuples::impl_for_tuples(30)] +pub trait OnKilledAccount { + /// The account with the given id was reaped. + fn on_killed_account(who: &AccountId); +} + +/// A simple, generic one-parameter event notifier/handler. +pub trait HandleLifetime { + /// An account was created. + fn created(_t: &T) -> Result<(), StoredMapError> { Ok(()) } + + /// An account was killed. + fn killed(_t: &T) -> Result<(), StoredMapError> { Ok(()) } +} + +impl HandleLifetime for () {} + +pub trait Time { + type Moment: AtLeast32Bit + Parameter + Default + Copy; + + fn now() -> Self::Moment; +} + +/// Trait to deal with unix time. +pub trait UnixTime { + /// Return duration since `SystemTime::UNIX_EPOCH`. + fn now() -> core::time::Duration; +} + +/// Trait to be used when types are exactly same. +/// +/// This allow to convert back and forth from type, a reference and a mutable reference. +pub trait IsType: Into + From { + /// Cast reference. + fn from_ref(t: &T) -> &Self; + + /// Cast reference. + fn into_ref(&self) -> &T; + + /// Cast mutable reference. + fn from_mut(t: &mut T) -> &mut Self; + + /// Cast mutable reference. + fn into_mut(&mut self) -> &mut T; +} + +impl IsType for T { + fn from_ref(t: &T) -> &Self { t } + fn into_ref(&self) -> &T { self } + fn from_mut(t: &mut T) -> &mut Self { t } + fn into_mut(&mut self) -> &mut T { self } +} + +/// Something that can be checked to be a of sub type `T`. +/// +/// This is useful for enums where each variant encapsulates a different sub type, and +/// you need access to these sub types. +/// +/// For example, in FRAME, this trait is implemented for the runtime `Call` enum. Pallets use this +/// to check if a certain call is an instance of the local pallet's `Call` enum. +/// +/// # Example +/// +/// ``` +/// # use frame_support::traits::IsSubType; +/// +/// enum Test { +/// String(String), +/// U32(u32), +/// } +/// +/// impl IsSubType for Test { +/// fn is_sub_type(&self) -> Option<&String> { +/// match self { +/// Self::String(ref r) => Some(r), +/// _ => None, +/// } +/// } +/// } +/// +/// impl IsSubType for Test { +/// fn is_sub_type(&self) -> Option<&u32> { +/// match self { +/// Self::U32(ref r) => Some(r), +/// _ => None, +/// } +/// } +/// } +/// +/// fn main() { +/// let data = Test::String("test".into()); +/// +/// assert_eq!("test", IsSubType::::is_sub_type(&data).unwrap().as_str()); +/// } +/// ``` +pub trait IsSubType { + /// Returns `Some(_)` if `self` is an instance of sub type `T`. + fn is_sub_type(&self) -> Option<&T>; +} + +/// Something that can execute a given block. +/// +/// Executing a block means that all extrinsics in a given block will be executed and the resulting +/// header will be checked against the header of the given block. +pub trait ExecuteBlock { + /// Execute the given `block`. + /// + /// This will execute all extrinsics in the block and check that the resulting header is correct. + /// + /// # Panic + /// + /// Panics when an extrinsics panics or the resulting header doesn't match the expected header. + fn execute_block(block: Block); +} + +/// Off-chain computation trait. +/// +/// Implementing this trait on a module allows you to perform long-running tasks +/// that make (by default) validators generate transactions that feed results +/// of those long-running computations back on chain. +/// +/// NOTE: This function runs off-chain, so it can access the block state, +/// but cannot preform any alterations. More specifically alterations are +/// not forbidden, but they are not persisted in any way after the worker +/// has finished. +#[impl_trait_for_tuples::impl_for_tuples(30)] +pub trait OffchainWorker { + /// This function is being called after every block import (when fully synced). + /// + /// Implement this and use any of the `Offchain` `sp_io` set of APIs + /// to perform off-chain computations, calls and submit transactions + /// with results to trigger any on-chain changes. + /// Any state alterations are lost and are not persisted. + fn offchain_worker(_n: BlockNumber) {} +} + diff --git a/frame/support/src/traits/randomness.rs b/frame/support/src/traits/randomness.rs new file mode 100644 index 0000000000000..865893f99b393 --- /dev/null +++ b/frame/support/src/traits/randomness.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with on-chain randomness. + +/// A trait that is able to provide randomness. +/// +/// Being a deterministic blockchain, real randomness is difficult to come by, different +/// implementations of this trait will provide different security guarantees. At best, +/// this will be randomness which was hard to predict a long time ago, but that has become +/// easy to predict recently. +pub trait Randomness { + /// Get the most recently determined random seed, along with the time in the past + /// since when it was determinable by chain observers. + /// + /// `subject` is a context identifier and allows you to get a different result to + /// other callers of this function; use it like `random(&b"my context"[..])`. + /// + /// NOTE: The returned seed should only be used to distinguish commitments made before + /// the returned block number. If the block number is too early (i.e. commitments were + /// made afterwards), then ensure no further commitments may be made and repeatedly + /// call this on later blocks until the block number returned is later than the latest + /// commitment. + fn random(subject: &[u8]) -> (Output, BlockNumber); + + /// Get the basic random seed. + /// + /// In general you won't want to use this, but rather `Self::random` which allows + /// you to give a subject for the random result and whose value will be + /// independently low-influence random from any other such seeds. + /// + /// NOTE: The returned seed should only be used to distinguish commitments made before + /// the returned block number. If the block number is too early (i.e. commitments were + /// made afterwards), then ensure no further commitments may be made and repeatedly + /// call this on later blocks until the block number returned is later than the latest + /// commitment. + fn random_seed() -> (Output, BlockNumber) { + Self::random(&[][..]) + } +} diff --git a/frame/support/src/traits/schedule.rs b/frame/support/src/traits/schedule.rs new file mode 100644 index 0000000000000..58e4c419f2813 --- /dev/null +++ b/frame/support/src/traits/schedule.rs @@ -0,0 +1,133 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits and associated utilities for scheduling dispatchables in FRAME. + +use sp_std::{prelude::*, fmt::Debug}; +use codec::{Encode, Decode, Codec, EncodeLike}; +use sp_runtime::{RuntimeDebug, DispatchError}; + +/// Information relating to the period of a scheduled task. First item is the length of the +/// period and the second is the number of times it should be executed in total before the task +/// is considered finished and removed. +pub type Period = (BlockNumber, u32); + +/// Priority with which a call is scheduled. It's just a linear amount with lowest values meaning +/// higher priority. +pub type Priority = u8; + +/// The dispatch time of a scheduled task. +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum DispatchTime { + /// At specified block. + At(BlockNumber), + /// After specified number of blocks. + After(BlockNumber), +} + +/// The highest priority. We invert the value so that normal sorting will place the highest +/// priority at the beginning of the list. +pub const HIGHEST_PRIORITY: Priority = 0; +/// Anything of this value or lower will definitely be scheduled on the block that they ask for, even +/// if it breaches the `MaximumWeight` limitation. +pub const HARD_DEADLINE: Priority = 63; +/// The lowest priority. Most stuff should be around here. +pub const LOWEST_PRIORITY: Priority = 255; + +/// A type that can be used as a scheduler. +pub trait Anon { + /// An address which can be used for removing a scheduled task. + type Address: Codec + Clone + Eq + EncodeLike + Debug; + + /// Schedule a dispatch to happen at the beginning of some block in the future. + /// + /// This is not named. + fn schedule( + when: DispatchTime, + maybe_periodic: Option>, + priority: Priority, + origin: Origin, + call: Call + ) -> Result; + + /// Cancel a scheduled task. If periodic, then it will cancel all further instances of that, + /// also. + /// + /// Will return an error if the `address` is invalid. + /// + /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. + /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. + /// + /// NOTE2: This will not work to cancel periodic tasks after their initial execution. For + /// that, you must name the task explicitly using the `Named` trait. + fn cancel(address: Self::Address) -> Result<(), ()>; + + /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed + /// only if it is executed *before* the currently scheduled block. For periodic tasks, + /// this dispatch is guaranteed to succeed only before the *initial* execution; for + /// others, use `reschedule_named`. + /// + /// Will return an error if the `address` is invalid. + fn reschedule( + address: Self::Address, + when: DispatchTime, + ) -> Result; + + /// Return the next dispatch time for a given task. + /// + /// Will return an error if the `address` is invalid. + fn next_dispatch_time(address: Self::Address) -> Result; +} + +/// A type that can be used as a scheduler. +pub trait Named { + /// An address which can be used for removing a scheduled task. + type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug; + + /// Schedule a dispatch to happen at the beginning of some block in the future. + /// + /// - `id`: The identity of the task. This must be unique and will return an error if not. + fn schedule_named( + id: Vec, + when: DispatchTime, + maybe_periodic: Option>, + priority: Priority, + origin: Origin, + call: Call + ) -> Result; + + /// Cancel a scheduled, named task. If periodic, then it will cancel all further instances + /// of that, also. + /// + /// Will return an error if the `id` is invalid. + /// + /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. + /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. + fn cancel_named(id: Vec) -> Result<(), ()>; + + /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed + /// only if it is executed *before* the currently scheduled block. + fn reschedule_named( + id: Vec, + when: DispatchTime, + ) -> Result; + + /// Return the next dispatch time for a given task. + /// + /// Will return an error if the `id` is invalid. + fn next_dispatch_time(id: Vec) -> Result; +} diff --git a/frame/support/src/traits/storage.rs b/frame/support/src/traits/storage.rs new file mode 100644 index 0000000000000..82e9c1e7a60f6 --- /dev/null +++ b/frame/support/src/traits/storage.rs @@ -0,0 +1,45 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for encoding data related to pallet's storage items. + +/// An instance of a pallet in the storage. +/// +/// It is required that these instances are unique, to support multiple instances per pallet in the same runtime! +/// +/// E.g. for module MyModule default instance will have prefix "MyModule" and other instances +/// "InstanceNMyModule". +pub trait Instance: 'static { + /// Unique module prefix. E.g. "InstanceNMyModule" or "MyModule" + const PREFIX: &'static str; +} + +/// An instance of a storage in a pallet. +/// +/// Define an instance for an individual storage inside a pallet. +/// The pallet prefix is used to isolate the storage between pallets, and the storage prefix is +/// used to isolate storages inside a pallet. +/// +/// NOTE: These information can be used to define storages in pallet such as a `StorageMap` which +/// can use keys after `twox_128(pallet_prefix())++twox_128(STORAGE_PREFIX)` +pub trait StorageInstance { + /// Prefix of a pallet to isolate it from other pallets. + fn pallet_prefix() -> &'static str; + + /// Prefix given to a storage to isolate from other storages in the pallet. + const STORAGE_PREFIX: &'static str; +} diff --git a/frame/support/src/traits/stored_map.rs b/frame/support/src/traits/stored_map.rs new file mode 100644 index 0000000000000..10964541ab32b --- /dev/null +++ b/frame/support/src/traits/stored_map.rs @@ -0,0 +1,141 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits and associated datatypes for managing abstract stored values. + +use codec::FullCodec; +use sp_runtime::traits::StoredMapError; +use crate::storage::StorageMap; +use crate::traits::misc::HandleLifetime; + +/// An abstraction of a value stored within storage, but possibly as part of a larger composite +/// item. +pub trait StoredMap { + /// Get the item, or its default if it doesn't yet exist; we make no distinction between the + /// two. + fn get(k: &K) -> T; + + /// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is + /// returned. It is removed or reset to default value if it has been mutated to `None` + fn try_mutate_exists>( + k: &K, + f: impl FnOnce(&mut Option) -> Result, + ) -> Result; + + // Everything past here has a default implementation. + + /// Mutate the item. + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> Result { + Self::mutate_exists(k, |maybe_account| match maybe_account { + Some(ref mut account) => f(account), + x @ None => { + let mut account = Default::default(); + let r = f(&mut account); + *x = Some(account); + r + } + }) + } + + /// Mutate the item, removing or resetting to default value if it has been mutated to `None`. + /// + /// This is infallible as long as the value does not get destroyed. + fn mutate_exists( + k: &K, + f: impl FnOnce(&mut Option) -> R, + ) -> Result { + Self::try_mutate_exists(k, |x| -> Result { Ok(f(x)) }) + } + + /// Set the item to something new. + fn insert(k: &K, t: T) -> Result<(), StoredMapError> { Self::mutate(k, |i| *i = t) } + + /// Remove the item or otherwise replace it with its default value; we don't care which. + fn remove(k: &K) -> Result<(), StoredMapError> { Self::mutate_exists(k, |x| *x = None) } +} + +/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this +/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this +/// would break the ability to have custom impls of `StoredValue`. The other workaround is to +/// implement it directly in the macro. +/// +/// This form has the advantage that two additional types are provides, `Created` and `Removed`, +/// which are both generic events that can be tied to handlers to do something in the case of being +/// about to create an account where one didn't previously exist (at all; not just where it used to +/// be the default value), or where the account is being removed or reset back to the default value +/// where previously it did exist (though may have been in a default state). This works well with +/// system module's `CallOnCreatedAccount` and `CallKillAccount`. +pub struct StorageMapShim(sp_std::marker::PhantomData<(S, L, K, T)>); +impl< + S: StorageMap, + L: HandleLifetime, + K: FullCodec, + T: FullCodec + Default, +> StoredMap for StorageMapShim { + fn get(k: &K) -> T { S::get(k) } + fn insert(k: &K, t: T) -> Result<(), StoredMapError> { + if !S::contains_key(&k) { + L::created(k)?; + } + S::insert(k, t); + Ok(()) + } + fn remove(k: &K) -> Result<(), StoredMapError> { + if S::contains_key(&k) { + L::killed(&k)?; + S::remove(k); + } + Ok(()) + } + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> Result { + if !S::contains_key(&k) { + L::created(k)?; + } + Ok(S::mutate(k, f)) + } + fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> Result { + S::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let r = f(maybe_value); + let exists = maybe_value.is_some(); + + if !existed && exists { + L::created(k)?; + } else if existed && !exists { + L::killed(k)?; + } + Ok(r) + }) + } + fn try_mutate_exists>( + k: &K, + f: impl FnOnce(&mut Option) -> Result, + ) -> Result { + S::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let r = f(maybe_value)?; + let exists = maybe_value.is_some(); + + if !existed && exists { + L::created(k).map_err(E::from)?; + } else if existed && !exists { + L::killed(k).map_err(E::from)?; + } + Ok(r) + }) + } +} diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs new file mode 100644 index 0000000000000..82af5dbade8f7 --- /dev/null +++ b/frame/support/src/traits/tokens.rs @@ -0,0 +1,28 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for working with tokens and their associated datastructures. + +pub mod fungible; +pub mod fungibles; +pub mod currency; +pub mod imbalance; +mod misc; +pub use misc::{ + WithdrawConsequence, DepositConsequence, ExistenceRequirement, BalanceStatus, WithdrawReasons, +}; +pub use imbalance::Imbalance; diff --git a/frame/support/src/traits/tokens/currency.rs b/frame/support/src/traits/tokens/currency.rs new file mode 100644 index 0000000000000..567ca44aa78c7 --- /dev/null +++ b/frame/support/src/traits/tokens/currency.rs @@ -0,0 +1,208 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The Currency trait and associated types. + +use sp_std::fmt::Debug; +use sp_runtime::traits::MaybeSerializeDeserialize; +use crate::dispatch::{DispatchResult, DispatchError}; +use super::misc::{Balance, WithdrawReasons, ExistenceRequirement}; +use super::imbalance::{Imbalance, SignedImbalance}; + + +mod reservable; +pub use reservable::ReservableCurrency; +mod lockable; +pub use lockable::{LockableCurrency, VestingSchedule, LockIdentifier}; + +/// Abstraction over a fungible assets system. +pub trait Currency { + /// The balance of an account. + type Balance: Balance + MaybeSerializeDeserialize + Debug; + + /// The opaque token type for an imbalance. This is returned by unbalanced operations + /// and must be dealt with. It may be dropped but cannot be cloned. + type PositiveImbalance: Imbalance; + + /// The opaque token type for an imbalance. This is returned by unbalanced operations + /// and must be dealt with. It may be dropped but cannot be cloned. + type NegativeImbalance: Imbalance; + + // PUBLIC IMMUTABLES + + /// The combined balance of `who`. + fn total_balance(who: &AccountId) -> Self::Balance; + + /// Same result as `slash(who, value)` (but without the side-effects) assuming there are no + /// balance changes in the meantime and only the reserved balance is not taken into account. + fn can_slash(who: &AccountId, value: Self::Balance) -> bool; + + /// The total amount of issuance in the system. + fn total_issuance() -> Self::Balance; + + /// The minimum balance any single account may have. This is equivalent to the `Balances` module's + /// `ExistentialDeposit`. + fn minimum_balance() -> Self::Balance; + + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn burn(amount: Self::Balance) -> Self::PositiveImbalance; + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(amount: Self::Balance) -> Self::NegativeImbalance; + + /// Produce a pair of imbalances that cancel each other out exactly. + /// + /// This is just the same as burning and issuing the same amount and has no effect on the + /// total issuance. + fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) { + (Self::burn(amount.clone()), Self::issue(amount)) + } + + /// The 'free' balance of a given account. + /// + /// This is the only balance that matters in terms of most operations on tokens. It alone + /// is used to determine the balance when in the contract execution environment. When this + /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is + /// deleted: specifically `FreeBalance`. + /// + /// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets + /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. + fn free_balance(who: &AccountId) -> Self::Balance; + + /// Returns `Ok` iff the account is able to make a withdrawal of the given amount + /// for the given reason. Basically, it's just a dry-run of `withdraw`. + /// + /// `Err(...)` with the reason why not otherwise. + fn ensure_can_withdraw( + who: &AccountId, + _amount: Self::Balance, + reasons: WithdrawReasons, + new_balance: Self::Balance, + ) -> DispatchResult; + + // PUBLIC MUTABLES (DANGEROUS) + + /// Transfer some liquid free balance to another staker. + /// + /// This is a very high-level function. It will ensure all appropriate fees are paid + /// and no imbalance in the system remains. + fn transfer( + source: &AccountId, + dest: &AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult; + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// The resulting imbalance is the first item of the tuple returned. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then a non-zero second item will be returned. + fn slash( + who: &AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance); + + /// Mints `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, nothing is done and an Err returned. + fn deposit_into_existing( + who: &AccountId, + value: Self::Balance + ) -> Result; + + /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on + /// success. + fn resolve_into_existing( + who: &AccountId, + value: Self::NegativeImbalance, + ) -> Result<(), Self::NegativeImbalance> { + let v = value.peek(); + match Self::deposit_into_existing(who, v) { + Ok(opposite) => Ok(drop(value.offset(opposite))), + _ => Err(value), + } + } + + /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created. + /// + /// Infallible. + fn deposit_creating( + who: &AccountId, + value: Self::Balance, + ) -> Self::PositiveImbalance; + + /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on + /// success. + fn resolve_creating( + who: &AccountId, + value: Self::NegativeImbalance, + ) { + let v = value.peek(); + drop(value.offset(Self::deposit_creating(who, v))); + } + + /// Removes some free balance from `who` account for `reason` if possible. If `liveness` is + /// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining. + /// + /// This checks any locks, vesting, and liquidity requirements. If the removal is not possible, + /// then it returns `Err`. + /// + /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value + /// is `value`. + fn withdraw( + who: &AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> Result; + + /// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success. + fn settle( + who: &AccountId, + value: Self::PositiveImbalance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> Result<(), Self::PositiveImbalance> { + let v = value.peek(); + match Self::withdraw(who, v, reasons, liveness) { + Ok(opposite) => Ok(drop(value.offset(opposite))), + _ => Err(value), + } + } + + /// Ensure an account's free balance equals some value; this will create the account + /// if needed. + /// + /// Returns a signed imbalance and status to indicate if the account was successfully updated or update + /// has led to killing of the account. + fn make_free_balance_be( + who: &AccountId, + balance: Self::Balance, + ) -> SignedImbalance; +} diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs new file mode 100644 index 0000000000000..ed3d1cf46362b --- /dev/null +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -0,0 +1,104 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The lockable currency trait and some associated types. + +use crate::dispatch::DispatchResult; +use crate::traits::misc::Get; +use super::Currency; +use super::super::misc::WithdrawReasons; + +/// An identifier for a lock. Used for disambiguating different locks so that +/// they can be individually replaced or removed. +pub type LockIdentifier = [u8; 8]; + +/// A currency whose accounts can have liquidity restrictions. +pub trait LockableCurrency: Currency { + /// The quantity used to denote time; usually just a `BlockNumber`. + type Moment; + + /// The maximum number of locks a user should have on their account. + type MaxLocks: Get; + + /// Create a new balance lock on account `who`. + /// + /// If the new lock is valid (i.e. not already expired), it will push the struct to + /// the `Locks` vec in storage. Note that you can lock more funds than a user has. + /// + /// If the lock `id` already exists, this will update it. + fn set_lock( + id: LockIdentifier, + who: &AccountId, + amount: Self::Balance, + reasons: WithdrawReasons, + ); + + /// Changes a balance lock (selected by `id`) so that it becomes less liquid in all + /// parameters or creates a new one if it does not exist. + /// + /// Calling `extend_lock` on an existing lock `id` differs from `set_lock` in that it + /// applies the most severe constraints of the two, while `set_lock` replaces the lock + /// with the new parameters. As in, `extend_lock` will set: + /// - maximum `amount` + /// - bitwise mask of all `reasons` + fn extend_lock( + id: LockIdentifier, + who: &AccountId, + amount: Self::Balance, + reasons: WithdrawReasons, + ); + + /// Remove an existing lock. + fn remove_lock( + id: LockIdentifier, + who: &AccountId, + ); +} + +/// A vesting schedule over a currency. This allows a particular currency to have vesting limits +/// applied to it. +pub trait VestingSchedule { + /// The quantity used to denote time; usually just a `BlockNumber`. + type Moment; + + /// The currency that this schedule applies to. + type Currency: Currency; + + /// Get the amount that is currently being vested and cannot be transferred out of this account. + /// Returns `None` if the account has no vesting schedule. + fn vesting_balance(who: &AccountId) -> Option<>::Balance>; + + /// Adds a vesting schedule to a given account. + /// + /// If there already exists a vesting schedule for the given account, an `Err` is returned + /// and nothing is updated. + /// + /// Is a no-op if the amount to be vested is zero. + /// + /// NOTE: This doesn't alter the free balance of the account. + fn add_vesting_schedule( + who: &AccountId, + locked: >::Balance, + per_block: >::Balance, + starting_block: Self::Moment, + ) -> DispatchResult; + + /// Remove a vesting schedule for a given account. + /// + /// NOTE: This doesn't alter the free balance of the account. + fn remove_vesting_schedule(who: &AccountId); +} diff --git a/frame/support/src/traits/tokens/currency/reservable.rs b/frame/support/src/traits/tokens/currency/reservable.rs new file mode 100644 index 0000000000000..14ea1d3a16fb6 --- /dev/null +++ b/frame/support/src/traits/tokens/currency/reservable.rs @@ -0,0 +1,83 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The reservable currency trait. + +use super::Currency; +use super::super::misc::BalanceStatus; +use crate::dispatch::{DispatchResult, DispatchError}; + +/// A currency where funds can be reserved from the user. +pub trait ReservableCurrency: Currency { + /// Same result as `reserve(who, value)` (but without the side-effects) assuming there + /// are no balance changes in the meantime. + fn can_reserve(who: &AccountId, value: Self::Balance) -> bool; + + /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If the reserve balance of `who` + /// is less than `value`, then a non-zero second item will be returned. + fn slash_reserved( + who: &AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance); + + /// The amount of the balance of a given account that is externally reserved; this can still get + /// slashed, but gets slashed last of all. + /// + /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens + /// that are still 'owned' by the account holder, but which are suspendable. + /// + /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' + /// is deleted: specifically, `ReservedBalance`. + /// + /// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets + /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. + fn reserved_balance(who: &AccountId) -> Self::Balance; + + /// Moves `value` from balance to reserved balance. + /// + /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will + /// be returned to notify of this. This is different behavior than `unreserve`. + fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult; + + /// Moves up to `value` from reserved balance to free balance. This function cannot fail. + /// + /// As much funds up to `value` will be moved as possible. If the reserve balance of `who` + /// is less than `value`, then the remaining amount will be returned. + /// + /// # NOTES + /// + /// - This is different from `reserve`. + /// - If the remaining reserved balance is less than `ExistentialDeposit`, it will + /// invoke `on_reserved_too_low` and could reap the account. + fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance; + + /// Moves up to `value` from reserved balance of account `slashed` to balance of account + /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be + /// returned. Funds will be placed in either the `free` balance or the `reserved` balance, + /// depending on the `status`. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Ok(non_zero)` will be returned. + fn repatriate_reserved( + slashed: &AccountId, + beneficiary: &AccountId, + value: Self::Balance, + status: BalanceStatus, + ) -> Result; +} diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs new file mode 100644 index 0000000000000..8e6b4ace3464a --- /dev/null +++ b/frame/support/src/traits/tokens/fungible.rs @@ -0,0 +1,218 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for dealing with a single fungible token class and any associated types. + +use super::*; +use sp_runtime::traits::Saturating; +use crate::traits::misc::Get; +use crate::dispatch::{DispatchResult, DispatchError}; +use super::misc::{DepositConsequence, WithdrawConsequence, Balance}; + +mod balanced; +mod imbalance; +pub use balanced::{Balanced, Unbalanced}; +pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf}; + +/// Trait for providing balance-inspection access to a fungible asset. +pub trait Inspect { + /// Scalar type for representing balance of an account. + type Balance: Balance; + /// The total amount of issuance in the system. + fn total_issuance() -> Self::Balance; + /// The minimum balance any single account may have. + fn minimum_balance() -> Self::Balance; + /// Get the balance of `who`. + fn balance(who: &AccountId) -> Self::Balance; + /// Returns `true` if the balance of `who` may be increased by `amount`. + fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence; + /// Returns `Failed` if the balance of `who` may not be decreased by `amount`, otherwise + /// the consequence. + fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence; +} + +/// Trait for providing an ERC-20 style fungible asset. +pub trait Mutate: Inspect { + /// Increase the balance of `who` by `amount`. + fn deposit(who: &AccountId, amount: Self::Balance) -> DispatchResult; + /// Attempt to reduce the balance of `who` by `amount`. + fn withdraw(who: &AccountId, amount: Self::Balance) -> Result; + /// Transfer funds from one account into another. + fn transfer( + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + ) -> Result { + let extra = Self::can_withdraw(&source, amount).into_result()?; + Self::can_deposit(&dest, amount.saturating_add(extra)).into_result()?; + let actual = Self::withdraw(source, amount)?; + debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed"); + match Self::deposit(dest, actual) { + Ok(_) => Ok(actual), + Err(err) => { + debug_assert!(false, "can_deposit returned true previously; qed"); + // attempt to return the funds back to source + let revert = Self::deposit(source, actual); + debug_assert!(revert.is_ok(), "withdrew funds previously; qed"); + Err(err) + } + } + } +} + +/// Trait for providing a fungible asset which can only be transferred. +pub trait Transfer: Inspect { + /// Transfer funds from one account into another. + fn transfer( + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + ) -> Result; +} + +/// Trait for providing a fungible asset which can be reserved. +pub trait Reserve: Inspect { + /// Amount of funds held in reserve by `who`. + fn reserved_balance(who: &AccountId) -> Self::Balance; + /// Amount of funds held in total by `who`. + fn total_balance(who: &AccountId) -> Self::Balance { + Self::reserved_balance(who).saturating_add(Self::balance(who)) + } + /// Check to see if some `amount` of funds may be reserved on the account of `who`. + fn can_reserve(who: &AccountId, amount: Self::Balance) -> bool; + /// Reserve some funds in an account. + fn reserve(who: &AccountId, amount: Self::Balance) -> DispatchResult; + /// Unreserve some funds in an account. + fn unreserve(who: &AccountId, amount: Self::Balance) -> DispatchResult; + /// Transfer reserved funds into another account. + fn repatriate_reserved( + who: &AccountId, + amount: Self::Balance, + status: BalanceStatus, + ) -> DispatchResult; +} + +pub struct ItemOf< + F: fungibles::Inspect, + A: Get<>::AssetId>, + AccountId, +>( + sp_std::marker::PhantomData<(F, A, AccountId)> +); + +impl< + F: fungibles::Inspect, + A: Get<>::AssetId>, + AccountId, +> Inspect for ItemOf { + type Balance = >::Balance; + fn total_issuance() -> Self::Balance { + >::total_issuance(A::get()) + } + fn minimum_balance() -> Self::Balance { + >::minimum_balance(A::get()) + } + fn balance(who: &AccountId) -> Self::Balance { + >::balance(A::get(), who) + } + fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence { + >::can_deposit(A::get(), who, amount) + } + fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { + >::can_withdraw(A::get(), who, amount) + } +} + +impl< + F: fungibles::Mutate, + A: Get<>::AssetId>, + AccountId, +> Mutate for ItemOf { + fn deposit(who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::deposit(A::get(), who, amount) + } + fn withdraw(who: &AccountId, amount: Self::Balance) -> Result { + >::withdraw(A::get(), who, amount) + } +} + +impl< + F: fungibles::Transfer, + A: Get<>::AssetId>, + AccountId, +> Transfer for ItemOf { + fn transfer(source: &AccountId, dest: &AccountId, amount: Self::Balance) + -> Result + { + >::transfer(A::get(), source, dest, amount) + } +} + +impl< + F: fungibles::Reserve, + A: Get<>::AssetId>, + AccountId, +> Reserve for ItemOf { + fn reserved_balance(who: &AccountId) -> Self::Balance { + >::reserved_balance(A::get(), who) + } + fn total_balance(who: &AccountId) -> Self::Balance { + >::total_balance(A::get(), who) + } + fn can_reserve(who: &AccountId, amount: Self::Balance) -> bool { + >::can_reserve(A::get(), who, amount) + } + fn reserve(who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::reserve(A::get(), who, amount) + } + fn unreserve(who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::unreserve(A::get(), who, amount) + } + fn repatriate_reserved( + who: &AccountId, + amount: Self::Balance, + status: BalanceStatus, + ) -> DispatchResult { + >::repatriate_reserved(A::get(), who, amount, status) + } +} + +impl< + F: fungibles::Unbalanced, + A: Get<>::AssetId>, + AccountId, +> Unbalanced for ItemOf { + fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult { + >::set_balance(A::get(), who, amount) + } + fn set_total_issuance(amount: Self::Balance) -> () { + >::set_total_issuance(A::get(), amount) + } + fn decrease_balance(who: &AccountId, amount: Self::Balance) -> Result { + >::decrease_balance(A::get(), who, amount) + } + fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { + >::decrease_balance_at_most(A::get(), who, amount) + } + fn increase_balance(who: &AccountId, amount: Self::Balance) -> Result { + >::increase_balance(A::get(), who, amount) + } + fn increase_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance { + >::increase_balance_at_most(A::get(), who, amount) + } +} + diff --git a/frame/support/src/traits/tokens/fungible/balanced.rs b/frame/support/src/traits/tokens/fungible/balanced.rs new file mode 100644 index 0000000000000..514a6f4c18814 --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/balanced.rs @@ -0,0 +1,363 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The trait and associated types for sets of fungible tokens that manage total issuance without +//! requiring atomic balanced operations. + +use super::*; +use sp_std::marker::PhantomData; +use sp_runtime::{TokenError, traits::{CheckedAdd, Zero}}; +use super::super::Imbalance as ImbalanceT; +use crate::traits::misc::{SameOrOther, TryDrop}; +use crate::dispatch::{DispatchResult, DispatchError}; + +/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the +/// total supply is maintained automatically. +/// +/// This is auto-implemented when a token class has `Unbalanced` implemented. +pub trait Balanced: Inspect { + /// The type for managing what happens when an instance of `Debt` is dropped without being used. + type OnDropDebt: HandleImbalanceDrop; + /// The type for managing what happens when an instance of `Credit` is dropped without being + /// used. + type OnDropCredit: HandleImbalanceDrop; + + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn rescind(amount: Self::Balance) -> DebtOf; + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(amount: Self::Balance) -> CreditOf; + + /// Produce a pair of imbalances that cancel each other out exactly. + /// + /// This is just the same as burning and issuing the same amount and has no effect on the + /// total issuance. + fn pair(amount: Self::Balance) + -> (DebtOf, CreditOf) + { + (Self::rescind(amount), Self::issue(amount)) + } + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// The resulting imbalance is the first item of the tuple returned. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then a non-zero second item will be returned. + fn slash( + who: &AccountId, + amount: Self::Balance, + ) -> (CreditOf, Self::Balance); + + /// Mints exactly `value` into the account of `who`. + /// + /// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it + /// the account doesn't yet exist and it isn't possible to create it under the current + /// circumstances and with `value` in it. + fn deposit( + who: &AccountId, + value: Self::Balance, + ) -> Result, DispatchError>; + + /// Removes `value` balance from `who` account if possible. + /// + /// If the removal is not possible, then it returns `Err` and nothing is changed. + /// + /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value + /// is no less than `value`. It may be more in the case that removing it reduced it below + /// `Self::minimum_balance()`. + fn withdraw( + who: &AccountId, + value: Self::Balance, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DispatchError>; + + /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` + /// cannot be countered, then nothing is changed and the original `credit` is returned in an + /// `Err`. + /// + /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must + /// already exist for this to succeed. + fn resolve( + who: &AccountId, + credit: CreditOf, + ) -> Result<(), CreditOf> { + let v = credit.peek(); + let debt = match Self::deposit(who, v) { + Err(_) => return Err(credit), + Ok(d) => d, + }; + let result = credit.offset(debt).try_drop(); + debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); + Ok(()) + } + + /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` + /// cannot be countered, then nothing is changed and the original `debt` is returned in an + /// `Err`. + fn settle( + who: &AccountId, + debt: DebtOf, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DebtOf> { + let amount = debt.peek(); + let credit = match Self::withdraw(who, amount) { + Err(_) => return Err(debt), + Ok(d) => d, + }; + match credit.offset(debt) { + SameOrOther::None => Ok(CreditOf::::zero()), + SameOrOther::Same(dust) => Ok(dust), + SameOrOther::Other(rest) => { + debug_assert!(false, "ok withdraw return must be at least debt value; qed"); + Err(rest) + } + } + } +} + +/// A fungible token class where the balance can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Set the balance of `who` to `amount`. If this cannot be done for some reason (e.g. + /// because the account cannot be created or an overflow) then an `Err` is returned. + fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Set the total issuance to `amount`. + fn set_total_issuance(amount: Self::Balance); + + /// Reduce the balance of `who` by `amount`. If it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance. + /// + /// Minimum balance will be respected and the returned imbalance may be up to + /// `Self::minimum_balance() - 1` greater than `amount`. + fn decrease_balance(who: &AccountId, amount: Self::Balance) + -> Result + { + let old_balance = Self::balance(who); + let (mut new_balance, mut amount) = if old_balance < amount { + Err(TokenError::NoFunds)? + } else { + (old_balance - amount, amount) + }; + if new_balance < Self::minimum_balance() { + amount = amount.saturating_add(new_balance); + new_balance = Zero::zero(); + } + // Defensive only - this should not fail now. + Self::set_balance(who, new_balance)?; + Ok(amount) + } + + /// Reduce the balance of `who` by the most that is possible, up to `amount`. + /// + /// Minimum balance will be respected and the returned imbalance may be up to + /// `Self::minimum_balance() - 1` greater than `amount`. + /// + /// Return the imbalance by which the account was reduced. + fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance) + -> Self::Balance + { + let old_balance = Self::balance(who); + let (mut new_balance, mut amount) = if old_balance < amount { + (Zero::zero(), old_balance) + } else { + (old_balance - amount, amount) + }; + let minimum_balance = Self::minimum_balance(); + if new_balance < minimum_balance { + amount = amount.saturating_add(new_balance); + new_balance = Zero::zero(); + } + let mut r = Self::set_balance(who, new_balance); + if r.is_err() { + // Some error, probably because we tried to destroy an account which cannot be destroyed. + if new_balance.is_zero() && amount >= minimum_balance { + new_balance = minimum_balance; + amount -= minimum_balance; + r = Self::set_balance(who, new_balance); + } + if r.is_err() { + // Still an error. Apparently it's not possible to reduce at all. + amount = Zero::zero(); + } + } + amount + } + + /// Increase the balance of `who` by `amount`. If it cannot be increased by that amount + /// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance. + /// + /// Minimum balance will be respected and an error will be returned if + /// `amount < Self::minimum_balance()` when the account of `who` is zero. + fn increase_balance(who: &AccountId, amount: Self::Balance) + -> Result + { + let old_balance = Self::balance(who); + let new_balance = old_balance.checked_add(&amount).ok_or(TokenError::Overflow)?; + if new_balance < Self::minimum_balance() { + Err(TokenError::BelowMinimum)? + } + if old_balance != new_balance { + Self::set_balance(who, new_balance)?; + } + Ok(amount) + } + + /// Increase the balance of `who` by the most that is possible, up to `amount`. + /// + /// Minimum balance will be respected and the returned imbalance will be zero in the case that + /// `amount < Self::minimum_balance()`. + /// + /// Return the imbalance by which the account was increased. + fn increase_balance_at_most(who: &AccountId, amount: Self::Balance) + -> Self::Balance + { + let old_balance = Self::balance(who); + let mut new_balance = old_balance.saturating_add(amount); + let mut amount = new_balance - old_balance; + if new_balance < Self::minimum_balance() { + new_balance = Zero::zero(); + amount = Zero::zero(); + } + if old_balance == new_balance || Self::set_balance(who, new_balance).is_ok() { + amount + } else { + Zero::zero() + } + } +} + +/// Simple handler for an imbalance drop which increases the total issuance of the system by the +/// imbalance amount. Used for leftover debt. +pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for IncreaseIssuance +{ + fn handle(amount: U::Balance) { + U::set_total_issuance(U::total_issuance().saturating_add(amount)) + } +} + +/// Simple handler for an imbalance drop which decreases the total issuance of the system by the +/// imbalance amount. Used for leftover credit. +pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for DecreaseIssuance +{ + fn handle(amount: U::Balance) { + U::set_total_issuance(U::total_issuance().saturating_sub(amount)) + } +} + +/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed. +/// +/// Basically means that funds in someone's account have been removed and not yet placed anywhere +/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply +/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts. +type Credit = Imbalance< + >::Balance, + DecreaseIssuance, + IncreaseIssuance, +>; + +/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed. +/// +/// Basically means that there are funds in someone's account whose origin is as yet unaccounted +/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply +/// will be accordingly increased to ensure it equals the sum of the balances of all accounts. +type Debt = Imbalance< + >::Balance, + IncreaseIssuance, + DecreaseIssuance, +>; + +/// Create some `Credit` item. Only for internal use. +fn credit>( + amount: U::Balance, +) -> Credit { + Imbalance::new(amount) +} + +/// Create some `Debt` item. Only for internal use. +fn debt>( + amount: U::Balance, +) -> Debt { + Imbalance::new(amount) +} + +impl> Balanced for U { + type OnDropCredit = DecreaseIssuance; + type OnDropDebt = IncreaseIssuance; + fn rescind(amount: Self::Balance) -> Debt { + let old = U::total_issuance(); + let new = old.saturating_sub(amount); + U::set_total_issuance(new); + debt(old - new) + } + fn issue(amount: Self::Balance) -> Credit { + let old = U::total_issuance(); + let new = old.saturating_add(amount); + U::set_total_issuance(new); + credit(new - old) + } + fn slash( + who: &AccountId, + amount: Self::Balance, + ) -> (Credit, Self::Balance) { + let slashed = U::decrease_balance_at_most(who, amount); + // `slashed` could be less than, greater than or equal to `amount`. + // If slashed == amount, it means the account had at least amount in it and it could all be + // removed without a problem. + // If slashed > amount, it means the account had more than amount in it, but not enough more + // to push it over minimum_balance. + // If slashed < amount, it means the account didn't have enough in it to be reduced by + // `amount` without being destroyed. + (credit(slashed), amount.saturating_sub(slashed)) + } + fn deposit( + who: &AccountId, + amount: Self::Balance + ) -> Result, DispatchError> { + let increase = U::increase_balance(who, amount)?; + Ok(debt(increase)) + } + fn withdraw( + who: &AccountId, + amount: Self::Balance, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DispatchError> { + let decrease = U::decrease_balance(who, amount)?; + Ok(credit(decrease)) + } +} diff --git a/frame/support/src/traits/tokens/fungible/imbalance.rs b/frame/support/src/traits/tokens/fungible/imbalance.rs new file mode 100644 index 0000000000000..c084fa97fbec0 --- /dev/null +++ b/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -0,0 +1,162 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The imbalance type and its associates, which handles keeps everything adding up properly with +//! unbalanced operations. + +use super::*; +use sp_std::marker::PhantomData; +use sp_runtime::traits::Zero; +use super::misc::Balance; +use super::balanced::Balanced; +use crate::traits::misc::{TryDrop, SameOrOther}; +use super::super::Imbalance as ImbalanceT; + +/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or +/// debt (positive) imbalance. +pub trait HandleImbalanceDrop { + /// Some something with the imbalance's value which is being dropped. + fn handle(amount: Balance); +} + +/// An imbalance in the system, representing a divergence of recorded token supply from the sum of +/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing +/// into an account, settling from an account or altering the supply). +/// +/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. +#[must_use] +pub struct Imbalance< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> { + amount: B, + _phantom: PhantomData<(OnDrop, OppositeOnDrop)>, +} + +impl< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop +> Drop for Imbalance { + fn drop(&mut self) { + if !self.amount.is_zero() { + OnDrop::handle(self.amount) + } + } +} + +impl< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> TryDrop for Imbalance { + /// Drop an instance cleanly. Only works if its value represents "no-operation". + fn try_drop(self) -> Result<(), Self> { + self.drop_zero() + } +} + +impl< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> Default for Imbalance { + fn default() -> Self { + Self::zero() + } +} + +impl< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> Imbalance { + pub(crate) fn new(amount: B) -> Self { + Self { amount, _phantom: PhantomData } + } +} + +impl< + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> ImbalanceT for Imbalance { + type Opposite = Imbalance; + + fn zero() -> Self { + Self { amount: Zero::zero(), _phantom: PhantomData } + } + + fn drop_zero(self) -> Result<(), Self> { + if self.amount.is_zero() { + sp_std::mem::forget(self); + Ok(()) + } else { + Err(self) + } + } + + fn split(self, amount: B) -> (Self, Self) { + let first = self.amount.min(amount); + let second = self.amount - first; + sp_std::mem::forget(self); + (Imbalance::new(first), Imbalance::new(second)) + } + fn merge(mut self, other: Self) -> Self { + self.amount = self.amount.saturating_add(other.amount); + sp_std::mem::forget(other); + self + } + fn subsume(&mut self, other: Self) { + self.amount = self.amount.saturating_add(other.amount); + sp_std::mem::forget(other); + } + fn offset(self, other: Imbalance) + -> SameOrOther> + { + let (a, b) = (self.amount, other.amount); + sp_std::mem::forget((self, other)); + + if a == b { + SameOrOther::None + } else if a > b { + SameOrOther::Same(Imbalance::new(a - b)) + } else { + SameOrOther::Other(Imbalance::::new(b - a)) + } + } + fn peek(&self) -> B { + self.amount + } +} + +/// Imbalance implying that the total_issuance value is less than the sum of all account balances. +pub type DebtOf = Imbalance< + >::Balance, + // This will generally be implemented by increasing the total_issuance value. + >::OnDropDebt, + >::OnDropCredit, +>; + +/// Imbalance implying that the total_issuance value is greater than the sum of all account balances. +pub type CreditOf = Imbalance< + >::Balance, + // This will generally be implemented by decreasing the total_issuance value. + >::OnDropCredit, + >::OnDropDebt, +>; diff --git a/frame/support/src/traits/tokens/fungibles.rs b/frame/support/src/traits/tokens/fungibles.rs new file mode 100644 index 0000000000000..8f6779881169c --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles.rs @@ -0,0 +1,143 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The traits for sets of fungible tokens and any associated types. + +use super::*; +use crate::dispatch::{DispatchError, DispatchResult}; +use super::misc::{AssetId, Balance}; +use sp_runtime::traits::Saturating; + +mod balanced; +pub use balanced::{Balanced, Unbalanced}; +mod imbalance; +pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf}; + +/// Trait for providing balance-inspection access to a set of named fungible assets. +pub trait Inspect { + /// Means of identifying one asset class from another. + type AssetId: AssetId; + /// Scalar type for representing balance of an account. + type Balance: Balance; + /// The total amount of issuance in the system. + fn total_issuance(asset: Self::AssetId) -> Self::Balance; + /// The minimum balance any single account may have. + fn minimum_balance(asset: Self::AssetId) -> Self::Balance; + /// Get the `asset` balance of `who`. + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + /// Returns `true` if the `asset` balance of `who` may be increased by `amount`. + fn can_deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> DepositConsequence; + /// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise + /// the consequence. + fn can_withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence; +} + +/// Trait for providing a set of named fungible assets which can be created and destroyed. +pub trait Mutate: Inspect { + /// Attempt to increase the `asset` balance of `who` by `amount`. + /// + /// If not possible then don't do anything. Possible reasons for failure include: + /// - Minimum balance not met. + /// - Account cannot be created (e.g. because there is no provider reference and/or the asset + /// isn't considered worth anything). + /// + /// Since this is an operation which should be possible to take alone, if successful it will + /// increase the overall supply of the underlying token. + fn deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Attempt to reduce the `asset` balance of `who` by `amount`. + /// + /// If not possible then don't do anything. Possible reasons for failure include: + /// - Less funds in the account than `amount` + /// - Liquidity requirements (locks, reservations) prevent the funds from being removed + /// - Operation would require destroying the account and it is required to stay alive (e.g. + /// because it's providing a needed provider reference). + /// + /// Since this is an operation which should be possible to take alone, if successful it will + /// reduce the overall supply of the underlying token. + /// + /// Due to minimum balance requirements, it's possible that the amount withdrawn could be up to + /// `Self::minimum_balance() - 1` more than the `amount`. The total amount withdrawn is returned + /// in an `Ok` result. This may be safely ignored if you don't mind the overall supply reducing. + fn withdraw(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> Result; + + /// Transfer funds from one account into another. + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + ) -> Result { + let extra = Self::can_withdraw(asset, &source, amount).into_result()?; + Self::can_deposit(asset, &dest, amount.saturating_add(extra)).into_result()?; + let actual = Self::withdraw(asset, source, amount)?; + debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed"); + match Self::deposit(asset, dest, actual) { + Ok(_) => Ok(actual), + Err(err) => { + debug_assert!(false, "can_deposit returned true previously; qed"); + // attempt to return the funds back to source + let revert = Self::deposit(asset, source, actual); + debug_assert!(revert.is_ok(), "withdrew funds previously; qed"); + Err(err) + } + } + } +} + +/// Trait for providing a set of named fungible assets which can only be transferred. +pub trait Transfer: Inspect { + /// Transfer funds from one account into another. + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + ) -> Result; +} + +/// Trait for providing a set of named fungible assets which can be reserved. +pub trait Reserve: Inspect { + /// Amount of funds held in reserve. + fn reserved_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + + /// Amount of funds held in reserve. + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance; + + /// Check to see if some `amount` of `asset` may be reserved on the account of `who`. + fn can_reserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> bool; + + /// Reserve some funds in an account. + fn reserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Unreserve some funds in an account. + fn unreserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Transfer reserved funds into another account. + fn repatriate_reserved( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + status: BalanceStatus, + ) -> DispatchResult; +} diff --git a/frame/support/src/traits/tokens/fungibles/balanced.rs b/frame/support/src/traits/tokens/fungibles/balanced.rs new file mode 100644 index 0000000000000..0af07228e0100 --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/balanced.rs @@ -0,0 +1,375 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The trait and associated types for sets of fungible tokens that manage total issuance without +//! requiring atomic balanced operations. + +use super::*; +use sp_std::marker::PhantomData; +use sp_runtime::{TokenError, traits::{Zero, CheckedAdd}}; +use sp_arithmetic::traits::Saturating; +use crate::dispatch::{DispatchError, DispatchResult}; +use crate::traits::misc::{SameOrOther, TryDrop}; + +/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the +/// total supply is maintained automatically. +/// +/// This is auto-implemented when a token class has `Unbalanced` implemented. +pub trait Balanced: Inspect { + type OnDropDebt: HandleImbalanceDrop; + type OnDropCredit: HandleImbalanceDrop; + + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn rescind(asset: Self::AssetId, amount: Self::Balance) -> DebtOf; + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(asset: Self::AssetId, amount: Self::Balance) -> CreditOf; + + /// Produce a pair of imbalances that cancel each other out exactly. + /// + /// This is just the same as burning and issuing the same amount and has no effect on the + /// total issuance. + fn pair(asset: Self::AssetId, amount: Self::Balance) + -> (DebtOf, CreditOf) + { + (Self::rescind(asset, amount), Self::issue(asset, amount)) + } + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// The resulting imbalance is the first item of the tuple returned. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then a non-zero second item will be returned. + fn slash( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> (CreditOf, Self::Balance); + + /// Mints exactly `value` into the `asset` account of `who`. + /// + /// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it + /// the account doesn't yet exist and it isn't possible to create it under the current + /// circumstances and with `value` in it. + fn deposit( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + ) -> Result, DispatchError>; + + /// Removes `value` free `asset` balance from `who` account if possible. + /// + /// If the removal is not possible, then it returns `Err` and nothing is changed. + /// + /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value + /// is no less than `value`. It may be more in the case that removing it reduced it below + /// `Self::minimum_balance()`. + fn withdraw( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DispatchError>; + + /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` + /// cannot be countered, then nothing is changed and the original `credit` is returned in an + /// `Err`. + /// + /// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must + /// already exist for this to succeed. + fn resolve( + who: &AccountId, + credit: CreditOf, + ) -> Result<(), CreditOf> { + let v = credit.peek(); + let debt = match Self::deposit(credit.asset(), who, v) { + Err(_) => return Err(credit), + Ok(d) => d, + }; + if let Ok(result) = credit.offset(debt) { + let result = result.try_drop(); + debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed"); + } else { + debug_assert!(false, "debt.asset is credit.asset; qed"); + } + Ok(()) + } + + /// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt` + /// cannot be countered, then nothing is changed and the original `debt` is returned in an + /// `Err`. + fn settle( + who: &AccountId, + debt: DebtOf, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DebtOf> { + let amount = debt.peek(); + let asset = debt.asset(); + let credit = match Self::withdraw(asset, who, amount) { + Err(_) => return Err(debt), + Ok(d) => d, + }; + match credit.offset(debt) { + Ok(SameOrOther::None) => Ok(CreditOf::::zero(asset)), + Ok(SameOrOther::Same(dust)) => Ok(dust), + Ok(SameOrOther::Other(rest)) => { + debug_assert!(false, "ok withdraw return must be at least debt value; qed"); + Err(rest) + } + Err(_) => { + debug_assert!(false, "debt.asset is credit.asset; qed"); + Ok(CreditOf::::zero(asset)) + } + } + } +} + +/// A fungible token class where the balance can be set arbitrarily. +/// +/// **WARNING** +/// Do not use this directly unless you want trouble, since it allows you to alter account balances +/// without keeping the issuance up to date. It has no safeguards against accidentally creating +/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to +/// use. +pub trait Unbalanced: Inspect { + /// Set the `asset` balance of `who` to `amount`. If this cannot be done for some reason (e.g. + /// because the account cannot be created or an overflow) then an `Err` is returned. + fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult; + + /// Set the total issuance of `asset` to `amount`. + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance); + + /// Reduce the `asset` balance of `who` by `amount`. If it cannot be reduced by that amount for + /// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance. + /// + /// Minimum balance will be respected and the returned imbalance may be up to + /// `Self::minimum_balance() - 1` greater than `amount`. + fn decrease_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> Result + { + let old_balance = Self::balance(asset, who); + let (mut new_balance, mut amount) = if old_balance < amount { + Err(TokenError::NoFunds)? + } else { + (old_balance - amount, amount) + }; + if new_balance < Self::minimum_balance(asset) { + amount = amount.saturating_add(new_balance); + new_balance = Zero::zero(); + } + // Defensive only - this should not fail now. + Self::set_balance(asset, who, new_balance)?; + Ok(amount) + } + + /// Reduce the `asset` balance of `who` by the most that is possible, up to `amount`. + /// + /// Minimum balance will be respected and the returned imbalance may be up to + /// `Self::minimum_balance() - 1` greater than `amount`. + /// + /// Return the imbalance by which the account was reduced. + fn decrease_balance_at_most(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> Self::Balance + { + let old_balance = Self::balance(asset, who); + let (mut new_balance, mut amount) = if old_balance < amount { + (Zero::zero(), old_balance) + } else { + (old_balance - amount, amount) + }; + let minimum_balance = Self::minimum_balance(asset); + if new_balance < minimum_balance { + amount = amount.saturating_add(new_balance); + new_balance = Zero::zero(); + } + let mut r = Self::set_balance(asset, who, new_balance); + if r.is_err() { + // Some error, probably because we tried to destroy an account which cannot be destroyed. + if new_balance.is_zero() && amount >= minimum_balance { + new_balance = minimum_balance; + amount -= minimum_balance; + r = Self::set_balance(asset, who, new_balance); + } + if r.is_err() { + // Still an error. Apparently it's not possible to reduce at all. + amount = Zero::zero(); + } + } + amount + } + + /// Increase the `asset` balance of `who` by `amount`. If it cannot be increased by that amount + /// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance. + /// + /// Minimum balance will be respected and an error will be returned if + /// `amount < Self::minimum_balance()` when the account of `who` is zero. + fn increase_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> Result + { + let old_balance = Self::balance(asset, who); + let new_balance = old_balance.checked_add(&amount).ok_or(TokenError::Overflow)?; + if new_balance < Self::minimum_balance(asset) { + Err(TokenError::BelowMinimum)? + } + if old_balance != new_balance { + Self::set_balance(asset, who, new_balance)?; + } + Ok(amount) + } + + /// Increase the `asset` balance of `who` by the most that is possible, up to `amount`. + /// + /// Minimum balance will be respected and the returned imbalance will be zero in the case that + /// `amount < Self::minimum_balance()`. + /// + /// Return the imbalance by which the account was increased. + fn increase_balance_at_most(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) + -> Self::Balance + { + let old_balance = Self::balance(asset, who); + let mut new_balance = old_balance.saturating_add(amount); + let mut amount = new_balance - old_balance; + if new_balance < Self::minimum_balance(asset) { + new_balance = Zero::zero(); + amount = Zero::zero(); + } + if old_balance == new_balance || Self::set_balance(asset, who, new_balance).is_ok() { + amount + } else { + Zero::zero() + } + } +} + +/// Simple handler for an imbalance drop which increases the total issuance of the system by the +/// imbalance amount. Used for leftover debt. +pub struct IncreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for IncreaseIssuance +{ + fn handle(asset: U::AssetId, amount: U::Balance) { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)) + } +} + +/// Simple handler for an imbalance drop which decreases the total issuance of the system by the +/// imbalance amount. Used for leftover credit. +pub struct DecreaseIssuance(PhantomData<(AccountId, U)>); +impl> HandleImbalanceDrop + for DecreaseIssuance +{ + fn handle(asset: U::AssetId, amount: U::Balance) { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)) + } +} + +/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed. +/// +/// Basically means that funds in someone's account have been removed and not yet placed anywhere +/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply +/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts. +type Credit = Imbalance< + >::AssetId, + >::Balance, + DecreaseIssuance, + IncreaseIssuance, +>; + +/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed. +/// +/// Basically means that there are funds in someone's account whose origin is as yet unaccounted +/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply +/// will be accordingly increased to ensure it equals the sum of the balances of all accounts. +type Debt = Imbalance< + >::AssetId, + >::Balance, + IncreaseIssuance, + DecreaseIssuance, +>; + +/// Create some `Credit` item. Only for internal use. +fn credit>( + asset: U::AssetId, + amount: U::Balance, +) -> Credit { + Imbalance::new(asset, amount) +} + +/// Create some `Debt` item. Only for internal use. +fn debt>( + asset: U::AssetId, + amount: U::Balance, +) -> Debt { + Imbalance::new(asset, amount) +} + +impl> Balanced for U { + type OnDropCredit = DecreaseIssuance; + type OnDropDebt = IncreaseIssuance; + fn rescind(asset: Self::AssetId, amount: Self::Balance) -> Debt { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount)); + debt(asset, amount) + } + fn issue(asset: Self::AssetId, amount: Self::Balance) -> Credit { + U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount)); + credit(asset, amount) + } + fn slash( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> (Credit, Self::Balance) { + let slashed = U::decrease_balance_at_most(asset, who, amount); + // `slashed` could be less than, greater than or equal to `amount`. + // If slashed == amount, it means the account had at least amount in it and it could all be + // removed without a problem. + // If slashed > amount, it means the account had more than amount in it, but not enough more + // to push it over minimum_balance. + // If slashed < amount, it means the account didn't have enough in it to be reduced by + // `amount` without being destroyed. + (credit(asset, slashed), amount.saturating_sub(slashed)) + } + fn deposit( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance + ) -> Result, DispatchError> { + let increase = U::increase_balance(asset, who, amount)?; + Ok(debt(asset, increase)) + } + fn withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + //TODO: liveness: ExistenceRequirement, + ) -> Result, DispatchError> { + let decrease = U::decrease_balance(asset, who, amount)?; + Ok(credit(asset, decrease)) + } +} diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs new file mode 100644 index 0000000000000..ecc415cb568bd --- /dev/null +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -0,0 +1,169 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The imbalance type and its associates, which handles keeps everything adding up properly with +//! unbalanced operations. + +use super::*; +use sp_std::marker::PhantomData; +use sp_runtime::traits::Zero; +use super::fungibles::{AssetId, Balance}; +use super::balanced::Balanced; +use crate::traits::misc::{TryDrop, SameOrOther}; + +/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or +/// debt (positive) imbalance. +pub trait HandleImbalanceDrop { + fn handle(asset: AssetId, amount: Balance); +} + +/// An imbalance in the system, representing a divergence of recorded token supply from the sum of +/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing +/// into an account, settling from an account or altering the supply). +/// +/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. +#[must_use] +pub struct Imbalance< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> { + asset: A, + amount: B, + _phantom: PhantomData<(OnDrop, OppositeOnDrop)>, +} + +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop +> Drop for Imbalance { + fn drop(&mut self) { + if !self.amount.is_zero() { + OnDrop::handle(self.asset, self.amount) + } + } +} + +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> TryDrop for Imbalance { + /// Drop an instance cleanly. Only works if its value represents "no-operation". + fn try_drop(self) -> Result<(), Self> { + self.drop_zero() + } +} + +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, +> Imbalance { + pub fn zero(asset: A) -> Self { + Self { asset, amount: Zero::zero(), _phantom: PhantomData } + } + + pub(crate) fn new(asset: A, amount: B) -> Self { + Self { asset, amount, _phantom: PhantomData } + } + + pub fn drop_zero(self) -> Result<(), Self> { + if self.amount.is_zero() { + sp_std::mem::forget(self); + Ok(()) + } else { + Err(self) + } + } + + pub fn split(self, amount: B) -> (Self, Self) { + let first = self.amount.min(amount); + let second = self.amount - first; + let asset = self.asset; + sp_std::mem::forget(self); + (Imbalance::new(asset, first), Imbalance::new(asset, second)) + } + pub fn merge(mut self, other: Self) -> Result { + if self.asset == other.asset { + self.amount = self.amount.saturating_add(other.amount); + sp_std::mem::forget(other); + Ok(self) + } else { + Err((self, other)) + } + } + pub fn subsume(&mut self, other: Self) -> Result<(), Self> { + if self.asset == other.asset { + self.amount = self.amount.saturating_add(other.amount); + sp_std::mem::forget(other); + Ok(()) + } else { + Err(other) + } + } + pub fn offset(self, other: Imbalance) -> Result< + SameOrOther>, + (Self, Imbalance), + > { + if self.asset == other.asset { + let (a, b) = (self.amount, other.amount); + let asset = self.asset; + sp_std::mem::forget((self, other)); + + if a == b { + Ok(SameOrOther::None) + } else if a > b { + Ok(SameOrOther::Same(Imbalance::new(asset, a - b))) + } else { + Ok(SameOrOther::Other(Imbalance::::new(asset, b - a))) + } + } else { + Err((self, other)) + } + } + pub fn peek(&self) -> B { + self.amount + } + + pub fn asset(&self) -> A { + self.asset + } +} + +/// Imbalance implying that the total_issuance value is less than the sum of all account balances. +pub type DebtOf = Imbalance< + >::AssetId, + >::Balance, + // This will generally be implemented by increasing the total_issuance value. + >::OnDropDebt, + >::OnDropCredit, +>; + +/// Imbalance implying that the total_issuance value is greater than the sum of all account balances. +pub type CreditOf = Imbalance< + >::AssetId, + >::Balance, + // This will generally be implemented by decreasing the total_issuance value. + >::OnDropCredit, + >::OnDropDebt, +>; diff --git a/frame/support/src/traits/tokens/imbalance.rs b/frame/support/src/traits/tokens/imbalance.rs new file mode 100644 index 0000000000000..9652b9a0275a1 --- /dev/null +++ b/frame/support/src/traits/tokens/imbalance.rs @@ -0,0 +1,174 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The imbalance trait type and its associates, which handles keeps everything adding up properly +//! with unbalanced operations. + +use sp_std::ops::Div; +use sp_runtime::traits::Saturating; +use crate::traits::misc::{TryDrop, SameOrOther}; + +mod split_two_ways; +mod signed_imbalance; +mod on_unbalanced; +pub use split_two_ways::SplitTwoWays; +pub use signed_imbalance::SignedImbalance; +pub use on_unbalanced::OnUnbalanced; + +/// A trait for a not-quite Linear Type that tracks an imbalance. +/// +/// Functions that alter account balances return an object of this trait to +/// express how much account balances have been altered in aggregate. If +/// dropped, the currency system will take some default steps to deal with +/// the imbalance (`balances` module simply reduces or increases its +/// total issuance). Your module should generally handle it in some way, +/// good practice is to do so in a configurable manner using an +/// `OnUnbalanced` type for each situation in which your module needs to +/// handle an imbalance. +/// +/// Imbalances can either be Positive (funds were added somewhere without +/// being subtracted elsewhere - e.g. a reward) or Negative (funds deducted +/// somewhere without an equal and opposite addition - e.g. a slash or +/// system fee payment). +/// +/// Since they are unsigned, the actual type is always Positive or Negative. +/// The trait makes no distinction except to define the `Opposite` type. +/// +/// New instances of zero value can be created (`zero`) and destroyed +/// (`drop_zero`). +/// +/// Existing instances can be `split` and merged either consuming `self` with +/// `merge` or mutating `self` with `subsume`. If the target is an `Option`, +/// then `maybe_merge` and `maybe_subsume` might work better. Instances can +/// also be `offset` with an `Opposite` that is less than or equal to in value. +/// +/// You can always retrieve the raw balance value using `peek`. +#[must_use] +pub trait Imbalance: Sized + TryDrop + Default { + /// The oppositely imbalanced type. They come in pairs. + type Opposite: Imbalance; + + /// The zero imbalance. Can be destroyed with `drop_zero`. + fn zero() -> Self; + + /// Drop an instance cleanly. Only works if its `self.value()` is zero. + fn drop_zero(self) -> Result<(), Self>; + + /// Consume `self` and return two independent instances; the first + /// is guaranteed to be at most `amount` and the second will be the remainder. + fn split(self, amount: Balance) -> (Self, Self); + + /// Consume `self` and return two independent instances; the amounts returned will be in + /// approximately the same ratio as `first`:`second`. + /// + /// NOTE: This requires up to `first + second` room for a multiply, and `first + second` should + /// fit into a `u32`. Overflow will safely saturate in both cases. + fn ration(self, first: u32, second: u32) -> (Self, Self) + where Balance: From + Saturating + Div + { + let total: u32 = first.saturating_add(second); + if total == 0 { return (Self::zero(), Self::zero()) } + let amount1 = self.peek().saturating_mul(first.into()) / total.into(); + self.split(amount1) + } + + /// Consume self and add its two components, defined by the first component's balance, + /// element-wise to two pre-existing Imbalances. + /// + /// A convenient replacement for `split` and `merge`. + fn split_merge(self, amount: Balance, others: (Self, Self)) -> (Self, Self) { + let (a, b) = self.split(amount); + (a.merge(others.0), b.merge(others.1)) + } + + /// Consume self and add its two components, defined by the ratio `first`:`second`, + /// element-wise to two pre-existing Imbalances. + /// + /// A convenient replacement for `split` and `merge`. + fn ration_merge(self, first: u32, second: u32, others: (Self, Self)) -> (Self, Self) + where Balance: From + Saturating + Div + { + let (a, b) = self.ration(first, second); + (a.merge(others.0), b.merge(others.1)) + } + + /// Consume self and add its two components, defined by the first component's balance, + /// element-wise into two pre-existing Imbalance refs. + /// + /// A convenient replacement for `split` and `subsume`. + fn split_merge_into(self, amount: Balance, others: &mut (Self, Self)) { + let (a, b) = self.split(amount); + others.0.subsume(a); + others.1.subsume(b); + } + + /// Consume self and add its two components, defined by the ratio `first`:`second`, + /// element-wise to two pre-existing Imbalances. + /// + /// A convenient replacement for `split` and `merge`. + fn ration_merge_into(self, first: u32, second: u32, others: &mut (Self, Self)) + where Balance: From + Saturating + Div + { + let (a, b) = self.ration(first, second); + others.0.subsume(a); + others.1.subsume(b); + } + + /// Consume `self` and an `other` to return a new instance that combines + /// both. + fn merge(self, other: Self) -> Self; + + /// Consume self to mutate `other` so that it combines both. Just like `subsume`, only with + /// reversed arguments. + fn merge_into(self, other: &mut Self) { + other.subsume(self) + } + + /// Consume `self` and maybe an `other` to return a new instance that combines + /// both. + fn maybe_merge(self, other: Option) -> Self { + if let Some(o) = other { + self.merge(o) + } else { + self + } + } + + /// Consume an `other` to mutate `self` into a new instance that combines + /// both. + fn subsume(&mut self, other: Self); + + /// Maybe consume an `other` to mutate `self` into a new instance that combines + /// both. + fn maybe_subsume(&mut self, other: Option) { + if let Some(o) = other { + self.subsume(o) + } + } + + /// Consume self and along with an opposite counterpart to return + /// a combined result. + /// + /// Returns `Ok` along with a new instance of `Self` if this instance has a + /// greater value than the `other`. Otherwise returns `Err` with an instance of + /// the `Opposite`. In both cases the value represents the combination of `self` + /// and `other`. + fn offset(self, other: Self::Opposite)-> SameOrOther; + + /// The raw value of self. + fn peek(&self) -> Balance; +} diff --git a/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs b/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs new file mode 100644 index 0000000000000..f3ecc14308e74 --- /dev/null +++ b/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Trait for handling imbalances. + +use crate::traits::misc::TryDrop; + +/// Handler for when some currency "account" decreased in balance for +/// some reason. +/// +/// The only reason at present for an increase would be for validator rewards, but +/// there may be other reasons in the future or for other chains. +/// +/// Reasons for decreases include: +/// +/// - Someone got slashed. +/// - Someone paid for a transaction to be included. +pub trait OnUnbalanced { + /// Handler for some imbalances. The different imbalances might have different origins or + /// meanings, dependent on the context. Will default to simply calling on_unbalanced for all + /// of them. Infallible. + fn on_unbalanceds(amounts: impl Iterator) where Imbalance: crate::traits::Imbalance { + Self::on_unbalanced(amounts.fold(Imbalance::zero(), |i, x| x.merge(i))) + } + + /// Handler for some imbalance. Infallible. + fn on_unbalanced(amount: Imbalance) { + amount.try_drop().unwrap_or_else(Self::on_nonzero_unbalanced) + } + + /// Actually handle a non-zero imbalance. You probably want to implement this rather than + /// `on_unbalanced`. + fn on_nonzero_unbalanced(amount: Imbalance) { drop(amount); } +} + +impl OnUnbalanced for () {} diff --git a/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs b/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs new file mode 100644 index 0000000000000..e3523f86804fd --- /dev/null +++ b/frame/support/src/traits/tokens/imbalance/signed_imbalance.rs @@ -0,0 +1,69 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Convenience type for managing an imbalance whose sign is unknown. + +use codec::FullCodec; +use sp_std::fmt::Debug; +use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; +use crate::traits::misc::SameOrOther; +use super::super::imbalance::Imbalance; + +/// Either a positive or a negative imbalance. +pub enum SignedImbalance>{ + /// A positive imbalance (funds have been created but none destroyed). + Positive(PositiveImbalance), + /// A negative imbalance (funds have been destroyed but none created). + Negative(PositiveImbalance::Opposite), +} + +impl< + P: Imbalance, + N: Imbalance, + B: AtLeast32BitUnsigned + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default, +> SignedImbalance { + /// Create a `Positive` instance of `Self` whose value is zero. + pub fn zero() -> Self { + SignedImbalance::Positive(P::zero()) + } + + /// Drop `Self` if and only if it is equal to zero. Return `Err` with `Self` if not. + pub fn drop_zero(self) -> Result<(), Self> { + match self { + SignedImbalance::Positive(x) => x.drop_zero().map_err(SignedImbalance::Positive), + SignedImbalance::Negative(x) => x.drop_zero().map_err(SignedImbalance::Negative), + } + } + + /// Consume `self` and an `other` to return a new instance that combines + /// both. + pub fn merge(self, other: Self) -> Self { + match (self, other) { + (SignedImbalance::Positive(one), SignedImbalance::Positive(other)) => + SignedImbalance::Positive(one.merge(other)), + (SignedImbalance::Negative(one), SignedImbalance::Negative(other)) => + SignedImbalance::Negative(one.merge(other)), + (SignedImbalance::Positive(one), SignedImbalance::Negative(other)) => + match one.offset(other) { + SameOrOther::Same(positive) => SignedImbalance::Positive(positive), + SameOrOther::Other(negative) => SignedImbalance::Negative(negative), + SameOrOther::None => SignedImbalance::Positive(P::zero()), + }, + (one, other) => other.merge(one), + } + } +} diff --git a/frame/support/src/traits/tokens/imbalance/split_two_ways.rs b/frame/support/src/traits/tokens/imbalance/split_two_ways.rs new file mode 100644 index 0000000000000..f3f9870b62cd2 --- /dev/null +++ b/frame/support/src/traits/tokens/imbalance/split_two_ways.rs @@ -0,0 +1,51 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Means for splitting an imbalance into two and hanlding them differently. + +use sp_std::{ops::Div, marker::PhantomData}; +use sp_core::u32_trait::Value as U32; +use sp_runtime::traits::Saturating; +use super::super::imbalance::{Imbalance, OnUnbalanced}; + +/// Split an unbalanced amount two ways between a common divisor. +pub struct SplitTwoWays< + Balance, + Imbalance, + Part1, + Target1, + Part2, + Target2, +>(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>); + +impl< + Balance: From + Saturating + Div, + I: Imbalance, + Part1: U32, + Target1: OnUnbalanced, + Part2: U32, + Target2: OnUnbalanced, +> OnUnbalanced for SplitTwoWays +{ + fn on_nonzero_unbalanced(amount: I) { + let total: u32 = Part1::VALUE + Part2::VALUE; + let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into(); + let (imb1, imb2) = amount.split(amount1); + Target1::on_unbalanced(imb1); + Target2::on_unbalanced(imb2); + } +} diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs new file mode 100644 index 0000000000000..303d183cf2749 --- /dev/null +++ b/frame/support/src/traits/tokens/misc.rs @@ -0,0 +1,164 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Miscellaneous types. + +use codec::{Encode, Decode, FullCodec}; +use sp_core::RuntimeDebug; +use sp_arithmetic::traits::{Zero, AtLeast32BitUnsigned}; +use sp_runtime::TokenError; + +/// One of a number of consequences of withdrawing a fungible from an account. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum WithdrawConsequence { + /// Withdraw could not happen since the amount to be withdrawn is less than the total funds in + /// the account. + NoFunds, + /// The withdraw would mean the account dying when it needs to exist (usually because it is a + /// provider and there are consumer references on it). + WouldDie, + /// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist + /// on the system. + UnknownAsset, + /// There has been an underflow in the system. This is indicative of a corrupt state and + /// likely unrecoverable. + Underflow, + /// Not enough of the funds in the account are unavailable for withdrawal. + Frozen, + /// Account balance would reduce to zero, potentially destroying it. The parameter is the + /// amount of balance which is destroyed. + ReducedToZero(Balance), + /// Account continued in existence. + Success, +} + +impl WithdrawConsequence { + /// Convert the type into a `Result` with `TokenError` as the error or the additional `Balance` + /// by which the account will be reduced. + pub fn into_result(self) -> Result { + use WithdrawConsequence::*; + match self { + NoFunds => Err(TokenError::NoFunds), + WouldDie => Err(TokenError::WouldDie), + UnknownAsset => Err(TokenError::UnknownAsset), + Underflow => Err(TokenError::Underflow), + Frozen => Err(TokenError::Frozen), + ReducedToZero(result) => Ok(result), + Success => Ok(Zero::zero()), + } + } +} + +/// One of a number of consequences of withdrawing a fungible from an account. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum DepositConsequence { + /// Deposit couldn't happen due to the amount being too low. This is usually because the + /// account doesn't yet exist and the deposit wouldn't bring it to at least the minimum needed + /// for existance. + BelowMinimum, + /// Deposit cannot happen since the account cannot be created (usually because it's a consumer + /// and there exists no provider reference). + CannotCreate, + /// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist + /// on the system. + UnknownAsset, + /// An overflow would occur. This is practically unexpected, but could happen in test systems + /// with extremely small balance types or balances that approach the max value of the balance + /// type. + Overflow, + /// Account continued in existence. + Success, +} + +impl DepositConsequence { + /// Convert the type into a `Result` with `TokenError` as the error. + pub fn into_result(self) -> Result<(), TokenError> { + use DepositConsequence::*; + Err(match self { + BelowMinimum => TokenError::BelowMinimum, + CannotCreate => TokenError::CannotCreate, + UnknownAsset => TokenError::UnknownAsset, + Overflow => TokenError::Overflow, + Success => return Ok(()), + }) + } +} + +/// Simple boolean for whether an account needs to be kept in existence. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum ExistenceRequirement { + /// Operation must not result in the account going out of existence. + /// + /// Note this implies that if the account never existed in the first place, then the operation + /// may legitimately leave the account unchanged and still non-existent. + KeepAlive, + /// Operation may result in account going out of existence. + AllowDeath, +} + +/// Status of funds. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] +pub enum BalanceStatus { + /// Funds are free, as corresponding to `free` item in Balances. + Free, + /// Funds are reserved, as corresponding to `reserved` item in Balances. + Reserved, +} + +bitflags::bitflags! { + /// Reasons for moving funds out of an account. + #[derive(Encode, Decode)] + pub struct WithdrawReasons: i8 { + /// In order to pay for (system) transaction costs. + const TRANSACTION_PAYMENT = 0b00000001; + /// In order to transfer ownership. + const TRANSFER = 0b00000010; + /// In order to reserve some funds for a later return or repatriation. + const RESERVE = 0b00000100; + /// In order to pay some other (higher-level) fees. + const FEE = 0b00001000; + /// In order to tip a validator for transaction inclusion. + const TIP = 0b00010000; + } +} + +impl WithdrawReasons { + /// Choose all variants except for `one`. + /// + /// ```rust + /// # use frame_support::traits::WithdrawReasons; + /// # fn main() { + /// assert_eq!( + /// WithdrawReasons::FEE | WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE | WithdrawReasons::TIP, + /// WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT), + /// ); + /// # } + /// ``` + pub fn except(one: WithdrawReasons) -> WithdrawReasons { + let mut flags = Self::all(); + flags.toggle(one); + flags + } +} + +/// Simple amalgamation trait to collect together properties for an AssetId under one roof. +pub trait AssetId: FullCodec + Copy + Default + Eq + PartialEq {} +impl AssetId for T {} + +/// Simple amalgamation trait to collect together properties for a Balance under one roof. +pub trait Balance: AtLeast32BitUnsigned + FullCodec + Copy + Default {} +impl Balance for T {} diff --git a/frame/support/src/traits/validation.rs b/frame/support/src/traits/validation.rs new file mode 100644 index 0000000000000..900be7bb8e7e2 --- /dev/null +++ b/frame/support/src/traits/validation.rs @@ -0,0 +1,242 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with validation and validators. + +use sp_std::prelude::*; +use codec::{Codec, Decode}; +use sp_runtime::traits::{Convert, Zero}; +use sp_runtime::{BoundToRuntimeAppPublic, ConsensusEngineId, Percent, RuntimeAppPublic}; +use sp_staking::SessionIndex; +use crate::dispatch::Parameter; +use crate::weights::Weight; + +/// A trait for online node inspection in a session. +/// +/// Something that can give information about the current validator set. +pub trait ValidatorSet { + /// Type for representing validator id in a session. + type ValidatorId: Parameter; + /// A type for converting `AccountId` to `ValidatorId`. + type ValidatorIdOf: Convert>; + + /// Returns current session index. + fn session_index() -> SessionIndex; + + /// Returns the active set of validators. + fn validators() -> Vec; +} + +/// [`ValidatorSet`] combined with an identification. +pub trait ValidatorSetWithIdentification: ValidatorSet { + /// Full identification of `ValidatorId`. + type Identification: Parameter; + /// A type for converting `ValidatorId` to `Identification`. + type IdentificationOf: Convert>; +} + +/// A trait for finding the author of a block header based on the `PreRuntime` digests contained +/// within it. +pub trait FindAuthor { + /// Find the author of a block based on the pre-runtime digests. + fn find_author<'a, I>(digests: I) -> Option + where I: 'a + IntoIterator; +} + +impl FindAuthor for () { + fn find_author<'a, I>(_: I) -> Option + where I: 'a + IntoIterator + { + None + } +} + +/// A trait for verifying the seal of a header and returning the author. +pub trait VerifySeal { + /// Verify a header and return the author, if any. + fn verify_seal(header: &Header) -> Result, &'static str>; +} + +/// A session handler for specific key type. +pub trait OneSessionHandler: BoundToRuntimeAppPublic { + /// The key type expected. + type Key: Decode + Default + RuntimeAppPublic; + + /// The given validator set will be used for the genesis session. + /// It is guaranteed that the given validator set will also be used + /// for the second session, therefore the first call to `on_new_session` + /// should provide the same validator set. + fn on_genesis_session<'a, I: 'a>(validators: I) + where I: Iterator, ValidatorId: 'a; + + /// Session set has changed; act appropriately. Note that this can be called + /// before initialization of your module. + /// + /// `changed` is true when at least one of the session keys + /// or the underlying economic identities/distribution behind one the + /// session keys has changed, false otherwise. + /// + /// The `validators` are the validators of the incoming session, and `queued_validators` + /// will follow. + fn on_new_session<'a, I: 'a>( + changed: bool, + validators: I, + queued_validators: I, + ) where I: Iterator, ValidatorId: 'a; + + /// A notification for end of the session. + /// + /// Note it is triggered before any `SessionManager::end_session` handlers, + /// so we can still affect the validator set. + fn on_before_session_ending() {} + + /// A validator got disabled. Act accordingly until a new session begins. + fn on_disabled(_validator_index: usize); +} + +/// Something that can estimate at which block the next session rotation will happen (i.e. a new +/// session starts). +/// +/// The accuracy of the estimates is dependent on the specific implementation, but in order to get +/// the best estimate possible these methods should be called throughout the duration of the session +/// (rather than calling once and storing the result). +/// +/// This should be the same logical unit that dictates `ShouldEndSession` to the session module. No +/// assumptions are made about the scheduling of the sessions. +pub trait EstimateNextSessionRotation { + /// Return the average length of a session. + /// + /// This may or may not be accurate. + fn average_session_length() -> BlockNumber; + + /// Return an estimate of the current session progress. + /// + /// None should be returned if the estimation fails to come to an answer. + fn estimate_current_session_progress(now: BlockNumber) -> (Option, Weight); + + /// Return the block number at which the next session rotation is estimated to happen. + /// + /// None should be returned if the estimation fails to come to an answer. + fn estimate_next_session_rotation(now: BlockNumber) -> (Option, Weight); +} + +impl EstimateNextSessionRotation for () { + fn average_session_length() -> BlockNumber { + Zero::zero() + } + + fn estimate_current_session_progress(_: BlockNumber) -> (Option, Weight) { + (None, Zero::zero()) + } + + fn estimate_next_session_rotation(_: BlockNumber) -> (Option, Weight) { + (None, Zero::zero()) + } +} + +/// Something that can estimate at which block scheduling of the next session will happen (i.e when +/// we will try to fetch new validators). +/// +/// This only refers to the point when we fetch the next session details and not when we enact them +/// (for enactment there's `EstimateNextSessionRotation`). With `pallet-session` this should be +/// triggered whenever `SessionManager::new_session` is called. +/// +/// For example, if we are using a staking module this would be the block when the session module +/// would ask staking what the next validator set will be, as such this must always be implemented +/// by the session module. +pub trait EstimateNextNewSession { + /// Return the average length of a session. + /// + /// This may or may not be accurate. + fn average_session_length() -> BlockNumber; + + /// Return the block number at which the next new session is estimated to happen. + /// + /// None should be returned if the estimation fails to come to an answer. + fn estimate_next_new_session(_: BlockNumber) -> (Option, Weight); +} + +impl EstimateNextNewSession for () { + fn average_session_length() -> BlockNumber { + Zero::zero() + } + + fn estimate_next_new_session(_: BlockNumber) -> (Option, Weight) { + (None, Zero::zero()) + } +} + +/// Something which can compute and check proofs of +/// a historical key owner and return full identification data of that +/// key owner. +pub trait KeyOwnerProofSystem { + /// The proof of membership itself. + type Proof: Codec; + /// The full identification of a key owner and the stash account. + type IdentificationTuple: Codec; + + /// Prove membership of a key owner in the current block-state. + /// + /// This should typically only be called off-chain, since it may be + /// computationally heavy. + /// + /// Returns `Some` iff the key owner referred to by the given `key` is a + /// member of the current set. + fn prove(key: Key) -> Option; + + /// Check a proof of membership on-chain. Return `Some` iff the proof is + /// valid and recent enough to check. + fn check_proof(key: Key, proof: Self::Proof) -> Option; +} + +impl KeyOwnerProofSystem for () { + // The proof and identification tuples is any bottom type to guarantee that the methods of this + // implementation can never be called or return anything other than `None`. + type Proof = crate::Void; + type IdentificationTuple = crate::Void; + + fn prove(_key: Key) -> Option { + None + } + + fn check_proof(_key: Key, _proof: Self::Proof) -> Option { + None + } +} + +/// Trait to be used by block producing consensus engine modules to determine +/// how late the current block is (e.g. in a slot-based proposal mechanism how +/// many slots were skipped since the previous block). +pub trait Lateness { + /// Returns a generic measure of how late the current block is compared to + /// its parent. + fn lateness(&self) -> N; +} + +impl Lateness for () { + fn lateness(&self) -> N { + Zero::zero() + } +} + +/// Implementors of this trait provide information about whether or not some validator has +/// been registered with them. The [Session module](../../pallet_session/index.html) is an implementor. +pub trait ValidatorRegistration { + /// Returns true if the provided validator ID has been registered with the implementing runtime + /// module + fn is_registered(id: &ValidatorId) -> bool; +} diff --git a/frame/support/src/traits/voting.rs b/frame/support/src/traits/voting.rs new file mode 100644 index 0000000000000..b6913a182d30b --- /dev/null +++ b/frame/support/src/traits/voting.rs @@ -0,0 +1,88 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits and associated data structures concerned with voting, and moving between tokens and +//! votes. + +use sp_arithmetic::traits::{UniqueSaturatedInto, UniqueSaturatedFrom, SaturatedConversion}; + +/// A trait similar to `Convert` to convert values from `B` an abstract balance type +/// into u64 and back from u128. (This conversion is used in election and other places where complex +/// calculation over balance type is needed) +/// +/// Total issuance of the currency is passed in, but an implementation of this trait may or may not +/// use it. +/// +/// # WARNING +/// +/// the total issuance being passed in implies that the implementation must be aware of the fact +/// that its values can affect the outcome. This implies that if the vote value is dependent on the +/// total issuance, it should never ber written to storage for later re-use. +pub trait CurrencyToVote { + /// Convert balance to u64. + fn to_vote(value: B, issuance: B) -> u64; + + /// Convert u128 to balance. + fn to_currency(value: u128, issuance: B) -> B; +} + +/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128. +/// +/// The factor is the `(total_issuance / u64::max()).max(1)`, represented as u64. Let's look at the +/// important cases: +/// +/// If the chain's total issuance is less than u64::max(), this will always be 1, which means that +/// the factor will not have any effect. In this case, any account's balance is also less. Thus, +/// both of the conversions are basically an `as`; Any balance can fit in u64. +/// +/// If the chain's total issuance is more than 2*u64::max(), then a factor might be multiplied and +/// divided upon conversion. +pub struct U128CurrencyToVote; + +impl U128CurrencyToVote { + fn factor(issuance: u128) -> u128 { + (issuance / u64::max_value() as u128).max(1) + } +} + +impl CurrencyToVote for U128CurrencyToVote { + fn to_vote(value: u128, issuance: u128) -> u64 { + (value / Self::factor(issuance)).saturated_into() + } + + fn to_currency(value: u128, issuance: u128) -> u128 { + value.saturating_mul(Self::factor(issuance)) + } +} + + +/// A naive implementation of `CurrencyConvert` that simply saturates all conversions. +/// +/// # Warning +/// +/// This is designed to be used mostly for testing. Use with care, and think about the consequences. +pub struct SaturatingCurrencyToVote; + +impl + UniqueSaturatedFrom> CurrencyToVote for SaturatingCurrencyToVote { + fn to_vote(value: B, _: B) -> u64 { + value.unique_saturated_into() + } + + fn to_currency(value: u128, _: B) -> B { + B::unique_saturated_from(value) + } +} diff --git a/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr b/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr index a2998788736ac..8a6ee8b8f5045 100644 --- a/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr +++ b/frame/support/test/tests/pallet_ui/genesis_default_not_satisfied.stderr @@ -1,10 +1,10 @@ error[E0277]: the trait bound `pallet::GenesisConfig: std::default::Default` is not satisfied - --> $DIR/genesis_default_not_satisfied.rs:22:18 - | -22 | impl GenesisBuild for GenesisConfig {} - | ^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `pallet::GenesisConfig` - | - ::: $WORKSPACE/frame/support/src/traits.rs - | - | pub trait GenesisBuild: Default + MaybeSerializeDeserialize { - | ------- required by this bound in `GenesisBuild` + --> $DIR/genesis_default_not_satisfied.rs:22:18 + | +22 | impl GenesisBuild for GenesisConfig {} + | ^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `pallet::GenesisConfig` + | + ::: $WORKSPACE/frame/support/src/traits/hooks.rs + | + | pub trait GenesisBuild: Default + MaybeSerializeDeserialize { + | ------- required by this bound in `GenesisBuild` diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index ebf9eb38375bb..9d3ecd6f41f5d 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1201,11 +1201,22 @@ impl Pallet { Account::::get(who).consumers } - /// True if the account has some outstanding references. + /// True if the account has some outstanding consumer references. pub fn is_provider_required(who: &T::AccountId) -> bool { Account::::get(who).consumers != 0 } + /// True if the account has no outstanding consumer references or more than one provider. + pub fn can_dec_provider(who: &T::AccountId) -> bool { + let a = Account::::get(who); + a.consumers == 0 || a.providers > 1 + } + + /// True if the account has at least one provider reference. + pub fn can_inc_consumer(who: &T::AccountId) -> bool { + Account::::get(who).providers > 0 + } + /// Deposits an event into this block's event record. pub fn deposit_event(event: impl Into) { Self::deposit_event_indexed(&[], event.into()); diff --git a/frame/transaction-payment/src/payment.rs b/frame/transaction-payment/src/payment.rs index f84b19d78c297..7292ef4dfee7e 100644 --- a/frame/transaction-payment/src/payment.rs +++ b/frame/transaction-payment/src/payment.rs @@ -117,6 +117,7 @@ where // merge the imbalance caused by paying the fees and refunding parts of it again. let adjusted_paid = paid .offset(refund_imbalance) + .same() .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; // Call someone else to handle the imbalance (fee and tip separately) let imbalances = adjusted_paid.split(tip); diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 4fb7d9c7737fb..090c9781eb13d 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -474,6 +474,8 @@ pub enum DispatchError { ConsumerRemaining, /// There are no providers so the account cannot be created. NoProviders, + /// An error to do with tokens. + Token(TokenError), } /// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about @@ -532,6 +534,49 @@ impl From for DispatchError { } } +/// Description of what went wrong when trying to complete an operation on a token. +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum TokenError { + /// Funds are unavailable. + NoFunds, + /// Account that must exist would die. + WouldDie, + /// Account cannot exist with the funds that would be given. + BelowMinimum, + /// Account cannot be created. + CannotCreate, + /// The asset in question is unknown. + UnknownAsset, + /// Funds exist but are frozen. + Frozen, + /// An underflow would occur. + Underflow, + /// An overflow would occur. + Overflow, +} + +impl From for &'static str { + fn from(e: TokenError) -> &'static str { + match e { + TokenError::NoFunds => "Funds are unavailable", + TokenError::WouldDie => "Account that must exist would die", + TokenError::BelowMinimum => "Account cannot exist with the funds that would be given", + TokenError::CannotCreate => "Account cannot be created", + TokenError::UnknownAsset => "The asset in question is unknown", + TokenError::Frozen => "Funds exist but are frozen", + TokenError::Underflow => "An underflow would occur", + TokenError::Overflow => "An overflow would occur", + } + } +} + +impl From for DispatchError { + fn from(e: TokenError) -> DispatchError { + DispatchError::Token(e) + } +} + impl From<&'static str> for DispatchError { fn from(err: &'static str) -> DispatchError { DispatchError::Other(err) @@ -547,6 +592,7 @@ impl From for &'static str { DispatchError::Module { message, .. } => message.unwrap_or("Unknown module error"), DispatchError::ConsumerRemaining => "Consumer remaining", DispatchError::NoProviders => "No providers", + DispatchError::Token(e) => e.into(), } } } @@ -575,6 +621,10 @@ impl traits::Printable for DispatchError { } Self::ConsumerRemaining => "Consumer remaining".print(), Self::NoProviders => "No providers".print(), + Self::Token(e) => { + "Token error: ".print(); + <&'static str>::from(*e).print(); + } } } } @@ -599,7 +649,9 @@ impl PartialEq for DispatchError { (ConsumerRemaining, ConsumerRemaining) | (NoProviders, NoProviders) => true, + (Token(l), Token(r)) => l == r, (Other(l), Other(r)) => l == r, + ( Module { index: index_l, error: error_l, .. }, Module { index: index_r, error: error_r, .. },